multi-dc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # multi-discord.js
2
+
3
+ Run multiple `discord.js` bots in a single Node.js process, where each bot lives in its own folder with its own code.
4
+
5
+ ## Features
6
+
7
+ - Run multiple bots from one config file.
8
+ - Works with JavaScript and TypeScript bot files.
9
+ - CLI starter to scaffold a 3-bot project.
10
+ - Uses latest `discord.js` v14 in templates.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install multi-dc discord.js
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```bash
21
+ npx multi-dc init --dir ./my-bots --lang ts
22
+ cd my-bots
23
+ npm install
24
+ npm run dev
25
+ ```
26
+
27
+ Set env vars before running:
28
+
29
+ - `TKN1`
30
+ - `TKN2`
31
+ - `TKN3`
32
+
33
+ ## Config
34
+
35
+ Create `multi-discord.config.ts` or `multi-discord.config.js`:
36
+
37
+ ```ts
38
+ import { defineMultiBotConfig } from "multi-dc";
39
+
40
+ export default defineMultiBotConfig({
41
+ envFile: ".env",
42
+ botsBasePath: "bots",
43
+ onError: "continue",
44
+ bots: [
45
+ { name: "bot1", path: "bot1", entry: "src/bot.ts", tokenEnv: "TKN1" },
46
+ { name: "bot2", path: "bot2", entry: "src/bot.ts", tokenEnv: "TKN2" },
47
+ { name: "bot3", path: "bot3", entry: "src/bot.ts", tokenEnv: "TKN3" },
48
+ ],
49
+ });
50
+ ```
51
+
52
+ Optional config fields:
53
+
54
+ - `envFile`: Path to a `.env` file (relative to config file location or absolute).
55
+ - `botsBasePath`: Base folder for all bot `path` values.
56
+
57
+ Each bot module should export `createBotClient` or a default function returning a `discord.js` `Client`.
58
+
59
+ ## CLI
60
+
61
+ ```bash
62
+ multi-dc run [--config <file>]
63
+ multi-dc init [--dir <folder>] [--lang ts|js]
64
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,105 @@
1
+ import path from "node:path";
2
+ import { spawn } from "node:child_process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { initProject } from "./init.js";
5
+ import { resolveDefaultConfigPath } from "./runtime.js";
6
+ function printHelp() {
7
+ process.stdout.write(`multi-dc
8
+
9
+ Usage:
10
+ multi-dc run [--config <file>]
11
+ multi-dc init [--dir <folder>] [--lang ts|js]
12
+
13
+ Examples:
14
+ multi-dc run
15
+ multi-dc run --config ./multi-discord.config.ts
16
+ multi-dc init --dir ./my-bots --lang ts
17
+ `);
18
+ }
19
+ function parseArgs(argv) {
20
+ const [commandRaw, ...rest] = argv;
21
+ const command = commandRaw ?? "help";
22
+ if (command === "run") {
23
+ let configPath;
24
+ for (let i = 0; i < rest.length; i++) {
25
+ const current = rest[i];
26
+ if (current === "--config" || current === "-c") {
27
+ const nextValue = rest[i + 1];
28
+ if (nextValue) {
29
+ configPath = nextValue;
30
+ }
31
+ i++;
32
+ }
33
+ }
34
+ return configPath ? { command: "run", configPath } : { command: "run" };
35
+ }
36
+ if (command === "init") {
37
+ let dir = "./multi-bot-project";
38
+ let language = "ts";
39
+ for (let i = 0; i < rest.length; i++) {
40
+ const current = rest[i];
41
+ if ((current === "--dir" || current === "-d") && rest[i + 1]) {
42
+ dir = rest[i + 1];
43
+ i++;
44
+ }
45
+ if ((current === "--lang" || current === "-l") && rest[i + 1]) {
46
+ const value = rest[i + 1];
47
+ if (value === "ts" || value === "js") {
48
+ language = value;
49
+ }
50
+ i++;
51
+ }
52
+ }
53
+ return { command: "init", dir, language };
54
+ }
55
+ return { command: "help" };
56
+ }
57
+ async function runCommand(configFromArg) {
58
+ const configPath = configFromArg ?? (await resolveDefaultConfigPath());
59
+ if (!configPath) {
60
+ process.stderr.write(`No config file found. Expected one of:\n- multi-discord.config.ts\n- multi-discord.config.js\n`);
61
+ return 1;
62
+ }
63
+ const cliFile = fileURLToPath(import.meta.url);
64
+ const cliDistDir = path.dirname(cliFile);
65
+ const runnerPath = path.resolve(cliDistDir, "internal-runner.js");
66
+ const child = spawn(process.execPath, ["--import", "tsx", runnerPath, "--config", configPath], {
67
+ stdio: "inherit",
68
+ });
69
+ const shutdown = () => {
70
+ if (!child.killed) {
71
+ child.kill("SIGTERM");
72
+ }
73
+ };
74
+ process.on("SIGINT", shutdown);
75
+ process.on("SIGTERM", shutdown);
76
+ return await new Promise((resolve) => {
77
+ child.on("exit", (code) => resolve(code ?? 1));
78
+ child.on("error", () => resolve(1));
79
+ });
80
+ }
81
+ async function initCommand(dir, language) {
82
+ const target = await initProject({ dir, language });
83
+ const invocationDir = process.env.INIT_CWD ?? process.cwd();
84
+ process.stdout.write(`[multi-dc] created starter project at ${target}\n`);
85
+ process.stdout.write(`[multi-dc] next: cd ${path.relative(invocationDir, target) || "."} && npm install && npm run dev\n`);
86
+ return 0;
87
+ }
88
+ async function main() {
89
+ const args = parseArgs(process.argv.slice(2));
90
+ if (args.command === "help") {
91
+ printHelp();
92
+ process.exit(0);
93
+ }
94
+ if (args.command === "init") {
95
+ const code = await initCommand(args.dir ?? "./multi-bot-project", args.language ?? "ts");
96
+ process.exit(code);
97
+ }
98
+ const code = await runCommand(args.configPath);
99
+ process.exit(code);
100
+ }
101
+ main().catch((error) => {
102
+ process.stderr.write(`[multi-dc] ${String(error)}\n`);
103
+ process.exit(1);
104
+ });
105
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AASxD,SAAS,SAAS;IAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;;;;;;;;;;CAUtB,CAAC,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACnC,MAAM,OAAO,GAAG,UAAU,IAAI,MAAM,CAAC;IAErC,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,IAAI,UAA8B,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,OAAO,KAAK,UAAU,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC9B,IAAI,SAAS,EAAE,CAAC;oBACd,UAAU,GAAG,SAAS,CAAC;gBACzB,CAAC;gBACD,CAAC,EAAE,CAAC;YACN,CAAC;QACH,CAAC;QAED,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC1E,CAAC;IAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,qBAAqB,CAAC;QAChC,IAAI,QAAQ,GAAgB,IAAI,CAAC;QAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC7D,GAAG,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;gBACnB,CAAC,EAAE,CAAC;YACN,CAAC;YACD,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACrC,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;gBACD,CAAC,EAAE,CAAC;YACN,CAAC;QACH,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,aAAsB;IAC9C,MAAM,UAAU,GAAG,aAAa,IAAI,CAAC,MAAM,wBAAwB,EAAE,CAAC,CAAC;IACvE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,gGAAgG,CACjG,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,KAAK,CACjB,OAAO,CAAC,QAAQ,EAChB,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,CAAC,EACvD;QACE,KAAK,EAAE,SAAS;KACjB,CACF,CAAC;IAEF,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhC,OAAO,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QAC3C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,GAAW,EACX,QAAqB;IAErB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,MAAM,IAAI,CAAC,CAAC;IAC1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uBAAuB,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,GAAG,kCAAkC,CACrG,CAAC;IACF,OAAO,CAAC,CAAC;AACX,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC5B,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,WAAW,CAC5B,IAAI,CAAC,GAAG,IAAI,qBAAqB,EACjC,IAAI,CAAC,QAAQ,IAAI,IAAI,CACtB,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const DEFAULT_CONFIG_FILES: readonly ["multi-discord.config.ts", "multi-discord.config.js", "multi-discord.config.mts", "multi-discord.config.mjs", "multi-discord.config.cts", "multi-discord.config.cjs", "multi-discord.js.config.ts", "multi-discord.js.config.js", "multi-discord.js.config.mts", "multi-discord.js.config.mjs", "multi-discord.js.config.cts", "multi-discord.js.config.cjs"];
2
+ export declare const DEFAULT_BOT_ENTRIES: readonly ["src/bot.ts", "src/bot.js", "bot.ts", "bot.js", "index.ts", "index.js", "src/index.ts", "src/index.js"];
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,yWAavB,CAAC;AAEX,eAAO,MAAM,mBAAmB,mHAStB,CAAC"}
@@ -0,0 +1,25 @@
1
+ export const DEFAULT_CONFIG_FILES = [
2
+ "multi-discord.config.ts",
3
+ "multi-discord.config.js",
4
+ "multi-discord.config.mts",
5
+ "multi-discord.config.mjs",
6
+ "multi-discord.config.cts",
7
+ "multi-discord.config.cjs",
8
+ "multi-discord.js.config.ts",
9
+ "multi-discord.js.config.js",
10
+ "multi-discord.js.config.mts",
11
+ "multi-discord.js.config.mjs",
12
+ "multi-discord.js.config.cts",
13
+ "multi-discord.js.config.cjs",
14
+ ];
15
+ export const DEFAULT_BOT_ENTRIES = [
16
+ "src/bot.ts",
17
+ "src/bot.js",
18
+ "bot.ts",
19
+ "bot.js",
20
+ "index.ts",
21
+ "index.js",
22
+ "src/index.ts",
23
+ "src/index.js",
24
+ ];
25
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,yBAAyB;IACzB,yBAAyB;IACzB,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,4BAA4B;IAC5B,4BAA4B;IAC5B,6BAA6B;IAC7B,6BAA6B;IAC7B,6BAA6B;IAC7B,6BAA6B;CACrB,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,YAAY;IACZ,YAAY;IACZ,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,UAAU;IACV,cAAc;IACd,cAAc;CACN,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { BotProjectConfig, CreateBotClientContext, MultiBotConfig } from "./types.js";
2
+ export { runBots, runBotsFromConfigFile, shutdownBots } from "./runtime.js";
3
+ import type { MultiBotConfig } from "./types.js";
4
+ export declare function defineMultiBotConfig(config: MultiBotConfig): MultiBotConfig;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC3F,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAE3E"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { runBots, runBotsFromConfigFile, shutdownBots } from "./runtime.js";
2
+ export function defineMultiBotConfig(config) {
3
+ return config;
4
+ }
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI5E,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/dist/init.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ type StarterLanguage = "ts" | "js";
2
+ export interface InitOptions {
3
+ dir: string;
4
+ language: StarterLanguage;
5
+ }
6
+ export declare function initProject(options: InitOptions): Promise<string>;
7
+ export {};
8
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAGA,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnC,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAuID,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAKvE"}
package/dist/init.js ADDED
@@ -0,0 +1,110 @@
1
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ function packageJsonTemplate(language) {
4
+ const isTs = language === "ts";
5
+ const scripts = isTs
6
+ ? `"dev": "multi-dc run",
7
+ "build": "tsc -p tsconfig.json"`
8
+ : `"dev": "multi-dc run"`;
9
+ const devDependencies = isTs
10
+ ? `,
11
+ "devDependencies": {
12
+ "typescript": "^5.7.3"
13
+ }`
14
+ : "";
15
+ return `{
16
+ "name": "my-multi-bots",
17
+ "private": true,
18
+ "type": "module",
19
+ "scripts": {
20
+ ${scripts}
21
+ },
22
+ "dependencies": {
23
+ "discord.js": "^14.19.3",
24
+ "multi-dc": "latest"
25
+ }${devDependencies}
26
+ }
27
+ `;
28
+ }
29
+ function tsconfigTemplate() {
30
+ return `{
31
+ "compilerOptions": {
32
+ "target": "ES2022",
33
+ "module": "NodeNext",
34
+ "moduleResolution": "NodeNext",
35
+ "strict": true,
36
+ "skipLibCheck": true
37
+ },
38
+ "include": [
39
+ "bots/**/*.ts",
40
+ "multi-discord.config.ts"
41
+ ]
42
+ }
43
+ `;
44
+ }
45
+ function configTemplate(language) {
46
+ const extension = language === "ts" ? "ts" : "js";
47
+ return `import { defineMultiBotConfig } from "multi-dc";
48
+
49
+ export default defineMultiBotConfig({
50
+ envFile: ".env",
51
+ botsBasePath: "bots",
52
+ onError: "continue",
53
+ bots: [
54
+ { name: "bot1", path: "bot1", entry: "src/bot.${extension}", tokenEnv: "TKN1" },
55
+ { name: "bot2", path: "bot2", entry: "src/bot.${extension}", tokenEnv: "TKN2" },
56
+ { name: "bot3", path: "bot3", entry: "src/bot.${extension}", tokenEnv: "TKN3" }
57
+ ]
58
+ });
59
+ `;
60
+ }
61
+ function botTemplate() {
62
+ return `import { Client, GatewayIntentBits } from "discord.js";
63
+
64
+ export function createBotClient({ name }) {
65
+ const client = new Client({
66
+ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages]
67
+ });
68
+
69
+ client.once("ready", () => {
70
+ console.log(\`[\${name}] logged in as \${client.user?.tag ?? "unknown"}\`);
71
+ });
72
+
73
+ return client;
74
+ }
75
+ `;
76
+ }
77
+ async function writeProjectFiles(targetDir, language) {
78
+ try {
79
+ const entries = await readdir(targetDir);
80
+ if (entries.length > 0) {
81
+ throw new Error(`Target directory is not empty: ${targetDir}. Use --dir <new-folder> to create a new project.`);
82
+ }
83
+ }
84
+ catch (error) {
85
+ if (error.code === "ENOENT") {
86
+ await mkdir(targetDir, { recursive: true });
87
+ }
88
+ else {
89
+ throw error;
90
+ }
91
+ }
92
+ await writeFile(path.join(targetDir, "package.json"), packageJsonTemplate(language), "utf8");
93
+ await writeFile(path.join(targetDir, language === "ts" ? "multi-discord.config.ts" : "multi-discord.config.js"), configTemplate(language), "utf8");
94
+ if (language === "ts") {
95
+ await writeFile(path.join(targetDir, "tsconfig.json"), tsconfigTemplate(), "utf8");
96
+ }
97
+ const bots = ["bot1", "bot2", "bot3"];
98
+ for (const bot of bots) {
99
+ const botSrcDir = path.join(targetDir, "bots", bot, "src");
100
+ await mkdir(botSrcDir, { recursive: true });
101
+ await writeFile(path.join(botSrcDir, `bot.${language}`), botTemplate(), "utf8");
102
+ }
103
+ }
104
+ export async function initProject(options) {
105
+ const invocationDir = process.env.INIT_CWD ?? process.cwd();
106
+ const targetDir = path.resolve(invocationDir, options.dir);
107
+ await writeProjectFiles(targetDir, options.language);
108
+ return targetDir;
109
+ }
110
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=internal-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-runner.d.ts","sourceRoot":"","sources":["../src/internal-runner.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { runBotsFromConfigFile, shutdownBots } from "./runtime.js";
2
+ function parseConfigArg(argv) {
3
+ for (let i = 0; i < argv.length; i++) {
4
+ if ((argv[i] === "--config" || argv[i] === "-c") && argv[i + 1]) {
5
+ return argv[i + 1];
6
+ }
7
+ }
8
+ return undefined;
9
+ }
10
+ async function main() {
11
+ const configPath = parseConfigArg(process.argv.slice(2));
12
+ if (!configPath) {
13
+ throw new Error("Missing --config <file>");
14
+ }
15
+ const result = await runBotsFromConfigFile(configPath);
16
+ if (result.started.length === 0) {
17
+ process.exit(1);
18
+ }
19
+ const shutdown = async () => {
20
+ process.stdout.write("\n[multi-discord.js] shutting down bots...\n");
21
+ await shutdownBots(result.started);
22
+ process.exit(0);
23
+ };
24
+ process.on("SIGINT", shutdown);
25
+ process.on("SIGTERM", shutdown);
26
+ process.stdout.write(`[multi-discord.js] running ${result.started.length} bot(s). Press Ctrl+C to stop.\n`);
27
+ }
28
+ main().catch((error) => {
29
+ process.stderr.write(`[multi-discord.js] ${String(error)}\n`);
30
+ process.exit(1);
31
+ });
32
+ //# sourceMappingURL=internal-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-runner.js","sourceRoot":"","sources":["../src/internal-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEnE,SAAS,cAAc,CAAC,IAAc;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAChE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,UAAU,CAAC,CAAC;IACvD,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACrE,MAAM,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8BAA8B,MAAM,CAAC,OAAO,CAAC,MAAM,kCAAkC,CACtF,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { Client } from "discord.js";
2
+ import type { MultiBotConfig } from "./types.js";
3
+ export interface RunningBot {
4
+ name: string;
5
+ client: Client;
6
+ }
7
+ export interface RunBotsResult {
8
+ started: RunningBot[];
9
+ failed: Array<{
10
+ name: string;
11
+ error: unknown;
12
+ }>;
13
+ }
14
+ export declare function runBots(config: MultiBotConfig, workspaceRoot?: string): Promise<RunBotsResult>;
15
+ export declare function shutdownBots(bots: RunningBot[]): Promise<void>;
16
+ export declare function runBotsFromConfigFile(configPath: string): Promise<RunBotsResult>;
17
+ export declare function resolveDefaultConfigPath(cwd?: string): Promise<string | undefined>;
18
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKpC,OAAO,KAAK,EAIV,cAAc,EACf,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACjD;AA0HD,wBAAsB,OAAO,CAC3B,MAAM,EAAE,cAAc,EACtB,aAAa,SAAgB,GAC5B,OAAO,CAAC,aAAa,CAAC,CA0BxB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAUpE;AAED,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAqBxB;AAED,wBAAsB,wBAAwB,CAC5C,GAAG,SAAgB,GAClB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAS7B"}
@@ -0,0 +1,143 @@
1
+ import { Client } from "discord.js";
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { DEFAULT_BOT_ENTRIES, DEFAULT_CONFIG_FILES } from "./constants.js";
6
+ import { fileExists, resolveFrom, withDotSlash } from "./utils.js";
7
+ async function resolveEntryFile(projectRoot, configuredEntry) {
8
+ if (configuredEntry) {
9
+ const resolved = resolveFrom(projectRoot, configuredEntry);
10
+ if (!(await fileExists(resolved))) {
11
+ throw new Error(`Configured entry file does not exist: ${resolved}`);
12
+ }
13
+ return resolved;
14
+ }
15
+ for (const candidate of DEFAULT_BOT_ENTRIES) {
16
+ const resolved = path.resolve(projectRoot, candidate);
17
+ if (await fileExists(resolved)) {
18
+ return resolved;
19
+ }
20
+ }
21
+ throw new Error(`Could not find a bot entry in ${projectRoot}. Checked: ${DEFAULT_BOT_ENTRIES.join(", ")}`);
22
+ }
23
+ async function loadClientFactory(entryFile) {
24
+ const moduleUrl = pathToFileURL(entryFile).href;
25
+ const imported = (await import(moduleUrl));
26
+ const factory = imported.createBotClient ?? imported.default;
27
+ if (typeof factory !== "function") {
28
+ throw new Error(`Bot module "${entryFile}" must export a function named "createBotClient" or a default function.`);
29
+ }
30
+ return factory;
31
+ }
32
+ function parseEnvLine(line) {
33
+ const trimmed = line.trim();
34
+ if (!trimmed || trimmed.startsWith("#")) {
35
+ return undefined;
36
+ }
37
+ const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(trimmed);
38
+ if (!match) {
39
+ return undefined;
40
+ }
41
+ const key = match[1];
42
+ if (!key) {
43
+ return undefined;
44
+ }
45
+ const rawValue = match[2] ?? "";
46
+ const value = rawValue.replace(/^['"]|['"]$/g, "");
47
+ return { key, value: value.replace(/\\n/g, "\n") };
48
+ }
49
+ async function loadEnvFile(envFilePath) {
50
+ if (!(await fileExists(envFilePath))) {
51
+ throw new Error(`Configured env file does not exist: ${envFilePath}`);
52
+ }
53
+ const envContent = await readFile(envFilePath, "utf8");
54
+ for (const line of envContent.split(/\r?\n/)) {
55
+ const parsed = parseEnvLine(line);
56
+ if (!parsed) {
57
+ continue;
58
+ }
59
+ if (process.env[parsed.key] === undefined) {
60
+ process.env[parsed.key] = parsed.value;
61
+ }
62
+ }
63
+ }
64
+ async function startBot(project, workspaceRoot, botsBasePath) {
65
+ const botRoot = botsBasePath
66
+ ? resolveFrom(workspaceRoot, botsBasePath)
67
+ : workspaceRoot;
68
+ const projectRoot = resolveFrom(botRoot, project.path);
69
+ const tokenEnv = project.tokenEnv ?? `${project.name.toUpperCase()}_TOKEN`;
70
+ const token = process.env[tokenEnv];
71
+ if (!token) {
72
+ throw new Error(`Missing token env var "${tokenEnv}" for bot "${project.name}".`);
73
+ }
74
+ const entryFile = await resolveEntryFile(projectRoot, project.entry);
75
+ const factory = await loadClientFactory(entryFile);
76
+ const ctx = {
77
+ name: project.name,
78
+ token,
79
+ tokenEnv,
80
+ rootPath: projectRoot,
81
+ };
82
+ const client = await factory(ctx);
83
+ if (!(client instanceof Client)) {
84
+ throw new Error(`Bot "${project.name}" did not return a discord.js Client instance.`);
85
+ }
86
+ await client.login(token);
87
+ return { name: project.name, client };
88
+ }
89
+ export async function runBots(config, workspaceRoot = process.cwd()) {
90
+ const started = [];
91
+ const failed = [];
92
+ const mode = config.onError ?? "continue";
93
+ for (const project of config.bots) {
94
+ try {
95
+ const running = await startBot(project, workspaceRoot, config.botsBasePath);
96
+ started.push(running);
97
+ process.stdout.write(`[multi-discord.js] started "${project.name}"\n`);
98
+ }
99
+ catch (error) {
100
+ failed.push({ name: project.name, error });
101
+ process.stderr.write(`[multi-discord.js] failed "${project.name}": ${String(error)}\n`);
102
+ if (mode === "stop") {
103
+ break;
104
+ }
105
+ }
106
+ }
107
+ return { started, failed };
108
+ }
109
+ export async function shutdownBots(bots) {
110
+ await Promise.all(bots.map(async ({ client }) => {
111
+ try {
112
+ await client.destroy();
113
+ }
114
+ catch {
115
+ // Ignore per-client shutdown errors.
116
+ }
117
+ }));
118
+ }
119
+ export async function runBotsFromConfigFile(configPath) {
120
+ const absoluteConfigPath = path.resolve(process.cwd(), configPath);
121
+ const configModuleUrl = pathToFileURL(absoluteConfigPath).href;
122
+ const imported = (await import(configModuleUrl));
123
+ const config = imported.default;
124
+ if (!config || !Array.isArray(config.bots)) {
125
+ throw new Error(`Config file "${configPath}" must default-export { bots: [...] }`);
126
+ }
127
+ const workspaceRoot = path.dirname(absoluteConfigPath);
128
+ if (config.envFile) {
129
+ const envFilePath = resolveFrom(workspaceRoot, config.envFile);
130
+ await loadEnvFile(envFilePath);
131
+ }
132
+ return runBots(config, workspaceRoot);
133
+ }
134
+ export async function resolveDefaultConfigPath(cwd = process.cwd()) {
135
+ for (const filename of DEFAULT_CONFIG_FILES) {
136
+ const candidate = path.resolve(cwd, filename);
137
+ if (await fileExists(candidate)) {
138
+ return withDotSlash(path.relative(cwd, candidate));
139
+ }
140
+ }
141
+ return undefined;
142
+ }
143
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { ClientOptions } from "discord.js";
2
+ export interface BotProjectConfig {
3
+ name: string;
4
+ path: string;
5
+ entry?: string;
6
+ tokenEnv?: string;
7
+ options?: ClientOptions;
8
+ }
9
+ export interface MultiBotConfig {
10
+ bots: BotProjectConfig[];
11
+ envFile?: string;
12
+ botsBasePath?: string;
13
+ onError?: "continue" | "stop";
14
+ }
15
+ export interface CreateBotClientContext {
16
+ name: string;
17
+ token: string;
18
+ tokenEnv: string;
19
+ rootPath: string;
20
+ }
21
+ export interface BotClientModule {
22
+ createBotClient?: (ctx: CreateBotClientContext) => Promise<import("discord.js").Client> | import("discord.js").Client;
23
+ default?: (ctx: CreateBotClientContext) => Promise<import("discord.js").Client> | import("discord.js").Client;
24
+ }
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAC,OAAO,YAAY,EAAE,MAAM,CAAC,GAAG,OAAO,YAAY,EAAE,MAAM,CAAC;IACtH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAC,OAAO,YAAY,EAAE,MAAM,CAAC,GAAG,OAAO,YAAY,EAAE,MAAM,CAAC;CAC/G"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export declare function fileExists(filepath: string): Promise<boolean>;
2
+ export declare function resolveFrom(base: string, target: string): string;
3
+ export declare function withDotSlash(input: string): string;
4
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD"}
package/dist/utils.js ADDED
@@ -0,0 +1,21 @@
1
+ import { access } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function fileExists(filepath) {
4
+ try {
5
+ await access(filepath);
6
+ return true;
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ export function resolveFrom(base, target) {
13
+ return path.isAbsolute(target) ? target : path.resolve(base, target);
14
+ }
15
+ export function withDotSlash(input) {
16
+ if (input.startsWith(".") || input.startsWith("/")) {
17
+ return input;
18
+ }
19
+ return `./${input}`;
20
+ }
21
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAc;IACtD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,KAAK,EAAE,CAAC;AACtB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "multi-dc",
3
+ "version": "0.1.0",
4
+ "description": "Run multiple discord.js bots from separate folders in one Node.js process.",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "multi-dc": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18.20.0"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc -p tsconfig.json",
18
+ "dev": "tsx src/cli.ts",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit"
20
+ },
21
+ "keywords": [
22
+ "discord",
23
+ "discord.js",
24
+ "bot",
25
+ "multi-bot",
26
+ "typescript",
27
+ "javascript"
28
+ ],
29
+ "author": "",
30
+ "license": "ISC",
31
+ "type": "module",
32
+ "dependencies": {
33
+ "discord.js": "^14.19.3",
34
+ "multi-dc": "^0.1.0",
35
+ "tsx": "^4.20.3"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.13.5",
39
+ "typescript": "^5.7.3"
40
+ }
41
+ }