js-discord-modularcommand 2.5.2 → 3.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
@@ -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
@@ -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();
@@ -111,6 +114,18 @@ class ModularCommand {
111
114
  };
112
115
  return this;
113
116
  }
117
+ /**
118
+ * Sets the localizations for subcommands, including names, descriptions, and options.
119
+ * @param {LocalizationMap} localizations The subcommand localizations map.
120
+ * @returns {ModularCommand} The command instance for chaining.
121
+ */
122
+ setLocalizationSubCommands(localizations) {
123
+ this.subCommandLocalizations = {
124
+ ...this.subCommandLocalizations,
125
+ ...localizations,
126
+ };
127
+ return this;
128
+ }
114
129
  /**
115
130
  * Sets the execute function for the command.
116
131
  * @param {CommandExecuteFunction} executeFunction The function to execute.
@@ -165,6 +180,15 @@ class ModularCommand {
165
180
  this.options.push(option);
166
181
  return this;
167
182
  }
183
+ /**
184
+ * Adds a subcommand to the command.
185
+ * @param {SubCommand} subCommand The subcommand configuration.
186
+ * @returns {ModularCommand} The command instance for chaining.
187
+ */
188
+ addSubCommand(subCommand) {
189
+ this.subCommands.push(subCommand);
190
+ return this;
191
+ }
168
192
  /**
169
193
  * Adds a custom ID handler for the command.
170
194
  * @param {string} customId The custom ID to match.
@@ -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
@@ -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.
@@ -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,149 @@ 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
+ subcommand
146
+ .setName(subCmd.name)
147
+ .setDescription(subCmdDescription);
148
+ // Add options to the subcommand
149
+ if (subCmd.options) {
150
+ subCmd.options.forEach(opt => {
151
+ // Get base description
152
+ let description = typeof opt.description === 'string'
153
+ ? opt.description
154
+ : (opt.description[discord_js_1.Locale.EnglishUS] || `The description for ${opt.name} in English.`);
155
+ // Apply subcommand-specific localization
156
+ description = getLocalizedOptionDescription(command, subCmd.name, opt.name, description);
157
+ if (!description) {
158
+ throw new Error(`Option '${opt.name}' in subcommand '${subCmd.name}' is missing a description.`);
159
+ }
160
+ options[opt.name] = opt.type;
161
+ const optionBuilder = createOptionBuilder(opt, description);
162
+ addOptionToBuilder(subcommand, opt, optionBuilder);
163
+ });
164
+ }
165
+ return subcommand;
166
+ });
167
+ });
168
+ }
169
+ /**
170
+ * @description Processes and adds regular options to a command builder.
171
+ * @param {SlashCommandBuilder} commandBuilder The command builder.
172
+ * @param {ModularCommand} command The command instance.
173
+ * @param {Record<string, OptionType>} options The options record to populate.
174
+ */
175
+ function processRegularOptions(commandBuilder, command, options) {
176
+ command.options.forEach(opt => {
177
+ const description = typeof opt.description === 'string'
178
+ ? opt.description
179
+ : (opt.description[discord_js_1.Locale.EnglishUS] || `The description for ${opt.name} in English.`);
180
+ if (!description) {
181
+ throw new Error(`Option '${opt.name}' is missing a description.`);
182
+ }
183
+ options[opt.name] = opt.type;
184
+ const optionBuilder = createOptionBuilder(opt, description);
185
+ addOptionToBuilder(commandBuilder, opt, optionBuilder);
186
+ });
187
+ }
45
188
  /**
46
189
  * @description Gets the appropriate localization object for a command based on the interaction's locale.
47
190
  * Falls back to EnglishUS if the target locale is not available.
@@ -93,6 +236,16 @@ function createChatInputExecutor(command, options) {
93
236
  (0, cooldown_1.cooldownSetUser)(command.name, interaction.user.id);
94
237
  // Argument Parsing
95
238
  const args = {};
239
+ // Check if this command has subcommands and get the current subcommand
240
+ let currentSubcommand = null;
241
+ if (command.subCommands && command.subCommands.length > 0) {
242
+ try {
243
+ currentSubcommand = interaction.options.getSubcommand();
244
+ }
245
+ catch {
246
+ throw new Error(`Subcommand not found for command ${command.name}.`);
247
+ }
248
+ }
96
249
  for (const optionName of Object.keys(options)) {
97
250
  switch (options[optionName]) {
98
251
  case discord_js_1.ApplicationCommandOptionType.String:
@@ -110,9 +263,19 @@ function createChatInputExecutor(command, options) {
110
263
  case discord_js_1.ApplicationCommandOptionType.User:
111
264
  args[optionName] = interaction.options.getUser(optionName, false);
112
265
  break;
266
+ case discord_js_1.ApplicationCommandOptionType.Role:
267
+ args[optionName] = interaction.options.getRole(optionName, false);
268
+ break;
269
+ case discord_js_1.ApplicationCommandOptionType.Channel:
270
+ args[optionName] = interaction.options.getChannel(optionName, false);
271
+ break;
113
272
  default: throw new Error(`Unsupported option type: ${options[optionName]}`);
114
273
  }
115
274
  }
275
+ // Add the current subcommand to args if it exists
276
+ if (currentSubcommand) {
277
+ args['subcommand'] = currentSubcommand;
278
+ }
116
279
  const locale = getCommandLocale(command, interaction);
117
280
  // Execute Handler
118
281
  await command.execute({ interaction, args, command, locale });
@@ -226,44 +389,13 @@ function RegisterCommand(commands) {
226
389
  .setDescriptionLocalizations(command.descriptionLocalizations || null);
227
390
  (0, cooldown_1.cooldownRegister)(command.name, command.cooldown);
228
391
  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
- });
392
+ // Process subcommands or regular options
393
+ if (command.subCommands && command.subCommands.length > 0) {
394
+ processSubCommands(commandBuilder, command, options);
395
+ }
396
+ else {
397
+ processRegularOptions(commandBuilder, command, options);
398
+ }
267
399
  // Assign Handlers using Constructors
268
400
  return {
269
401
  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.0.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "discord",