added base
This commit is contained in:
+18
-122
@@ -1,77 +1,19 @@
|
|||||||
# ---> Node
|
# dependencies (bun install)
|
||||||
# Logs
|
node_modules
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# output
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
# Runtime data
|
# code coverage
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
coverage
|
||||||
*.lcov
|
*.lcov
|
||||||
|
|
||||||
# nyc test coverage
|
# logs
|
||||||
.nyc_output
|
logs
|
||||||
|
_.log
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
.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
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
# dotenv environment variable files
|
||||||
.env
|
.env
|
||||||
@@ -80,59 +22,13 @@ web_modules/
|
|||||||
.env.production.local
|
.env.production.local
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
# caches
|
||||||
|
.eslintcache
|
||||||
.cache
|
.cache
|
||||||
.parcel-cache
|
*.tsbuildinfo
|
||||||
|
|
||||||
# Next.js build output
|
# IntelliJ based IDEs
|
||||||
.next
|
.idea
|
||||||
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.*
|
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
|||||||
|
# WSH {USER} TA TJR PAS REJOINS LE NOUVEAU PV UHQ
|
||||||
|
discord.gg/pvsephora WSH
|
||||||
@@ -1,3 +1,15 @@
|
|||||||
# dmall
|
# dm-all
|
||||||
|
|
||||||
dmall
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun init` in bun v1.3.14. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "dm-all",
|
||||||
|
"dependencies": {
|
||||||
|
"discord.js": "^14.27.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@discordjs/builders": ["@discordjs/[email protected]", "", { "dependencies": { "@discordjs/formatters": "^0.6.2", "@discordjs/util": "^1.2.0", "@sapphire/shapeshift": "^4.0.0", "discord-api-types": "^0.38.40", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.3" } }, "sha512-gSKkhXLqs96TCzk66VZuHHl8z2bQMJFGwrXC0f33ngK+FLNau4hU1PYny3DNJfNdSH+gVMzE85/d5FQ2BpcNwQ=="],
|
||||||
|
|
||||||
|
"@discordjs/collection": ["@discordjs/[email protected]", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="],
|
||||||
|
|
||||||
|
"@discordjs/formatters": ["@discordjs/[email protected]", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ=="],
|
||||||
|
|
||||||
|
"@discordjs/rest": ["@discordjs/[email protected]", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.2.0", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.5", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.50", "magic-bytes.js": "^1.13.0", "tslib": "^2.6.3", "undici": "^6.27.0" } }, "sha512-wvOylxNYJkwKjctS/Mn5GP1w9r3/rzyH+ThD1JlAca6zEdlHs8QWBBUQJpU5Q+W6DoIj/Ljh1IPlZs7hTU+UAg=="],
|
||||||
|
|
||||||
|
"@discordjs/util": ["@discordjs/[email protected]", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg=="],
|
||||||
|
|
||||||
|
"@discordjs/ws": ["@discordjs/[email protected]", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
||||||
|
|
||||||
|
"@sapphire/async-queue": ["@sapphire/[email protected]", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="],
|
||||||
|
|
||||||
|
"@sapphire/shapeshift": ["@sapphire/[email protected]", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" } }, "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg=="],
|
||||||
|
|
||||||
|
"@sapphire/snowflake": ["@sapphire/[email protected]", "", {}, "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ=="],
|
||||||
|
|
||||||
|
"@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg=="],
|
||||||
|
|
||||||
|
"@types/ws": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||||
|
|
||||||
|
"@vladfrangu/async_event_emitter": ["@vladfrangu/[email protected]", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="],
|
||||||
|
|
||||||
|
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
||||||
|
|
||||||
|
"discord-api-types": ["[email protected]", "", {}, "sha512-HL1zz/UuZ+bbJjA/X8Kbxx9gk8v9rJAbTeWRNYKmIdjwJ7EovjlHgoJTxcLpATfNJ+AonOtMdy3Y5MVIJAAd/A=="],
|
||||||
|
|
||||||
|
"discord.js": ["[email protected]", "", { "dependencies": { "@discordjs/builders": "^1.14.1", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.2", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.5", "discord-api-types": "^0.38.49", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.13.0", "tslib": "^2.6.3", "undici": "^6.27.0" } }, "sha512-qHbFlFG2N7y3LjPySYsL6A1+BnX6bkTVgo842EX0CqVPk/KTMwZkojPHEXKsQUpWZNyz5BISNHK1cPpQw0+m4A=="],
|
||||||
|
|
||||||
|
"fast-deep-equal": ["[email protected]", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
||||||
|
"lodash": ["[email protected]", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
|
||||||
|
|
||||||
|
"lodash.snakecase": ["[email protected]", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="],
|
||||||
|
|
||||||
|
"magic-bytes.js": ["[email protected]", "", {}, "sha512-x5sn4UX2k5gCWlcfmoFwG4TPie8+dctESyqOBdhB5p6MsgWXdBKGmt9nXPObj/JI50TTL928lc5Yt1WntMn1bw=="],
|
||||||
|
|
||||||
|
"ts-mixer": ["[email protected]", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
||||||
|
|
||||||
|
"tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
|
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici": ["[email protected]", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="],
|
||||||
|
|
||||||
|
"undici-types": ["[email protected]", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
|
||||||
|
|
||||||
|
"ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="],
|
||||||
|
|
||||||
|
"@discordjs/rest/@discordjs/collection": ["@discordjs/[email protected]", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||||
|
|
||||||
|
"@discordjs/ws/@discordjs/collection": ["@discordjs/[email protected]", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<GuildMember[]> {
|
||||||
|
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<Client> {
|
||||||
|
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("========================================");
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user