js-discord-modularcommand 2.5.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -281,6 +281,118 @@ feedbackCommand.setExecute(async ({ interaction, locale }) => {
281
281
  module.exports = RegisterCommand(feedbackCommand);
282
282
  ```
283
283
 
284
+ ### Subcommands
285
+
286
+ Create commands with subcommands for better organization of related functionality.
287
+
288
+ ```javascript
289
+ // filepath: /commands/settings.js
290
+ const { ModularCommand, RegisterCommand } = require('js-discord-modularcommand');
291
+ const { ApplicationCommandOptionType, PermissionFlagsBits, Locale, EmbedBuilder, Colors } = require('discord.js');
292
+
293
+ const settingsCommand = new ModularCommand('settings')
294
+ .setDescription('Configure bot settings for this server.')
295
+ .setCooldown(5)
296
+ .setPermissionCheck((interaction) => interaction.member.permissions.has(PermissionFlagsBits.ManageGuild));
297
+
298
+ // Add subcommands with their own options
299
+ settingsCommand.addSubCommand({
300
+ name: 'set-prefix',
301
+ description: 'Set the command prefix for this server',
302
+ options: [
303
+ {
304
+ name: 'prefix',
305
+ description: 'The new prefix to use',
306
+ type: ApplicationCommandOptionType.String,
307
+ required: true
308
+ }
309
+ ]
310
+ });
311
+
312
+ settingsCommand.addSubCommand({
313
+ name: 'set-channel',
314
+ description: 'Set the default channel for bot messages',
315
+ options: [
316
+ {
317
+ name: 'channel',
318
+ description: 'The channel to use for bot messages',
319
+ type: ApplicationCommandOptionType.Channel,
320
+ required: true
321
+ }
322
+ ]
323
+ });
324
+
325
+ settingsCommand.addSubCommand({
326
+ name: 'view-config',
327
+ description: 'View current server configuration'
328
+ });
329
+
330
+ // Localize subcommands and their options
331
+ settingsCommand.setLocalizationSubCommands({
332
+ [Locale.EnglishUS]: {
333
+ 'set-prefix': 'Set Command Prefix',
334
+ 'set-prefix.description': 'Set the command prefix for this server',
335
+ 'set-prefix.prefix': 'Prefix',
336
+ 'set-prefix.prefix.description': 'The new prefix to use for commands',
337
+
338
+ 'set-channel': 'Set Default Channel',
339
+ 'set-channel.description': 'Set the default channel for bot messages',
340
+ 'set-channel.channel': 'Channel',
341
+ 'set-channel.channel.description': 'The channel to use for bot messages',
342
+
343
+ 'view-config': 'View Configuration',
344
+ 'view-config.description': 'View current server configuration'
345
+ }
346
+ });
347
+
348
+ // Set up localized phrases
349
+ settingsCommand.setLocalizationPhrases({
350
+ [Locale.EnglishUS]: {
351
+ 'success.prefix_set': 'Command prefix has been set to `{prefix}`.',
352
+ 'success.channel_set': 'Default channel has been set to {channel}.',
353
+ 'config.title': 'Server Configuration - {serverName}',
354
+ 'config.description': 'Current bot settings for this server:'
355
+ }
356
+ });
357
+
358
+ // Handle all subcommands in a single execute function
359
+ settingsCommand.setExecute(async ({ interaction, locale, args }) => {
360
+ await interaction.deferReply();
361
+
362
+ const subcommand = args.subcommand; // The subcommand name is automatically added to args
363
+
364
+ switch (subcommand) {
365
+ case 'set-prefix':
366
+ const newPrefix = args.prefix;
367
+ // Save the prefix to your database
368
+ await interaction.editReply({
369
+ content: locale['success.prefix_set'].replace('{prefix}', newPrefix)
370
+ });
371
+ break;
372
+
373
+ case 'set-channel':
374
+ const channel = args.channel;
375
+ // Save the channel to your database
376
+ await interaction.editReply({
377
+ content: locale['success.channel_set'].replace('{channel}', `<#${channel.id}>`)
378
+ });
379
+ break;
380
+
381
+ case 'view-config':
382
+ const embed = new EmbedBuilder()
383
+ .setColor(Colors.Blue)
384
+ .setTitle(locale['config.title'].replace('{serverName}', interaction.guild.name))
385
+ .setDescription(locale['config.description'])
386
+ .setTimestamp();
387
+
388
+ await interaction.editReply({ embeds: [embed] });
389
+ break;
390
+ }
391
+ });
392
+
393
+ module.exports = RegisterCommand(settingsCommand);
394
+ ```
395
+
284
396
  ## License
285
397
 
286
398
  This project is licensed under the MIT License. See the `LICENSE` file for details.
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @file Manages command cooldowns for users to prevent spam.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/cooldown.ts
4
+ * @author vicentefelipechile
5
+ * @description Manages command cooldowns for users in a Discord bot using Discord.js.
5
6
  */
6
7
  /**
7
8
  * @interface CooldownStatus
package/dist/cooldown.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Manages command cooldowns for users to prevent spam.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/cooldown.ts
5
+ * @author vicentefelipechile
6
+ * @description Manages command cooldowns for users in a Discord bot using Discord.js.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
8
9
  exports.cooldownRegister = cooldownRegister;
@@ -66,7 +67,7 @@ function isUserInCooldown(name, userId) {
66
67
  if (lastTime === undefined) {
67
68
  return { inCooldown: false, waitTime: 0 };
68
69
  }
69
- const timePassed = (Date.now() - lastTime) / 1000; // Time passed in seconds
70
+ const timePassed = Math.floor((Date.now() - lastTime) / 1000); // Time passed in seconds
70
71
  const cooldownCommand = COMMAND_CONFIG.get(name);
71
72
  // This error indicates a developer mistake, as a command should be registered before being checked.
72
73
  if (cooldownCommand === undefined) {
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { LOCALE_DELAY, LOCALE_ERROR, LOCALE_FORBIDDEN, LOCALE_NSFW } from "./locales";
2
2
  import ModularCommandHandler from "./interaction";
3
- import { CommandData } from "./types";
3
+ import { CommandData, SubCommand } from "./types";
4
4
  import ModularModal from "./modularmodal";
5
5
  import ModularCommand from "./modularcommand";
6
6
  import ModularButton from "./modularbutton";
7
7
  import RegisterCommand from "./registercommand";
8
8
  import LoadCommand from "./loadcommands";
9
9
  export default ModularCommand;
10
- export { ModularCommand, ModularButton, ModularModal, CommandData, RegisterCommand, LoadCommand, ModularCommandHandler, LOCALE_DELAY, LOCALE_ERROR, LOCALE_FORBIDDEN, LOCALE_NSFW };
10
+ export { ModularCommand, ModularButton, ModularModal, CommandData, SubCommand, RegisterCommand, LoadCommand, ModularCommandHandler, LOCALE_DELAY, LOCALE_ERROR, LOCALE_FORBIDDEN, LOCALE_NSFW };
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @file Contains the logic for handling Discord bot interactions.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/interaction.ts
4
+ * @author vicentefelipechile
5
+ * @description Provides a modular command handler for a Discord bot using Discord.js.
5
6
  */
6
7
  import { BaseInteraction, CommandInteraction, MessageComponentInteraction, ModalSubmitInteraction } from "discord.js";
7
8
  import { ClientWithCommands } from "./types";
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the logic for handling Discord bot interactions.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/interaction.ts
5
+ * @author vicentefelipechile
6
+ * @description Provides a modular command handler for a Discord bot using Discord.js.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
8
9
  exports.default = ModularCommandHandler;
10
+ // =================================================================================================
11
+ // Imports
12
+ // =================================================================================================
9
13
  const discord_js_1 = require("discord.js");
10
14
  const locales_1 = require("./locales");
11
15
  // =================================================================================================
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @file Contains the logic for loading modular commands into the Discord bot client.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/loadcommands.ts
4
+ * @author vicentefelipechile
5
+ * @description Loads one or more commands into an array.
5
6
  */
6
7
  import { CommandData } from "./types";
7
8
  /**
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the logic for loading modular commands into the Discord bot client.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/loadcommands.ts
5
+ * @author vicentefelipechile
6
+ * @description Loads one or more commands into an array.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
8
9
  exports.default = LoadCommand;
@@ -32,6 +33,12 @@ exports.default = LoadCommand;
32
33
  function LoadCommand(commandsArray, command) {
33
34
  const commands = Array.isArray(command) ? command : [command];
34
35
  for (const cmd of commands) {
36
+ if (!cmd || typeof cmd !== "object") {
37
+ throw new TypeError(`Invalid command provided. Expected an object but received: ${typeof cmd}`);
38
+ }
39
+ if (!cmd.data || typeof cmd.data.name !== "string") {
40
+ throw new TypeError(`Command is missing a valid 'name' property.`);
41
+ }
35
42
  if (typeof cmd.execute !== "function") {
36
43
  throw new TypeError(`Command "${cmd.data.name}" is missing a required 'execute' function.`);
37
44
  }
package/dist/locales.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
- * @file Contains the generic localization phrases used throughout the application.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/locales.ts
4
+ * @author vicentefelipechile
5
+ * @description Localization phrases for various command responses in a Discord bot using Discord.js.
5
6
  */
6
7
  import { Locale } from "discord.js";
7
- import LOCALE_DELAY, { ModularLocale } from "./modularlocale";
8
+ import LOCALE_DELAY from "./modularlocale";
8
9
  /**
9
10
  * @description Localization phrases for various commands, specifically for permission errors.
10
11
  */
@@ -17,4 +18,4 @@ declare const LOCALE_NSFW: Record<Locale, string>;
17
18
  * @description Localization phrases for general application errors.
18
19
  */
19
20
  declare const LOCALE_ERROR: Record<Locale, string>;
20
- export { LOCALE_DELAY, LOCALE_ERROR, LOCALE_FORBIDDEN, LOCALE_NSFW, ModularLocale };
21
+ export { LOCALE_DELAY, LOCALE_ERROR, LOCALE_FORBIDDEN, LOCALE_NSFW, };
package/dist/locales.js CHANGED
@@ -1,48 +1,21 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the generic localization phrases used throughout the application.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/locales.ts
5
+ * @author vicentefelipechile
6
+ * @description Localization phrases for various command responses in a Discord bot using Discord.js.
6
7
  */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
40
11
  Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.ModularLocale = exports.LOCALE_NSFW = exports.LOCALE_FORBIDDEN = exports.LOCALE_ERROR = exports.LOCALE_DELAY = void 0;
12
+ exports.LOCALE_NSFW = exports.LOCALE_FORBIDDEN = exports.LOCALE_ERROR = exports.LOCALE_DELAY = void 0;
13
+ // =================================================================================================
14
+ // Imports
15
+ // =================================================================================================
42
16
  const discord_js_1 = require("discord.js");
43
- const modularlocale_1 = __importStar(require("./modularlocale"));
17
+ const modularlocale_1 = __importDefault(require("./modularlocale"));
44
18
  exports.LOCALE_DELAY = modularlocale_1.default;
45
- Object.defineProperty(exports, "ModularLocale", { enumerable: true, get: function () { return modularlocale_1.ModularLocale; } });
46
19
  // =================================================================================================
47
20
  // Localization Phrases
48
21
  // =================================================================================================
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @file Contains the structure for creating reusable button components.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/modularbutton.ts
4
+ * @author vicentefelipechile
5
+ * @description A class to create and manage reusable button components in a Discord bot using Discord.js.
5
6
  */
6
7
  import { ButtonBuilder } from "discord.js";
7
8
  import { ButtonExecuteFunction, LocaleKey } from "./types";
@@ -1,13 +1,17 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the structure for creating reusable button components.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/modularbutton.ts
5
+ * @author vicentefelipechile
6
+ * @description A class to create and manage reusable button components in a Discord bot using Discord.js.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ // =================================================================================================
10
+ // Imports
11
+ // =================================================================================================
8
12
  const discord_js_1 = require("discord.js");
9
13
  // =================================================================================================
10
- // Main Class
14
+ // ModularButton Class
11
15
  // =================================================================================================
12
16
  /**
13
17
  * @class ModularButton
@@ -21,6 +25,12 @@ class ModularButton {
21
25
  constructor(customId, command) {
22
26
  /** Use other mechanisms to handle button interactions. */
23
27
  this.execute = async () => { };
28
+ if (!customId || typeof customId !== "string") {
29
+ throw new Error("Custom ID must be a non-empty string.");
30
+ }
31
+ if (!command.name || typeof command.name !== "string") {
32
+ throw new Error("ModularCommand must have a valid name.");
33
+ }
24
34
  this.buttonObject = new discord_js_1.ButtonBuilder()
25
35
  .setCustomId(`${command.name}_${customId}`);
26
36
  this.command = command;
@@ -56,7 +66,14 @@ class ModularButton {
56
66
  * @returns {ButtonBuilder} The built ButtonBuilder instance with localized label.
57
67
  */
58
68
  build(locale) {
59
- this.buttonObject.setLabel(locale[`${this.command.name}.${this.buttonId}`]);
69
+ if (!locale || typeof locale !== "object") {
70
+ throw new Error("Locale must be a valid object.");
71
+ }
72
+ const labelKey = `${this.command.name}.${this.buttonId}`;
73
+ if (!locale[labelKey]) {
74
+ throw new Error(`Locale key "${labelKey}" not found in the provided locale object.`);
75
+ }
76
+ this.buttonObject.setLabel(locale[labelKey]);
60
77
  return this.buttonObject;
61
78
  }
62
79
  }
@@ -1,13 +1,15 @@
1
1
  /**
2
- * @file Contains the main class for creating modular commands.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/modularcommand.ts
4
+ * @author vicentefelipechile
5
+ * @description Provides a modular command structure for a Discord bot using Discord.js.
5
6
  */
6
7
  import { LocalizationMap } from 'discord.js';
7
8
  import ModularModal from './modularmodal.js';
8
9
  import ModularButton from './modularbutton.js';
9
10
  import ModularSelectMenu from './modularselectmenu.js';
10
- import { ButtonExecuteFunction, CommandExecuteFunction, CommandOption, ComponentExecuteFunction, ModalExecuteFunction, PermissionCheckFunction, SelectMenuExecuteFunction } from './types.js';
11
+ import { ButtonExecuteFunction, CommandExecuteFunction, CommandOption, ComponentExecuteFunction, ModalExecuteFunction, PermissionCheckFunction, SelectMenuExecuteFunction, // <- ADD THIS
12
+ SubCommand } from './types.js';
11
13
  /**
12
14
  * @description Represents a modular command that can be registered with Discord.js.
13
15
  * It allows for dynamic command creation and execution in a simple way.
@@ -39,6 +41,10 @@ export default class ModularCommand {
39
41
  options: CommandOption[];
40
42
  /** An object containing localizations for the names and descriptions of the options. */
41
43
  optionsLocalizations: LocalizationMap;
44
+ /** The subcommands that this command supports. */
45
+ subCommands: SubCommand[];
46
+ /** An object containing localizations for subcommand names, descriptions and options. */
47
+ subCommandLocalizations: LocalizationMap;
42
48
  /** The main function that executes when the command is invoked. */
43
49
  execute: CommandExecuteFunction;
44
50
  /** (Optional) The function to handle button interactions. */
@@ -101,6 +107,12 @@ export default class ModularCommand {
101
107
  * @returns {ModularCommand} The command instance for chaining.
102
108
  */
103
109
  setLocalizationPhrases(localizationPhrases: LocalizationMap): this;
110
+ /**
111
+ * Sets the localizations for subcommands, including names, descriptions, and options.
112
+ * @param {LocalizationMap} localizations The subcommand localizations map.
113
+ * @returns {ModularCommand} The command instance for chaining.
114
+ */
115
+ setLocalizationSubCommands(localizations: LocalizationMap): this;
104
116
  /**
105
117
  * Sets the execute function for the command.
106
118
  * @param {CommandExecuteFunction} executeFunction The function to execute.
@@ -137,6 +149,12 @@ export default class ModularCommand {
137
149
  * @returns {ModularCommand} The command instance for chaining.
138
150
  */
139
151
  addOption(option: CommandOption): this;
152
+ /**
153
+ * Adds a subcommand to the command.
154
+ * @param {SubCommand} subCommand The subcommand configuration.
155
+ * @returns {ModularCommand} The command instance for chaining.
156
+ */
157
+ addSubCommand(subCommand: SubCommand): this;
140
158
  /**
141
159
  * Adds a custom ID handler for the command.
142
160
  * @param {string} customId The custom ID to match.
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the main class for creating modular commands.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/modularcommand.ts
5
+ * @author vicentefelipechile
6
+ * @description Provides a modular command structure for a Discord bot using Discord.js.
6
7
  */
7
8
  var __importDefault = (this && this.__importDefault) || function (mod) {
8
9
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -16,7 +17,7 @@ const modularmodal_js_1 = __importDefault(require("./modularmodal.js"));
16
17
  const modularbutton_js_1 = __importDefault(require("./modularbutton.js"));
17
18
  const modularselectmenu_js_1 = __importDefault(require("./modularselectmenu.js")); // <- ADD THIS
18
19
  // =================================================================================================
19
- // Class: ModularCommand
20
+ // ModularCommand Class
20
21
  // =================================================================================================
21
22
  /**
22
23
  * @description Represents a modular command that can be registered with Discord.js.
@@ -47,6 +48,8 @@ class ModularCommand {
47
48
  this.modalExecute = undefined;
48
49
  this.options = [];
49
50
  this.optionsLocalizations = {};
51
+ this.subCommands = [];
52
+ this.subCommandLocalizations = {};
50
53
  this.customIdHandlers = {};
51
54
  this.cooldown = 3;
52
55
  this.modals = new Map();
@@ -62,6 +65,9 @@ class ModularCommand {
62
65
  * @returns {ModularCommand} The command instance for chaining.
63
66
  */
64
67
  setDescription(description) {
68
+ if (!description || typeof description !== "string") {
69
+ throw new Error("Description must be a non-empty string.");
70
+ }
65
71
  this.description = description;
66
72
  return this;
67
73
  }
@@ -72,6 +78,9 @@ class ModularCommand {
72
78
  * @returns {ModularCommand} The command instance for chaining.
73
79
  */
74
80
  setLocalizationsDescription(localizations) {
81
+ if (!localizations || typeof localizations !== "object") {
82
+ throw new Error("Localizations must be a valid object.");
83
+ }
75
84
  return this.setLocalizationDescription(localizations);
76
85
  }
77
86
  /**
@@ -80,6 +89,9 @@ class ModularCommand {
80
89
  * @returns {ModularCommand} The command instance for chaining.
81
90
  */
82
91
  setLocalizationDescription(localizations) {
92
+ if (!localizations || typeof localizations !== "object") {
93
+ throw new Error("Localizations must be a valid object.");
94
+ }
83
95
  this.description = localizations[discord_js_1.Locale.EnglishUS] || this.description;
84
96
  this.descriptionLocalizations = {
85
97
  ...this.descriptionLocalizations,
@@ -93,6 +105,9 @@ class ModularCommand {
93
105
  * @returns {ModularCommand} The command instance for chaining.
94
106
  */
95
107
  setLocalizationOptions(localizations) {
108
+ if (!localizations || typeof localizations !== "object") {
109
+ throw new Error("Localizations must be a valid object.");
110
+ }
96
111
  this.optionsLocalizations = {
97
112
  ...this.optionsLocalizations,
98
113
  ...localizations,
@@ -105,18 +120,39 @@ class ModularCommand {
105
120
  * @returns {ModularCommand} The command instance for chaining.
106
121
  */
107
122
  setLocalizationPhrases(localizationPhrases) {
123
+ if (!localizationPhrases || typeof localizationPhrases !== "object") {
124
+ throw new Error("Localization phrases must be a valid object.");
125
+ }
108
126
  this.localizationPhrases = {
109
127
  ...this.localizationPhrases,
110
128
  ...localizationPhrases,
111
129
  };
112
130
  return this;
113
131
  }
132
+ /**
133
+ * Sets the localizations for subcommands, including names, descriptions, and options.
134
+ * @param {LocalizationMap} localizations The subcommand localizations map.
135
+ * @returns {ModularCommand} The command instance for chaining.
136
+ */
137
+ setLocalizationSubCommands(localizations) {
138
+ if (!localizations || typeof localizations !== "object") {
139
+ throw new Error("Localizations must be a valid object.");
140
+ }
141
+ this.subCommandLocalizations = {
142
+ ...this.subCommandLocalizations,
143
+ ...localizations,
144
+ };
145
+ return this;
146
+ }
114
147
  /**
115
148
  * Sets the execute function for the command.
116
149
  * @param {CommandExecuteFunction} executeFunction The function to execute.
117
150
  * @returns {ModularCommand} The command instance for chaining.
118
151
  */
119
152
  setExecute(executeFunction) {
153
+ if (!executeFunction || typeof executeFunction !== "function") {
154
+ throw new Error("Execute function must be a valid function.");
155
+ }
120
156
  this.execute = executeFunction;
121
157
  return this;
122
158
  }
@@ -127,6 +163,12 @@ class ModularCommand {
127
163
  * @returns {ModularCommand} The command instance for chaining.
128
164
  */
129
165
  setComponentExecute(componentId, executeFunction) {
166
+ if (!componentId || typeof componentId !== "string") {
167
+ throw new Error("Component ID must be a non-empty string.");
168
+ }
169
+ if (!executeFunction || typeof executeFunction !== "function") {
170
+ throw new Error("Execute function must be a valid function.");
171
+ }
130
172
  this.componentId = componentId;
131
173
  this.componentExecute = executeFunction;
132
174
  return this;
@@ -137,6 +179,9 @@ class ModularCommand {
137
179
  * @returns {ModularCommand} The command instance for chaining.
138
180
  */
139
181
  setPermissionCheck(permissionCheckFunction) {
182
+ if (!permissionCheckFunction || typeof permissionCheckFunction !== "function") {
183
+ throw new Error("Permission check function must be a valid function.");
184
+ }
140
185
  this.permissionCheck = permissionCheckFunction;
141
186
  return this;
142
187
  }
@@ -146,6 +191,9 @@ class ModularCommand {
146
191
  * @returns {ModularCommand} The command instance for chaining.
147
192
  */
148
193
  setCooldown(cooldown) {
194
+ if (typeof cooldown !== "number" || cooldown < 0) {
195
+ throw new Error("Cooldown must be a non-negative number.");
196
+ }
149
197
  this.cooldown = cooldown;
150
198
  return this;
151
199
  }
@@ -162,9 +210,24 @@ class ModularCommand {
162
210
  * @returns {ModularCommand} The command instance for chaining.
163
211
  */
164
212
  addOption(option) {
213
+ if (!option || typeof option !== "object") {
214
+ throw new Error("Option must be a valid object.");
215
+ }
165
216
  this.options.push(option);
166
217
  return this;
167
218
  }
219
+ /**
220
+ * Adds a subcommand to the command.
221
+ * @param {SubCommand} subCommand The subcommand configuration.
222
+ * @returns {ModularCommand} The command instance for chaining.
223
+ */
224
+ addSubCommand(subCommand) {
225
+ if (!subCommand || typeof subCommand !== "object") {
226
+ throw new Error("SubCommand must be a valid object.");
227
+ }
228
+ this.subCommands.push(subCommand);
229
+ return this;
230
+ }
168
231
  /**
169
232
  * Adds a custom ID handler for the command.
170
233
  * @param {string} customId The custom ID to match.
@@ -172,6 +235,12 @@ class ModularCommand {
172
235
  * @returns {ModularCommand} The command instance for chaining.
173
236
  */
174
237
  addCustomIDHandler(customId, handlerFunction) {
238
+ if (!customId || typeof customId !== "string") {
239
+ throw new Error("Custom ID must be a non-empty string.");
240
+ }
241
+ if (!handlerFunction || typeof handlerFunction !== "function") {
242
+ throw new Error("Handler function must be a valid function.");
243
+ }
175
244
  this.customIdHandlers[customId] = handlerFunction;
176
245
  return this;
177
246
  }
@@ -181,6 +250,9 @@ class ModularCommand {
181
250
  * @returns {ModularModal} The created modal instance.
182
251
  */
183
252
  addModal(modalId) {
253
+ if (!modalId || typeof modalId !== "string") {
254
+ throw new Error("Modal ID must be a non-empty string.");
255
+ }
184
256
  const modal = new modularmodal_js_1.default(modalId, this);
185
257
  this.modals.set(modalId, modal);
186
258
  return modal;
@@ -192,6 +264,12 @@ class ModularCommand {
192
264
  * @return {ModularButton} The created button instance.
193
265
  */
194
266
  addButton(customId, execute) {
267
+ if (!customId || typeof customId !== "string") {
268
+ throw new Error("Custom ID must be a non-empty string.");
269
+ }
270
+ if (!execute || typeof execute !== "function") {
271
+ throw new Error("Execute function must be a valid function.");
272
+ }
195
273
  const button = new modularbutton_js_1.default(customId, this);
196
274
  button.setExecute(execute);
197
275
  this.buttons.set(customId, button);
@@ -204,6 +282,9 @@ class ModularCommand {
204
282
  * @returns {ModularSelectMenu} The created select menu instance.
205
283
  */
206
284
  addSelectMenu(selectMenuId) {
285
+ if (!selectMenuId || typeof selectMenuId !== "string") {
286
+ throw new Error("Select Menu ID must be a non-empty string.");
287
+ }
207
288
  const menu = new modularselectmenu_js_1.default(selectMenuId, this);
208
289
  this.selectMenus.set(selectMenuId, menu);
209
290
  this.selectMenusArray.push(menu);
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @module ModularLocale
2
+ * @license MIT
3
+ * @file src/modularlocale.ts
4
+ * @author vicentefelipechile
3
5
  * @description Generic localization phrases used throughout the application.
4
- * @license MIT
5
6
  */
6
7
  import { Locale } from "discord.js";
7
8
  /**
@@ -100,4 +101,3 @@ declare const LOCALE_DELAY: {
100
101
  vi: ModularLocale;
101
102
  };
102
103
  export default LOCALE_DELAY;
103
- export { LOCALE_DELAY, ModularLocale };
@@ -1,12 +1,18 @@
1
1
  "use strict";
2
2
  /**
3
- * @module ModularLocale
3
+ * @license MIT
4
+ * @file src/modularlocale.ts
5
+ * @author vicentefelipechile
4
6
  * @description Generic localization phrases used throughout the application.
5
- * @license MIT
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.ModularLocale = exports.LOCALE_DELAY = void 0;
9
+ // =================================================================================================
10
+ // Imports
11
+ // =================================================================================================
9
12
  const discord_js_1 = require("discord.js");
13
+ // =================================================================================================
14
+ // Helper Functions
15
+ // =================================================================================================
10
16
  /**
11
17
  * Format a time unit (seconds or minutes) for localization.
12
18
  * @param unit The time unit ('s' for seconds, 'm' for minutes).
@@ -23,6 +29,9 @@ function formatUnit(unit, unitCount, unitData) {
23
29
  }
24
30
  }
25
31
  ;
32
+ // =================================================================================================
33
+ // ModularLocale Class
34
+ // =================================================================================================
26
35
  /**
27
36
  * @description Class to handle localization in a modular way.
28
37
  */
@@ -111,7 +120,9 @@ class ModularLocale {
111
120
  return formattedPhrase.replace(/\s+/g, ' ').trim();
112
121
  }
113
122
  }
114
- exports.ModularLocale = ModularLocale;
123
+ // =================================================================================================
124
+ // Localization Phrases
125
+ // =================================================================================================
115
126
  /**
116
127
  * @description Localization phrases for delay commands in ModularLocale structure.
117
128
  * @example ```js
@@ -316,6 +327,8 @@ const LOCALE_DELAY = {
316
327
  .setPhrasePlural('Bạn phải đợi {seconds} và {minutes} trước khi sử dụng lại lệnh này.')
317
328
  .setPhraseOnlyMinutes('Bạn phải đợi {minutes} trước khi sử dụng lại lệnh này.')
318
329
  };
319
- exports.LOCALE_DELAY = LOCALE_DELAY;
320
330
  Object.freeze(LOCALE_DELAY);
331
+ // =================================================================================================
332
+ // Export
333
+ // =================================================================================================
321
334
  exports.default = LOCALE_DELAY;
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @file Contains the structure and logic for creating modular modals.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/modularmodal.ts
4
+ * @author vicentefelipechile
5
+ * @description Represents a modular modal that can be dynamically created and managed.
5
6
  */
6
7
  import { ModalBuilder, TextInputBuilder } from "discord.js";
7
8
  import { LocaleKey, ModalExecuteFunction } from "./types";
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the structure and logic for creating modular modals.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/modularmodal.ts
5
+ * @author vicentefelipechile
6
+ * @description Represents a modular modal that can be dynamically created and managed.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ // =================================================================================================
10
+ // Imports
11
+ // =================================================================================================
8
12
  const discord_js_1 = require("discord.js");
9
13
  // =================================================================================================
10
14
  // Main Class
@@ -22,6 +26,12 @@ class ModularModal {
22
26
  constructor(modalId, command) {
23
27
  /** The function to execute when the modal is submitted. */
24
28
  this.execute = async () => { };
29
+ if (!modalId || typeof modalId !== "string") {
30
+ throw new Error("Modal ID must be a non-empty string.");
31
+ }
32
+ if (!command.name || typeof command.name !== "string") {
33
+ throw new Error("ModularCommand must have a valid name.");
34
+ }
25
35
  this.customId = `${command.name}_${modalId}`;
26
36
  this.modalId = modalId;
27
37
  this.command = command;
@@ -48,6 +58,9 @@ class ModularModal {
48
58
  * @returns {this} The current ModularModal instance for method chaining.
49
59
  */
50
60
  setExecute(executeFunction) {
61
+ if (!executeFunction || typeof executeFunction !== "function") {
62
+ throw new Error("Execute function must be a valid function.");
63
+ }
51
64
  this.execute = executeFunction;
52
65
  return this;
53
66
  }
@@ -57,6 +70,9 @@ class ModularModal {
57
70
  * @returns {TextInputBuilder} The created text input instance.
58
71
  */
59
72
  newTextInput(id) {
73
+ if (!id || typeof id !== "string") {
74
+ throw new Error("Text input ID must be a non-empty string.");
75
+ }
60
76
  const textInput = new discord_js_1.TextInputBuilder()
61
77
  .setCustomId(id);
62
78
  this.modalInputs.set(id, textInput);
@@ -70,16 +86,25 @@ class ModularModal {
70
86
  * @returns {ModalBuilder} The fully constructed modal object ready to be sent to a user.
71
87
  */
72
88
  build(locale) {
73
- this.modalObject.setTitle(locale[`${this.command.name}.${this.modalId}.title`]);
89
+ if (!locale || typeof locale !== "object") {
90
+ throw new Error("Locale must be a valid object.");
91
+ }
92
+ const titleKey = `${this.command.name}.${this.modalId}.title`;
93
+ if (!locale[titleKey]) {
94
+ throw new Error(`Missing locale entry for modal title: ${titleKey}`);
95
+ }
96
+ this.modalObject.setTitle(locale[titleKey]);
74
97
  this.modalInputs.forEach((input, id) => {
75
98
  const labelKey = `${this.command.name}.${id}.label`;
76
99
  const placeholderKey = `${this.command.name}.${id}.placeholder`;
77
- if (locale[labelKey]) {
78
- input.setLabel(locale[labelKey]);
100
+ if (!locale[labelKey]) {
101
+ throw new Error(`Missing locale entry for text input label: ${labelKey}`);
79
102
  }
80
- if (locale[placeholderKey]) {
81
- input.setPlaceholder(locale[placeholderKey]);
103
+ if (!locale[placeholderKey]) {
104
+ throw new Error(`Missing locale entry for text input placeholder: ${placeholderKey}`);
82
105
  }
106
+ input.setLabel(locale[labelKey]);
107
+ input.setPlaceholder(locale[placeholderKey]);
83
108
  });
84
109
  return this.modalObject;
85
110
  }
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @file Contains the structure and logic for creating modular select menus.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/modularselectmenu.ts
4
+ * @author vicentefelipechile
5
+ * @description Represents a modular select menu that can be dynamically created and managed.
5
6
  */
6
7
  import { StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from "discord.js";
7
8
  import { LocaleKey, SelectMenuExecuteFunction } from "./types";
@@ -1,11 +1,18 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the structure and logic for creating modular select menus.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/modularselectmenu.ts
5
+ * @author vicentefelipechile
6
+ * @description Represents a modular select menu that can be dynamically created and managed.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ // =================================================================================================
10
+ // Imports
11
+ // =================================================================================================
8
12
  const discord_js_1 = require("discord.js");
13
+ // =================================================================================================
14
+ // ModularSelectMenu Class
15
+ // =================================================================================================
9
16
  /**
10
17
  * @class ModularSelectMenu
11
18
  * @description Represents a modular select menu that can be dynamically created and managed.
@@ -19,6 +26,12 @@ class ModularSelectMenu {
19
26
  constructor(selectMenuId, command) {
20
27
  /** The function to execute when the select menu is interacted with. */
21
28
  this.execute = async () => { };
29
+ if (!selectMenuId || typeof selectMenuId !== "string") {
30
+ throw new Error("Select Menu ID must be a non-empty string.");
31
+ }
32
+ if (!command.name || typeof command.name !== "string") {
33
+ throw new Error("ModularCommand must have a valid name.");
34
+ }
22
35
  this.selectMenuId = selectMenuId;
23
36
  this.command = command;
24
37
  this.customId = `${command.name}_${selectMenuId}`;
@@ -45,6 +58,9 @@ class ModularSelectMenu {
45
58
  * @returns {this} The current ModularSelectMenu instance for method chaining.
46
59
  */
47
60
  setExecute(executeFunction) {
61
+ if (!executeFunction || typeof executeFunction !== "function") {
62
+ throw new Error("Execute function must be a valid function.");
63
+ }
48
64
  this.execute = executeFunction;
49
65
  return this;
50
66
  }
@@ -55,6 +71,9 @@ class ModularSelectMenu {
55
71
  * @returns {StringSelectMenuOptionBuilder} The created option instance for further configuration (e.g., `setDefault`).
56
72
  */
57
73
  addOption(value) {
74
+ if (!value || typeof value !== "string") {
75
+ throw new Error("Option value must be a non-empty string.");
76
+ }
58
77
  const option = new discord_js_1.StringSelectMenuOptionBuilder().setValue(value);
59
78
  this.options.set(value, option);
60
79
  return option;
@@ -65,6 +84,9 @@ class ModularSelectMenu {
65
84
  * @returns {StringSelectMenuBuilder} The fully constructed select menu object ready to be sent to a user.
66
85
  */
67
86
  build(locale) {
87
+ if (!locale || typeof locale !== "object") {
88
+ throw new Error("Locale must be a valid object.");
89
+ }
68
90
  const placeholderKey = `${this.command.name}.${this.selectMenuId}.placeholder`;
69
91
  if (locale[placeholderKey]) {
70
92
  this.selectMenuObject.setPlaceholder(locale[placeholderKey]);
@@ -1,7 +1,10 @@
1
1
  /**
2
- * @file Contains the logic for registering modular commands and building their Discord.js data structures.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/registercommand.ts
4
+ * @author vicentefelipechile
5
+ * @description This module provides functionality to register modular commands for a Discord bot using Discord.js.
6
+ * It constructs command data objects, sets up execution contexts for various interaction types,
7
+ * and handles localization, permissions, NSFW checks, and cooldowns.
5
8
  */
6
9
  import { CommandData } from "./types";
7
10
  import ModularCommand from "./modularcommand";
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the logic for registering modular commands and building their Discord.js data structures.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/registercommand.ts
5
+ * @author vicentefelipechile
6
+ * @description This module provides functionality to register modular commands for a Discord bot using Discord.js.
7
+ * It constructs command data objects, sets up execution contexts for various interaction types,
8
+ * and handles localization, permissions, NSFW checks, and cooldowns.
6
9
  */
7
10
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
11
  if (k2 === undefined) k2 = k;
@@ -39,9 +42,152 @@ var __importStar = (this && this.__importStar) || (function () {
39
42
  })();
40
43
  Object.defineProperty(exports, "__esModule", { value: true });
41
44
  exports.default = RegisterCommand;
45
+ // =================================================================================================
46
+ // Imports
47
+ // =================================================================================================
42
48
  const discord_js_1 = require("discord.js");
43
49
  const cooldown_1 = __importStar(require("./cooldown"));
44
50
  const locales_1 = require("./locales");
51
+ /**
52
+ * @description Gets localized description for a subcommand.
53
+ * @param {ModularCommand} command The command instance.
54
+ * @param {string} subCommandName The name of the subcommand.
55
+ * @param {string} defaultDescription The default description.
56
+ * @returns {string} The localized description.
57
+ */
58
+ function getLocalizedSubCommandDescription(command, subCommandName, defaultDescription) {
59
+ if (!command.subCommandLocalizations)
60
+ return defaultDescription;
61
+ const localizations = command.subCommandLocalizations;
62
+ const enLocalizations = localizations[discord_js_1.Locale.EnglishUS];
63
+ return enLocalizations?.[`${subCommandName}.description`] || defaultDescription;
64
+ }
65
+ /**
66
+ * @description Gets localized description for a subcommand option.
67
+ * @param {ModularCommand} command The command instance.
68
+ * @param {string} subCommandName The name of the subcommand.
69
+ * @param {string} optionName The name of the option.
70
+ * @param {string} defaultDescription The default description.
71
+ * @returns {string} The localized description.
72
+ */
73
+ function getLocalizedOptionDescription(command, subCommandName, optionName, defaultDescription) {
74
+ if (!command.subCommandLocalizations)
75
+ return defaultDescription;
76
+ const localizations = command.subCommandLocalizations;
77
+ const enLocalizations = localizations[discord_js_1.Locale.EnglishUS];
78
+ return enLocalizations?.[`${subCommandName}.${optionName}.description`] || defaultDescription;
79
+ }
80
+ /**
81
+ * @description Creates an option builder function with common configuration.
82
+ * @param {CommandOption} opt The option configuration.
83
+ * @param {string} description The resolved description.
84
+ * @returns {Function} The option builder function.
85
+ */
86
+ function createOptionBuilder(opt, description) {
87
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
+ return (option) => {
89
+ option.setName(opt.name)
90
+ .setDescription(description)
91
+ .setRequired(opt.required || false)
92
+ .setDescriptionLocalizations(typeof opt.description === 'object' ? opt.description : {});
93
+ if (opt.choices && opt.choices.length > 0) {
94
+ option.addChoices(...opt.choices);
95
+ }
96
+ return option;
97
+ };
98
+ }
99
+ /**
100
+ * @description Adds an option to a command or subcommand builder based on its type.
101
+ * @param {any} builder The command or subcommand builder.
102
+ * @param {CommandOption} opt The option to add.
103
+ * @param {Function} optionBuilder The option builder function.
104
+ */
105
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ function addOptionToBuilder(builder, opt, optionBuilder) {
107
+ switch (opt.type) {
108
+ case discord_js_1.ApplicationCommandOptionType.String:
109
+ builder.addStringOption(optionBuilder);
110
+ break;
111
+ case discord_js_1.ApplicationCommandOptionType.Boolean:
112
+ builder.addBooleanOption(optionBuilder);
113
+ break;
114
+ case discord_js_1.ApplicationCommandOptionType.Integer:
115
+ builder.addIntegerOption(optionBuilder);
116
+ break;
117
+ case discord_js_1.ApplicationCommandOptionType.Number:
118
+ builder.addNumberOption(optionBuilder);
119
+ break;
120
+ case discord_js_1.ApplicationCommandOptionType.User:
121
+ builder.addUserOption(optionBuilder);
122
+ break;
123
+ case discord_js_1.ApplicationCommandOptionType.Role:
124
+ builder.addRoleOption(optionBuilder);
125
+ break;
126
+ case discord_js_1.ApplicationCommandOptionType.Channel:
127
+ builder.addChannelOption(optionBuilder);
128
+ break;
129
+ default: throw new Error(`Unsupported option type: ${opt.type}`);
130
+ }
131
+ }
132
+ /**
133
+ * @description Processes and adds subcommands to a command builder.
134
+ * @param {SlashCommandBuilder} commandBuilder The command builder.
135
+ * @param {ModularCommand} command The command instance.
136
+ * @param {Record<string, OptionType>} options The options record to populate.
137
+ */
138
+ function processSubCommands(commandBuilder, command, options) {
139
+ if (!command.subCommands || command.subCommands.length === 0)
140
+ return;
141
+ command.subCommands.forEach(subCmd => {
142
+ commandBuilder.addSubcommand((subcommand) => {
143
+ // Get localized description for subcommand
144
+ const subCmdDescription = getLocalizedSubCommandDescription(command, subCmd.name, subCmd.description);
145
+ if (typeof subCmdDescription !== 'string' || subCmdDescription.trim() === '') {
146
+ throw new Error(`Subcommand '${subCmd.name}' is missing a description.`);
147
+ }
148
+ subcommand
149
+ .setName(subCmd.name)
150
+ .setDescription(subCmdDescription);
151
+ // Add options to the subcommand
152
+ if (subCmd.options) {
153
+ subCmd.options.forEach(opt => {
154
+ // Get base description
155
+ let description = typeof opt.description === 'string'
156
+ ? opt.description
157
+ : (opt.description[discord_js_1.Locale.EnglishUS] || `The description for ${opt.name} in English.`);
158
+ // Apply subcommand-specific localization
159
+ description = getLocalizedOptionDescription(command, subCmd.name, opt.name, description);
160
+ if (!description) {
161
+ throw new Error(`Option '${opt.name}' in subcommand '${subCmd.name}' is missing a description.`);
162
+ }
163
+ options[opt.name] = opt.type;
164
+ const optionBuilder = createOptionBuilder(opt, description);
165
+ addOptionToBuilder(subcommand, opt, optionBuilder);
166
+ });
167
+ }
168
+ return subcommand;
169
+ });
170
+ });
171
+ }
172
+ /**
173
+ * @description Processes and adds regular options to a command builder.
174
+ * @param {SlashCommandBuilder} commandBuilder The command builder.
175
+ * @param {ModularCommand} command The command instance.
176
+ * @param {Record<string, OptionType>} options The options record to populate.
177
+ */
178
+ function processRegularOptions(commandBuilder, command, options) {
179
+ command.options.forEach(opt => {
180
+ const description = typeof opt.description === 'string'
181
+ ? opt.description
182
+ : (opt.description[discord_js_1.Locale.EnglishUS] || `The description for ${opt.name} in English.`);
183
+ if (!description) {
184
+ throw new Error(`Option '${opt.name}' is missing a description.`);
185
+ }
186
+ options[opt.name] = opt.type;
187
+ const optionBuilder = createOptionBuilder(opt, description);
188
+ addOptionToBuilder(commandBuilder, opt, optionBuilder);
189
+ });
190
+ }
45
191
  /**
46
192
  * @description Gets the appropriate localization object for a command based on the interaction's locale.
47
193
  * Falls back to EnglishUS if the target locale is not available.
@@ -93,6 +239,16 @@ function createChatInputExecutor(command, options) {
93
239
  (0, cooldown_1.cooldownSetUser)(command.name, interaction.user.id);
94
240
  // Argument Parsing
95
241
  const args = {};
242
+ // Check if this command has subcommands and get the current subcommand
243
+ let currentSubcommand = null;
244
+ if (command.subCommands && command.subCommands.length > 0) {
245
+ try {
246
+ currentSubcommand = interaction.options.getSubcommand();
247
+ }
248
+ catch {
249
+ throw new Error(`Subcommand not found for command ${command.name}.`);
250
+ }
251
+ }
96
252
  for (const optionName of Object.keys(options)) {
97
253
  switch (options[optionName]) {
98
254
  case discord_js_1.ApplicationCommandOptionType.String:
@@ -110,9 +266,19 @@ function createChatInputExecutor(command, options) {
110
266
  case discord_js_1.ApplicationCommandOptionType.User:
111
267
  args[optionName] = interaction.options.getUser(optionName, false);
112
268
  break;
269
+ case discord_js_1.ApplicationCommandOptionType.Role:
270
+ args[optionName] = interaction.options.getRole(optionName, false);
271
+ break;
272
+ case discord_js_1.ApplicationCommandOptionType.Channel:
273
+ args[optionName] = interaction.options.getChannel(optionName, false);
274
+ break;
113
275
  default: throw new Error(`Unsupported option type: ${options[optionName]}`);
114
276
  }
115
277
  }
278
+ // Add the current subcommand to args if it exists
279
+ if (currentSubcommand) {
280
+ args['subcommand'] = currentSubcommand;
281
+ }
116
282
  const locale = getCommandLocale(command, interaction);
117
283
  // Execute Handler
118
284
  await command.execute({ interaction, args, command, locale });
@@ -226,44 +392,13 @@ function RegisterCommand(commands) {
226
392
  .setDescriptionLocalizations(command.descriptionLocalizations || null);
227
393
  (0, cooldown_1.cooldownRegister)(command.name, command.cooldown);
228
394
  const options = {};
229
- command.options.forEach(opt => {
230
- const description = typeof opt.description === 'string'
231
- ? opt.description
232
- : (opt.description[discord_js_1.Locale.EnglishUS] || `The description for ${opt.name} in English.`);
233
- if (!description) {
234
- throw new Error(`Option '${opt.name}' is missing a description.`);
235
- }
236
- options[opt.name] = opt.type;
237
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
238
- const optionBuilder = (option) => {
239
- option.setName(opt.name)
240
- .setDescription(description)
241
- .setRequired(opt.required || false)
242
- .setDescriptionLocalizations(typeof opt.description === 'object' ? opt.description : {});
243
- if (opt.choices && opt.choices.length > 0) {
244
- option.addChoices(...opt.choices);
245
- }
246
- return option;
247
- };
248
- switch (opt.type) {
249
- case discord_js_1.ApplicationCommandOptionType.String:
250
- commandBuilder.addStringOption(optionBuilder);
251
- break;
252
- case discord_js_1.ApplicationCommandOptionType.Boolean:
253
- commandBuilder.addBooleanOption(optionBuilder);
254
- break;
255
- case discord_js_1.ApplicationCommandOptionType.Integer:
256
- commandBuilder.addIntegerOption(optionBuilder);
257
- break;
258
- case discord_js_1.ApplicationCommandOptionType.Number:
259
- commandBuilder.addNumberOption(optionBuilder);
260
- break;
261
- case discord_js_1.ApplicationCommandOptionType.User:
262
- commandBuilder.addUserOption(optionBuilder);
263
- break;
264
- default: throw new Error(`Unsupported option type: ${opt.type}`);
265
- }
266
- });
395
+ // Process subcommands or regular options
396
+ if (command.subCommands && command.subCommands.length > 0) {
397
+ processSubCommands(commandBuilder, command, options);
398
+ }
399
+ else {
400
+ processRegularOptions(commandBuilder, command, options);
401
+ }
267
402
  // Assign Handlers using Constructors
268
403
  return {
269
404
  data: commandBuilder,
package/dist/types.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
- * @file Contains the type and interface definitions for the Discord bot's modular commands.
3
- * @author vicentefelipechile
4
- * @license MIT
2
+ * @license MIT
3
+ * @file src/types.ts
4
+ * @author vicentefelipechile
5
+ * @description Defines types and interfaces used throughout the modular command framework.
5
6
  */
6
- import { ApplicationCommandOptionType as OptionType, APIApplicationCommandOptionChoice, ChatInputCommandInteraction, MessageComponentInteraction, ModalSubmitInteraction, StringSelectMenuInteraction, CommandInteraction, ButtonInteraction, SlashCommandBuilder, PartialDMChannel, ThreadChannel, GuildChannel, Collection, Message, Locale, Client, User } from "discord.js";
7
+ import { ApplicationCommandOptionType as OptionType, APIApplicationCommandOptionChoice, ChatInputCommandInteraction, MessageComponentInteraction, ModalSubmitInteraction, StringSelectMenuInteraction, CommandInteraction, ButtonInteraction, SlashCommandBuilder, PartialDMChannel, ThreadChannel, GuildChannel, Collection, Message, Locale, Client, User, Role, APIRole, BaseChannel, SlashCommandBooleanOption, SlashCommandChannelOption, SlashCommandNumberOption, SlashCommandStringOption, SlashCommandUserOption, SlashCommandRoleOption, SlashCommandIntegerOption } from "discord.js";
7
8
  import ModularCommand from "./modularcommand";
8
9
  /**
9
10
  * @interface CommandOption
@@ -21,6 +22,18 @@ export interface CommandOption {
21
22
  /** An array of predefined choices the user can select from. */
22
23
  choices?: APIApplicationCommandOptionChoice[];
23
24
  }
25
+ /**
26
+ * @interface SubCommand
27
+ * @description Defines the structure of a subcommand for a slash command.
28
+ */
29
+ export interface SubCommand {
30
+ /** The name of the subcommand, must be unique within the command. */
31
+ name: string;
32
+ /** The description of the subcommand. */
33
+ description: string;
34
+ /** The options (arguments) that the subcommand accepts. */
35
+ options?: CommandOption[];
36
+ }
24
37
  /**
25
38
  * @interface CommandData
26
39
  * @description Represents the final structure of a registered command, ready to be used by the Discord client.
@@ -45,7 +58,7 @@ export interface CommandData {
45
58
  * @const ALLOWED_OPTION_TYPE
46
59
  * @description A list of allowed option types for slash commands.
47
60
  */
48
- export declare const ALLOWED_OPTION_TYPE: readonly [OptionType.String, OptionType.Boolean, OptionType.Integer, OptionType.Number, OptionType.User];
61
+ export declare const ALLOWED_OPTION_TYPE: readonly [OptionType.String, OptionType.Boolean, OptionType.Integer, OptionType.Number, OptionType.User, OptionType.Role, OptionType.Channel];
49
62
  /**
50
63
  * @type AllowedOptionType
51
64
  * @description The union type of allowed option types for slash commands.
@@ -68,7 +81,12 @@ export type ClientWithCommands = Client & {
68
81
  * @type CommandArgumentValue
69
82
  * @description Represents the possible value types that a command argument can have.
70
83
  */
71
- export type CommandArgumentValue = string | boolean | number | User | GuildChannel | ThreadChannel | PartialDMChannel | null;
84
+ export type CommandArgumentValue = string | boolean | number | User | GuildChannel | ThreadChannel | PartialDMChannel | Role | APIRole | BaseChannel | null;
85
+ /**
86
+ * @type OptionBuilderType
87
+ * @description Represents the type of an option builder function.
88
+ */
89
+ export type OptionBuilderType = SlashCommandStringOption | SlashCommandBooleanOption | SlashCommandIntegerOption | SlashCommandNumberOption | SlashCommandUserOption | SlashCommandRoleOption | SlashCommandChannelOption;
72
90
  /**
73
91
  * @type BaseExecuteParams
74
92
  * @description Defines the base parameters shared by all execution functions.
package/dist/types.js CHANGED
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  /**
3
- * @file Contains the type and interface definitions for the Discord bot's modular commands.
4
- * @author vicentefelipechile
5
- * @license MIT
3
+ * @license MIT
4
+ * @file src/types.ts
5
+ * @author vicentefelipechile
6
+ * @description Defines types and interfaces used throughout the modular command framework.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
8
9
  exports.ALLOWED_OPTION_TYPE = void 0;
10
+ // =================================================================================================
11
+ // Imports
12
+ // =================================================================================================
9
13
  const discord_js_1 = require("discord.js");
10
14
  // =================================================================================================
11
15
  // Generic and Utility Types
@@ -20,4 +24,6 @@ exports.ALLOWED_OPTION_TYPE = [
20
24
  discord_js_1.ApplicationCommandOptionType.Integer,
21
25
  discord_js_1.ApplicationCommandOptionType.Number,
22
26
  discord_js_1.ApplicationCommandOptionType.User,
27
+ discord_js_1.ApplicationCommandOptionType.Role,
28
+ discord_js_1.ApplicationCommandOptionType.Channel,
23
29
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-discord-modularcommand",
3
- "version": "2.5.2",
3
+ "version": "3.1.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "discord",
@@ -9,7 +9,7 @@
9
9
  ],
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "https://github.com/vicentefelipechile/js-discord-modularcommand.git"
12
+ "url": "git+https://github.com/vicentefelipechile/js-discord-modularcommand.git"
13
13
  },
14
14
  "license": "MIT",
15
15
  "author": "vicentefelipechile",