Files
EJClaw/scripts/diagnose-discord-perms.ts
Codex 90c17c6890 style(scripts): prettier reflow on Discord perms diagnostic
Follow-up to 0d87778; the pre-commit prettier hook rewrapped a few long
console.log lines after the commit landed.
2026-06-01 10:44:51 +09:00

99 lines
2.9 KiB
TypeScript

#!/usr/bin/env bun
/**
* One-off diagnostic. Logs in with each configured Discord bot token, walks
* every guild it sees, and prints the bot member's effective permission bits.
*
* Output is meant to verify capability claims like "we have Read Message
* History" / "we have Manage Channels" empirically rather than guessing from
* intents.
*/
import { Client, GatewayIntentBits, PermissionsBitField } from 'discord.js';
import fs from 'fs';
import path from 'path';
function loadEnv(): Record<string, string> {
const envPath = path.join(import.meta.dir, '..', '.env');
if (!fs.existsSync(envPath)) return {};
const out: Record<string, string> = {};
for (const line of fs.readFileSync(envPath, 'utf-8').split('\n')) {
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
if (m) out[m[1]] = m[2].replace(/^["']|["']$/g, '');
}
return out;
}
const env = loadEnv();
const TOKENS: Array<[string, string]> = [
['owner', env.DISCORD_OWNER_BOT_TOKEN ?? ''],
['reviewer', env.DISCORD_REVIEWER_BOT_TOKEN ?? ''],
['arbiter', env.DISCORD_ARBITER_BOT_TOKEN ?? ''],
].filter(([, t]) => t.length > 0) as Array<[string, string]>;
// Subset of permissions we actually care about for the capability matrix.
const RELEVANT = [
'ViewChannel',
'SendMessages',
'EmbedLinks',
'AttachFiles',
'ReadMessageHistory',
'AddReactions',
'ManageMessages',
'ManageChannels',
'ManageThreads',
'CreatePublicThreads',
'CreatePrivateThreads',
'SendMessagesInThreads',
'MentionEveryone',
'UseExternalEmojis',
] as const;
async function diagnose(role: string, token: string) {
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.DirectMessages,
],
});
await new Promise<void>((resolve, reject) => {
client.once('ready', () => resolve());
client.once('error', reject);
client.login(token).catch(reject);
});
console.log(
`\n=== role=${role} bot=${client.user?.tag} (id=${client.user?.id}) ===`,
);
const guilds = await client.guilds.fetch();
for (const [gid, partial] of guilds) {
const guild = await partial.fetch();
const me = await guild.members.fetchMe();
const perms = me.permissions;
const has: Record<string, boolean> = {};
for (const name of RELEVANT) {
has[name] = perms.has(
PermissionsBitField.Flags[
name as keyof typeof PermissionsBitField.Flags
],
);
}
console.log(` guild=${guild.name} (id=${gid})`);
console.log(
` admin=${perms.has(PermissionsBitField.Flags.Administrator)}`,
);
for (const name of RELEVANT) console.log(` ${name}=${has[name]}`);
}
client.destroy();
}
for (const [role, token] of TOKENS) {
try {
await diagnose(role, token);
} catch (err) {
console.log(`\n=== role=${role} FAILED ===`);
console.log(err);
}
}