import { Client, Collection, GatewayIntentBits } from 'discord.js'; import { createLogger } from './_src/logger.js'; import { initializePocketbase } from './_src/pocketbase.js'; import { loadModules } from './_src/loader.js'; import config from './config.js'; // For deprecated ephemeral option: convert to flags import { ansi, wrapAnsi } from './_src/ansiColors.js'; // Initialize Discord client const initializeClient = async (clientConfig) => { // Create Discord client with intents const client = new Client({ // Include GuildMembers intent to allow fetching all guild members intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMembers ] }); // Attach config to client client.config = clientConfig; // Set up Winston logger client.logger = createLogger(clientConfig); client.logger.info(`Initializing client: ${clientConfig.id}`); // Set up Pocketbase client.pb = await initializePocketbase(clientConfig, client.logger); // Commands collection client.commands = new Collection(); // ANSI helper attached to client client.ansi = ansi; client.wrapAnsi = wrapAnsi; // Load optional modules await loadModules(clientConfig, client); // TODO: If the logger level is debug, create event binds to raw and debug. // Discord client events client.on('interactionCreate', async (interaction) => { if (!interaction.isChatInputCommand()) return; const commandName = interaction.commandName; try { // Find command in collection const command = client.commands.get(commandName); if (!command) { client.logger.warn(`Command not found: ${commandName}`); await interaction.reply({ content: 'Sorry, this command is not properly registered.', ephemeral: true }); return; } // Execute the command client.logger.debug(`Executing command: ${commandName}`); await command.execute(interaction, client); } catch (error) { client.logger.error(`Error executing command ${commandName}: ${error.message}`); // Handle already replied interactions const replyContent = { content: 'There was an error while executing this command.', ephemeral: true }; if (interaction.replied || interaction.deferred) { await interaction.followUp(replyContent).catch(err => { client.logger.error(`Failed to send followUp: ${err.message}`); }); } else { await interaction.reply(replyContent).catch(err => { client.logger.error(`Failed to reply: ${err.message}`); }); } } }); client.on('ready', () => { client.logger.info(`Logged in as ${client.user.tag}`); }); client.on('error', (error) => { client.logger.error(`Client error: ${error.message}`); }); // Login to Discord try { await client.login(clientConfig.discord.token); return client; } catch (error) { client.logger.error(`Failed to login: ${error.message}`); throw error; } }; // Main function to start bot const startBot = async () => { const clients = []; // Initialize each client from config for (const clientConfig of config.clients) { try { const client = await initializeClient(clientConfig); clients.push(client); } catch (error) { console.error(`Failed to initialize client ${clientConfig.id}:`, error); } } return clients; }; // Launch the bot startBot().then(clients => { console.log(`[main] Successfully initialized ${clients.length} Discord clients`); }).catch(error => { console.error(`[main] Failed to start bot: ${error.message}`); process.exit(1); });