create-djskit 0.0.0-stage → 1.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
@@ -1,3 +1,46 @@
1
- # Temporary Holding Version
1
+ # create-djskit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Create a new [DJSKit](https://github.com/NZ-Linix/djskit) bot, a discord.js v14 template powered by TypeScript.
4
+
5
+ ```bash
6
+ npm create djskit
7
+ # or
8
+ npx create-djskit my-bot
9
+ ```
10
+
11
+ The walkthrough asks for the project directory, description, author, GitHub repository, bot token, message command prefix, development server ID and dev mode, then installs dependencies and initializes a git repository.
12
+
13
+ ## Non-interactive
14
+
15
+ Pass `-y` (or run without a TTY, e.g. in CI) to skip all prompts. Anything not passed as a flag uses its default.
16
+
17
+ ```bash
18
+ npx create-djskit my-bot -y --prefix "!" --dev-guild-id 123456789012345678 --repo NZ-Linix/my-bot --no-git
19
+ ```
20
+
21
+ | Option | Default | Description |
22
+ | --- | --- | --- |
23
+ | `[directory]` | `my-djskit-bot` | Where to create the bot. Its name is used as the package name |
24
+ | `-y, --yes` | | Skip all prompts |
25
+ | `-f, --force` | | Allow a non-empty directory |
26
+ | `--description <text>` | `A discord.js bot made with DJSKit.` | package.json description |
27
+ | `--author <name>` | git `user.name` | package.json author |
28
+ | `--repo <owner/repo>` | | GitHub repository, adds `repository`, `bugs` and `homepage` |
29
+ | `--token <token>` | `CLIENT_TOKEN` env var | Bot token written to `.env` |
30
+ | `--prefix <prefix>` | `>` | Message command prefix |
31
+ | `--dev-guild-id <id>` | | Development server ID |
32
+ | `--[no-]dev-mode` | on | `DEV_MODE` in `.env`: register commands only to the dev server |
33
+ | `--[no-]install` | on | Install dependencies |
34
+ | `--[no-]git` | on | Initialize a git repository |
35
+
36
+ Prefer the `CLIENT_TOKEN` environment variable over `--token`, so the token doesn't end up in your shell history.
37
+
38
+ ## Development
39
+
40
+ ```bash
41
+ cd cli
42
+ npm install
43
+ npm run dev -- --help
44
+ ```
45
+
46
+ `npm run build` copies the bot template from the repository root into `cli/template` and compiles the CLI to `cli/dist`.
package/dist/index.js ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { execSync } from "node:child_process";
5
+ import { parseArgs, styleText } from "node:util";
6
+ import * as p from "@clack/prompts";
7
+ import { createProject, detectPackageManager, initGit, isEmptyDir, isValidGuildId, isValidPackageName, isValidPrefix, parseRepo, run, toPackageName } from "./scaffold.js";
8
+ const { version } = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
9
+ const help = `
10
+ ${styleText("bold", "create-djskit")} v${version}: create a new DJSKit discord.js bot
11
+
12
+ ${styleText("bold", "Usage")}
13
+ npm create djskit [directory] -- [options]
14
+ npx create-djskit [directory] [options]
15
+
16
+ ${styleText("bold", "Options")}
17
+ -y, --yes Skip all prompts, use flags and defaults (also used when not in a TTY)
18
+ -f, --force Allow creating the bot in a non-empty directory
19
+ --description <text> package.json description
20
+ --author <name> package.json author (default: git user.name)
21
+ --repo <owner/repo> GitHub repository (owner/repo or URL)
22
+ --token <token> Bot token, written to .env (or set the CLIENT_TOKEN env var)
23
+ --prefix <prefix> Message command prefix (default: ">")
24
+ --dev-guild-id <id> Development server ID
25
+ --[no-]dev-mode Register commands only to the dev server (default: on)
26
+ --[no-]install Install dependencies (default: on)
27
+ --[no-]git Initialize a git repository (default: on)
28
+ -h, --help Show this help
29
+ -v, --version Show the version
30
+
31
+ ${styleText("bold", "Example")}
32
+ npx create-djskit my-bot -y --prefix ! --dev-guild-id 123456789012345678 --repo NZ-Linix/my-bot
33
+ `;
34
+ // Arguments -----------------------------------------------------------------------
35
+ const parse = () => {
36
+ try {
37
+ return parseArgs({
38
+ allowPositionals: true,
39
+ allowNegative: true,
40
+ options: {
41
+ "yes": { type: "boolean", short: "y" },
42
+ "force": { type: "boolean", short: "f" },
43
+ "description": { type: "string" },
44
+ "author": { type: "string" },
45
+ "repo": { type: "string" },
46
+ "token": { type: "string" },
47
+ "prefix": { type: "string" },
48
+ "dev-guild-id": { type: "string" },
49
+ "dev-mode": { type: "boolean" },
50
+ "install": { type: "boolean" },
51
+ "git": { type: "boolean" },
52
+ "help": { type: "boolean", short: "h" },
53
+ "version": { type: "boolean", short: "v" }
54
+ }
55
+ });
56
+ }
57
+ catch (err) {
58
+ return fail(err.message + "\nRun with --help to see all options.");
59
+ }
60
+ };
61
+ const fail = (message) => {
62
+ console.error(styleText("red", "✖ " + message));
63
+ process.exit(1);
64
+ };
65
+ const gitUserName = () => {
66
+ try {
67
+ return execSync("git config user.name", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
68
+ }
69
+ catch {
70
+ return "";
71
+ }
72
+ };
73
+ // Prompts -------------------------------------------------------------------------
74
+ const ask = async (prompt) => {
75
+ const value = await prompt;
76
+ if (p.isCancel(value)) {
77
+ p.cancel("Setup cancelled.");
78
+ process.exit(0);
79
+ }
80
+ return value;
81
+ };
82
+ const validDirectory = (force) => (value) => {
83
+ const dir = value?.trim() || "";
84
+ if (!dir)
85
+ return "Please enter a directory.";
86
+ if (!isValidPackageName(toPackageName(path.basename(path.resolve(dir)))))
87
+ return "Invalid project name.";
88
+ if (!force && !isEmptyDir(dir))
89
+ return `"${dir}" is not empty. Pick another directory or run with --force.`;
90
+ };
91
+ // Main ----------------------------------------------------------------------------
92
+ const main = async () => {
93
+ const { values: flags, positionals } = parse();
94
+ if (flags.help)
95
+ return console.log(help);
96
+ if (flags.version)
97
+ return console.log(version);
98
+ const interactive = !flags.yes && process.stdin.isTTY && process.stdout.isTTY && !p.isCI();
99
+ const force = flags.force ?? false;
100
+ // Validate flags up front, so non-interactive runs fail before anything is written
101
+ const repoFlag = flags.repo ? parseRepo(flags.repo) ?? fail(`Invalid GitHub repository "${flags.repo}". Use owner/repo.`) : undefined;
102
+ if (flags.prefix !== undefined && !isValidPrefix(flags.prefix))
103
+ fail("The prefix can't be empty or contain spaces.");
104
+ if (flags["dev-guild-id"] !== undefined && !isValidGuildId(flags["dev-guild-id"]))
105
+ fail("The dev server ID must be a 17 to 20 digit number.");
106
+ let options;
107
+ let install = flags.install ?? true;
108
+ let git = flags.git ?? true;
109
+ if (interactive) {
110
+ p.intro(styleText(["bgGreen", "black"], " 🌿 create-djskit ") + styleText("dim", ` v${version}`));
111
+ let directory = positionals[0];
112
+ const directoryError = directory ? validDirectory(force)(directory) : undefined;
113
+ if (directoryError)
114
+ p.log.warn(directoryError);
115
+ if (!directory || directoryError) {
116
+ directory = await ask(p.text({
117
+ message: "Where should the bot be created?",
118
+ placeholder: "my-djskit-bot",
119
+ defaultValue: "my-djskit-bot",
120
+ validate: (value) => validDirectory(force)(value || "my-djskit-bot")
121
+ }));
122
+ }
123
+ const name = toPackageName(path.basename(path.resolve(directory)));
124
+ const description = flags.description ?? await ask(p.text({ message: "Description", placeholder: "A discord.js bot made with DJSKit.", defaultValue: "A discord.js bot made with DJSKit." }));
125
+ const author = flags.author ?? await ask(p.text({ message: "Author", placeholder: gitUserName() || "Optional", defaultValue: gitUserName() }));
126
+ const repo = repoFlag ?? parseRepo(await ask(p.text({
127
+ message: "GitHub repository",
128
+ placeholder: "owner/repo (optional)",
129
+ defaultValue: "",
130
+ validate: (value) => !value || parseRepo(value) ? undefined : "Use owner/repo or a github.com URL."
131
+ }))) ?? "";
132
+ const token = flags.token ?? process.env.CLIENT_TOKEN ?? await ask(p.password({ message: "Bot token (leave empty to add it to .env later)" }));
133
+ const prefix = flags.prefix ?? await ask(p.text({
134
+ message: "Message command prefix",
135
+ placeholder: ">",
136
+ defaultValue: ">",
137
+ validate: (value) => isValidPrefix(value || ">") ? undefined : "The prefix can't contain spaces."
138
+ }));
139
+ const devGuildId = flags["dev-guild-id"] ?? await ask(p.text({
140
+ message: "Development server ID",
141
+ placeholder: "Optional, right-click your server > Copy Server ID",
142
+ defaultValue: "",
143
+ validate: (value) => isValidGuildId(value ?? "") ? undefined : "The server ID must be a 17 to 20 digit number."
144
+ }));
145
+ const devMode = flags["dev-mode"] ?? await ask(p.confirm({
146
+ message: "Enable dev mode? (register commands only to the dev server)",
147
+ initialValue: true
148
+ }));
149
+ if (devMode && !devGuildId)
150
+ p.log.warn("Dev mode is on, but no dev server ID is set. Commands will be registered globally until you add devGuildId to src/config.ts.");
151
+ if (flags.install === undefined)
152
+ install = await ask(p.confirm({ message: "Install dependencies?", initialValue: true }));
153
+ if (flags.git === undefined)
154
+ git = await ask(p.confirm({ message: "Initialize a git repository?", initialValue: true }));
155
+ options = { directory, name, description, author, repo, token, prefix, devGuildId, devMode };
156
+ }
157
+ else {
158
+ const directory = positionals[0] ?? "my-djskit-bot";
159
+ const error = validDirectory(force)(directory);
160
+ if (error)
161
+ fail(error);
162
+ options = {
163
+ directory,
164
+ name: toPackageName(path.basename(path.resolve(directory))),
165
+ description: flags.description ?? "A discord.js bot made with DJSKit.",
166
+ author: flags.author ?? gitUserName(),
167
+ repo: repoFlag ?? "",
168
+ token: flags.token ?? process.env.CLIENT_TOKEN ?? "",
169
+ prefix: flags.prefix ?? ">",
170
+ devGuildId: flags["dev-guild-id"] ?? "",
171
+ devMode: flags["dev-mode"] ?? true
172
+ };
173
+ }
174
+ // Create --------------------------------------------------------------------------
175
+ const log = (message) => interactive ? p.log.success(message) : console.log(styleText("green", "✔ ") + message);
176
+ const warn = (message) => interactive ? p.log.warn(message) : console.warn(styleText("yellow", "▲ ") + message);
177
+ const target = createProject(options);
178
+ log(`Created ${options.name} in ${target}`);
179
+ const pm = detectPackageManager();
180
+ if (install) {
181
+ const spinner = interactive ? p.spinner() : undefined;
182
+ spinner?.start(`Installing dependencies with ${pm}`);
183
+ const ok = await run(pm, ["install"], target, interactive);
184
+ if (ok)
185
+ spinner ? spinner.stop("Installed dependencies") : log("Installed dependencies");
186
+ else
187
+ spinner ? spinner.error(`Installing failed, run "${pm} install" yourself`) : warn(`Installing failed, run "${pm} install" yourself`);
188
+ }
189
+ if (git && !await initGit(target, true))
190
+ warn("Could not initialize a git repository. Is git installed?");
191
+ // Next steps ----------------------------------------------------------------------
192
+ const relative = path.relative(process.cwd(), target) || ".";
193
+ const steps = [
194
+ relative !== "." && `cd ${relative}`,
195
+ !install && `${pm} install`,
196
+ !options.token && "Add your bot token to .env",
197
+ `${pm} start`
198
+ ].filter(Boolean).join("\n");
199
+ if (interactive) {
200
+ p.note(steps, "Next steps");
201
+ p.outro("Happy coding! 🌿");
202
+ }
203
+ else {
204
+ console.log("\nNext steps:\n" + steps.replace(/^/gm, " "));
205
+ }
206
+ };
207
+ main().catch((err) => fail(err instanceof Error ? err.message : String(err)));
@@ -0,0 +1,137 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+ const templateDir = fileURLToPath(new URL("../template", import.meta.url));
6
+ // Validation ----------------------------------------------------------------------
7
+ export const toPackageName = (input) => input.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]+/, "").replace(/[^a-z0-9-~._]+/g, "-");
8
+ export const isValidPackageName = (name) => /^[a-z0-9-~][a-z0-9-._~]*$/.test(name) && name.length <= 214;
9
+ export const isValidGuildId = (id) => id === "" || /^\d{17,20}$/.test(id);
10
+ export const isValidPrefix = (prefix) => prefix.length > 0 && !/\s/.test(prefix);
11
+ // Accepts "owner/repo", "https://github.com/owner/repo(.git)" and "git@github.com:owner/repo.git"
12
+ export const parseRepo = (input) => {
13
+ const match = input.trim().match(/^(?:https?:\/\/github\.com\/|git@github\.com:)?([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/);
14
+ return match ? `${match[1]}/${match[2]}` : null;
15
+ };
16
+ export const isEmptyDir = (dir) => !fs.existsSync(dir) || fs.readdirSync(dir).every((file) => file === ".git" || file === ".DS_Store");
17
+ // Scaffolding ---------------------------------------------------------------------
18
+ export const createProject = (options) => {
19
+ const target = path.resolve(options.directory);
20
+ fs.mkdirSync(target, { recursive: true });
21
+ fs.cpSync(templateDir, target, {
22
+ recursive: true,
23
+ filter: (source) => path.basename(source) !== "_package.json"
24
+ });
25
+ fs.renameSync(path.join(target, "gitignore"), path.join(target, ".gitignore"));
26
+ writeConfig(target, options);
27
+ writeEnv(target, options);
28
+ writePackageJson(target, options);
29
+ fs.writeFileSync(path.join(target, "README.md"), readme(options));
30
+ return target;
31
+ };
32
+ const writeConfig = (target, options) => {
33
+ const file = path.join(target, "src", "config.ts");
34
+ let config = fs.readFileSync(file, "utf8");
35
+ for (const [key, value] of [["devGuildId", options.devGuildId], ["messageCommandPrefix", options.prefix]]) {
36
+ const pattern = new RegExp(`(${key}:\\s*)"[^"]*"`);
37
+ if (!pattern.test(config))
38
+ throw new Error(`Could not find "${key}" in src/config.ts of the template.`);
39
+ config = config.replace(pattern, (_, start) => start + JSON.stringify(value));
40
+ }
41
+ fs.writeFileSync(file, config);
42
+ };
43
+ const writeEnv = (target, options) => {
44
+ const env = [
45
+ `CLIENT_TOKEN = "${options.token || "[ YOUR CLIENT TOKEN HERE ]"}"`,
46
+ "",
47
+ "# true = register commands only to the dev server (devGuildId in src/config.ts), false = register globally",
48
+ `DEV_MODE = ${options.devMode}`,
49
+ ""
50
+ ].join("\n");
51
+ fs.writeFileSync(path.join(target, ".env"), env, { mode: 0o600 });
52
+ };
53
+ const writePackageJson = (target, options) => {
54
+ const base = JSON.parse(fs.readFileSync(path.join(templateDir, "_package.json"), "utf8"));
55
+ const repo = options.repo;
56
+ const pkg = {
57
+ name: options.name,
58
+ version: "1.0.0",
59
+ description: options.description,
60
+ private: true,
61
+ author: options.author || undefined,
62
+ homepage: repo ? `https://github.com/${repo}#readme` : undefined,
63
+ bugs: repo ? { url: `https://github.com/${repo}/issues` } : undefined,
64
+ repository: repo ? { type: "git", url: `git+https://github.com/${repo}.git` } : undefined,
65
+ type: base.type,
66
+ main: base.main,
67
+ engines: { node: ">=22" },
68
+ scripts: { ...base.scripts, typecheck: "tsc" },
69
+ dependencies: base.dependencies,
70
+ devDependencies: base.devDependencies
71
+ };
72
+ fs.writeFileSync(path.join(target, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
73
+ };
74
+ const readme = (options) => `# ${options.name}
75
+
76
+ ${options.description}
77
+ ${options.repo ? `\n**Repository:** https://github.com/${options.repo}\n` : ""}
78
+ Made with [DJSKit](https://github.com/NZ-Linix/djskit), a discord.js v14 bot template powered by TypeScript.
79
+
80
+ ## Getting started
81
+
82
+ Requires Node.js 22 or newer.
83
+
84
+ \`\`\`bash
85
+ npm install
86
+ npm start
87
+ \`\`\`
88
+
89
+ ## Configuration
90
+
91
+ - \`.env\`: \`CLIENT_TOKEN\` is your bot token, \`DEV_MODE\` controls where commands are registered (see below). Never commit this file.
92
+ - \`src/config.ts\`: \`devGuildId\` (your development server), \`messageCommandPrefix\` (currently \`${options.prefix}\`) and status rotations.
93
+
94
+ ### Dev mode
95
+
96
+ | \`DEV_MODE\` | Commands are registered to | Message commands respond in |
97
+ | --- | --- | --- |
98
+ | \`true\` | Only the dev server (\`devGuildId\`) | Only the dev server |
99
+ | \`false\` | Every server (global) | Every server |
100
+
101
+ Dev server commands update instantly, which makes testing much faster. If \`DEV_MODE\` is \`true\` but no \`devGuildId\` is set, commands are registered globally.
102
+
103
+ ## Project structure
104
+
105
+ | Folder | Contents |
106
+ | --- | --- |
107
+ | \`src/commands/\` | Slash commands (\`SlashCommandBuilder\`, with optional \`autocomplete\`) and prefix message commands |
108
+ | \`src/context/Message/\` | Message context menu commands (right-click a message > Apps) |
109
+ | \`src/context/User/\` | User context menu commands (right-click a user > Apps) |
110
+ | \`src/components/\` | Button, select menu and modal handlers |
111
+ | \`src/events/\` | Discord event listeners |
112
+ `;
113
+ // Post setup ----------------------------------------------------------------------
114
+ export const detectPackageManager = () => {
115
+ const agent = process.env.npm_config_user_agent ?? "";
116
+ for (const pm of ["pnpm", "yarn", "bun"]) {
117
+ if (agent.startsWith(pm))
118
+ return pm;
119
+ }
120
+ return "npm";
121
+ };
122
+ export const run = (command, args, cwd, quiet) => new Promise((resolve) => {
123
+ const child = spawn(command, args, {
124
+ cwd,
125
+ stdio: quiet ? "ignore" : "inherit",
126
+ shell: process.platform === "win32"
127
+ });
128
+ child.on("error", () => resolve(false));
129
+ child.on("close", (code) => resolve(code === 0));
130
+ });
131
+ export const initGit = async (target, quiet) => {
132
+ if (!await run("git", ["init", "-q"], target, quiet))
133
+ return false;
134
+ await run("git", ["add", "-A"], target, quiet);
135
+ await run("git", ["commit", "-q", "-m", "Initial commit from create-djskit"], target, quiet);
136
+ return true;
137
+ };
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "create-djskit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Create a new DJSKit discord.js bot in seconds.",
5
+ "keywords": [
6
+ "discord",
7
+ "discord.js",
8
+ "bot",
9
+ "template",
10
+ "typescript",
11
+ "create",
12
+ "cli"
13
+ ],
14
+ "homepage": "https://github.com/NZ-Linix/djskit#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/NZ-Linix/djskit/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/NZ-Linix/djskit.git",
21
+ "directory": "cli"
22
+ },
23
+ "license": "MIT",
24
+ "author": "NZ-Linix",
25
+ "type": "module",
26
+ "bin": {
27
+ "create-djskit": "dist/index.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "template"
32
+ ],
33
+ "engines": {
34
+ "node": ">=22"
35
+ },
36
+ "scripts": {
37
+ "build": "node scripts/sync-template.mjs && tsc",
38
+ "prepack": "npm run build",
39
+ "dev": "npm run build && node dist/index.js"
40
+ },
41
+ "dependencies": {
42
+ "@clack/prompts": "^1.8.1"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^24.19.2",
46
+ "typescript": "^7.0.2"
47
+ }
48
+ }
@@ -0,0 +1,4 @@
1
+ CLIENT_TOKEN = "[ YOUR CLIENT TOKEN HERE ]"
2
+
3
+ # true = register commands only to the dev server (devGuildId in src/config.ts), false = register globally
4
+ DEV_MODE = false
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "djskit",
3
+ "version": "1.0.0",
4
+ "description": "Discord.js Kit: TypeScript discord bot handler.",
5
+ "keywords": [],
6
+ "homepage": "https://github.com/NZ-Linix/djskit#readme",
7
+ "bugs": {
8
+ "url": "https://github.com/NZ-Linix/djskit/issues"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/NZ-Linix/djskit.git"
13
+ },
14
+ "license": "MIT",
15
+ "author": "NZ-Linix",
16
+ "type": "commonjs",
17
+ "main": "src/index.ts",
18
+ "scripts": {
19
+ "start": "tsx ."
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^24.19.2",
23
+ "tsx": "^4.23.15",
24
+ "typescript": "^7.0.2"
25
+ },
26
+ "dependencies": {
27
+ "chalk": "^6.0.1",
28
+ "discord.js": "^14.27.0",
29
+ "dotdatabase": "^1.1.6",
30
+ "dotenv": "^18.0.9"
31
+ }
32
+ }
@@ -0,0 +1,5 @@
1
+ /node_modules
2
+ /src/database/*
3
+
4
+ package-lock.json
5
+ .env
@@ -0,0 +1,17 @@
1
+ import { Message, Client } from "discord.js";
2
+
3
+ export default {
4
+
5
+ data: {
6
+ name: "test",
7
+ description: "Test command with $ prefix",
8
+ customPrefix: "$"
9
+ },
10
+
11
+ async execute( message: Message, client: Client, args: string[] ) {
12
+
13
+ await message.reply({ content: "Test command executed with $ prefix!" });
14
+
15
+ }
16
+
17
+ }
@@ -0,0 +1,16 @@
1
+ import { Message, Client } from "discord.js";
2
+
3
+ export default {
4
+
5
+ data: {
6
+ name: "test",
7
+ description: "Test command",
8
+ },
9
+
10
+ async execute( message: Message, client: Client, args: string[] ) {
11
+
12
+ await message.reply({ content: "Test command executed!" });
13
+
14
+ }
15
+
16
+ }
@@ -0,0 +1,60 @@
1
+ import { AutocompleteInteraction, ChatInputCommandInteraction, Client, SlashCommandBuilder } from "discord.js";
2
+
3
+ const fruits: Record<string, string[]> = {
4
+ Apple: ["Fuji", "Gala", "Granny Smith", "Honeycrisp"],
5
+ Grape: ["Concord", "Cotton Candy", "Thompson Seedless"],
6
+ Mango: ["Alphonso", "Ataulfo", "Kent"],
7
+ Orange: ["Blood Orange", "Navel", "Valencia"],
8
+ Pear: ["Bartlett", "Bosc", "Conference"]
9
+ };
10
+
11
+ export default {
12
+
13
+ data: new SlashCommandBuilder()
14
+ .setName("testautocomplete")
15
+ .setDescription("Test command with autocomplete")
16
+ .addStringOption((option) => option
17
+ .setName("fruit")
18
+ .setDescription("Pick a fruit")
19
+ .setRequired(true)
20
+ .setAutocomplete(true)
21
+ )
22
+ .addStringOption((option) => option
23
+ .setName("variety")
24
+ .setDescription("Pick a variety of the fruit")
25
+ .setRequired(true)
26
+ .setAutocomplete(true)
27
+ ),
28
+
29
+ async autocomplete( interaction: AutocompleteInteraction, client: Client ) {
30
+
31
+ const focused = interaction.options.getFocused(true);
32
+ const value = focused.value.toLowerCase();
33
+
34
+ let choices: string[] = [];
35
+
36
+ if ( focused.name === "fruit" ) {
37
+ choices = Object.keys(fruits);
38
+ }
39
+
40
+ if ( focused.name === "variety" ) {
41
+ const fruit = interaction.options.getString("fruit");
42
+ choices = fruit && fruits[fruit] ? fruits[fruit] : Object.values(fruits).flat();
43
+ }
44
+
45
+ choices = choices.filter((choice) => choice.toLowerCase().includes(value)).slice(0, 25);
46
+
47
+ await interaction.respond(choices.map((choice) => ({ name: choice, value: choice })));
48
+
49
+ },
50
+
51
+ async execute( interaction: ChatInputCommandInteraction, client: Client ) {
52
+
53
+ const fruit = interaction.options.getString("fruit", true);
54
+ const variety = interaction.options.getString("variety", true);
55
+
56
+ await interaction.reply({ content: "You picked: " + variety + " (" + fruit + ")" });
57
+
58
+ }
59
+
60
+ }
@@ -0,0 +1,22 @@
1
+ import { ChatInputCommandInteraction, Client, SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, MessageFlags } from "discord.js";
2
+
3
+ export default {
4
+ data: new SlashCommandBuilder()
5
+ .setName("testbutton")
6
+ .setDescription("Sends a test button"),
7
+
8
+ async execute(interaction: ChatInputCommandInteraction, client: Client) {
9
+ const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
10
+ new ButtonBuilder()
11
+ .setCustomId("test_button:name=" + interaction.user.displayName)
12
+ .setLabel("Test Button")
13
+ .setStyle(ButtonStyle.Primary)
14
+ );
15
+
16
+ await interaction.reply({
17
+ content: "Click the button below!",
18
+ components: [row],
19
+ flags: [MessageFlags.Ephemeral]
20
+ });
21
+ }
22
+ };
@@ -0,0 +1,15 @@
1
+ import { ChatInputCommandInteraction, Client, SlashCommandBuilder } from "discord.js";
2
+
3
+ export default {
4
+
5
+ data: new SlashCommandBuilder()
6
+ .setName("test")
7
+ .setDescription("Test command"),
8
+
9
+ async execute( interaction: ChatInputCommandInteraction, client: Client ) {
10
+
11
+ await interaction.reply({ content: "Test command executed!" });
12
+
13
+ }
14
+
15
+ }
@@ -0,0 +1,11 @@
1
+ import { ButtonInteraction, Client, MessageFlags } from "discord.js";
2
+
3
+ export default {
4
+
5
+ customId: "test_button",
6
+
7
+ async execute(interaction: ButtonInteraction, client: Client, args: Record<string, string>) {
8
+ await interaction.reply({ content: "Test button clicked! Name: " + args.name, flags: [MessageFlags.Ephemeral] });
9
+ },
10
+
11
+ };
@@ -0,0 +1,10 @@
1
+ export default {
2
+
3
+ devGuildId: "",
4
+ messageCommandPrefix: ">",
5
+
6
+ status: {
7
+ rotations: [],
8
+ }
9
+
10
+ }