create-discord-app 3.1.0 → 4.0.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 CHANGED
@@ -7,6 +7,7 @@
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
9
  <a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.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,9 +20,9 @@
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-bot ./your/chosen/directory
23
- yarn create discord-bot ./your/chosen/directory
24
- pnpm create discord-bot ./your/chosen/directory
23
+ npm create discord-app ./your/chosen/directory
24
+ yarn create discord-app ./your/chosen/directory
25
+ pnpm create discord-app ./your/chosen/directory
25
26
  bunx create-discord-bot ./your/chosen/directory
26
27
  ```
27
28
 
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"node:process";import{Option,program}from"commander";import picocolors3 from"picocolors";import prompts from"prompts";import validateProjectName from"validate-npm-package-name";var package_default_name="create-discord-app",package_default_version="3.1.0";import{cp,stat,mkdir,readdir,readFile,writeFile}from"node:fs/promises";import path from"node:path";import process2 from"node:process";import{URL}from"node:url";import glob from"fast-glob";import picocolors2 from"picocolors";import{execSync}from"node:child_process";import process from"node:process";import picocolors from"picocolors";var NODE_PACKAGE_MANAGERS=["npm","pnpm","yarn"];function resolvePackageManager(){const npmConfigUserAgent=process.env.npm_config_user_agent;return npmConfigUserAgent?npmConfigUserAgent.startsWith("npm")?"npm":npmConfigUserAgent.startsWith("yarn")?"yarn":npmConfigUserAgent.startsWith("pnpm")?"pnpm":npmConfigUserAgent.startsWith("bun")?"bun":(console.error(picocolors.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(picocolors2.red(`The directory ${picocolors2.yellow(`"${directoryName}"`)} is either not a directory or is not empty.`)),console.error(picocolors2.red("Please specify an empty directory.")),process2.exit(1)),console.log(`Creating ${directoryName} in ${picocolors2.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 globStream=glob.stream("./src/**/*.ts");for await(const file of globStream){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(picocolors2.red("Installation aborted.")):(console.error(picocolors2.red("Installation failed.")),process2.exit(1))}console.log(),console.log(picocolors2.green("All done! Be sure to read through the discord.js guide for help on your journey.")),console.log(`Link: ${picocolors2.cyan("https://guide.discordjs.dev")}`)}__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(`${picocolors3.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(picocolors3.red(`- ${error}`));return picocolors3.red(`Cannot create a project named ${picocolors3.yellow(`"${directory}"`)} due to npm naming restrictions.\n\nErrors:\n${errors.join("\n")}\n\n${picocolors3.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});
2
+ var __defProp=Object.defineProperty,__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0});import process3 from"process";import{Option,program}from"commander";import picocolors3 from"picocolors";import prompts from"prompts";import validateProjectName from"validate-npm-package-name";var package_default_name="create-discord-app",package_default_version="4.0.0";import{cp,stat,mkdir,readdir,readFile,writeFile}from"fs/promises";import path from"path";import process2 from"process";import{URL}from"url";import glob from"fast-glob";import picocolors2 from"picocolors";import{execSync}from"child_process";import process from"process";import picocolors from"picocolors";var NODE_PACKAGE_MANAGERS=["npm","pnpm","yarn"];function resolvePackageManager(){const npmConfigUserAgent=process.env.npm_config_user_agent;return npmConfigUserAgent?npmConfigUserAgent.startsWith("npm")?"npm":npmConfigUserAgent.startsWith("yarn")?"yarn":npmConfigUserAgent.startsWith("pnpm")?"pnpm":npmConfigUserAgent.startsWith("bun")?"bun":(console.error(picocolors.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(picocolors2.red(`The directory ${picocolors2.yellow(`"${directoryName}"`)} is either not a directory or is not empty.`)),console.error(picocolors2.red("Please specify an empty directory.")),process2.exit(1)),console.log(`Creating ${directoryName} in ${picocolors2.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 globStream=glob.stream("./src/**/*.ts");for await(const file of globStream){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(picocolors2.red("Installation aborted.")):(console.error(picocolors2.red("Installation failed.")),process2.exit(1))}console.log(),console.log(picocolors2.green("All done! Be sure to read through the discord.js guide for help on your journey.")),console.log(`Link: ${picocolors2.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(`${picocolors3.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(picocolors3.red(`- ${error}`));return picocolors3.red(`Cannot create a project named ${picocolors3.yellow(`"${directory}"`)} due to npm naming restrictions.\n\nErrors:\n${errors.join("\n")}\n\n${picocolors3.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": "3.1.0",
4
+ "version": "4.0.0",
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,40 @@
41
41
  "homepage": "https://discord.js.org",
42
42
  "funding": "https://github.com/discordjs/discord.js?sponsor",
43
43
  "dependencies": {
44
- "commander": "^12.1.0",
45
- "fast-glob": "^3.3.2",
44
+ "commander": "^13.1.0",
45
+ "fast-glob": "^3.3.3",
46
46
  "picocolors": "^1.1.1",
47
47
  "prompts": "^2.4.2",
48
- "validate-npm-package-name": "^5.0.1"
48
+ "validate-npm-package-name": "^6.0.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@favware/cliff-jumper": "^4.1.0",
52
- "@types/node": "^20.17.10",
52
+ "@types/node": "^22.15.2",
53
53
  "@types/prompts": "^2.4.9",
54
54
  "@types/validate-npm-package-name": "^4.0.2",
55
- "@vitest/coverage-v8": "^2.1.8",
56
55
  "cross-env": "^7.0.3",
57
- "eslint": "^8.57.1",
58
- "eslint-config-neon": "^0.1.62",
56
+ "eslint": "^9.25.1",
57
+ "eslint-config-neon": "^0.2.7",
58
+ "eslint-formatter-compact": "^8.40.0",
59
59
  "eslint-formatter-pretty": "^6.0.1",
60
- "prettier": "^3.4.2",
61
- "terser": "^5.37.0",
62
- "tsup": "^8.3.5",
63
- "typescript": "~5.5.4",
64
- "vitest": "^2.1.8",
65
- "@discordjs/api-extractor": "^7.38.1"
60
+ "prettier": "^3.5.3",
61
+ "terser": "^5.39.0",
62
+ "tsup": "^8.4.0",
63
+ "typescript": "~5.8.3",
64
+ "@discordjs/api-extractor": "^7.52.7"
66
65
  },
67
66
  "engines": {
68
- "node": ">=20"
67
+ "node": ">=22.12.0"
69
68
  },
70
69
  "publishConfig": {
71
- "access": "public",
72
- "provenance": false
70
+ "access": "public"
73
71
  },
74
72
  "scripts": {
75
73
  "build": "tsc --noEmit && tsup",
76
74
  "lint": "prettier --check . && cross-env TIMING=1 eslint --format=pretty src",
77
75
  "format": "prettier --write . && cross-env TIMING=1 eslint --fix --format=pretty src",
78
76
  "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/create-discord-bot/*'",
79
- "release": "cliff-jumper"
77
+ "release": "cliff-jumper",
78
+ "rename-to-app": "node scripts/rename-to-app.mjs"
80
79
  }
81
80
  }
@@ -11,14 +11,15 @@
11
11
  "start": "bun run src/index.[REPLACE_IMPORT_EXT]"
12
12
  },
13
13
  "dependencies": {
14
- "@discordjs/core": "^2.0.1",
15
- "discord.js": "^14.17.0"
14
+ "@discordjs/core": "^2.1.1",
15
+ "discord.js": "^14.20.0"
16
16
  },
17
17
  "devDependencies": {
18
- "eslint": "^8.57.1",
19
- "eslint-config-neon": "^0.1.62",
18
+ "eslint": "^9.24.0",
19
+ "eslint-config-neon": "^0.2.7",
20
+ "eslint-formatter-compact": "^8.40.0",
20
21
  "eslint-formatter-pretty": "^6.0.1",
21
- "prettier": "^3.4.2",
22
+ "prettier": "^3.5.3",
22
23
  "zod": "^3.24.1"
23
24
  }
24
25
  }
@@ -11,16 +11,17 @@
11
11
  "start": "bun run src/index.[REPLACE_IMPORT_EXT]"
12
12
  },
13
13
  "dependencies": {
14
- "@discordjs/core": "^2.0.1",
15
- "discord.js": "^14.17.0"
14
+ "@discordjs/core": "^2.1.1",
15
+ "discord.js": "^14.20.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@sapphire/ts-config": "^5.0.1",
19
19
  "@types/bun": "^1.1.14",
20
- "eslint": "^8.57.1",
21
- "eslint-config-neon": "^0.1.62",
20
+ "eslint": "^9.24.0",
21
+ "eslint-config-neon": "^0.2.7",
22
+ "eslint-formatter-compact": "^8.40.0",
22
23
  "eslint-formatter-pretty": "^6.0.1",
23
- "prettier": "^3.4.2",
24
+ "prettier": "^3.5.3",
24
25
  "typescript": "~5.5.4",
25
26
  "zod": "^3.24.1"
26
27
  }
@@ -1,4 +1,4 @@
1
- import type { RESTPostAPIApplicationCommandsJSONBody, CommandInteraction } from 'npm:discord.js@^14.17.0';
1
+ import type { RESTPostAPIApplicationCommandsJSONBody, CommandInteraction } from 'npm:discord.js@^14.20.0';
2
2
  import { z } from 'npm:zod@^3.24.1';
3
3
  import type { StructurePredicate } from '../util/loaders.ts';
4
4
 
@@ -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,4 +1,4 @@
1
- import type { ClientEvents } from 'npm:discord.js@^14.17.0';
1
+ import type { ClientEvents } from 'npm:discord.js@^14.20.0';
2
2
  import { z } from 'npm:zod@^3.24.1';
3
3
  import type { StructurePredicate } from '../util/loaders.ts';
4
4
 
@@ -0,0 +1,20 @@
1
+ import { Events } from 'npm:discord.js@^14.20.0';
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,4 +1,4 @@
1
- import { Events } from 'npm:discord.js@^14.17.0';
1
+ import { Events } from 'npm:discord.js@^14.20.0';
2
2
  import type { Event } from './index.ts';
3
3
 
4
4
  export default {
@@ -1,18 +1,24 @@
1
1
  import 'https://deno.land/std@0.223.0/dotenv/load.ts';
2
2
  import { URL } from 'node:url';
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';
3
+ import { Client, GatewayIntentBits } from 'npm:discord.js@^14.20.0';
4
+ import { loadEvents } from './util/loaders.ts';
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
- registerEvents(commands, events, client);
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(Deno.env.get('DISCORD_TOKEN'));
@@ -1,7 +1,7 @@
1
1
  import 'https://deno.land/std@0.223.0/dotenv/load.ts';
2
2
  import { URL } from 'node:url';
3
- import { API } from 'npm:@discordjs/core@^2.0.1/http-only';
4
- import { REST } from 'npm:discord.js@^14.17.0';
3
+ import { API } from 'npm:@discordjs/core@^2.1.1/http-only';
4
+ import { REST } from 'npm:discord.js@^14.20.0';
5
5
  import { loadCommands } from './loaders.ts';
6
6
 
7
7
  const commands = await loadCommands(new URL('../commands/', import.meta.url));
@@ -40,22 +40,24 @@ export async function loadStructures<T>(
40
40
 
41
41
  // Loop through all the files in the directory
42
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
- }
43
+ const fileUrl = new URL(`${dir}/${file}`, import.meta.url);
47
44
 
48
45
  // Get the stats of the file
49
- const statFile = await stat(new URL(`${dir}/${file}`));
46
+ const statFile = await stat(fileUrl);
50
47
 
51
48
  // If the file is a directory and recursive is true, recursively load the structures in the directory
52
49
  if (statFile.isDirectory() && recursive) {
53
- structures.push(...(await loadStructures(`${dir}/${file}`, predicate, recursive)));
50
+ structures.push(...(await loadStructures(fileUrl, predicate, recursive)));
51
+ continue;
52
+ }
53
+
54
+ // If the file is index.ts or the file does not end with .ts, skip the file
55
+ if (file === 'index.ts' || !file.endsWith('.ts')) {
54
56
  continue;
55
57
  }
56
58
 
57
59
  // Import the structure dynamically from the file
58
- const structure = (await import(`${dir}/${file}`)).default;
60
+ const structure = (await import(fileUrl.toString())).default;
59
61
 
60
62
  // If the structure is a valid structure, add it
61
63
  if (predicate(structure)) {
@@ -11,15 +11,19 @@
11
11
  "deploy": "node --require dotenv/config src/util/deploy.js"
12
12
  },
13
13
  "dependencies": {
14
- "@discordjs/core": "^2.0.1",
15
- "discord.js": "^14.16.3",
14
+ "@discordjs/core": "^2.1.1",
15
+ "discord.js": "^14.20.0",
16
16
  "dotenv": "^16.4.5"
17
17
  },
18
18
  "devDependencies": {
19
- "eslint": "^8.57.1",
20
- "eslint-config-neon": "^0.1.62",
19
+ "eslint": "^9.24.0",
20
+ "eslint-config-neon": "^0.2.7",
21
+ "eslint-formatter-compact": "^8.40.0",
21
22
  "eslint-formatter-pretty": "^6.0.1",
22
- "prettier": "^3.4.2",
23
+ "prettier": "^3.5.3",
23
24
  "zod": "^3.24.1"
25
+ },
26
+ "engines": {
27
+ "node": ">=22.12.0"
24
28
  }
25
29
  }
@@ -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
+ };
@@ -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 { loadCommands, loadEvents } from './util/loaders.js';
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
- registerEvents(commands, events, client);
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);
@@ -37,22 +37,24 @@ export async function loadStructures(dir, predicate, recursive = true) {
37
37
 
38
38
  // Loop through all the files in the directory
39
39
  for (const file of files) {
40
- // If the file is index.js or the file does not end with .js, skip the file
41
- if (file === 'index.js' || !file.endsWith('.js')) {
42
- continue;
43
- }
40
+ const fileUrl = new URL(`${dir}/${file}`, import.meta.url);
44
41
 
45
42
  // Get the stats of the file
46
- const statFile = await stat(new URL(`${dir}/${file}`));
43
+ const statFile = await stat(fileUrl);
47
44
 
48
45
  // If the file is a directory and recursive is true, recursively load the structures in the directory
49
46
  if (statFile.isDirectory() && recursive) {
50
- structures.push(...(await loadStructures(`${dir}/${file}`, predicate, recursive)));
47
+ structures.push(...(await loadStructures(fileUrl, predicate, recursive)));
48
+ continue;
49
+ }
50
+
51
+ // If the file is index.js or the file does not end with .js, skip the file
52
+ if (file === 'index.js' || !file.endsWith('.js')) {
51
53
  continue;
52
54
  }
53
55
 
54
56
  // Import the structure dynamically from the file
55
- const structure = (await import(`${dir}/${file}`)).default;
57
+ const structure = (await import(fileUrl.toString())).default;
56
58
 
57
59
  // If the structure is a valid structure, add it
58
60
  if (predicate(structure)) {
@@ -12,18 +12,22 @@
12
12
  "start": "node --require dotenv/config dist/index.js"
13
13
  },
14
14
  "dependencies": {
15
- "@discordjs/core": "^2.0.1",
16
- "discord.js": "^14.16.3",
15
+ "@discordjs/core": "^2.1.1",
16
+ "discord.js": "^14.20.0",
17
17
  "dotenv": "^16.4.7"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@sapphire/ts-config": "^5.0.1",
21
- "@types/node": "^20.17.10",
22
- "eslint": "^8.57.1",
23
- "eslint-config-neon": "^0.1.62",
21
+ "@types/node": "^22.10.10",
22
+ "eslint": "^9.24.0",
23
+ "eslint-config-neon": "^0.2.7",
24
+ "eslint-formatter-compact": "^8.40.0",
24
25
  "eslint-formatter-pretty": "^6.0.1",
25
- "prettier": "^3.4.2",
26
+ "prettier": "^3.5.3",
26
27
  "typescript": "~5.5.4",
27
28
  "zod": "^3.24.1"
29
+ },
30
+ "engines": {
31
+ "node": ">=22.12.0"
28
32
  }
29
33
  }
@@ -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;
@@ -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 { loadCommands, loadEvents } from './util/loaders.[REPLACE_IMPORT_EXT]';
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
- registerEvents(commands, events, client);
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);
@@ -40,22 +40,24 @@ export async function loadStructures<T>(
40
40
 
41
41
  // Loop through all the files in the directory
42
42
  for (const file of files) {
43
- // If the file is index.[REPLACE_IMPORT_EXT] or the file does not end with .[REPLACE_IMPORT_EXT], skip the file
44
- if (file === 'index.[REPLACE_IMPORT_EXT]' || !file.endsWith('.[REPLACE_IMPORT_EXT]')) {
45
- continue;
46
- }
43
+ const fileUrl = new URL(`${dir}/${file}`, import.meta.url);
47
44
 
48
45
  // Get the stats of the file
49
- const statFile = await stat(new URL(`${dir}/${file}`));
46
+ const statFile = await stat(fileUrl);
50
47
 
51
48
  // If the file is a directory and recursive is true, recursively load the structures in the directory
52
49
  if (statFile.isDirectory() && recursive) {
53
- structures.push(...(await loadStructures(`${dir}/${file}`, predicate, recursive)));
50
+ structures.push(...(await loadStructures(fileUrl, predicate, recursive)));
51
+ continue;
52
+ }
53
+
54
+ // If the file is index.[REPLACE_IMPORT_EXT] or the file does not end with .[REPLACE_IMPORT_EXT], skip the file
55
+ if (file === 'index.[REPLACE_IMPORT_EXT]' || !file.endsWith('.[REPLACE_IMPORT_EXT]')) {
54
56
  continue;
55
57
  }
56
58
 
57
59
  // Import the structure dynamically from the file
58
- const structure = (await import(`${dir}/${file}`)).default;
60
+ const structure = (await import(fileUrl.toString())).default;
59
61
 
60
62
  // If the structure is a valid structure, add it
61
63
  if (predicate(structure)) structures.push(structure);
@@ -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
- }