diff --git a/dave/bot.mjs b/dave/bot.mjs index a0fad90..e8e710c 100644 --- a/dave/bot.mjs +++ b/dave/bot.mjs @@ -161,9 +161,14 @@ if (process.argv.includes('--invite')) { if (!TOKEN) { console.error('no DISCORD_BOT_TOKEN in env or ../.env'); process.exit(2); } // ---------- client ---------- -const client = new Client({ - intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates], -}); +// Listing ALL server members needs the privileged "Server Members Intent". +// Gate it behind an env flag: enabling the intent in code while the Developer +// Portal toggle is OFF makes login fail ("disallowed intents"). So this stays +// off until the user turns the portal intent on and sets WSAI_MEMBERS_INTENT=1. +const MEMBERS_INTENT = (process.env.WSAI_MEMBERS_INTENT || env.WSAI_MEMBERS_INTENT) === '1'; +const intents = [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates]; +if (MEMBERS_INTENT) intents.push(GatewayIntentBits.GuildMembers); +const client = new Client({ intents }); const perUser = new Map(); // userId -> { opusPackets, pcmFrames } @@ -271,7 +276,7 @@ function buildState() { roles: [...g.roles.cache.values()] .filter((r) => r.id !== g.id && r.name !== '@everyone') .map((r) => ({ id: r.id, name: r.name })), - members: [...g.members.cache.values()].slice(0, 200).map((m) => ({ + members: [...g.members.cache.values()].slice(0, 2000).map((m) => ({ id: m.id, name: m.displayName, bot: m.user?.bot === true, roleIds: [...m.roles.cache.keys()], })), @@ -342,6 +347,16 @@ client.once('clientReady', async () => { log('no default channel — waiting for the dashboard to select a server/voice channel…'); } + // With the members intent on, pull the full member list of each guild so the + // whitelist/blacklist popup can search every user (not just cached speakers). + if (MEMBERS_INTENT) { + for (const g of client.guilds.cache.values()) { + g.members.fetch() + .then((m) => log(`fetched ${m.size} members of "${g.name}"`)) + .catch((e) => log(`member fetch failed ("${g.name}"): ${e.message}`)); + } + } + // Report state + poll commands forever. This is what powers the dashboard's // bot info, server/voice-channel pickers, participant list, and join/leave. reportLoop();