Add help system, deployment docs, and improve setup

Introduces a new interactive help command with button navigation, adds a detailed DEPLOYMENT.md guide, and improves server setup validation and error handling. Updates command registration to include all 9 games, adds version reporting, enhances Docker deployment with a multi-platform script, and removes local .env files from the repo. Also refactors bot startup for better diagnostics and graceful shutdown.
This commit is contained in:
VinceC
2025-07-13 04:00:39 -05:00
parent 6b904d765d
commit a8e836fd5b
17 changed files with 1874 additions and 132 deletions

140
index.js
View File

@@ -1,6 +1,7 @@
const winston = require('winston');
const { createClient } = require('@supabase/supabase-js');
const Bot = require('./src/Bot');
const packageInfo = require('./package.json');
require('dotenv').config();
// Initialize logger
@@ -20,38 +21,155 @@ const logger = winston.createLogger({
]
});
// Display startup banner with version info
logger.info('🤖 ===== VRBattles Discord Bot Starting ===== 🤖');
logger.info(`📦 Version: ${packageInfo.version}`);
logger.info(`📝 Name: ${packageInfo.name}`);
logger.info(`📅 Started: ${new Date().toISOString()}`);
logger.info(`🌍 Environment: ${process.env.NODE_ENV || 'development'}`);
logger.info(`🐧 Platform: ${process.platform} ${process.arch}`);
logger.info(`⚡ Node.js: ${process.version}`);
// Check required environment variables
const requiredEnvVars = ['DISCORD_TOKEN', 'SUPABASE_URL', 'SUPABASE_KEY'];
const missingEnvVars = requiredEnvVars.filter(varName => !process.env[varName]);
if (missingEnvVars.length > 0) {
logger.error(`❌ Missing required environment variables: ${missingEnvVars.join(', ')}`);
process.exit(1);
}
logger.info('✅ Environment variables validated');
// Initialize Supabase client
logger.info('🔗 Initializing Supabase connection...');
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_KEY
);
// Initialize bot variable at module level
let bot = null;
// Initialize and start bot
const bot = new Bot(process.env.DISCORD_TOKEN, supabase, logger);
bot.start().catch(error => {
console.error('Failed to start bot:', error);
process.exit(1);
});
async function startBot() {
try {
// Test Supabase connection
logger.info('🧪 Testing Supabase connection...');
// Test 1: Read access to games table
const { data: games, error } = await supabase
.from('games')
.select('id, name, active')
.eq('active', true)
.limit(3);
if (error) {
logger.error('❌ Supabase read test failed:', {
message: error.message,
details: error.details,
hint: error.hint,
code: error.code
});
process.exit(1);
}
logger.info(`✅ Supabase read access OK - Found ${games.length} active games`);
logger.info(`📋 Sample games: ${games.map(g => g.name).join(', ')}`);
// Test 2: Write access to servers table (dry run)
logger.info('🧪 Testing database write permissions...');
const testGuildId = 'test_connection_' + Date.now();
try {
// Try to insert a test record
const { data: testServer, error: insertError } = await supabase
.from('servers')
.insert([{
discord_server_id: testGuildId,
server_name: 'Connection Test Server',
active: false // Mark as inactive so it doesn't interfere
}])
.select()
.single();
if (insertError) {
logger.error('❌ Database write test failed:', {
message: insertError.message,
details: error.details,
hint: insertError.hint,
code: insertError.code
});
logger.error('💡 This suggests a database permissions issue. Check row-level security policies.');
process.exit(1);
}
// Clean up test record
if (testServer?.id) {
await supabase
.from('servers')
.delete()
.eq('id', testServer.id);
}
logger.info('✅ Database write access OK');
} catch (writeError) {
logger.error('❌ Database write test exception:', {
message: writeError.message,
stack: writeError.stack
});
process.exit(1);
}
// Initialize and start bot
logger.info('🚀 Starting Discord bot...');
bot = new Bot(process.env.DISCORD_TOKEN, supabase, logger);
await bot.start();
return bot;
} catch (error) {
logger.error('❌ Failed to start bot:', {
message: error.message,
stack: error.stack,
name: error.name
});
process.exit(1);
}
}
startBot();
// Handle process termination
process.on('SIGINT', async () => {
console.log('Received SIGINT. Shutting down...');
logger.info('📥 Received SIGINT. Shutting down gracefully...');
try {
await bot.stop();
if (bot) {
await bot.stop();
}
logger.info('✅ Bot shutdown completed');
process.exit(0);
} catch (error) {
console.error('Error during shutdown:', error);
logger.error('Error during shutdown:', {
message: error.message,
stack: error.stack
});
process.exit(1);
}
});
process.on('SIGTERM', async () => {
console.log('Received SIGTERM. Shutting down...');
logger.info('📥 Received SIGTERM. Shutting down gracefully...');
try {
await bot.stop();
if (bot) {
await bot.stop();
}
logger.info('✅ Bot shutdown completed');
process.exit(0);
} catch (error) {
console.error('Error during shutdown:', error);
logger.error('Error during shutdown:', {
message: error.message,
stack: error.stack
});
process.exit(1);
}
});