// Load environment variables from .env file require('dotenv').config(); const { Client, GatewayIntentBits, Collection } = require('discord.js'); const { Manager } = require('erela.js'); const logger = require('./utils/logger'); const fs = require('fs'); const path = require('path'); // Validate essential environment variables if (!process.env.DISCORD_TOKEN) { logger.error('DISCORD_TOKEN is missing in the .env file!'); process.exit(1); } if (!process.env.LAVALINK_HOST || !process.env.LAVALINK_PORT || !process.env.LAVALINK_PASSWORD) { logger.warn('Lavalink connection details (HOST, PORT, PASSWORD) are missing or incomplete in .env. Music functionality will be limited.'); // Decide if the bot should exit or continue without music // process.exit(1); // Uncomment to exit if Lavalink is mandatory } // Create a new Discord client instance with necessary intents const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, // Add if needed for prefix commands or message content GatewayIntentBits.MessageContent, // Add if needed for message content ], }); // Initialize Erela.js Manager // We need the client to be ready before fully initializing the manager client.manager = new Manager({ nodes: [ { host: process.env.LAVALINK_HOST || 'localhost', // Default host if not set port: parseInt(process.env.LAVALINK_PORT || '2333'), // Default port if not set password: process.env.LAVALINK_PASSWORD || 'youshallnotpass', // Default password if not set secure: process.env.LAVALINK_SECURE === 'true', // Optional: Use true for wss:// }, ], // Function to send raw voice data to Discord send(id, payload) { const guild = client.guilds.cache.get(id); if (guild) guild.shard.send(payload); }, }); // Collections for commands client.commands = new Collection(); // --- Command Loading --- const commandsPath = path.join(__dirname, 'commands'); const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); for (const file of commandFiles) { const filePath = path.join(commandsPath, file); try { const command = require(filePath); // Set a new item in the Collection with the key as the command name and the value as the exported module if ('data' in command && 'execute' in command) { client.commands.set(command.data.name, command); logger.info(`Loaded command: ${command.data.name}`); } else { logger.warn(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`); } } catch (error) { logger.error(`Error loading command at ${filePath}: ${error.message}`, error); } } // --- Event Handling --- const eventsPath = path.join(__dirname, 'events'); const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js')); for (const file of eventFiles) { const filePath = path.join(eventsPath, file); const event = require(filePath); if (event.once) { client.once(event.name, (...args) => event.execute(...args, client)); // Pass client to event handlers logger.info(`Loaded event ${event.name} (once)`); } else { client.on(event.name, (...args) => event.execute(...args, client)); // Pass client to event handlers logger.info(`Loaded event ${event.name}`); } } // --- Erela.js Event Handling --- // (This is handled within the 'ready' event after manager initialization) logger.info('Erela.js event handling will be attached in the ready event.'); // Log in to Discord with your client's token client.login(process.env.DISCORD_TOKEN) .then(() => logger.info('Successfully logged in to Discord.')) .catch(error => logger.error(`Failed to log in: ${error.message}`)); // Basic error handling process.on('unhandledRejection', error => { logger.error('Unhandled promise rejection:', error); }); process.on('uncaughtException', error => { logger.error('Uncaught exception:', error); // Optional: exit process on critical uncaught exceptions // process.exit(1); });