Follow-up to 0d87778; the pre-commit prettier hook rewrapped a few long console.log lines after the commit landed.
99 lines
2.9 KiB
TypeScript
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);
|
|
}
|
|
}
|