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.
175 lines
5.4 KiB
JavaScript
175 lines
5.4 KiB
JavaScript
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
|
|
const logger = winston.createLogger({
|
|
level: 'debug',
|
|
format: winston.format.combine(
|
|
winston.format.timestamp(),
|
|
winston.format.json()
|
|
),
|
|
transports: [
|
|
new winston.transports.Console({
|
|
format: winston.format.combine(
|
|
winston.format.colorize(),
|
|
winston.format.simple()
|
|
)
|
|
})
|
|
]
|
|
});
|
|
|
|
// 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
|
|
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 () => {
|
|
logger.info('📥 Received SIGINT. Shutting down gracefully...');
|
|
try {
|
|
if (bot) {
|
|
await bot.stop();
|
|
}
|
|
logger.info('✅ Bot shutdown completed');
|
|
process.exit(0);
|
|
} catch (error) {
|
|
logger.error('❌ Error during shutdown:', {
|
|
message: error.message,
|
|
stack: error.stack
|
|
});
|
|
process.exit(1);
|
|
}
|
|
});
|
|
|
|
process.on('SIGTERM', async () => {
|
|
logger.info('📥 Received SIGTERM. Shutting down gracefully...');
|
|
try {
|
|
if (bot) {
|
|
await bot.stop();
|
|
}
|
|
logger.info('✅ Bot shutdown completed');
|
|
process.exit(0);
|
|
} catch (error) {
|
|
logger.error('❌ Error during shutdown:', {
|
|
message: error.message,
|
|
stack: error.stack
|
|
});
|
|
process.exit(1);
|
|
}
|
|
}); |