const discord = require("discord.js"); const Client = discord.Client; const GatewayIntentBits = discord.GatewayIntentBits; const IntentsBitField = discord.IntentsBitField; const Intents = discord.Intents; const Partials = discord.Partials; const { getSetting, setSetting } = require("./settings"); const { incrementMessages } = require("./stats"); const { ensureUserForIdentity } = require("./users"); let client = null; async function startBot({ commandRouter } = {}) { const token = getSetting("discord_bot_token"); if (!token) { return null; } const intents = [ resolveIntent("Guilds", "GUILDS"), resolveIntent("GuildMessages", "GUILD_MESSAGES"), resolveIntent("GuildMembers", "GUILD_MEMBERS"), resolveIntent("MessageContent", "MESSAGE_CONTENT"), resolveIntent("GuildVoiceStates", "GUILD_VOICE_STATES"), resolveIntent("GuildPresences", "GUILD_PRESENCES") ].filter(Boolean); const options = {}; if (intents.length) { options.intents = intents; } console.log("Discord bot starting with intents", { intents, guildMembers: Boolean(resolveIntent("GuildMembers", "GUILD_MEMBERS")) }); if (Partials?.Channel) { options.partials = [Partials.Channel]; } client = new Client(options); client.on("ready", () => { console.log(`Discord bot ready: ${client.user?.tag}`); const avatarUrl = getBotAvatarUrl(client.user); if (avatarUrl) { setSetting("bot_avatar_url", avatarUrl); } }); client.on("messageCreate", async (message) => { if (!message.guild || message.author.bot) { return; } const displayName = message.author.globalName || message.author.username || message.author.tag; let avatarUrl = null; if (typeof message.author.displayAvatarURL === "function") { try { avatarUrl = message.author.displayAvatarURL({ format: "png", size: 128 }); } catch { avatarUrl = message.author.displayAvatarURL(); } } const profile = ensureUserForIdentity({ provider: "discord", providerUserId: message.author.id, displayName, avatar: avatarUrl }); incrementMessages(profile.id); if (commandRouter) { await commandRouter.handleMessage({ platform: "discord", raw: message.content, user: profile, platformUser: { id: message.author.id, displayName, username: message.author.username, tag: message.author.tag, avatar: avatarUrl }, meta: { message, client }, reply: async (content) => { try { await message.reply(content); } catch (error) { console.error("Discord command reply failed", error); } } }); } }); await client.login(token); return client; } function getBotAvatarUrl(user) { if (!user) { return null; } if (typeof user.displayAvatarURL === "function") { try { return user.displayAvatarURL({ format: "png", size: 128 }); } catch { return user.displayAvatarURL(); } } if (user.avatar) { return `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png?size=128`; } return null; } function resolveIntent(key, legacyKey) { if (GatewayIntentBits?.[key]) { return GatewayIntentBits[key]; } if (IntentsBitField?.Flags?.[key]) { return IntentsBitField.Flags[key]; } if (Intents?.FLAGS?.[legacyKey]) { return Intents.FLAGS[legacyKey]; } return null; } async function stopBot() { if (client) { await client.destroy(); client = null; } } function getClient() { return client; } module.exports = { startBot, stopBot, getClient };