create-discord-app 3.1.0 → 5.0.0-dev.1759924903-0c2975e3f
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 +8 -7
- package/dist/index.js +1 -1
- package/package.json +18 -20
- package/template/Bun/JavaScript/package.json +8 -7
- package/template/Bun/TypeScript/package.json +10 -9
- package/template/Deno/.vscode/extensions.json +1 -1
- package/template/Deno/.vscode/settings.json +3 -6
- package/template/Deno/deno.jsonc +11 -26
- package/template/Deno/src/commands/index.ts +2 -2
- package/template/Deno/src/commands/utility/user.ts +11 -0
- package/template/Deno/src/events/index.ts +5 -5
- package/template/Deno/src/events/interactionCreate.ts +20 -0
- package/template/Deno/src/events/ready.ts +1 -1
- package/template/Deno/src/index.ts +11 -7
- package/template/Deno/src/util/deploy.ts +2 -4
- package/template/Deno/src/util/loaders.ts +17 -24
- package/template/JavaScript/package.json +13 -10
- package/template/JavaScript/src/commands/utility/user.js +10 -0
- package/template/JavaScript/src/events/index.js +4 -4
- package/template/JavaScript/src/events/interactionCreate.js +20 -0
- package/template/JavaScript/src/index.js +10 -4
- package/template/JavaScript/src/util/loaders.js +18 -26
- package/template/TypeScript/.vscode/settings.json +2 -2
- package/template/TypeScript/package.json +15 -12
- package/template/TypeScript/src/commands/utility/user.ts +11 -0
- package/template/TypeScript/src/events/index.ts +3 -3
- package/template/TypeScript/src/events/interactionCreate.ts +21 -0
- package/template/TypeScript/src/index.ts +10 -4
- package/template/TypeScript/src/util/loaders.ts +20 -25
- package/template/Deno/.prettierrc.json +0 -9
- package/template/Deno/src/util/registerEvents.ts +0 -25
- package/template/JavaScript/src/util/registerEvents.js +0 -29
- package/template/TypeScript/src/util/registerEvents.ts +0 -25
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
<br />
|
|
7
7
|
<p>
|
|
8
8
|
<a href="https://discord.gg/djs"><img src="https://img.shields.io/discord/222078108977594368?color=5865F2&logo=discord&logoColor=white" alt="Discord server" /></a>
|
|
9
|
-
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/
|
|
9
|
+
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/tests.yml/badge.svg" alt="Build status" /></a>
|
|
10
|
+
<a href="https://github.com/discordjs/discord.js/commits/main/packages/create-discord-bot"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fcreate-discord-bot" /></a>
|
|
10
11
|
</p>
|
|
11
12
|
<p>
|
|
12
13
|
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
|
@@ -19,10 +20,10 @@
|
|
|
19
20
|
It's easy to create a simple Discord bot to begin your journey with the Discord API.
|
|
20
21
|
|
|
21
22
|
```sh
|
|
22
|
-
npm create discord-
|
|
23
|
-
yarn create discord-
|
|
24
|
-
pnpm create discord-
|
|
25
|
-
|
|
23
|
+
npm create discord-app ./your/chosen/directory
|
|
24
|
+
yarn create discord-app ./your/chosen/directory
|
|
25
|
+
pnpm create discord-app ./your/chosen/directory
|
|
26
|
+
bun create discord-app ./your/chosen/directory
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
## Links
|
|
@@ -30,7 +31,7 @@ bunx create-discord-bot ./your/chosen/directory
|
|
|
30
31
|
- [Guide] ([source][guide-source])
|
|
31
32
|
- Also see the v13 to v14 [Update Guide][guide-update], which includes updated and removed items from the library.
|
|
32
33
|
- [discord.js Discord server][discord]
|
|
33
|
-
- [Discord
|
|
34
|
+
- [Discord Developers Discord server][discord-developers]
|
|
34
35
|
- [GitHub][source]
|
|
35
36
|
|
|
36
37
|
## Contributing
|
|
@@ -46,6 +47,6 @@ If you don't understand something in the documentation, you are experiencing pro
|
|
|
46
47
|
[guide-source]: https://github.com/discordjs/guide
|
|
47
48
|
[guide-update]: https://discordjs.guide/additional-info/changes-in-v14.html
|
|
48
49
|
[discord]: https://discord.gg/djs
|
|
49
|
-
[discord-
|
|
50
|
+
[discord-developers]: https://discord.gg/discord-developers
|
|
50
51
|
[source]: https://github.com/discordjs/discord.js/tree/main/packages/create-discord-bot
|
|
51
52
|
[contributing]: https://github.com/discordjs/discord.js/blob/main/.github/CONTRIBUTING.md
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __defProp=Object.defineProperty,__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0});import process3 from"
|
|
2
|
+
var __defProp=Object.defineProperty,__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0});import process3 from"process";import{styleText as styleText3}from"util";import{Option,program}from"commander";import prompts from"prompts";import validateProjectName from"validate-npm-package-name";var package_default_name="create-discord-app",package_default_version="5.0.0-dev.1759924903-0c2975e3f";import{cp,glob,mkdir,stat,readdir,readFile,writeFile}from"fs/promises";import path from"path";import process2 from"process";import{URL}from"url";import{styleText as styleText2}from"util";import{execSync}from"child_process";import process from"process";import{styleText}from"util";var NODE_PACKAGE_MANAGERS=["npm","pnpm","yarn"];function resolvePackageManager(){const npmConfigUserAgent=process.env.npm_config_user_agent;return"undefined"!=typeof Deno?"deno":npmConfigUserAgent?npmConfigUserAgent.startsWith("npm")?"npm":npmConfigUserAgent.startsWith("yarn")?"yarn":npmConfigUserAgent.startsWith("pnpm")?"pnpm":npmConfigUserAgent.startsWith("bun")?"bun":(console.error(styleText("yellow",`Detected an unsupported package manager (${npmConfigUserAgent}). Falling back to npm.`)),"npm"):"npm"}function install(packageManager2){let installCommand=`${packageManager2} install`;switch(console.log(`Installing dependencies with ${packageManager2}...`),packageManager2){case"yarn":console.log(),installCommand=[`${packageManager2} set version stable`,`${packageManager2} config set nodeLinker node-modules`,`${packageManager2} config set logFilters --json '[{ "code": "YN0002", "level": "discard" }, { "code": "YN0013", "level": "discard" }, { "code": "YN0032", "level": "discard" }, { "code": "YN0060", "level": "discard" }]'`,`${packageManager2} plugin import interactive-tools`,`${packageManager2} plugin import workspace-tools`,installCommand];break;case"deno":installCommand=`${packageManager2} cache --reload src/index.ts`;break;case"pnpm":case"bun":console.log()}const env={...process.env,ADBLOCK:"1",NODE_ENV:"development",DISABLE_OPENCOLLECTIVE:"1"};if(Array.isArray(installCommand))for(const[index,command]of installCommand.entries()){if(index===installCommand.length-1){execSync(command,{stdio:"inherit",env:env});break}execSync(command,{stdio:"ignore",env:env})}else execSync(installCommand,{stdio:"inherit",env:env})}function isNodePackageManager(packageManager2){return NODE_PACKAGE_MANAGERS.includes(packageManager2)}async function createDiscordBot({directory:directory,installPackages:installPackages2,typescript:typescript2,packageManager:packageManager2}){const root=path.resolve(directory),directoryName=path.basename(root);console.log();(!(await stat(root).catch(async error=>{if("ENOENT"===error.code)return await mkdir(root,{recursive:!0}),stat(root);throw error})).isDirectory()||(await readdir(root)).length>0)&&(console.error(styleText2("red",`The directory ${styleText2("yellow",`"${directoryName}"`)} is either not a directory or is not empty.`)),console.error(styleText2("red","Please specify an empty directory.")),process2.exit(1)),console.log(`Creating ${directoryName} in ${styleText2("green",root)}.`);const deno2="deno"===packageManager2;await cp(new URL("../template/"+(deno2?"Deno":typescript2?"TypeScript":"JavaScript"),import.meta.url),root,{recursive:!0});const bun="bun"===packageManager2;bun&&(await cp(new URL(`../template/Bun/${typescript2?"TypeScript":"JavaScript"}/package.json`,import.meta.url),`${root}/package.json`),typescript2&&(await cp(new URL("../template/Bun/TypeScript/tsconfig.eslint.json",import.meta.url),`${root}/tsconfig.eslint.json`),await cp(new URL("../template/Bun/TypeScript/tsconfig.json",import.meta.url),`${root}/tsconfig.json`))),process2.chdir(root);const newVSCodeSettings=await readFile("./.vscode/settings.json",{encoding:"utf8"}).then(str=>{let newStr=str.replace("[REPLACE_ME]",deno2||bun?"auto":packageManager2);return deno2&&(newStr=newStr.replaceAll('"[REPLACE_BOOL]"',!0)),newStr});await writeFile("./.vscode/settings.json",newVSCodeSettings);const globIterator=glob("./src/**/*.ts");for await(const file of globIterator){const newData=await readFile(file,{encoding:"utf8"}).then(str=>str.replaceAll("[REPLACE_IMPORT_EXT]",typescript2&&!isNodePackageManager(packageManager2)?"ts":"js"));await writeFile(file,newData)}if(!deno2){const newPackageJSON=await readFile("./package.json",{encoding:"utf8"}).then(str=>{let newStr=str.replace("[REPLACE_ME]",directoryName);return newStr=newStr.replaceAll("[REPLACE_IMPORT_EXT]",typescript2&&!isNodePackageManager(packageManager2)?"ts":"js"),newStr});await writeFile("./package.json",newPackageJSON)}if(installPackages2)try{install(packageManager2)}catch(error){console.log();"SIGINT"===error.signal?console.log(styleText2("red","Installation aborted.")):(console.error(styleText2("red","Installation failed.")),process2.exit(1))}console.log(),console.log(styleText2("green","All done! Be sure to read through the discord.js guide for help on your journey.")),console.log(`Link: ${styleText2("cyan","https://discordjs.guide")}`)}__name(resolvePackageManager,"resolvePackageManager"),__name(install,"install"),__name(isNodePackageManager,"isNodePackageManager"),__name(createDiscordBot,"createDiscordBot");var projectDirectory="",handleSigTerm=__name(()=>process3.exit(0),"handleSigTerm");process3.on("SIGINT",handleSigTerm),process3.on("SIGTERM",handleSigTerm);var onPromptState=__name(state=>{state.aborted&&(process3.stdout.write("[?25h"),process3.stdout.write("\n"),process3.exit(1))},"onPromptState");program.name(package_default_name).version(package_default_version).description("Create a basic discord.js bot.").argument("[directory]","What is the name of the directory you want to create this project in?").usage(`${styleText3("green","<directory>")}`).action(directory=>{projectDirectory=directory}).option("--typescript","Whether to use the TypeScript template.").option("--javascript","Whether to use the JavaScript template.").option("--no-install","Whether to not automatically install the packages.").addOption(new Option("--package-manager <packageManager>","The package manager to use.").choices(["npm","pnpm","yarn","bun","deno"]).default(resolvePackageManager())).allowUnknownOption().parse();var{typescript:typescript,javascript:javascript,packageManager:packageManager,install:installPackages}=program.opts();if(projectDirectory||(projectDirectory=(await prompts({onState:onPromptState,type:"text",name:"directory",initial:"my-bot",message:"What is the name of the directory you want to create this project in?",validate:__name(directory=>{const validationResult=validateProjectName(directory);if(!validationResult.validForNewPackages){const errors=[];for(const error of[...validationResult.errors??[],...validationResult.warnings??[]])errors.push(styleText3("red",`- ${error}`));return styleText3("red",`Cannot create a project named ${styleText3("yellow",`"${directory}"`)} due to npm naming restrictions.\n\nErrors:\n${errors.join("\n")}\n\n${styleText3("red","\nSee https://docs.npmjs.com/cli/configuring-npm/package-json for more details.")}}`)}return!0},"validate")})).directory),!("deno"===packageManager)&&void 0===typescript&&void 0===javascript){const{useTypescript:useTypescript}=await prompts({onState:onPromptState,type:"toggle",name:"useTypescript",message:"Do you want to use TypeScript?",initial:!0,active:"Yes",inactive:"No"});typescript=useTypescript}await createDiscordBot({typescript:typescript,directory:projectDirectory,packageManager:packageManager,installPackages:installPackages});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "create-discord-app",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0-dev.1759924903-0c2975e3f",
|
|
5
5
|
"description": "A simple way to create a startup Discord bot.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": "./dist/index.js",
|
|
@@ -41,41 +41,39 @@
|
|
|
41
41
|
"homepage": "https://discord.js.org",
|
|
42
42
|
"funding": "https://github.com/discordjs/discord.js?sponsor",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"commander": "^
|
|
45
|
-
"fast-glob": "^3.3.2",
|
|
46
|
-
"picocolors": "^1.1.1",
|
|
44
|
+
"commander": "^14.0.1",
|
|
47
45
|
"prompts": "^2.4.2",
|
|
48
|
-
"validate-npm-package-name": "^
|
|
46
|
+
"validate-npm-package-name": "^6.0.2"
|
|
49
47
|
},
|
|
50
48
|
"devDependencies": {
|
|
51
49
|
"@favware/cliff-jumper": "^4.1.0",
|
|
52
|
-
"@types/node": "^
|
|
50
|
+
"@types/node": "^22.18.8",
|
|
53
51
|
"@types/prompts": "^2.4.9",
|
|
54
52
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"eslint": "^
|
|
58
|
-
"eslint-
|
|
59
|
-
"eslint-formatter-pretty": "^
|
|
60
|
-
"prettier": "^3.
|
|
61
|
-
"terser": "^5.
|
|
62
|
-
"tsup": "^8.
|
|
63
|
-
"typescript": "~5.
|
|
64
|
-
"
|
|
65
|
-
"@discordjs/api-extractor": "^7.38.1"
|
|
53
|
+
"cross-env": "^10.1.0",
|
|
54
|
+
"eslint": "^9.37.0",
|
|
55
|
+
"eslint-config-neon": "^0.2.7",
|
|
56
|
+
"eslint-formatter-compact": "^8.40.0",
|
|
57
|
+
"eslint-formatter-pretty": "^7.0.0",
|
|
58
|
+
"prettier": "^3.6.2",
|
|
59
|
+
"terser": "^5.44.0",
|
|
60
|
+
"tsup": "^8.5.0",
|
|
61
|
+
"typescript": "~5.9.3",
|
|
62
|
+
"@discordjs/api-extractor": "^7.52.7"
|
|
66
63
|
},
|
|
67
64
|
"engines": {
|
|
68
|
-
"node": ">=
|
|
65
|
+
"node": ">=22.12.0"
|
|
69
66
|
},
|
|
70
67
|
"publishConfig": {
|
|
71
68
|
"access": "public",
|
|
72
|
-
"provenance":
|
|
69
|
+
"provenance": true
|
|
73
70
|
},
|
|
74
71
|
"scripts": {
|
|
75
72
|
"build": "tsc --noEmit && tsup",
|
|
76
73
|
"lint": "prettier --check . && cross-env TIMING=1 eslint --format=pretty src",
|
|
77
74
|
"format": "prettier --write . && cross-env TIMING=1 eslint --fix --format=pretty src",
|
|
78
75
|
"changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/create-discord-bot/*'",
|
|
79
|
-
"release": "cliff-jumper"
|
|
76
|
+
"release": "cliff-jumper",
|
|
77
|
+
"rename-to-app": "node scripts/rename-to-app.mjs"
|
|
80
78
|
}
|
|
81
79
|
}
|
|
@@ -11,14 +11,15 @@
|
|
|
11
11
|
"start": "bun run src/index.[REPLACE_IMPORT_EXT]"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@discordjs/core": "^2.
|
|
15
|
-
"discord.js": "^14.
|
|
14
|
+
"@discordjs/core": "^2.2.2",
|
|
15
|
+
"discord.js": "^14.22.1"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"eslint": "^
|
|
19
|
-
"eslint-config-neon": "^0.
|
|
20
|
-
"eslint-formatter-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
18
|
+
"eslint": "^9.37.0",
|
|
19
|
+
"eslint-config-neon": "^0.2.7",
|
|
20
|
+
"eslint-formatter-compact": "^8.40.0",
|
|
21
|
+
"eslint-formatter-pretty": "^7.0.0",
|
|
22
|
+
"prettier": "^3.6.2",
|
|
23
|
+
"zod": "^3.25.76"
|
|
23
24
|
}
|
|
24
25
|
}
|
|
@@ -11,17 +11,18 @@
|
|
|
11
11
|
"start": "bun run src/index.[REPLACE_IMPORT_EXT]"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@discordjs/core": "^2.
|
|
15
|
-
"discord.js": "^14.
|
|
14
|
+
"@discordjs/core": "^2.2.2",
|
|
15
|
+
"discord.js": "^14.22.1"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@sapphire/ts-config": "^5.0.1",
|
|
19
|
-
"@types/bun": "^1.
|
|
20
|
-
"eslint": "^
|
|
21
|
-
"eslint-config-neon": "^0.
|
|
22
|
-
"eslint-formatter-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
19
|
+
"@types/bun": "^1.2.23",
|
|
20
|
+
"eslint": "^9.37.0",
|
|
21
|
+
"eslint-config-neon": "^0.2.7",
|
|
22
|
+
"eslint-formatter-compact": "^8.40.0",
|
|
23
|
+
"eslint-formatter-pretty": "^7.0.0",
|
|
24
|
+
"prettier": "^3.6.2",
|
|
25
|
+
"typescript": "~5.9.3",
|
|
26
|
+
"zod": "^3.25.76"
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
"editor.defaultFormatter": "denoland.vscode-deno",
|
|
3
3
|
"editor.formatOnSave": true,
|
|
4
4
|
"editor.codeActionsOnSave": {
|
|
5
|
-
"source.fixAll":
|
|
6
|
-
"source.organizeImports":
|
|
5
|
+
"source.fixAll": "always",
|
|
6
|
+
"source.organizeImports": "always"
|
|
7
7
|
},
|
|
8
8
|
"editor.trimAutoWhitespace": false,
|
|
9
9
|
"files.insertFinalNewline": true,
|
|
10
10
|
"files.eol": "\n",
|
|
11
|
-
"
|
|
12
|
-
"deno.enable": "[REPLACE_BOOL]",
|
|
13
|
-
"deno.lint": "[REPLACE_BOOL]",
|
|
14
|
-
"deno.unstable": false
|
|
11
|
+
"deno.enable": "[REPLACE_BOOL]"
|
|
15
12
|
}
|
package/template/Deno/deno.jsonc
CHANGED
|
@@ -2,40 +2,25 @@
|
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
|
|
3
3
|
"tasks": {
|
|
4
4
|
"lint": "deno lint",
|
|
5
|
-
"deploy": "deno run --allow-read --allow-env --allow-net src/util/deploy.ts",
|
|
5
|
+
"deploy": "deno run --env-file --allow-read --allow-env --allow-net src/util/deploy.ts",
|
|
6
6
|
"format": "deno fmt",
|
|
7
7
|
"fmt": "deno fmt",
|
|
8
|
-
"start": "deno run --allow-read --allow-env --allow-net src/index.ts",
|
|
8
|
+
"start": "deno run --env-file --allow-read --allow-env --allow-net src/index.ts",
|
|
9
|
+
},
|
|
10
|
+
"fmt": {
|
|
11
|
+
"useTabs": true,
|
|
12
|
+
"lineWidth": 120,
|
|
13
|
+
"singleQuote": true,
|
|
9
14
|
},
|
|
10
15
|
"lint": {
|
|
11
|
-
"include": ["src/"],
|
|
12
16
|
"rules": {
|
|
13
17
|
"tags": ["recommended"],
|
|
14
18
|
"exclude": ["require-await", "no-await-in-sync-fn"],
|
|
15
19
|
},
|
|
16
20
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"singleQuote": true,
|
|
22
|
-
"proseWrap": "preserve",
|
|
23
|
-
"include": ["src/"],
|
|
24
|
-
},
|
|
25
|
-
"compilerOptions": {
|
|
26
|
-
"alwaysStrict": true,
|
|
27
|
-
"emitDecoratorMetadata": true,
|
|
28
|
-
"verbatimModuleSyntax": true,
|
|
29
|
-
"lib": ["deno.window"],
|
|
30
|
-
"noFallthroughCasesInSwitch": true,
|
|
31
|
-
"noImplicitReturns": true,
|
|
32
|
-
"noUnusedLocals": true,
|
|
33
|
-
"noUnusedParameters": true,
|
|
34
|
-
"removeComments": false,
|
|
35
|
-
"strict": true,
|
|
36
|
-
"allowUnreachableCode": false,
|
|
37
|
-
"allowUnusedLabels": false,
|
|
38
|
-
"exactOptionalPropertyTypes": false,
|
|
39
|
-
"noImplicitOverride": true,
|
|
21
|
+
"imports": {
|
|
22
|
+
"@discordjs/core": "npm:@discordjs/core@^2.2.2",
|
|
23
|
+
"discord.js": "npm:discord.js@^14.22.1",
|
|
24
|
+
"zod": "npm:zod@^3.25.76",
|
|
40
25
|
},
|
|
41
26
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { z } from '
|
|
1
|
+
import type { CommandInteraction, RESTPostAPIApplicationCommandsJSONBody } from 'discord.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
3
|
import type { StructurePredicate } from '../util/loaders.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Command } from '../index.ts';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
data: {
|
|
5
|
+
name: 'user',
|
|
6
|
+
description: 'Provides information about the user.',
|
|
7
|
+
},
|
|
8
|
+
async execute(interaction) {
|
|
9
|
+
await interaction.reply(`This command was run by ${interaction.user.username}.`);
|
|
10
|
+
},
|
|
11
|
+
} satisfies Command;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import type { ClientEvents } from '
|
|
2
|
-
import { z } from '
|
|
1
|
+
import type { ClientEvents } from 'discord.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
3
|
import type { StructurePredicate } from '../util/loaders.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Defines the structure of an event.
|
|
7
7
|
*/
|
|
8
|
-
export type Event<
|
|
8
|
+
export type Event<EventName extends keyof ClientEvents = keyof ClientEvents> = {
|
|
9
9
|
/**
|
|
10
10
|
* The function to execute when the event is emitted.
|
|
11
11
|
*
|
|
12
12
|
* @param parameters - The parameters of the event
|
|
13
13
|
*/
|
|
14
|
-
execute(...parameters: ClientEvents[
|
|
14
|
+
execute(...parameters: ClientEvents[EventName]): Promise<void> | void;
|
|
15
15
|
/**
|
|
16
16
|
* The name of the event to listen to
|
|
17
17
|
*/
|
|
18
|
-
name:
|
|
18
|
+
name: EventName;
|
|
19
19
|
/**
|
|
20
20
|
* Whether or not the event should only be listened to once
|
|
21
21
|
*
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Events } from 'discord.js';
|
|
2
|
+
import type { Event } from './index.ts';
|
|
3
|
+
import { loadCommands } from '../util/loaders.ts';
|
|
4
|
+
|
|
5
|
+
const commands = await loadCommands(new URL('../commands/', import.meta.url));
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: Events.InteractionCreate,
|
|
9
|
+
async execute(interaction) {
|
|
10
|
+
if (interaction.isCommand()) {
|
|
11
|
+
const command = commands.get(interaction.commandName);
|
|
12
|
+
|
|
13
|
+
if (!command) {
|
|
14
|
+
throw new Error(`Command '${interaction.commandName}' not found.`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
await command.execute(interaction);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
} satisfies Event<Events.InteractionCreate>;
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import {
|
|
3
|
-
import { Client, GatewayIntentBits } from 'npm:discord.js@^14.17.0';
|
|
4
|
-
import { loadCommands, loadEvents } from './util/loaders.ts';
|
|
5
|
-
import { registerEvents } from './util/registerEvents.ts';
|
|
1
|
+
import { Client, GatewayIntentBits } from 'discord.js';
|
|
2
|
+
import { loadEvents } from './util/loaders.ts';
|
|
6
3
|
|
|
7
4
|
// Initialize the client
|
|
8
5
|
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
9
6
|
|
|
10
7
|
// Load the events and commands
|
|
11
8
|
const events = await loadEvents(new URL('events/', import.meta.url));
|
|
12
|
-
const commands = await loadCommands(new URL('commands/', import.meta.url));
|
|
13
9
|
|
|
14
10
|
// Register the event handlers
|
|
15
|
-
|
|
11
|
+
for (const event of events) {
|
|
12
|
+
client[event.once ? 'once' : 'on'](event.name, async (...args) => {
|
|
13
|
+
try {
|
|
14
|
+
await event.execute(...args);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error(`Error executing event ${String(event.name)}:`, error);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
16
20
|
|
|
17
21
|
// Login to the client
|
|
18
22
|
void client.login(Deno.env.get('DISCORD_TOKEN'));
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import {
|
|
3
|
-
import { API } from 'npm:@discordjs/core@^2.0.1/http-only';
|
|
4
|
-
import { REST } from 'npm:discord.js@^14.17.0';
|
|
1
|
+
import { API } from '@discordjs/core/http-only';
|
|
2
|
+
import { REST } from 'discord.js';
|
|
5
3
|
import { loadCommands } from './loaders.ts';
|
|
6
4
|
|
|
7
5
|
const commands = await loadCommands(new URL('../commands/', import.meta.url));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PathLike } from 'node:fs';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { glob, stat } from 'node:fs/promises';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import type { Command } from '../commands/index.ts';
|
|
5
6
|
import { predicate as commandPredicate } from '../commands/index.ts';
|
|
6
7
|
import type { Event } from '../events/index.ts';
|
|
@@ -9,7 +10,7 @@ import { predicate as eventPredicate } from '../events/index.ts';
|
|
|
9
10
|
/**
|
|
10
11
|
* A predicate to check if the structure is valid
|
|
11
12
|
*/
|
|
12
|
-
export type StructurePredicate<
|
|
13
|
+
export type StructurePredicate<Structure> = (structure: unknown) => structure is Structure;
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Loads all the structures in the provided directory
|
|
@@ -19,11 +20,11 @@ export type StructurePredicate<T> = (structure: unknown) => structure is T;
|
|
|
19
20
|
* @param recursive - Whether to recursively load the structures in the directory
|
|
20
21
|
* @returns
|
|
21
22
|
*/
|
|
22
|
-
export async function loadStructures<
|
|
23
|
+
export async function loadStructures<Structure>(
|
|
23
24
|
dir: PathLike,
|
|
24
|
-
predicate: StructurePredicate<
|
|
25
|
+
predicate: StructurePredicate<Structure>,
|
|
25
26
|
recursive = true,
|
|
26
|
-
): Promise<
|
|
27
|
+
): Promise<Structure[]> {
|
|
27
28
|
// Get the stats of the directory
|
|
28
29
|
const statDir = await stat(dir);
|
|
29
30
|
|
|
@@ -32,32 +33,24 @@ export async function loadStructures<T>(
|
|
|
32
33
|
throw new Error(`The directory '${dir}' is not a directory.`);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
// Get all the files in the directory
|
|
36
|
-
const files = await readdir(dir);
|
|
37
|
-
|
|
38
36
|
// Create an empty array to store the structures
|
|
39
|
-
const structures:
|
|
40
|
-
|
|
41
|
-
// Loop through all the files in the directory
|
|
42
|
-
for (const file of files) {
|
|
43
|
-
// If the file is index.js or the file does not end with .js, skip the file
|
|
44
|
-
if (file === 'index.ts' || !file.endsWith('.ts')) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
37
|
+
const structures: Structure[] = [];
|
|
47
38
|
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
// Create a glob pattern to match the .ts files
|
|
40
|
+
const basePath = dir instanceof URL ? fileURLToPath(dir) : dir.toString();
|
|
41
|
+
const pattern = resolve(basePath, recursive ? '**/*.ts' : '*.ts');
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
// Loop through all the matching files in the directory
|
|
44
|
+
for await (const file of glob(pattern)) {
|
|
45
|
+
// If the file is index.ts, skip the file
|
|
46
|
+
if (file.endsWith('/index.ts')) {
|
|
54
47
|
continue;
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
// Import the structure dynamically from the file
|
|
58
|
-
const structure =
|
|
51
|
+
const { default: structure } = await import(file);
|
|
59
52
|
|
|
60
|
-
// If the
|
|
53
|
+
// If the default export is a valid structure, add it
|
|
61
54
|
if (predicate(structure)) {
|
|
62
55
|
structures.push(structure);
|
|
63
56
|
}
|
|
@@ -7,19 +7,22 @@
|
|
|
7
7
|
"scripts": {
|
|
8
8
|
"lint": "prettier --check . && eslint --ext .js,.mjs,.cjs --format=pretty src",
|
|
9
9
|
"format": "prettier --write . && eslint --ext .js,.mjs,.cjs --fix --format=pretty src",
|
|
10
|
-
"start": "node --
|
|
11
|
-
"deploy": "node --
|
|
10
|
+
"start": "node --env-file=.env src/index.js",
|
|
11
|
+
"deploy": "node --env-file=.env src/util/deploy.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@discordjs/core": "^2.
|
|
15
|
-
"discord.js": "^14.
|
|
16
|
-
"dotenv": "^16.4.5"
|
|
14
|
+
"@discordjs/core": "^2.2.2",
|
|
15
|
+
"discord.js": "^14.22.1"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
19
|
-
"eslint": "^
|
|
20
|
-
"eslint-config-neon": "^0.
|
|
21
|
-
"eslint-formatter-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
18
|
+
"eslint": "^9.37.0",
|
|
19
|
+
"eslint-config-neon": "^0.2.7",
|
|
20
|
+
"eslint-formatter-compact": "^8.40.0",
|
|
21
|
+
"eslint-formatter-pretty": "^7.0.0",
|
|
22
|
+
"prettier": "^3.6.2",
|
|
23
|
+
"zod": "^3.25.76"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22.12.0"
|
|
24
27
|
}
|
|
25
28
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** @type {import('../index.js').Command} */
|
|
2
|
+
export default {
|
|
3
|
+
data: {
|
|
4
|
+
name: 'user',
|
|
5
|
+
description: 'Provides information about the user.',
|
|
6
|
+
},
|
|
7
|
+
async execute(interaction) {
|
|
8
|
+
await interaction.reply(`This command was run by ${interaction.user.username}.`);
|
|
9
|
+
},
|
|
10
|
+
};
|
|
@@ -3,10 +3,10 @@ import { z } from 'zod';
|
|
|
3
3
|
/**
|
|
4
4
|
* Defines the structure of an event.
|
|
5
5
|
*
|
|
6
|
-
* @template {keyof import('discord.js').ClientEvents} [
|
|
6
|
+
* @template {keyof import('discord.js').ClientEvents} [EventName=keyof import('discord.js').ClientEvents]
|
|
7
7
|
* @typedef {object} Event
|
|
8
|
-
* @property {(...parameters: import('discord.js').ClientEvents[
|
|
9
|
-
* @property {
|
|
8
|
+
* @property {(...parameters: import('discord.js').ClientEvents[EventName]) => Promise<void> | void} execute The function to execute the command
|
|
9
|
+
* @property {EventName} name The name of the event to listen to
|
|
10
10
|
* @property {boolean} [once] Whether or not the event should only be listened to once
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -23,7 +23,7 @@ export const schema = z.object({
|
|
|
23
23
|
/**
|
|
24
24
|
* Defines the predicate to check if an object is a valid Event type.
|
|
25
25
|
*
|
|
26
|
-
* @type {import('../util/loaders').StructurePredicate<Event>}
|
|
26
|
+
* @type {import('../util/loaders.js').StructurePredicate<Event>}
|
|
27
27
|
* @returns {structure is Event}
|
|
28
28
|
*/
|
|
29
29
|
export const predicate = (structure) => schema.safeParse(structure).success;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Events } from 'discord.js';
|
|
2
|
+
import { loadCommands } from '../util/loaders.js';
|
|
3
|
+
|
|
4
|
+
const commands = await loadCommands(new URL('../commands/', import.meta.url));
|
|
5
|
+
|
|
6
|
+
/** @type {import('../events/index.js').Event<Events.InteractionCreate>} */
|
|
7
|
+
export default {
|
|
8
|
+
name: Events.InteractionCreate,
|
|
9
|
+
async execute(interaction) {
|
|
10
|
+
if (interaction.isCommand()) {
|
|
11
|
+
const command = commands.get(interaction.commandName);
|
|
12
|
+
|
|
13
|
+
if (!command) {
|
|
14
|
+
throw new Error(`Command '${interaction.commandName}' not found.`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
await command.execute(interaction);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
import { URL } from 'node:url';
|
|
3
3
|
import { Client, GatewayIntentBits } from 'discord.js';
|
|
4
|
-
import {
|
|
5
|
-
import { registerEvents } from './util/registerEvents.js';
|
|
4
|
+
import { loadEvents } from './util/loaders.js';
|
|
6
5
|
|
|
7
6
|
// Initialize the client
|
|
8
7
|
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
9
8
|
|
|
10
9
|
// Load the events and commands
|
|
11
10
|
const events = await loadEvents(new URL('events/', import.meta.url));
|
|
12
|
-
const commands = await loadCommands(new URL('commands/', import.meta.url));
|
|
13
11
|
|
|
14
12
|
// Register the event handlers
|
|
15
|
-
|
|
13
|
+
for (const event of events) {
|
|
14
|
+
client[event.once ? 'once' : 'on'](event.name, async (...args) => {
|
|
15
|
+
try {
|
|
16
|
+
await event.execute(...args);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`Error executing event ${String(event.name)}:`, error);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
16
22
|
|
|
17
23
|
// Login to the client
|
|
18
24
|
void client.login(process.env.DISCORD_TOKEN);
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { URL } from 'node:url';
|
|
1
|
+
import { glob, stat } from 'node:fs/promises';
|
|
2
|
+
import { fileURLToPath, resolve, URL } from 'node:url';
|
|
3
3
|
import { predicate as commandPredicate } from '../commands/index.js';
|
|
4
4
|
import { predicate as eventPredicate } from '../events/index.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A predicate to check if the structure is valid.
|
|
8
8
|
*
|
|
9
|
-
* @template
|
|
10
|
-
* @typedef {(structure: unknown) => structure is
|
|
9
|
+
* @template Structure
|
|
10
|
+
* @typedef {(structure: unknown) => structure is Structure} StructurePredicate
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Loads all the structures in the provided directory.
|
|
15
15
|
*
|
|
16
|
-
* @template
|
|
16
|
+
* @template Structure
|
|
17
17
|
* @param {import('node:fs').PathLike} dir - The directory to load the structures from
|
|
18
|
-
* @param {StructurePredicate<
|
|
18
|
+
* @param {StructurePredicate<Structure>} predicate - The predicate to check if the structure is valid
|
|
19
19
|
* @param {boolean} recursive - Whether to recursively load the structures in the directory
|
|
20
|
-
* @returns {Promise<
|
|
20
|
+
* @returns {Promise<Structure[]>}
|
|
21
21
|
*/
|
|
22
22
|
export async function loadStructures(dir, predicate, recursive = true) {
|
|
23
23
|
// Get the stats of the directory
|
|
@@ -28,33 +28,25 @@ export async function loadStructures(dir, predicate, recursive = true) {
|
|
|
28
28
|
throw new Error(`The directory '${dir}' is not a directory.`);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// Get all the files in the directory
|
|
32
|
-
const files = await readdir(dir);
|
|
33
|
-
|
|
34
31
|
// Create an empty array to store the structures
|
|
35
|
-
/** @type {
|
|
32
|
+
/** @type {Structure[]} */
|
|
36
33
|
const structures = [];
|
|
37
34
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (file === 'index.js' || !file.endsWith('.js')) {
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Get the stats of the file
|
|
46
|
-
const statFile = await stat(new URL(`${dir}/${file}`));
|
|
35
|
+
// Create a glob pattern to match the .js files
|
|
36
|
+
const basePath = dir instanceof URL ? fileURLToPath(dir) : dir.toString();
|
|
37
|
+
const pattern = resolve(basePath, recursive ? '**/*.js' : '*.js');
|
|
47
38
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
// Loop through all the matching files in the directory
|
|
40
|
+
for await (const file of glob(pattern)) {
|
|
41
|
+
// If the file is index.js, skip the file
|
|
42
|
+
if (file.endsWith('/index.js')) {
|
|
51
43
|
continue;
|
|
52
44
|
}
|
|
53
45
|
|
|
54
46
|
// Import the structure dynamically from the file
|
|
55
|
-
const structure =
|
|
47
|
+
const { default: structure } = await import(file);
|
|
56
48
|
|
|
57
|
-
// If the
|
|
49
|
+
// If the default export is a valid structure, add it
|
|
58
50
|
if (predicate(structure)) {
|
|
59
51
|
structures.push(structure);
|
|
60
52
|
}
|
|
@@ -66,7 +58,7 @@ export async function loadStructures(dir, predicate, recursive = true) {
|
|
|
66
58
|
/**
|
|
67
59
|
* @param {import('node:fs').PathLike} dir
|
|
68
60
|
* @param {boolean} [recursive]
|
|
69
|
-
* @returns {Promise<Map<string,import('../commands/index.js').Command>>}
|
|
61
|
+
* @returns {Promise<Map<string, import('../commands/index.js').Command>>}
|
|
70
62
|
*/
|
|
71
63
|
export async function loadCommands(dir, recursive = true) {
|
|
72
64
|
return (await loadStructures(dir, commandPredicate, recursive)).reduce(
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
4
4
|
"editor.formatOnSave": true,
|
|
5
5
|
"editor.codeActionsOnSave": {
|
|
6
|
-
"source.fixAll":
|
|
7
|
-
"source.organizeImports":
|
|
6
|
+
"source.fixAll": "explicit",
|
|
7
|
+
"source.organizeImports": "never"
|
|
8
8
|
},
|
|
9
9
|
"editor.trimAutoWhitespace": false,
|
|
10
10
|
"files.insertFinalNewline": true,
|
|
@@ -7,23 +7,26 @@
|
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"lint": "prettier --check . && eslint --ext .ts --format=pretty src",
|
|
10
|
-
"deploy": "node --
|
|
10
|
+
"deploy": "node --env-file=.env dist/util/deploy.js",
|
|
11
11
|
"format": "prettier --write . && eslint --ext .ts --fix --format=pretty src",
|
|
12
|
-
"start": "node --
|
|
12
|
+
"start": "node --env-file=.env dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@discordjs/core": "^2.
|
|
16
|
-
"discord.js": "^14.
|
|
17
|
-
"dotenv": "^16.4.7"
|
|
15
|
+
"@discordjs/core": "^2.2.2",
|
|
16
|
+
"discord.js": "^14.22.1"
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
19
|
"@sapphire/ts-config": "^5.0.1",
|
|
21
|
-
"@types/node": "^
|
|
22
|
-
"eslint": "^
|
|
23
|
-
"eslint-config-neon": "^0.
|
|
24
|
-
"eslint-formatter-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
20
|
+
"@types/node": "^22.18.8",
|
|
21
|
+
"eslint": "^9.37.0",
|
|
22
|
+
"eslint-config-neon": "^0.2.7",
|
|
23
|
+
"eslint-formatter-compact": "^8.40.0",
|
|
24
|
+
"eslint-formatter-pretty": "^7.0.0",
|
|
25
|
+
"prettier": "^3.6.2",
|
|
26
|
+
"typescript": "~5.9.3",
|
|
27
|
+
"zod": "^3.25.76"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.12.0"
|
|
28
31
|
}
|
|
29
32
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Command } from '../index.[REPLACE_IMPORT_EXT]';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
data: {
|
|
5
|
+
name: 'user',
|
|
6
|
+
description: 'Provides information about the user.',
|
|
7
|
+
},
|
|
8
|
+
async execute(interaction) {
|
|
9
|
+
await interaction.reply(`This command was run by ${interaction.user.username}.`);
|
|
10
|
+
},
|
|
11
|
+
} satisfies Command;
|
|
@@ -5,17 +5,17 @@ import type { StructurePredicate } from '../util/loaders.[REPLACE_IMPORT_EXT]';
|
|
|
5
5
|
/**
|
|
6
6
|
* Defines the structure of an event.
|
|
7
7
|
*/
|
|
8
|
-
export type Event<
|
|
8
|
+
export type Event<EventName extends keyof ClientEvents = keyof ClientEvents> = {
|
|
9
9
|
/**
|
|
10
10
|
* The function to execute when the event is emitted.
|
|
11
11
|
*
|
|
12
12
|
* @param parameters - The parameters of the event
|
|
13
13
|
*/
|
|
14
|
-
execute(...parameters: ClientEvents[
|
|
14
|
+
execute(...parameters: ClientEvents[EventName]): Promise<void> | void;
|
|
15
15
|
/**
|
|
16
16
|
* The name of the event to listen to
|
|
17
17
|
*/
|
|
18
|
-
name:
|
|
18
|
+
name: EventName;
|
|
19
19
|
/**
|
|
20
20
|
* Whether or not the event should only be listened to once
|
|
21
21
|
*
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { URL } from 'node:url';
|
|
2
|
+
import { Events } from 'discord.js';
|
|
3
|
+
import { loadCommands } from '../util/loaders.[REPLACE_IMPORT_EXT]';
|
|
4
|
+
import type { Event } from './index.[REPLACE_IMPORT_EXT]';
|
|
5
|
+
|
|
6
|
+
const commands = await loadCommands(new URL('../commands/', import.meta.url));
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: Events.InteractionCreate,
|
|
10
|
+
async execute(interaction) {
|
|
11
|
+
if (interaction.isCommand()) {
|
|
12
|
+
const command = commands.get(interaction.commandName);
|
|
13
|
+
|
|
14
|
+
if (!command) {
|
|
15
|
+
throw new Error(`Command '${interaction.commandName}' not found.`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
await command.execute(interaction);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
} satisfies Event<Events.InteractionCreate>;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
import { URL } from 'node:url';
|
|
3
3
|
import { Client, GatewayIntentBits } from 'discord.js';
|
|
4
|
-
import {
|
|
5
|
-
import { registerEvents } from './util/registerEvents.[REPLACE_IMPORT_EXT]';
|
|
4
|
+
import { loadEvents } from './util/loaders.[REPLACE_IMPORT_EXT]';
|
|
6
5
|
|
|
7
6
|
// Initialize the client
|
|
8
7
|
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
9
8
|
|
|
10
9
|
// Load the events and commands
|
|
11
10
|
const events = await loadEvents(new URL('events/', import.meta.url));
|
|
12
|
-
const commands = await loadCommands(new URL('commands/', import.meta.url));
|
|
13
11
|
|
|
14
12
|
// Register the event handlers
|
|
15
|
-
|
|
13
|
+
for (const event of events) {
|
|
14
|
+
client[event.once ? 'once' : 'on'](event.name, async (...args) => {
|
|
15
|
+
try {
|
|
16
|
+
await event.execute(...args);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`Error executing event ${String(event.name)}:`, error);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
16
22
|
|
|
17
23
|
// Login to the client
|
|
18
24
|
void client.login(process.env.DISCORD_TOKEN);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PathLike } from 'node:fs';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { glob, stat } from 'node:fs/promises';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import type { Command } from '../commands/index.[REPLACE_IMPORT_EXT]';
|
|
5
6
|
import { predicate as commandPredicate } from '../commands/index.[REPLACE_IMPORT_EXT]';
|
|
6
7
|
import type { Event } from '../events/index.[REPLACE_IMPORT_EXT]';
|
|
@@ -9,7 +10,7 @@ import { predicate as eventPredicate } from '../events/index.[REPLACE_IMPORT_EXT
|
|
|
9
10
|
/**
|
|
10
11
|
* A predicate to check if the structure is valid
|
|
11
12
|
*/
|
|
12
|
-
export type StructurePredicate<
|
|
13
|
+
export type StructurePredicate<Structure> = (structure: unknown) => structure is Structure;
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Loads all the structures in the provided directory
|
|
@@ -19,11 +20,11 @@ export type StructurePredicate<T> = (structure: unknown) => structure is T;
|
|
|
19
20
|
* @param recursive - Whether to recursively load the structures in the directory
|
|
20
21
|
* @returns
|
|
21
22
|
*/
|
|
22
|
-
export async function loadStructures<
|
|
23
|
+
export async function loadStructures<Structure>(
|
|
23
24
|
dir: PathLike,
|
|
24
|
-
predicate: StructurePredicate<
|
|
25
|
+
predicate: StructurePredicate<Structure>,
|
|
25
26
|
recursive = true,
|
|
26
|
-
): Promise<
|
|
27
|
+
): Promise<Structure[]> {
|
|
27
28
|
// Get the stats of the directory
|
|
28
29
|
const statDir = await stat(dir);
|
|
29
30
|
|
|
@@ -32,33 +33,27 @@ export async function loadStructures<T>(
|
|
|
32
33
|
throw new Error(`The directory '${dir}' is not a directory.`);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
// Get all the files in the directory
|
|
36
|
-
const files = await readdir(dir);
|
|
37
|
-
|
|
38
36
|
// Create an empty array to store the structures
|
|
39
|
-
const structures:
|
|
37
|
+
const structures: Structure[] = [];
|
|
40
38
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (file === 'index.[REPLACE_IMPORT_EXT]' || !file.endsWith('.[REPLACE_IMPORT_EXT]')) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
39
|
+
// Create a glob pattern to match the .[REPLACE_IMPORT_EXT] files
|
|
40
|
+
const basePath = dir instanceof URL ? fileURLToPath(dir) : dir.toString();
|
|
41
|
+
const pattern = resolve(basePath, recursive ? '**/*.[REPLACE_IMPORT_EXT]' : '*.[REPLACE_IMPORT_EXT]');
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (statFile.isDirectory() && recursive) {
|
|
53
|
-
structures.push(...(await loadStructures(`${dir}/${file}`, predicate, recursive)));
|
|
43
|
+
// Loop through all the matching files in the directory
|
|
44
|
+
for await (const file of glob(pattern)) {
|
|
45
|
+
// If the file is index.[REPLACE_IMPORT_EXT], skip the file
|
|
46
|
+
if (file.endsWith('/index.[REPLACE_IMPORT_EXT]')) {
|
|
54
47
|
continue;
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
// Import the structure dynamically from the file
|
|
58
|
-
const structure =
|
|
51
|
+
const { default: structure } = await import(file);
|
|
59
52
|
|
|
60
|
-
// If the
|
|
61
|
-
if (predicate(structure))
|
|
53
|
+
// If the default export is a valid structure, add it
|
|
54
|
+
if (predicate(structure)) {
|
|
55
|
+
structures.push(structure);
|
|
56
|
+
}
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
return structures;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Events, type Client } from 'npm:discord.js@^14.17.0';
|
|
2
|
-
import type { Command } from '../commands/index.ts';
|
|
3
|
-
import type { Event } from '../events/index.ts';
|
|
4
|
-
|
|
5
|
-
export function registerEvents(commands: Map<string, Command>, events: Event[], client: Client): void {
|
|
6
|
-
// Create an event to handle command interactions
|
|
7
|
-
const interactionCreateEvent: Event<Events.InteractionCreate> = {
|
|
8
|
-
name: Events.InteractionCreate,
|
|
9
|
-
async execute(interaction) {
|
|
10
|
-
if (interaction.isCommand()) {
|
|
11
|
-
const command = commands.get(interaction.commandName);
|
|
12
|
-
|
|
13
|
-
if (!command) {
|
|
14
|
-
throw new Error(`Command '${interaction.commandName}' not found.`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
await command.execute(interaction);
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
for (const event of [...events, interactionCreateEvent]) {
|
|
23
|
-
client[event.once ? 'once' : 'on'](event.name, async (...args) => event.execute(...args));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Events } from 'discord.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {Map<string, import('../commands/index.js').Command>} commands
|
|
5
|
-
* @param {import('../events/index.js').Event[]} events
|
|
6
|
-
* @param {import('discord.js').Client} client
|
|
7
|
-
*/
|
|
8
|
-
export function registerEvents(commands, events, client) {
|
|
9
|
-
// Create an event to handle command interactions
|
|
10
|
-
/** @type {import('../events/index.js').Event<Events.InteractionCreate>} */
|
|
11
|
-
const interactionCreateEvent = {
|
|
12
|
-
name: Events.InteractionCreate,
|
|
13
|
-
async execute(interaction) {
|
|
14
|
-
if (interaction.isCommand()) {
|
|
15
|
-
const command = commands.get(interaction.commandName);
|
|
16
|
-
|
|
17
|
-
if (!command) {
|
|
18
|
-
throw new Error(`Command '${interaction.commandName}' not found.`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
await command.execute(interaction);
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
for (const event of [...events, interactionCreateEvent]) {
|
|
27
|
-
client[event.once ? 'once' : 'on'](event.name, async (...args) => event.execute(...args));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Events, type Client } from 'discord.js';
|
|
2
|
-
import type { Command } from '../commands/index.[REPLACE_IMPORT_EXT]';
|
|
3
|
-
import type { Event } from '../events/index.[REPLACE_IMPORT_EXT]';
|
|
4
|
-
|
|
5
|
-
export function registerEvents(commands: Map<string, Command>, events: Event[], client: Client): void {
|
|
6
|
-
// Create an event to handle command interactions
|
|
7
|
-
const interactionCreateEvent: Event<Events.InteractionCreate> = {
|
|
8
|
-
name: Events.InteractionCreate,
|
|
9
|
-
async execute(interaction) {
|
|
10
|
-
if (interaction.isCommand()) {
|
|
11
|
-
const command = commands.get(interaction.commandName);
|
|
12
|
-
|
|
13
|
-
if (!command) {
|
|
14
|
-
throw new Error(`Command '${interaction.commandName}' not found.`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
await command.execute(interaction);
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
for (const event of [...events, interactionCreateEvent]) {
|
|
23
|
-
client[event.once ? 'once' : 'on'](event.name, async (...args) => event.execute(...args));
|
|
24
|
-
}
|
|
25
|
-
}
|