js-discord-modularcommand 2.4.0 → 2.5.1
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 +252 -35
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.js +13 -5
- package/dist/modularcommand.d.ts +15 -5
- package/dist/modularcommand.js +15 -2
- package/dist/modularselectmenu.d.ts +61 -0
- package/dist/modularselectmenu.js +89 -0
- package/dist/registercommand.d.ts +1 -1
- package/dist/registercommand.js +24 -1
- package/dist/types.d.ts +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,69 +1,286 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Discord.js Modular Command
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/js-discord-modularcommand)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A powerful and elegant library for creating modular, feature-rich slash commands for your [Discord.js](https://discord.js.org/) bot.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
`js-discord-modularcommand` simplifies command management by providing a clean, chainable structure for defining commands, handling interactions, and managing localizations. Move away from boilerplate code and focus on what truly matters: your bot's logic.
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Features
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
- **Modular by Design:** Structure each command in its own file for a clean and scalable project architecture.
|
|
13
|
+
- **Effortless Localization:** Built-in support for multiple languages for command descriptions, options, and in-command responses.
|
|
14
|
+
- **Interactive Components Made Easy:** Fluent builders for creating and managing Buttons, Modals, and Select Menus with their own handlers, all within the command's context.
|
|
15
|
+
- **Chainable Configuration:** Use a fluent, chainable API to configure every aspect of your command, from descriptions and options to permissions and cooldowns.
|
|
16
|
+
- **Simplified Handlers:** The library abstracts away the complexity of handling different interaction types. You just provide the logic.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Install the package using npm or your favorite package manager:
|
|
12
21
|
|
|
13
22
|
```sh
|
|
14
|
-
npm install js-discord-modularcommand
|
|
23
|
+
npm install js-discord-modularcommand
|
|
15
24
|
```
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
## Getting Started
|
|
27
|
+
|
|
28
|
+
The core of the library is the `ModularCommand` class. You create an instance of it for each command and chain methods to configure it.
|
|
29
|
+
|
|
30
|
+
Here is a basic example of a `ping` command:
|
|
18
31
|
|
|
19
32
|
```javascript
|
|
20
|
-
// filepath: commands/ping.js
|
|
33
|
+
// filepath: /commands/ping.js
|
|
21
34
|
const { ModularCommand, RegisterCommand } = require('js-discord-modularcommand');
|
|
22
|
-
const {
|
|
35
|
+
const { Locale } = require('discord.js');
|
|
36
|
+
|
|
37
|
+
// 1. Create a new command instance
|
|
38
|
+
const pingCommand = new ModularCommand('ping');
|
|
23
39
|
|
|
24
|
-
//
|
|
25
|
-
|
|
40
|
+
// 2. Configure the command using chainable methods
|
|
41
|
+
pingCommand.setDescription('Replies with Pong!')
|
|
26
42
|
|
|
27
|
-
// Set
|
|
28
|
-
|
|
43
|
+
// Optional: Set a 5-second cooldown
|
|
44
|
+
pingCommand.setCooldown(5)
|
|
45
|
+
|
|
46
|
+
// Optional: Add localized descriptions for the command itself
|
|
47
|
+
pingCommand.setLocalizationDescription({
|
|
48
|
+
[Locale.EnglishUS]: 'Replies with Pong!',
|
|
49
|
+
[Locale.SpanishLATAM]: '¡Responde con Pong!',
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Optional: Define response phrases for different languages
|
|
53
|
+
pingCommand.setLocalizationPhrases({
|
|
54
|
+
[Locale.EnglishUS]: {
|
|
55
|
+
'pong_reply': 'Pong! 🏓',
|
|
56
|
+
},
|
|
57
|
+
[Locale.SpanishLATAM]: {
|
|
58
|
+
'pong_reply': '¡Pong! 🏓',
|
|
59
|
+
}
|
|
60
|
+
})
|
|
29
61
|
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
62
|
+
// 3. Define the execution logic
|
|
63
|
+
// The 'locale' object contains the phrases for the user's language
|
|
64
|
+
pingCommand.setExecute(async ({ interaction, locale }) => {
|
|
65
|
+
await interaction.reply({
|
|
66
|
+
content: locale['pong_reply']
|
|
67
|
+
});
|
|
33
68
|
});
|
|
34
69
|
|
|
35
|
-
//
|
|
36
|
-
|
|
70
|
+
// 4. Export the command for the handler
|
|
71
|
+
module.exports = RegisterCommand(pingCommand);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Advanced Usage: Interactive Components
|
|
75
|
+
|
|
76
|
+
Easily add interactive components to your commands.
|
|
77
|
+
|
|
78
|
+
### Buttons
|
|
79
|
+
|
|
80
|
+
Create buttons and attach specific logic to each one.
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
// filepath: /commands/vote.js
|
|
84
|
+
const { ModularCommand, RegisterCommand } = require('js-discord-modularcommand');
|
|
85
|
+
const { ButtonStyle, Locale, MessageFlags } = require('discord.js');
|
|
86
|
+
const { ActionRowBuilder } = require('@discordjs/builders');
|
|
87
|
+
|
|
88
|
+
const voteCommand = new ModularCommand('votecommand');
|
|
89
|
+
|
|
90
|
+
voteCommand.setDescription('Starts a simple poll.');
|
|
91
|
+
|
|
92
|
+
voteCommand.setLocalizationPhrases({
|
|
37
93
|
[Locale.EnglishUS]: {
|
|
38
|
-
|
|
94
|
+
'poll_question': 'What is your favorite color?',
|
|
95
|
+
'votecommand.yes': 'Green',
|
|
96
|
+
'votecommand.no': 'Blue',
|
|
97
|
+
'reply.yes': 'You voted for Green!',
|
|
98
|
+
'reply.no': 'You voted for Blue!',
|
|
39
99
|
},
|
|
40
100
|
[Locale.SpanishLATAM]: {
|
|
41
|
-
|
|
101
|
+
'poll_question': '¿Cuál es tu color favorito?',
|
|
102
|
+
'votecommand.yes': 'Verde',
|
|
103
|
+
'votecommand.no': 'Azul',
|
|
104
|
+
'reply.yes': '¡Has votado por el Verde!',
|
|
105
|
+
'reply.no': '¡Has votado por el Azul!',
|
|
42
106
|
}
|
|
43
107
|
});
|
|
44
108
|
|
|
45
|
-
//
|
|
46
|
-
|
|
109
|
+
// Create and handle the "Yes" button
|
|
110
|
+
const yesButton = voteCommand.addButton('yes', async ({ interaction, locale }) => {
|
|
111
|
+
await interaction.reply({
|
|
112
|
+
content: locale['reply.yes'],
|
|
113
|
+
flags: MessageFlags.Ephemeral
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Create and handle the "No" button
|
|
118
|
+
const noButton = voteCommand.addButton('no', async ({ interaction, locale }) => {
|
|
119
|
+
await interaction.reply({
|
|
120
|
+
content: locale['reply.no'],
|
|
121
|
+
flags: MessageFlags.Ephemeral
|
|
122
|
+
});
|
|
123
|
+
});
|
|
47
124
|
|
|
48
|
-
//
|
|
49
|
-
|
|
125
|
+
// Customize the underlying discord.js button
|
|
126
|
+
yesButton.getButton().setStyle(ButtonStyle.Success);
|
|
127
|
+
noButton.getButton().setStyle(ButtonStyle.Primary);
|
|
50
128
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
129
|
+
// Main command execution: sends the message with the buttons
|
|
130
|
+
voteCommand.setExecute(async ({ interaction, locale }) => {
|
|
131
|
+
const row = new ActionRowBuilder();
|
|
132
|
+
|
|
133
|
+
row.addComponents(
|
|
134
|
+
yesButton.build(locale), // .build(locale) applies the correct localization
|
|
135
|
+
noButton.build(locale)
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
await interaction.reply({
|
|
139
|
+
content: locale['poll_question'],
|
|
140
|
+
components: [row]
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
module.exports = RegisterCommand(voteCommand);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Select Menus
|
|
148
|
+
|
|
149
|
+
Build and handle string select menus seamlessly.
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// filepath: /commands/starter.js
|
|
153
|
+
const { ModularCommand, RegisterCommand } = require('js-discord-modularcommand');
|
|
154
|
+
const { ActionRowBuilder } = require('@discordjs/builders');
|
|
155
|
+
const { Locale, MessageFlags } = require('discord.js');
|
|
156
|
+
|
|
157
|
+
const starterCommand = new ModularCommand('starter');
|
|
158
|
+
|
|
159
|
+
starterCommand.setDescription('Choose your starter Pokémon.')
|
|
160
|
+
|
|
161
|
+
starterCommand.setLocalizationPhrases({
|
|
162
|
+
[Locale.EnglishUS]: {
|
|
163
|
+
'select_prompt': 'Please select your starter:',
|
|
164
|
+
'menuselection.placeholder': 'Make a selection!',
|
|
165
|
+
'menuselection.bulbasaur.label': 'Bulbasaur',
|
|
166
|
+
'menuselection.bulbasaur.description': 'The Seed Pokémon.',
|
|
167
|
+
'menuselection.charmander.label': 'Charmander',
|
|
168
|
+
'menuselection.charmander.description': 'The Lizard Pokémon.',
|
|
169
|
+
'menuselection.squirtle.label': 'Squirtle',
|
|
170
|
+
'menuselection.squirtle.description': 'The Tiny Turtle Pokémon.',
|
|
171
|
+
'response': 'You chose {selection}!',
|
|
172
|
+
},
|
|
173
|
+
[Locale.SpanishLATAM]: {
|
|
174
|
+
'select_prompt': 'Por favor, elige tu inicial:',
|
|
175
|
+
'menuselection.placeholder': '¡Haz una selección!',
|
|
176
|
+
'menuselection.bulbasaur.label': 'Bulbasaur',
|
|
177
|
+
'menuselection.bulbasaur.description': 'El Pokémon Semilla.',
|
|
178
|
+
'menuselection.charmander.label': 'Charmander',
|
|
179
|
+
'menuselection.charmander.description': 'El Pokémon Lagartija.',
|
|
180
|
+
'menuselection.squirtle.label': 'Squirtle',
|
|
181
|
+
'menuselection.squirtle.description': 'El Pokémon Agua.',
|
|
182
|
+
'response': '¡Elegiste a {selection}!',
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const starterMenu = starterCommand.addSelectMenu('menuselection');
|
|
187
|
+
|
|
188
|
+
// Value must match the key in localization phrases
|
|
189
|
+
starterMenu.addOption('bulbasaur')
|
|
190
|
+
starterMenu.addOption('charmander')
|
|
191
|
+
starterMenu.addOption('squirtle')
|
|
192
|
+
|
|
193
|
+
starterMenu.setExecute(async ({ interaction, selected, locale }) => {
|
|
194
|
+
// 'selected' directly gives you the value of the chosen option
|
|
195
|
+
const selectionLabel = locale[`menuselection.${selected}.label`];
|
|
196
|
+
await interaction.update({
|
|
197
|
+
content: locale['response'].replace('{selection}', selectionLabel),
|
|
198
|
+
components: [] // Remove menu after selection
|
|
199
|
+
});
|
|
55
200
|
});
|
|
56
201
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
202
|
+
starterCommand.setExecute(async ({ interaction, locale }) => {
|
|
203
|
+
const row = new ActionRowBuilder()
|
|
204
|
+
row.addComponents(starterMenu.build(locale));
|
|
205
|
+
|
|
206
|
+
await interaction.reply({
|
|
207
|
+
content: locale['select_prompt'],
|
|
208
|
+
components: [row],
|
|
209
|
+
flags: MessageFlags.Ephemeral
|
|
210
|
+
});
|
|
60
211
|
});
|
|
61
212
|
|
|
62
|
-
module.exports = RegisterCommand(
|
|
213
|
+
module.exports = RegisterCommand(starterCommand);
|
|
63
214
|
```
|
|
64
215
|
|
|
65
|
-
|
|
216
|
+
### Modals (Pop-up Forms)
|
|
217
|
+
|
|
218
|
+
Display pop-up forms to collect detailed user input.
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
// filepath: /commands/feedback.js
|
|
222
|
+
const { ModularCommand, RegisterCommand } = require('js-discord-modularcommand');
|
|
223
|
+
const { TextInputStyle, Locale, MessageFlags } = require('discord.js');
|
|
224
|
+
|
|
225
|
+
const feedbackCommand = new ModularCommand('feedback');
|
|
226
|
+
|
|
227
|
+
feedbackCommand.setDescription('Submit feedback about the bot.')
|
|
228
|
+
|
|
229
|
+
feedbackCommand.setLocalizationPhrases({
|
|
230
|
+
[Locale.EnglishUS]: {
|
|
231
|
+
'form.title': 'Feedback Form',
|
|
232
|
+
'form.subject.label': 'Subject',
|
|
233
|
+
'form.subject.placeholder': 'e.g., Feature Request',
|
|
234
|
+
'form.message.label': 'Message',
|
|
235
|
+
'form.message.placeholder': 'Your detailed feedback here...',
|
|
236
|
+
'success_reply': 'Thank you for your feedback!',
|
|
237
|
+
},
|
|
238
|
+
[Locale.SpanishLATAM]: {
|
|
239
|
+
'form.title': 'Formulario de Comentarios',
|
|
240
|
+
'form.subject.label': 'Asunto',
|
|
241
|
+
'form.subject.placeholder': 'Ej: Solicitud de función',
|
|
242
|
+
'form.message.label': 'Mensaje',
|
|
243
|
+
'form.message.placeholder': 'Tus comentarios detallados aquí...',
|
|
244
|
+
'success_reply': '¡Gracias por tus comentarios!',
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const feedbackModal = feedbackCommand.addModal('form');
|
|
249
|
+
|
|
250
|
+
// Define text inputs
|
|
251
|
+
const subjectInput = feedbackModal.newTextInput('subject')
|
|
252
|
+
.setStyle(TextInputStyle.Short)
|
|
253
|
+
.setRequired(true)
|
|
254
|
+
.data
|
|
255
|
+
.custom_id;
|
|
256
|
+
|
|
257
|
+
const messageInput = feedbackModal.newTextInput('message')
|
|
258
|
+
.setStyle(TextInputStyle.Paragraph)
|
|
259
|
+
.setRequired(true)
|
|
260
|
+
.data
|
|
261
|
+
.custom_id;
|
|
262
|
+
|
|
263
|
+
// This function runs when the user submits the modal
|
|
264
|
+
feedbackModal.setExecute(async ({ interaction, args, locale }) => {
|
|
265
|
+
const subject = args[subjectInput];
|
|
266
|
+
const message = args[messageInput];
|
|
267
|
+
|
|
268
|
+
// Process the data
|
|
269
|
+
console.log(`New Feedback: ${subject} - ${message}`);
|
|
270
|
+
await interaction.reply({
|
|
271
|
+
content: locale['success_reply'],
|
|
272
|
+
flags: MessageFlags.Ephemeral
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// This function runs when the /feedback command is used, showing the modal
|
|
277
|
+
feedbackCommand.setExecute(async ({ interaction, locale }) => {
|
|
278
|
+
await interaction.showModal(feedbackModal.build(locale));
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
module.exports = RegisterCommand(feedbackCommand);
|
|
282
|
+
```
|
|
66
283
|
|
|
67
284
|
## License
|
|
68
285
|
|
|
69
|
-
This project is under the MIT License. See the
|
|
286
|
+
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
package/dist/interaction.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ type InteractionHandler = (args: InteractionHandlerArgs) => Promise<boolean | un
|
|
|
23
23
|
* @description Creates a modular command handler function for the Discord client.
|
|
24
24
|
* @param {ClientWithCommands} client The Discord client instance with a commands collection.
|
|
25
25
|
* @param {InteractionHandler} customHandler A custom function to handle interactions before the default logic.
|
|
26
|
-
* @returns {(interaction: BaseInteraction)
|
|
26
|
+
* @returns {(interaction: BaseInteraction) => Promise<void>} The main interaction handler function.
|
|
27
27
|
*/
|
|
28
28
|
export default function ModularCommandHandler(client: ClientWithCommands, customHandler: InteractionHandler): (interaction: BaseInteraction) => Promise<void>;
|
|
29
29
|
export {};
|
package/dist/interaction.js
CHANGED
|
@@ -15,7 +15,7 @@ const locales_1 = require("./locales");
|
|
|
15
15
|
* @description Creates a modular command handler function for the Discord client.
|
|
16
16
|
* @param {ClientWithCommands} client The Discord client instance with a commands collection.
|
|
17
17
|
* @param {InteractionHandler} customHandler A custom function to handle interactions before the default logic.
|
|
18
|
-
* @returns {(interaction: BaseInteraction)
|
|
18
|
+
* @returns {(interaction: BaseInteraction) => Promise<void>} The main interaction handler function.
|
|
19
19
|
*/
|
|
20
20
|
function ModularCommandHandler(client, customHandler) {
|
|
21
21
|
if (!client.commands) {
|
|
@@ -41,12 +41,15 @@ function ModularCommandHandler(client, customHandler) {
|
|
|
41
41
|
commandName = interaction.customId.split('_')[0];
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
44
|
-
const errorMessage = locales_1.LOCALE_ERROR[interaction.locale];
|
|
44
|
+
const errorMessage = locales_1.LOCALE_ERROR[interaction.locale] || 'An unexpected error occurred.';
|
|
45
45
|
if (interaction.replied || interaction.deferred) {
|
|
46
46
|
await interaction.followUp({ content: errorMessage, flags: discord_js_1.MessageFlags.Ephemeral });
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
49
|
-
|
|
49
|
+
// Type guard to ensure reply method exists
|
|
50
|
+
if ('reply' in interaction) {
|
|
51
|
+
await interaction.reply({ content: errorMessage, flags: discord_js_1.MessageFlags.Ephemeral });
|
|
52
|
+
}
|
|
50
53
|
}
|
|
51
54
|
console.error(`Interaction does not have a commandName or customId: ${interaction.id}`);
|
|
52
55
|
return;
|
|
@@ -63,6 +66,9 @@ function ModularCommandHandler(client, customHandler) {
|
|
|
63
66
|
else if (interaction.isButton() && command.buttonExecute) {
|
|
64
67
|
await command.buttonExecute(interaction);
|
|
65
68
|
}
|
|
69
|
+
else if (interaction.isStringSelectMenu() && command.selectMenuExecute) {
|
|
70
|
+
await command.selectMenuExecute(interaction);
|
|
71
|
+
}
|
|
66
72
|
else if (interaction.isMessageComponent() && command.componentExecute) {
|
|
67
73
|
await command.componentExecute(interaction);
|
|
68
74
|
}
|
|
@@ -71,12 +77,14 @@ function ModularCommandHandler(client, customHandler) {
|
|
|
71
77
|
}
|
|
72
78
|
}
|
|
73
79
|
catch (error) {
|
|
74
|
-
const errorMessage = locales_1.LOCALE_ERROR[interaction.locale];
|
|
80
|
+
const errorMessage = locales_1.LOCALE_ERROR[interaction.locale] || 'An unexpected error occurred.';
|
|
75
81
|
if (interaction.replied || interaction.deferred) {
|
|
76
82
|
await interaction.followUp({ content: errorMessage, flags: discord_js_1.MessageFlags.Ephemeral });
|
|
77
83
|
}
|
|
78
84
|
else {
|
|
79
|
-
|
|
85
|
+
if ('reply' in interaction) {
|
|
86
|
+
await interaction.reply({ content: errorMessage, flags: discord_js_1.MessageFlags.Ephemeral });
|
|
87
|
+
}
|
|
80
88
|
}
|
|
81
89
|
console.error(`Error handling interaction: ${interaction.id}`, error);
|
|
82
90
|
}
|
package/dist/modularcommand.d.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import { LocalizationMap } from 'discord.js';
|
|
7
7
|
import ModularModal from './modularmodal.js';
|
|
8
8
|
import ModularButton from './modularbutton.js';
|
|
9
|
-
import
|
|
9
|
+
import ModularSelectMenu from './modularselectmenu.js';
|
|
10
|
+
import { ButtonExecuteFunction, CommandExecuteFunction, CommandOption, ComponentExecuteFunction, ModalExecuteFunction, PermissionCheckFunction, SelectMenuExecuteFunction } from './types.js';
|
|
10
11
|
/**
|
|
11
12
|
* @description Represents a modular command that can be registered with Discord.js.
|
|
12
13
|
* It allows for dynamic command creation and execution in a simple way.
|
|
@@ -47,7 +48,7 @@ export default class ModularCommand {
|
|
|
47
48
|
/** (Optional) The function to handle modal submissions. */
|
|
48
49
|
modalExecute?: ModalExecuteFunction;
|
|
49
50
|
/** A record of handlers for specific component custom IDs. */
|
|
50
|
-
customIdHandlers: Record<string, CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction>;
|
|
51
|
+
customIdHandlers: Record<string, CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction | SelectMenuExecuteFunction>;
|
|
51
52
|
/** The command's cooldown time in seconds. */
|
|
52
53
|
cooldown: number;
|
|
53
54
|
/** Whether the command is marked as Not Safe For Work (NSFW). */
|
|
@@ -64,6 +65,10 @@ export default class ModularCommand {
|
|
|
64
65
|
buttons: Map<string, ModularButton>;
|
|
65
66
|
/** An array containing all ModularButton instances for easy access. */
|
|
66
67
|
buttonsArray: ModularButton[];
|
|
68
|
+
/** A map of select menus associated with this command, keyed by the select menu's custom ID. */
|
|
69
|
+
selectMenus: Map<string, ModularSelectMenu>;
|
|
70
|
+
/** An array containing all ModularSelectMenu instances for easy access. */
|
|
71
|
+
selectMenusArray: ModularSelectMenu[];
|
|
67
72
|
constructor(name: string);
|
|
68
73
|
/**
|
|
69
74
|
* Sets the description of the command.
|
|
@@ -92,7 +97,6 @@ export default class ModularCommand {
|
|
|
92
97
|
setLocalizationOptions(localizations: LocalizationMap): this;
|
|
93
98
|
/**
|
|
94
99
|
* Sets the localization phrases for the command.
|
|
95
|
-
* Accepts a partial record, so not all locales need to be provided.
|
|
96
100
|
* @param {LocalizationMap} localizationPhrases The localization phrases.
|
|
97
101
|
* @returns {ModularCommand} The command instance for chaining.
|
|
98
102
|
*/
|
|
@@ -136,10 +140,10 @@ export default class ModularCommand {
|
|
|
136
140
|
/**
|
|
137
141
|
* Adds a custom ID handler for the command.
|
|
138
142
|
* @param {string} customId The custom ID to match.
|
|
139
|
-
* @param {CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction} handlerFunction The function to execute when the custom ID matches.
|
|
143
|
+
* @param {CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction | SelectMenuExecuteFunction} handlerFunction The function to execute when the custom ID matches.
|
|
140
144
|
* @returns {ModularCommand} The command instance for chaining.
|
|
141
145
|
*/
|
|
142
|
-
addCustomIDHandler(customId: string, handlerFunction: CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction): this;
|
|
146
|
+
addCustomIDHandler(customId: string, handlerFunction: CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction | SelectMenuExecuteFunction): this;
|
|
143
147
|
/**
|
|
144
148
|
* Creates a new modal for the command.
|
|
145
149
|
* @param {string} modalId The ID for the modal.
|
|
@@ -153,4 +157,10 @@ export default class ModularCommand {
|
|
|
153
157
|
* @return {ModularButton} The created button instance.
|
|
154
158
|
*/
|
|
155
159
|
addButton(customId: string, execute: ButtonExecuteFunction): ModularButton;
|
|
160
|
+
/**
|
|
161
|
+
* Creates a new select menu for the command.
|
|
162
|
+
* @param {string} selectMenuId The ID for the select menu.
|
|
163
|
+
* @returns {ModularSelectMenu} The created select menu instance.
|
|
164
|
+
*/
|
|
165
|
+
addSelectMenu(selectMenuId: string): ModularSelectMenu;
|
|
156
166
|
}
|
package/dist/modularcommand.js
CHANGED
|
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
14
14
|
const discord_js_1 = require("discord.js");
|
|
15
15
|
const modularmodal_js_1 = __importDefault(require("./modularmodal.js"));
|
|
16
16
|
const modularbutton_js_1 = __importDefault(require("./modularbutton.js"));
|
|
17
|
+
const modularselectmenu_js_1 = __importDefault(require("./modularselectmenu.js")); // <- ADD THIS
|
|
17
18
|
// =================================================================================================
|
|
18
19
|
// Class: ModularCommand
|
|
19
20
|
// =================================================================================================
|
|
@@ -50,7 +51,9 @@ class ModularCommand {
|
|
|
50
51
|
this.cooldown = 3;
|
|
51
52
|
this.modals = new Map();
|
|
52
53
|
this.buttons = new Map();
|
|
54
|
+
this.selectMenus = new Map(); // <- ADD THIS
|
|
53
55
|
this.buttonsArray = [];
|
|
56
|
+
this.selectMenusArray = []; // <- ADD THIS
|
|
54
57
|
this.isNSFW = false;
|
|
55
58
|
}
|
|
56
59
|
/**
|
|
@@ -98,7 +101,6 @@ class ModularCommand {
|
|
|
98
101
|
}
|
|
99
102
|
/**
|
|
100
103
|
* Sets the localization phrases for the command.
|
|
101
|
-
* Accepts a partial record, so not all locales need to be provided.
|
|
102
104
|
* @param {LocalizationMap} localizationPhrases The localization phrases.
|
|
103
105
|
* @returns {ModularCommand} The command instance for chaining.
|
|
104
106
|
*/
|
|
@@ -166,7 +168,7 @@ class ModularCommand {
|
|
|
166
168
|
/**
|
|
167
169
|
* Adds a custom ID handler for the command.
|
|
168
170
|
* @param {string} customId The custom ID to match.
|
|
169
|
-
* @param {CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction} handlerFunction The function to execute when the custom ID matches.
|
|
171
|
+
* @param {CommandExecuteFunction | ButtonExecuteFunction | ModalExecuteFunction | SelectMenuExecuteFunction} handlerFunction The function to execute when the custom ID matches.
|
|
170
172
|
* @returns {ModularCommand} The command instance for chaining.
|
|
171
173
|
*/
|
|
172
174
|
addCustomIDHandler(customId, handlerFunction) {
|
|
@@ -196,5 +198,16 @@ class ModularCommand {
|
|
|
196
198
|
this.buttonsArray.push(button);
|
|
197
199
|
return button;
|
|
198
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Creates a new select menu for the command.
|
|
203
|
+
* @param {string} selectMenuId The ID for the select menu.
|
|
204
|
+
* @returns {ModularSelectMenu} The created select menu instance.
|
|
205
|
+
*/
|
|
206
|
+
addSelectMenu(selectMenuId) {
|
|
207
|
+
const menu = new modularselectmenu_js_1.default(selectMenuId, this);
|
|
208
|
+
this.selectMenus.set(selectMenuId, menu);
|
|
209
|
+
this.selectMenusArray.push(menu);
|
|
210
|
+
return menu;
|
|
211
|
+
}
|
|
199
212
|
}
|
|
200
213
|
exports.default = ModularCommand;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Contains the structure and logic for creating modular select menus.
|
|
3
|
+
* @author vicentefelipechile
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
import { StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from "discord.js";
|
|
7
|
+
import { LocaleKey, SelectMenuExecuteFunction } from "./types";
|
|
8
|
+
import ModularCommand from "./modularcommand";
|
|
9
|
+
/**
|
|
10
|
+
* @class ModularSelectMenu
|
|
11
|
+
* @description Represents a modular select menu that can be dynamically created and managed.
|
|
12
|
+
*/
|
|
13
|
+
export default class ModularSelectMenu {
|
|
14
|
+
/** The Discord.js StringSelectMenuBuilder instance. */
|
|
15
|
+
selectMenuObject: StringSelectMenuBuilder;
|
|
16
|
+
/** The unique custom ID for the select menu, formatted as `${command.name}_${selectMenuId}`. */
|
|
17
|
+
customId: string;
|
|
18
|
+
/** The base ID for the select menu, used for localization. */
|
|
19
|
+
selectMenuId: string;
|
|
20
|
+
/** A map to store the option components of the select menu. */
|
|
21
|
+
options: Map<string, StringSelectMenuOptionBuilder>;
|
|
22
|
+
/** The command instance to which this select menu belongs. */
|
|
23
|
+
command: ModularCommand;
|
|
24
|
+
/** The function to execute when the select menu is interacted with. */
|
|
25
|
+
execute: SelectMenuExecuteFunction;
|
|
26
|
+
/**
|
|
27
|
+
* @description Creates a new ModularSelectMenu instance.
|
|
28
|
+
* @param {string} selectMenuId The base ID for the select menu.
|
|
29
|
+
* @param {ModularCommand} command The command that this select menu is associated with.
|
|
30
|
+
*/
|
|
31
|
+
constructor(selectMenuId: string, command: ModularCommand);
|
|
32
|
+
/**
|
|
33
|
+
* @description Retrieves the underlying StringSelectMenuBuilder instance.
|
|
34
|
+
* @returns {StringSelectMenuBuilder} The StringSelectMenuBuilder instance.
|
|
35
|
+
*/
|
|
36
|
+
getSelectMenu(): StringSelectMenuBuilder;
|
|
37
|
+
/**
|
|
38
|
+
* @description Retrieves the custom ID of the select menu.
|
|
39
|
+
* @returns {string} The custom ID of the select menu.
|
|
40
|
+
*/
|
|
41
|
+
getCustomId(): string;
|
|
42
|
+
/**
|
|
43
|
+
* @description Sets the execution function for the select menu's submission event.
|
|
44
|
+
* @param {SelectMenuExecuteFunction} executeFunction The function to run when the select menu is used.
|
|
45
|
+
* @returns {this} The current ModularSelectMenu instance for method chaining.
|
|
46
|
+
*/
|
|
47
|
+
setExecute(executeFunction: SelectMenuExecuteFunction): this;
|
|
48
|
+
/**
|
|
49
|
+
* @description Creates a new option for the select menu.
|
|
50
|
+
* The label and description should be set in your localization files.
|
|
51
|
+
* @param {string} value The unique value for this option.
|
|
52
|
+
* @returns {StringSelectMenuOptionBuilder} The created option instance for further configuration (e.g., `setDefault`).
|
|
53
|
+
*/
|
|
54
|
+
addOption(value: string): StringSelectMenuOptionBuilder;
|
|
55
|
+
/**
|
|
56
|
+
* @description Builds the final select menu object, applying localized placeholder, labels, and descriptions.
|
|
57
|
+
* @param {LocaleKey} locale The localization object containing translated texts.
|
|
58
|
+
* @returns {StringSelectMenuBuilder} The fully constructed select menu object ready to be sent to a user.
|
|
59
|
+
*/
|
|
60
|
+
build(locale: LocaleKey): StringSelectMenuBuilder;
|
|
61
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file Contains the structure and logic for creating modular select menus.
|
|
4
|
+
* @author vicentefelipechile
|
|
5
|
+
* @license MIT
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const discord_js_1 = require("discord.js");
|
|
9
|
+
/**
|
|
10
|
+
* @class ModularSelectMenu
|
|
11
|
+
* @description Represents a modular select menu that can be dynamically created and managed.
|
|
12
|
+
*/
|
|
13
|
+
class ModularSelectMenu {
|
|
14
|
+
/**
|
|
15
|
+
* @description Creates a new ModularSelectMenu instance.
|
|
16
|
+
* @param {string} selectMenuId The base ID for the select menu.
|
|
17
|
+
* @param {ModularCommand} command The command that this select menu is associated with.
|
|
18
|
+
*/
|
|
19
|
+
constructor(selectMenuId, command) {
|
|
20
|
+
/** The function to execute when the select menu is interacted with. */
|
|
21
|
+
this.execute = async () => { };
|
|
22
|
+
this.selectMenuId = selectMenuId;
|
|
23
|
+
this.command = command;
|
|
24
|
+
this.customId = `${command.name}_${selectMenuId}`;
|
|
25
|
+
this.selectMenuObject = new discord_js_1.StringSelectMenuBuilder().setCustomId(this.customId);
|
|
26
|
+
this.options = new Map();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @description Retrieves the underlying StringSelectMenuBuilder instance.
|
|
30
|
+
* @returns {StringSelectMenuBuilder} The StringSelectMenuBuilder instance.
|
|
31
|
+
*/
|
|
32
|
+
getSelectMenu() {
|
|
33
|
+
return this.selectMenuObject;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* @description Retrieves the custom ID of the select menu.
|
|
37
|
+
* @returns {string} The custom ID of the select menu.
|
|
38
|
+
*/
|
|
39
|
+
getCustomId() {
|
|
40
|
+
return this.customId;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @description Sets the execution function for the select menu's submission event.
|
|
44
|
+
* @param {SelectMenuExecuteFunction} executeFunction The function to run when the select menu is used.
|
|
45
|
+
* @returns {this} The current ModularSelectMenu instance for method chaining.
|
|
46
|
+
*/
|
|
47
|
+
setExecute(executeFunction) {
|
|
48
|
+
this.execute = executeFunction;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* @description Creates a new option for the select menu.
|
|
53
|
+
* The label and description should be set in your localization files.
|
|
54
|
+
* @param {string} value The unique value for this option.
|
|
55
|
+
* @returns {StringSelectMenuOptionBuilder} The created option instance for further configuration (e.g., `setDefault`).
|
|
56
|
+
*/
|
|
57
|
+
addOption(value) {
|
|
58
|
+
const option = new discord_js_1.StringSelectMenuOptionBuilder().setValue(value);
|
|
59
|
+
this.options.set(value, option);
|
|
60
|
+
return option;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @description Builds the final select menu object, applying localized placeholder, labels, and descriptions.
|
|
64
|
+
* @param {LocaleKey} locale The localization object containing translated texts.
|
|
65
|
+
* @returns {StringSelectMenuBuilder} The fully constructed select menu object ready to be sent to a user.
|
|
66
|
+
*/
|
|
67
|
+
build(locale) {
|
|
68
|
+
const placeholderKey = `${this.command.name}.${this.selectMenuId}.placeholder`;
|
|
69
|
+
if (locale[placeholderKey]) {
|
|
70
|
+
this.selectMenuObject.setPlaceholder(locale[placeholderKey]);
|
|
71
|
+
}
|
|
72
|
+
const builtOptions = [];
|
|
73
|
+
this.options.forEach((optionBuilder, value) => {
|
|
74
|
+
const labelKey = `${this.command.name}.${this.selectMenuId}.${value}.label`;
|
|
75
|
+
const descriptionKey = `${this.command.name}.${this.selectMenuId}.${value}.description`;
|
|
76
|
+
// Set label from locale, fallback to the value if not found
|
|
77
|
+
optionBuilder.setLabel(locale[labelKey] || value);
|
|
78
|
+
if (locale[descriptionKey]) {
|
|
79
|
+
optionBuilder.setDescription(locale[descriptionKey]);
|
|
80
|
+
}
|
|
81
|
+
builtOptions.push(optionBuilder);
|
|
82
|
+
});
|
|
83
|
+
if (builtOptions.length > 0) {
|
|
84
|
+
this.selectMenuObject.setOptions(builtOptions);
|
|
85
|
+
}
|
|
86
|
+
return this.selectMenuObject;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.default = ModularSelectMenu;
|
|
@@ -8,7 +8,7 @@ import ModularCommand from "./modularcommand";
|
|
|
8
8
|
/**
|
|
9
9
|
* @description Registers an array of modular commands, building their final `CommandData` objects.
|
|
10
10
|
* This function processes the command definitions, sets up command builders, and assigns the execution logic.
|
|
11
|
-
* @param {ModularCommand[]} commands An array of ModularCommand
|
|
11
|
+
* @param {ModularCommand[] | ModularCommand} commands An array of or a single ModularCommand instance.
|
|
12
12
|
* @returns {CommandData[]} An array of command data objects ready for the Discord.js client.
|
|
13
13
|
*/
|
|
14
14
|
export default function RegisterCommand(commands: ModularCommand[] | ModularCommand): CommandData[];
|
package/dist/registercommand.js
CHANGED
|
@@ -181,13 +181,35 @@ function createButtonExecutor(command) {
|
|
|
181
181
|
});
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* @description Creates the execution function for select menu interactions.
|
|
186
|
+
* @param {ModularCommand} command The command to create the executor for.
|
|
187
|
+
* @returns {Function|undefined} The async function or undefined if not needed.
|
|
188
|
+
*/
|
|
189
|
+
function createSelectMenuExecutor(command) {
|
|
190
|
+
if (command.selectMenus.size === 0)
|
|
191
|
+
return undefined;
|
|
192
|
+
return async (interaction) => {
|
|
193
|
+
const menuObject = command.selectMenus.get(interaction.customId.split('_')[1]);
|
|
194
|
+
if (!menuObject)
|
|
195
|
+
return;
|
|
196
|
+
// The user's selected option value is abstracted into `selected`.
|
|
197
|
+
await menuObject.execute({
|
|
198
|
+
interaction,
|
|
199
|
+
command,
|
|
200
|
+
locale: getCommandLocale(command, interaction),
|
|
201
|
+
message: interaction.message,
|
|
202
|
+
selected: interaction.values[0],
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
}
|
|
184
206
|
// =================================================================================================
|
|
185
207
|
// Main Registration Function
|
|
186
208
|
// =================================================================================================
|
|
187
209
|
/**
|
|
188
210
|
* @description Registers an array of modular commands, building their final `CommandData` objects.
|
|
189
211
|
* This function processes the command definitions, sets up command builders, and assigns the execution logic.
|
|
190
|
-
* @param {ModularCommand[]} commands An array of ModularCommand
|
|
212
|
+
* @param {ModularCommand[] | ModularCommand} commands An array of or a single ModularCommand instance.
|
|
191
213
|
* @returns {CommandData[]} An array of command data objects ready for the Discord.js client.
|
|
192
214
|
*/
|
|
193
215
|
function RegisterCommand(commands) {
|
|
@@ -249,6 +271,7 @@ function RegisterCommand(commands) {
|
|
|
249
271
|
componentExecute: createComponentExecutor(command),
|
|
250
272
|
modalExecute: createModalExecutor(command),
|
|
251
273
|
buttonExecute: createButtonExecutor(command),
|
|
274
|
+
selectMenuExecute: createSelectMenuExecutor(command),
|
|
252
275
|
cooldown: command.cooldown,
|
|
253
276
|
};
|
|
254
277
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @author vicentefelipechile
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
6
|
-
import { ApplicationCommandOptionType as OptionType, APIApplicationCommandOptionChoice, ChatInputCommandInteraction, MessageComponentInteraction, ModalSubmitInteraction, CommandInteraction, ButtonInteraction, SlashCommandBuilder, PartialDMChannel, ThreadChannel, GuildChannel, Collection, Message, Locale, Client, User } from "discord.js";
|
|
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
7
|
import ModularCommand from "./modularcommand";
|
|
8
8
|
/**
|
|
9
9
|
* @interface CommandOption
|
|
@@ -36,6 +36,8 @@ export interface CommandData {
|
|
|
36
36
|
modalExecute?: (interaction: ModalSubmitInteraction) => Promise<void>;
|
|
37
37
|
/** (Optional) Function to handle button interactions. */
|
|
38
38
|
buttonExecute?: (interaction: ButtonInteraction) => Promise<void>;
|
|
39
|
+
/** (Optional) Function to handle string select menu interactions. */
|
|
40
|
+
selectMenuExecute?: (interaction: StringSelectMenuInteraction) => Promise<void>;
|
|
39
41
|
/** The command's cooldown time in seconds. */
|
|
40
42
|
cooldown: number;
|
|
41
43
|
}
|
|
@@ -103,6 +105,16 @@ export type ModalExecuteParams = BaseExecuteParams<ModalSubmitInteraction> & {
|
|
|
103
105
|
/** An object with the values of the fields submitted in the modal. */
|
|
104
106
|
args: Record<string, string>;
|
|
105
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
* @type SelectMenuExecuteParams
|
|
110
|
+
* @description Parameters for the execution function of a select menu.
|
|
111
|
+
*/
|
|
112
|
+
export type SelectMenuExecuteParams = BaseExecuteParams<StringSelectMenuInteraction> & {
|
|
113
|
+
/** The message to which the select menu is attached. */
|
|
114
|
+
message: Message;
|
|
115
|
+
/** The value selected by the user. */
|
|
116
|
+
selected: string;
|
|
117
|
+
};
|
|
106
118
|
/**
|
|
107
119
|
* @type CommandExecuteFunction
|
|
108
120
|
* @description Defines the signature for the main execution function of a command.
|
|
@@ -123,6 +135,11 @@ export type ButtonExecuteFunction = (params: ButtonExecuteParams) => Promise<voi
|
|
|
123
135
|
* @description Defines the signature for the function that handles a modal submission.
|
|
124
136
|
*/
|
|
125
137
|
export type ModalExecuteFunction = (params: ModalExecuteParams) => Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* @type SelectMenuExecuteFunction
|
|
140
|
+
* @description Defines the signature for the function that handles select menu interactions.
|
|
141
|
+
*/
|
|
142
|
+
export type SelectMenuExecuteFunction = (params: SelectMenuExecuteParams) => Promise<void>;
|
|
126
143
|
/**
|
|
127
144
|
* @type PermissionCheckFunction
|
|
128
145
|
* @description Defines the signature for a function that checks a user's permissions to execute a command.
|