From 03e50185c1376fe41f1b3a07ecd3b2ae435ea671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=AFs=20Saraiva?= <53227802+Kisakay@users.noreply.github.com> Date: Tue, 8 Sep 2026 07:22:15 +0200 Subject: [PATCH] added base --- .gitignore | 140 +------ ...Y_KISAKAY_MULTI_TOKEN(ALLBOT)_(WALLAH).zip | Bin 0 -> 7159 bytes Message.txt | 2 + README.md | 16 +- bun.lock | 75 ++++ index.ts | 349 ++++++++++++++++++ package.json | 15 + tokens.txt | 0 tsconfig.json | 30 ++ 9 files changed, 503 insertions(+), 124 deletions(-) create mode 100644 DM_ALL_BY_KISAKAY_MULTI_TOKEN(ALLBOT)_(WALLAH).zip create mode 100644 Message.txt create mode 100644 bun.lock create mode 100644 index.ts create mode 100644 package.json create mode 100644 tokens.txt create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 2309cc8..a14702c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,77 +1,19 @@ -# ---> Node -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* +# dependencies (bun install) +node_modules -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +# output +out +dist +*.tgz -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul +# code coverage coverage *.lcov -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # dotenv environment variable files .env @@ -80,59 +22,13 @@ web_modules/ .env.production.local .env.local -# parcel-bundler cache (https://parceljs.org/) +# caches +.eslintcache .cache -.parcel-cache +*.tsbuildinfo -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# vitepress build output -**/.vitepress/dist - -# vitepress cache directory -**/.vitepress/cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* +# IntelliJ based IDEs +.idea +# Finder (MacOS) folder config +.DS_Store diff --git a/DM_ALL_BY_KISAKAY_MULTI_TOKEN(ALLBOT)_(WALLAH).zip b/DM_ALL_BY_KISAKAY_MULTI_TOKEN(ALLBOT)_(WALLAH).zip new file mode 100644 index 0000000000000000000000000000000000000000..5bd1c5aed7604e4ed1e104da051db74337973ad6 GIT binary patch literal 7159 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.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], + + "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], + + "@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..1245c49 --- /dev/null +++ b/index.ts @@ -0,0 +1,349 @@ +import { + Client, + GatewayIntentBits, + Guild, + GuildMember, +} from "discord.js"; + +const tokenFile = Bun.file("tokens.txt"); +const messageFile = Bun.file("Message.txt"); +const optInFile = Bun.file("opted-in.txt"); + +const tokens = (await tokenFile.text()) + .split(/\r?\n/) + .map((token) => token.trim()) + .filter(Boolean); + +const message = await messageFile.text(); + +export const GUILD_ID = process.env.GUILD_ID; + +if (!GUILD_ID) { + throw new Error("GUILD_ID n'est pas défini."); +} + +const clients: Client[] = []; + +/** + * Membres ayant explicitement accepté de recevoir les DM. + * + * opted-in.txt : + * + * 123456789012345678 + * 987654321098765432 + */ +async function getOptedInMembers(guild: Guild): Promise { + const content = await optInFile.text(); + + const optedInIds = new Set( + content + .split(/\r?\n/) + .map((id) => id.trim()) + .filter(Boolean), + ); + + const members = await guild.members.fetch(); + + return members + .filter((member) => optedInIds.has(member.id)) + .toJSON(); +} + +/** + * Connecte un bot et attend réellement son clientReady. + */ +function loginClient(token: string): Promise { + const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, + ], + }); + + clients.push(client); + + return new Promise((resolve, reject) => { + const onReady = () => { + cleanup(); + + console.log( + `[${client.user?.username ?? client.user?.id}] ready`, + ); + + resolve(client); + }; + + const onError = (error: Error) => { + cleanup(); + reject(error); + }; + + const cleanup = () => { + client.off("clientReady", onReady); + client.off("error", onError); + }; + + client.once("clientReady", onReady); + client.once("error", onError); + + client.login(token).catch(onError); + }); +} + +/** + * Répartit les membres équitablement entre les bots. + */ +function batchMembers( + members: GuildMember[], + botCount: number, +): GuildMember[][] { + const batches: GuildMember[][] = Array.from( + { length: botCount }, + () => [], + ); + + members.forEach((member, index) => { + batches[index % botCount].push(member); + }); + + return batches; +} + +/** + * Envoie le message uniquement aux membres opt-in + * assignés à ce bot. + */ +async function sendBatch( + client: Client, + members: GuildMember[], +): Promise<{ + sent: number; + failed: number; +}> { + let sent = 0; + let failed = 0; + + console.log( + `[${client.user?.username}] ${members.length} membre(s) dans son batch.`, + ); + + for (const member of members) { + const parsedMessage = message.replace( + /\{USER\}/g, + member.toString(), + ); + + try { + await member.send(parsedMessage); + + sent++; + + console.log( + `[${client.user?.username}] DM envoyé à @${member.user.username}`, + ); + } catch (error) { + failed++; + + console.error( + `[${client.user?.username}] Impossible de DM @${member.user.username}`, + error, + ); + } + + // Petit délai entre les envois. + await Bun.sleep(1500); + } + + return { + sent, + failed, + }; +} + +process.on("uncaughtException", (error) => { + console.error("uncaughtException:", error); +}); + +process.on("unhandledRejection", (error) => { + console.error("unhandledRejection:", error); +}); + +/* + * ============================================================ + * 1. CONNECTER TOUS LES BOTS + * ============================================================ + */ + +console.log(`Connexion de ${tokens.length} bot(s)...`); + +const loginResults = await Promise.allSettled( + tokens.map((token) => loginClient(token)), +); + +const readyClients: Client[] = []; + +for (const [index, result] of loginResults.entries()) { + if (result.status === "fulfilled") { + readyClients.push(result.value); + } else { + console.error( + `[Bot ${index + 1}] Échec de connexion :`, + result.reason, + ); + } +} + +console.log( + `${readyClients.length}/${tokens.length} bot(s) sont prêts.`, +); + +if (readyClients.length === 0) { + throw new Error("Aucun bot n'est connecté."); +} + +/* + * ============================================================ + * 2. RÉCUPÉRER LE SERVEUR + * ============================================================ + */ + +const guilds = await Promise.all( + readyClients.map(async (client) => { + const guild = await client.guilds + .fetch(GUILD_ID) + .catch(() => null); + + return { + client, + guild, + }; + }), +); + +const availableBots = guilds.filter( + ( + item, + ): item is { + client: Client; + guild: Guild; + } => item.guild !== null, +); + +for (const { client } of guilds) { + const guild = await client.guilds + .fetch(GUILD_ID) + .catch(() => null); + + if (!guild) { + console.log( + `[${client.user?.username}] Le bot n'est pas présent sur le serveur.`, + ); + + console.log( + `https://discord.com/oauth2/authorize?client_id=${client.user?.id}&scope=bot`, + ); + } +} + +if (availableBots.length === 0) { + throw new Error( + "Aucun des bots connectés n'est présent sur ce serveur.", + ); +} + +/* + * ============================================================ + * 3. RÉCUPÉRER LES MEMBRES OPT-IN + * ============================================================ + */ + +const mainGuild = availableBots[0].guild; + +console.log( + `[${mainGuild.name}] Récupération des membres opt-in...`, +); + +const optedInMembers = await getOptedInMembers(mainGuild); + +console.log( + `[${mainGuild.name}] ${optedInMembers.length} membre(s) ont accepté les DM.`, +); + +if (optedInMembers.length === 0) { + console.log("Aucun membre opt-in. Rien à envoyer."); + process.exit(0); +} + +/* + * ============================================================ + * 4. RÉPARTITION DES MEMBRES ENTRE LES BOTS + * ============================================================ + */ + +const batches = batchMembers( + optedInMembers, + availableBots.length, +); + +console.log( + `Répartition de ${optedInMembers.length} membre(s) entre ${availableBots.length} bot(s).`, +); + +for (let i = 0; i < availableBots.length; i++) { + console.log( + `[${availableBots[i].client.user?.username}] ${batches[i].length} membre(s)`, + ); +} + +/* + * ============================================================ + * 5. ENVOI DES BATCHS + * ============================================================ + */ + +console.log("Début de l'envoi..."); + +const sendResults = await Promise.allSettled( + availableBots.map(({ client }, index) => + sendBatch(client, batches[index]), + ), +); + +/* + * ============================================================ + * 6. RÉSULTATS + * ============================================================ + */ + +let totalSent = 0; +let totalFailed = 0; + +for (const [index, result] of sendResults.entries()) { + const username = + availableBots[index].client.user?.username ?? + availableBots[index].client.user?.id ?? + `Bot ${index + 1}`; + + if (result.status === "fulfilled") { + totalSent += result.value.sent; + totalFailed += result.value.failed; + + console.log( + `[${username}] terminé : ${result.value.sent} envoyé(s), ${result.value.failed} échec(s).`, + ); + } else { + console.error( + `[${username}] le batch a échoué :`, + result.reason, + ); + } +} + +console.log(""); +console.log("========================================"); +console.log(" ENVOI TERMINÉ"); +console.log("========================================"); +console.log(`Membres opt-in : ${optedInMembers.length}`); +console.log(`Messages envoyés : ${totalSent}`); +console.log(`Échecs : ${totalFailed}`); +console.log("========================================"); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..699231b --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "dm-all", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "discord.js": "^14.27.0" + } +} diff --git a/tokens.txt b/tokens.txt new file mode 100644 index 0000000..e69de29 diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..359f52e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + "types": ["bun"], + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": false, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}