generated from nhcarrigan/template
This commit is contained in:
+3
-5
@@ -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"
|
||||||
|
|||||||
Generated
+9
-10
@@ -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
|
||||||
|
|||||||
@@ -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 };
|
||||||
Reference in New Issue
Block a user