feat: add script to get discord guild count
Node.js CI / Lint and Test (push) Failing after 22s

This commit is contained in:
2025-12-03 13:53:51 -08:00
parent 18f3d15791
commit dc324a307b
3 changed files with 177 additions and 15 deletions
+3 -5
View File
@@ -5,7 +5,7 @@
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc --noEmit",
"lint": "eslint src --max-warnings 0", "lint": "eslint src --max-warnings 0",
"start": "op run --env-file=prod.env --no-masking -- tsx", "start": "op run --env-file=prod.env --no-masking -- tsx",
"test": "echo \"Error: no test specified\" && exit 0" "test": "echo \"Error: no test specified\" && exit 0"
@@ -14,7 +14,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"packageManager": "pnpm@10.15.0", "packageManager": "pnpm@10.15.0",
"devDependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.940.0", "@aws-sdk/client-s3": "3.940.0",
"@inquirer/prompts": "7.8.6", "@inquirer/prompts": "7.8.6",
"@nhcarrigan/eslint-config": "5.2.0", "@nhcarrigan/eslint-config": "5.2.0",
@@ -22,9 +22,7 @@
"@types/node": "24.3.0", "@types/node": "24.3.0",
"eslint": "9.34.0", "eslint": "9.34.0",
"tsx": "4.20.5", "tsx": "4.20.5",
"typescript": "5.9.2" "typescript": "5.9.2",
},
"dependencies": {
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
"@types/cli-progress": "3.11.6", "@types/cli-progress": "3.11.6",
"cli-progress": "3.12.0" "cli-progress": "3.12.0"
+9 -10
View File
@@ -8,16 +8,6 @@ importers:
.: .:
dependencies: dependencies:
'@octokit/rest':
specifier: 22.0.0
version: 22.0.0
'@types/cli-progress':
specifier: 3.11.6
version: 3.11.6
cli-progress:
specifier: 3.12.0
version: 3.12.0
devDependencies:
'@aws-sdk/client-s3': '@aws-sdk/client-s3':
specifier: 3.940.0 specifier: 3.940.0
version: 3.940.0 version: 3.940.0
@@ -30,9 +20,18 @@ importers:
'@nhcarrigan/typescript-config': '@nhcarrigan/typescript-config':
specifier: 4.0.0 specifier: 4.0.0
version: 4.0.0(typescript@5.9.2) version: 4.0.0(typescript@5.9.2)
'@octokit/rest':
specifier: 22.0.0
version: 22.0.0
'@types/cli-progress':
specifier: 3.11.6
version: 3.11.6
'@types/node': '@types/node':
specifier: 24.3.0 specifier: 24.3.0
version: 24.3.0 version: 24.3.0
cli-progress:
specifier: 3.12.0
version: 3.12.0
eslint: eslint:
specifier: 9.34.0 specifier: 9.34.0
version: 9.34.0 version: 9.34.0
+165
View File
@@ -0,0 +1,165 @@
/**
* @copyright NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
/* eslint-disable complexity, max-lines-per-function -- This is a chonky boi script. */
/**
* DISCORD SERVER STATS ANALYZER
* * Instructions:
* 1. Get your User Token (Open Discord in Browser -> F12 -> Network Tab -> Type a message -> Look for "authorization" in request headers).
* 2. Set it in the .env file as TOKEN.
* 3. Run with: node discord_stats.js.
*/
interface Guild {
name?: string;
permissions?: string;
features?: Array<string>;
owner?: boolean;
id: string;
}
interface Stats {
community: Array<string>;
moderating: Array<string>;
owned: Array<string>;
partnered: Array<string>;
total: number;
verified: Array<string>;
}
// Permission Flags (BigInt)
const permissions = {
administrator: 0x00_00_00_00_00_00_00_08n,
banMembers: 0x00_00_00_00_00_00_00_04n,
kickMembers: 0x00_00_00_00_00_00_00_02n,
manageGuild: 0x00_00_00_00_00_00_00_20n,
manageMessages: 0x00_00_00_00_00_00_20_00n,
moderateMembers: 0x00_00_01_00_00_00_00_00n,
};
const printList = (title: string, list: Array<string>, icon: string): void => {
console.log(`${icon} ${title}: ${list.length.toString()}`);
if (list.length > 0) {
for (const name of list) {
console.log(` - ${name}`);
}
}
console.log("\n");
};
const printReport = (stats: Stats): void => {
console.log("\n====== DISCORD SERVER BREAKDOWN ======");
console.log(`Total Servers Joined: ${stats.total.toString()}\n`);
printList("Owned Servers", stats.owned, "👑");
printList("Moderating (Non-Owned)", stats.moderating, "🛡️ ");
printList("Partnered Servers", stats.partnered, "🤝");
printList("Verified Servers", stats.verified, "✅");
printList("Community/Public Servers", stats.community, "🌍");
console.log(`======================================\n`);
};
const checkForPermission = (perms: bigint, permission: bigint): boolean => {
// eslint-disable-next-line no-bitwise -- Since Discord uses bit flags...
return (perms & permission) === permission;
};
const analyzeGuilds = (guilds: Array<Guild>): void => {
// Arrays to store names instead of just counts
const stats: Stats = {
community: [],
moderating: [],
owned: [],
partnered: [],
total: guilds.length,
verified: [],
};
for (const guild of guilds) {
const perms = BigInt(guild.permissions ?? 0);
const features = guild.features ?? [];
const { name, owner, id } = guild;
// 1. Ownership
if (owner === true) {
stats.owned.push(name ?? "Unknown");
}
/*
* 2. Moderation
* We consider you a "Moderator" if you have specific mod permissions,
* Even if you don't own the server.
*/
const isModerator
= checkForPermission(perms, permissions.administrator)
|| checkForPermission(perms, permissions.manageGuild)
|| checkForPermission(perms, permissions.banMembers)
|| checkForPermission(perms, permissions.kickMembers)
|| checkForPermission(perms, permissions.moderateMembers);
if (isModerator && owner !== true) {
stats.moderating.push(name ?? id);
}
// 3. Partnered
if (features.includes("PARTNERED")) {
stats.partnered.push(name ?? id);
}
// 4. Verified
if (features.includes("VERIFIED")) {
stats.verified.push(name ?? id);
}
/*
* 5. Community / Public
* "COMMUNITY" feature enables public facing screens (Welcome Screen, Rules, etc)
* "DISCOVERABLE" means it appears in Server Discovery
*/
if (features.includes("COMMUNITY") || features.includes("DISCOVERABLE")) {
stats.community.push(name ?? id);
}
}
printReport(stats);
};
/**
*
*/
async function getGuilds(): Promise<void> {
const token = process.env.TOKEN;
if (token === undefined) {
throw new Error("Missing TOKEN");
}
console.log("Fetching servers...");
// Discord allows max 200 servers per user (with Nitro), so limit=200 catches all.
const response = await fetch(
"https://discord.com/api/v10/users/@me/guilds?limit=200",
{
headers: {
authorization: token,
},
},
);
if (!response.ok) {
if (response.status === 401) {
console.error("Error: Invalid Token. Please check your TOKEN.");
} else {
console.error(`Error: API returned status ${response.status.toString()}`);
}
return;
}
const guilds: Array<Guild> = await response.json();
analyzeGuilds(guilds);
}
await getGuilds();
export { getGuilds };