@julanzw/ttoolbox-discordjs-framework 1.3.0 → 1.5.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/dist/classes/Command.class.d.ts +93 -0
- package/dist/classes/Command.class.js +18 -2
- package/dist/classes/CommandManager.class.d.ts +123 -10
- package/dist/classes/CommandManager.class.js +231 -16
- package/dist/classes/MessageContextMenuCommand.class.d.ts +79 -0
- package/dist/classes/MessageContextMenuCommand.class.js +120 -0
- package/dist/classes/SubcommandGroup.class.d.ts +15 -14
- package/dist/classes/SubcommandGroup.class.js +18 -33
- package/dist/classes/UserContextMenuCommand.class.d.ts +78 -0
- package/dist/classes/UserContextMenuCommand.class.js +119 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -1
- package/dist/types/loadCommands.d.ts +29 -0
- package/dist/types/loadCommands.js +1 -0
- package/dist/utils/editAndReply.d.ts +2 -2
- package/dist/utils/loadCommands.d.ts +27 -0
- package/dist/utils/loadCommands.js +129 -0
- package/package.json +1 -1
|
@@ -173,4 +173,97 @@ export declare abstract class Command {
|
|
|
173
173
|
* Set the error reporter for this command
|
|
174
174
|
*/
|
|
175
175
|
setErrorReporter(reporter: ErrorReporter): void;
|
|
176
|
+
/**
|
|
177
|
+
* Lifecycle hook called before command execution.
|
|
178
|
+
*
|
|
179
|
+
* Can be used for:
|
|
180
|
+
* - Custom validation
|
|
181
|
+
* - Logging/analytics
|
|
182
|
+
* - Loading user preferences
|
|
183
|
+
* - Rate limiting checks
|
|
184
|
+
* - And more...
|
|
185
|
+
*
|
|
186
|
+
* If this hook throws an error or returns false, execution is stopped.
|
|
187
|
+
*
|
|
188
|
+
* @param interaction - The command interaction
|
|
189
|
+
* @returns true to continue execution, false to stop (or throw an error)
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* protected async beforeExecute(interaction: ChatInputCommandInteraction): Promise<boolean> {
|
|
194
|
+
* // Log command usage
|
|
195
|
+
* console.log(`${interaction.user.tag} used ${this.name}`);
|
|
196
|
+
*
|
|
197
|
+
* // Check custom rate limit
|
|
198
|
+
* if (await this.isRateLimited(interaction.user.id)) {
|
|
199
|
+
* await interaction.reply('You are rate limited!');
|
|
200
|
+
* return false; // Stop execution
|
|
201
|
+
* }
|
|
202
|
+
*
|
|
203
|
+
* return true; // Continue
|
|
204
|
+
* }
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
protected beforeExecute?(interaction: ChatInputCommandInteraction, client: Client): Promise<boolean | void>;
|
|
208
|
+
/**
|
|
209
|
+
* Lifecycle hook called after successful command execution.
|
|
210
|
+
*
|
|
211
|
+
* Can be used for:
|
|
212
|
+
* - Analytics tracking
|
|
213
|
+
* - Cleanup tasks
|
|
214
|
+
* - Success logging
|
|
215
|
+
* - Updating usage statistics
|
|
216
|
+
* - And more...
|
|
217
|
+
*
|
|
218
|
+
* Note: This is **NOT** called if the command throws an error (use onError for that).
|
|
219
|
+
*
|
|
220
|
+
* @param interaction - The command interaction
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* protected async afterExecute(interaction: ChatInputCommandInteraction): Promise<void> {
|
|
225
|
+
* // Track command usage
|
|
226
|
+
* await analytics.track('command_used', {
|
|
227
|
+
* command: this.name,
|
|
228
|
+
* user: interaction.user.id,
|
|
229
|
+
* });
|
|
230
|
+
*
|
|
231
|
+
* // Update user stats
|
|
232
|
+
* await incrementCommandCount(interaction.user.id);
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
protected afterExecute?(interaction: ChatInputCommandInteraction, client: Client): Promise<void>;
|
|
237
|
+
/**
|
|
238
|
+
* Lifecycle hook called when command execution fails.
|
|
239
|
+
*
|
|
240
|
+
* Can be used for:
|
|
241
|
+
* - Custom error handling
|
|
242
|
+
* - Error reporting to external services
|
|
243
|
+
* - User-friendly error messages
|
|
244
|
+
* - Cleanup after errors
|
|
245
|
+
* - And more...
|
|
246
|
+
*
|
|
247
|
+
* The error is rethrown after this hook.
|
|
248
|
+
*
|
|
249
|
+
* @param interaction - The command interaction
|
|
250
|
+
* @param error - The error that occurred
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* protected async onError(interaction: ChatInputCommandInteraction, error: Error): Promise<void> {
|
|
255
|
+
* // Send to Sentry
|
|
256
|
+
* Sentry.captureException(error, {
|
|
257
|
+
* tags: { command: this.name },
|
|
258
|
+
* user: { id: interaction.user.id },
|
|
259
|
+
* });
|
|
260
|
+
*
|
|
261
|
+
* // Custom error message
|
|
262
|
+
* if (error.message.includes('DATABASE')) {
|
|
263
|
+
* await interaction.reply('Database is temporarily unavailable. Try again later.');
|
|
264
|
+
* }
|
|
265
|
+
* }
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
protected onError?(interaction: ChatInputCommandInteraction, error: Error, client: Client): Promise<void>;
|
|
176
269
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
1
|
import { SlashCommandBuilder, } from 'discord.js';
|
|
3
2
|
import { safeReply } from '../utils/editAndReply.js';
|
|
4
3
|
import { getPermissionsForLevel } from '../utils/permissions.js';
|
|
@@ -116,7 +115,24 @@ export class Command {
|
|
|
116
115
|
const error = this.validate(interaction);
|
|
117
116
|
if (error)
|
|
118
117
|
return await safeReply(interaction, error, true);
|
|
119
|
-
|
|
118
|
+
if (this.beforeExecute) {
|
|
119
|
+
const shouldContinue = await this.beforeExecute(interaction, client);
|
|
120
|
+
if (shouldContinue === false) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
await this.run(interaction, client);
|
|
126
|
+
if (this.afterExecute) {
|
|
127
|
+
await this.afterExecute(interaction, client);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (this.onError) {
|
|
132
|
+
await this.onError(interaction, err, client);
|
|
133
|
+
}
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
120
136
|
});
|
|
121
137
|
}
|
|
122
138
|
/**
|
|
@@ -1,36 +1,105 @@
|
|
|
1
|
-
import { ChatInputCommandInteraction, Client,
|
|
1
|
+
import { ChatInputCommandInteraction, Client, MessageContextMenuCommandInteraction, RESTPostAPIApplicationCommandsJSONBody, UserContextMenuCommandInteraction } from 'discord.js';
|
|
2
2
|
import { ILogger } from '../types/logger.js';
|
|
3
3
|
import { Command } from './Command.class.js';
|
|
4
4
|
import { SubcommandGroup } from './SubcommandGroup.class.js';
|
|
5
5
|
import { ErrorReporter } from '../utils/ErrorReporter.js';
|
|
6
|
+
import { MessageContextMenuCommand } from './MessageContextMenuCommand.class.js';
|
|
7
|
+
import { UserContextMenuCommand } from './UserContextMenuCommand.class.js';
|
|
8
|
+
import { LoadCommandsOptions } from '../types/loadCommands.js';
|
|
6
9
|
export declare class CommandManager {
|
|
7
10
|
private commands;
|
|
11
|
+
private userContextMenuCommands;
|
|
12
|
+
private messageContextMenuCommands;
|
|
8
13
|
protected logger?: ILogger;
|
|
9
14
|
protected errorReporter?: ErrorReporter;
|
|
10
15
|
/**
|
|
16
|
+
* Register commands (accepts single, array, or mixed types)
|
|
17
|
+
* @param command - Command(s) to register
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Single command
|
|
21
|
+
* commandManager.registerCommand(new PingCommand());
|
|
22
|
+
*
|
|
23
|
+
* // Array of commands
|
|
24
|
+
* commandManager.registerCommand([cmd1, cmd2, cmd3]);
|
|
25
|
+
*
|
|
26
|
+
* // Mixed types
|
|
27
|
+
* commandManager.registerCommand([
|
|
28
|
+
* new PingCommand(),
|
|
29
|
+
* new UserContextMenu(),
|
|
30
|
+
* new SubcommandGroup(),
|
|
31
|
+
* ]);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
registerCommand(command: Command | SubcommandGroup | UserContextMenuCommand | MessageContextMenuCommand | Array<Command | SubcommandGroup | UserContextMenuCommand | MessageContextMenuCommand | any>): void;
|
|
35
|
+
/**
|
|
36
|
+
* @experimental
|
|
37
|
+
* Automatically load and register all commands from a directory.
|
|
38
|
+
*
|
|
39
|
+
* This is a convenience wrapper around the loadCommands utility.
|
|
40
|
+
*
|
|
41
|
+
* @param dirPath - Absolute path to the directory containing commands
|
|
42
|
+
* @param options - Optional configuration
|
|
43
|
+
* @returns Number of commands successfully loaded
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Basic usage
|
|
48
|
+
* const count = await commandManager.loadFromDirectory('./src/commands');
|
|
49
|
+
* console.log(`Loaded ${count} commands`);
|
|
50
|
+
*
|
|
51
|
+
* // With options
|
|
52
|
+
* await commandManager.loadFromDirectory('./src/commands', {
|
|
53
|
+
* verbose: true,
|
|
54
|
+
* skipDirs: ['subcommands', 'test'],
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
loadFromDirectory(dirPath: string, options?: LoadCommandsOptions): Promise<number>;
|
|
59
|
+
/**
|
|
60
|
+
* @deprecated Use `registerCommand()` instead
|
|
61
|
+
*
|
|
11
62
|
* Register a single command or subcommand group
|
|
12
63
|
*/
|
|
13
64
|
register(command: Command | SubcommandGroup): this;
|
|
14
65
|
/**
|
|
66
|
+
* @deprecated Use `registerCommand()` with an array instead
|
|
67
|
+
*
|
|
15
68
|
* Register multiple commands at once
|
|
16
69
|
*/
|
|
17
70
|
registerMultiple(commands: Array<Command | SubcommandGroup>): this;
|
|
18
71
|
/**
|
|
19
|
-
* Get a specific command by name
|
|
72
|
+
* Get a specific slash command by name
|
|
20
73
|
*/
|
|
21
74
|
get(name: string): Command | SubcommandGroup | undefined;
|
|
22
75
|
/**
|
|
23
|
-
* Get
|
|
76
|
+
* Get a specific user context menu command by name
|
|
77
|
+
*/
|
|
78
|
+
getUserContextMenu(name: string): UserContextMenuCommand | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Get a specific message context menu command by name
|
|
81
|
+
*/
|
|
82
|
+
getMessageContextMenu(name: string): MessageContextMenuCommand | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Get all registered slash commands
|
|
24
85
|
*/
|
|
25
86
|
getAll(): Array<Command | SubcommandGroup>;
|
|
26
87
|
/**
|
|
27
|
-
* Get all
|
|
88
|
+
* Get all registered user context menu commands
|
|
89
|
+
*/
|
|
90
|
+
getAllUserContextMenus(): UserContextMenuCommand[];
|
|
91
|
+
/**
|
|
92
|
+
* Get all registered message context menu commands
|
|
93
|
+
*/
|
|
94
|
+
getAllMessageContextMenus(): MessageContextMenuCommand[];
|
|
95
|
+
/**
|
|
96
|
+
* Get all slash commands sorted alphabetically by name
|
|
28
97
|
*/
|
|
29
98
|
getAllSorted(): Array<Command | SubcommandGroup>;
|
|
30
99
|
/**
|
|
31
100
|
* Convert all commands to Discord JSON format for registration
|
|
32
101
|
*/
|
|
33
|
-
|
|
102
|
+
toJSON(): RESTPostAPIApplicationCommandsJSONBody[];
|
|
34
103
|
/**
|
|
35
104
|
* Generate paginated help pages for display in help command
|
|
36
105
|
* Returns a 2D array where each inner array is a page of command descriptions
|
|
@@ -48,25 +117,69 @@ export declare class CommandManager {
|
|
|
48
117
|
*/
|
|
49
118
|
executeCommand(commandName: string, interaction: ChatInputCommandInteraction, client: Client): Promise<void>;
|
|
50
119
|
/**
|
|
51
|
-
*
|
|
120
|
+
* Execute a user context menu command.
|
|
121
|
+
*/
|
|
122
|
+
executeUserContextMenu(commandName: string, interaction: UserContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Execute a message context menu command.
|
|
125
|
+
*/
|
|
126
|
+
executeMessageContextMenu(commandName: string, interaction: MessageContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Get total number of registered slash commands
|
|
52
129
|
*/
|
|
53
130
|
get size(): number;
|
|
54
131
|
/**
|
|
55
|
-
*
|
|
132
|
+
* Get total number of all registered commands
|
|
133
|
+
*/
|
|
134
|
+
get totalSize(): number;
|
|
135
|
+
/**
|
|
136
|
+
* Check if a slash command exists
|
|
56
137
|
*/
|
|
57
138
|
has(name: string): boolean;
|
|
58
139
|
/**
|
|
59
|
-
*
|
|
140
|
+
* Check if a user context menu command exists
|
|
141
|
+
*/
|
|
142
|
+
hasUserContextMenu(name: string): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Check if a message context menu command exists
|
|
145
|
+
*/
|
|
146
|
+
hasMessageContextMenu(name: string): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Check if any command (slash, user context, or message context) exists
|
|
149
|
+
*/
|
|
150
|
+
hasAny(name: string): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Remove a slash command (useful for hot-reloading in dev)
|
|
60
153
|
*/
|
|
61
154
|
unregister(name: string): boolean;
|
|
62
155
|
/**
|
|
63
|
-
*
|
|
156
|
+
* Remove a user context menu command
|
|
157
|
+
*/
|
|
158
|
+
unregisterUserContextMenu(name: string): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Remove a message context menu command
|
|
161
|
+
*/
|
|
162
|
+
unregisterMessageContextMenu(name: string): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Clear all commands (slash commands, user context menus, and message context menus)
|
|
64
165
|
*/
|
|
65
166
|
clear(): void;
|
|
66
167
|
/**
|
|
67
|
-
* Get command names as an array
|
|
168
|
+
* Get slash command names as an array
|
|
68
169
|
*/
|
|
69
170
|
getCommandNames(): string[];
|
|
171
|
+
/**
|
|
172
|
+
* Get user context menu command names as an array
|
|
173
|
+
*/
|
|
174
|
+
getUserContextMenuNames(): string[];
|
|
175
|
+
/**
|
|
176
|
+
* Get message context menu command names as an array
|
|
177
|
+
*/
|
|
178
|
+
getMessageContextMenuNames(): string[];
|
|
179
|
+
/**
|
|
180
|
+
* Get all command names (slash + context menus) as an array
|
|
181
|
+
*/
|
|
182
|
+
getAllCommandNames(): string[];
|
|
70
183
|
setLogger(logger: ILogger): this;
|
|
71
184
|
/**
|
|
72
185
|
* Set the error reporter for all commands
|
|
@@ -1,42 +1,148 @@
|
|
|
1
|
+
import { Command } from './Command.class.js';
|
|
1
2
|
import { SubcommandGroup } from './SubcommandGroup.class.js';
|
|
3
|
+
import { MessageContextMenuCommand } from './MessageContextMenuCommand.class.js';
|
|
4
|
+
import { UserContextMenuCommand } from './UserContextMenuCommand.class.js';
|
|
5
|
+
import { loadCommands } from '../utils/loadCommands.js';
|
|
2
6
|
export class CommandManager {
|
|
3
7
|
constructor() {
|
|
4
8
|
this.commands = new Map();
|
|
9
|
+
this.userContextMenuCommands = new Map();
|
|
10
|
+
this.messageContextMenuCommands = new Map();
|
|
5
11
|
}
|
|
6
12
|
/**
|
|
7
|
-
* Register
|
|
13
|
+
* Register commands (accepts single, array, or mixed types)
|
|
14
|
+
* @param command - Command(s) to register
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Single command
|
|
18
|
+
* commandManager.registerCommand(new PingCommand());
|
|
19
|
+
*
|
|
20
|
+
* // Array of commands
|
|
21
|
+
* commandManager.registerCommand([cmd1, cmd2, cmd3]);
|
|
22
|
+
*
|
|
23
|
+
* // Mixed types
|
|
24
|
+
* commandManager.registerCommand([
|
|
25
|
+
* new PingCommand(),
|
|
26
|
+
* new UserContextMenu(),
|
|
27
|
+
* new SubcommandGroup(),
|
|
28
|
+
* ]);
|
|
29
|
+
* ```
|
|
8
30
|
*/
|
|
9
|
-
|
|
10
|
-
if (
|
|
31
|
+
registerCommand(command) {
|
|
32
|
+
if (Array.isArray(command)) {
|
|
33
|
+
for (const cmd of command) {
|
|
34
|
+
this.registerCommand(cmd);
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (this.logger && 'setLogger' in command) {
|
|
11
39
|
command.setLogger(this.logger);
|
|
12
40
|
}
|
|
13
|
-
if (this.errorReporter) {
|
|
41
|
+
if (this.errorReporter && 'setErrorReporter' in command) {
|
|
14
42
|
command.setErrorReporter(this.errorReporter);
|
|
15
43
|
}
|
|
16
|
-
|
|
44
|
+
if (command instanceof Command || command instanceof SubcommandGroup) {
|
|
45
|
+
this.commands.set(command.name, command);
|
|
46
|
+
}
|
|
47
|
+
else if (command instanceof UserContextMenuCommand) {
|
|
48
|
+
this.userContextMenuCommands.set(command.name, command);
|
|
49
|
+
}
|
|
50
|
+
else if (command instanceof MessageContextMenuCommand) {
|
|
51
|
+
this.messageContextMenuCommands.set(command.name, command);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.logger?.warn(`Attempted to register invalid command type: ${command?.constructor?.name || 'unknown'}`, 'command-manager');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @experimental
|
|
59
|
+
* Automatically load and register all commands from a directory.
|
|
60
|
+
*
|
|
61
|
+
* This is a convenience wrapper around the loadCommands utility.
|
|
62
|
+
*
|
|
63
|
+
* @param dirPath - Absolute path to the directory containing commands
|
|
64
|
+
* @param options - Optional configuration
|
|
65
|
+
* @returns Number of commands successfully loaded
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* // Basic usage
|
|
70
|
+
* const count = await commandManager.loadFromDirectory('./src/commands');
|
|
71
|
+
* console.log(`Loaded ${count} commands`);
|
|
72
|
+
*
|
|
73
|
+
* // With options
|
|
74
|
+
* await commandManager.loadFromDirectory('./src/commands', {
|
|
75
|
+
* verbose: true,
|
|
76
|
+
* skipDirs: ['subcommands', 'test'],
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
async loadFromDirectory(dirPath, options = {}) {
|
|
81
|
+
const commands = await loadCommands(dirPath, options);
|
|
82
|
+
this.registerCommand(commands);
|
|
83
|
+
const validCount = commands.filter(cmd => cmd instanceof Command ||
|
|
84
|
+
cmd instanceof SubcommandGroup ||
|
|
85
|
+
cmd instanceof UserContextMenuCommand ||
|
|
86
|
+
cmd instanceof MessageContextMenuCommand).length;
|
|
87
|
+
this.logger?.info(`Auto-loaded ${validCount} commands from ${dirPath}`, 'command-manager');
|
|
88
|
+
return validCount;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* @deprecated Use `registerCommand()` instead
|
|
92
|
+
*
|
|
93
|
+
* Register a single command or subcommand group
|
|
94
|
+
*/
|
|
95
|
+
register(command) {
|
|
96
|
+
this.registerCommand(command);
|
|
17
97
|
return this;
|
|
18
98
|
}
|
|
19
99
|
/**
|
|
100
|
+
* @deprecated Use `registerCommand()` with an array instead
|
|
101
|
+
*
|
|
20
102
|
* Register multiple commands at once
|
|
21
103
|
*/
|
|
22
104
|
registerMultiple(commands) {
|
|
23
|
-
|
|
105
|
+
this.registerCommand(commands);
|
|
24
106
|
return this;
|
|
25
107
|
}
|
|
26
108
|
/**
|
|
27
|
-
* Get a specific command by name
|
|
109
|
+
* Get a specific slash command by name
|
|
28
110
|
*/
|
|
29
111
|
get(name) {
|
|
30
112
|
return this.commands.get(name);
|
|
31
113
|
}
|
|
32
114
|
/**
|
|
33
|
-
* Get
|
|
115
|
+
* Get a specific user context menu command by name
|
|
116
|
+
*/
|
|
117
|
+
getUserContextMenu(name) {
|
|
118
|
+
return this.userContextMenuCommands.get(name);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get a specific message context menu command by name
|
|
122
|
+
*/
|
|
123
|
+
getMessageContextMenu(name) {
|
|
124
|
+
return this.messageContextMenuCommands.get(name);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get all registered slash commands
|
|
34
128
|
*/
|
|
35
129
|
getAll() {
|
|
36
130
|
return Array.from(this.commands.values());
|
|
37
131
|
}
|
|
38
132
|
/**
|
|
39
|
-
* Get all
|
|
133
|
+
* Get all registered user context menu commands
|
|
134
|
+
*/
|
|
135
|
+
getAllUserContextMenus() {
|
|
136
|
+
return Array.from(this.userContextMenuCommands.values());
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get all registered message context menu commands
|
|
140
|
+
*/
|
|
141
|
+
getAllMessageContextMenus() {
|
|
142
|
+
return Array.from(this.messageContextMenuCommands.values());
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get all slash commands sorted alphabetically by name
|
|
40
146
|
*/
|
|
41
147
|
getAllSorted() {
|
|
42
148
|
return this.getAll().sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -44,13 +150,26 @@ export class CommandManager {
|
|
|
44
150
|
/**
|
|
45
151
|
* Convert all commands to Discord JSON format for registration
|
|
46
152
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
153
|
+
toJSON() {
|
|
154
|
+
const slashCommands = Array.from(this.commands.values()).map((cmd) => {
|
|
49
155
|
if (process.env.ENV === 'dev') {
|
|
50
156
|
console.log(`Registering: ${cmd.name}`);
|
|
51
157
|
}
|
|
52
158
|
return cmd.toJSON();
|
|
53
159
|
});
|
|
160
|
+
const userContextMenus = Array.from(this.userContextMenuCommands.values()).map((cmd) => {
|
|
161
|
+
if (process.env.ENV === 'dev') {
|
|
162
|
+
console.log(`Registering user context menu: ${cmd.name}`);
|
|
163
|
+
}
|
|
164
|
+
return cmd.toJSON();
|
|
165
|
+
});
|
|
166
|
+
const messageContextMenus = Array.from(this.messageContextMenuCommands.values()).map((cmd) => {
|
|
167
|
+
if (process.env.ENV === 'dev') {
|
|
168
|
+
console.log(`Registering message context menu: ${cmd.name}`);
|
|
169
|
+
}
|
|
170
|
+
return cmd.toJSON();
|
|
171
|
+
});
|
|
172
|
+
return [...slashCommands, ...userContextMenus, ...messageContextMenus];
|
|
54
173
|
}
|
|
55
174
|
/**
|
|
56
175
|
* Generate paginated help pages for display in help command
|
|
@@ -112,41 +231,131 @@ export class CommandManager {
|
|
|
112
231
|
await command.execute(interaction, client);
|
|
113
232
|
}
|
|
114
233
|
/**
|
|
115
|
-
*
|
|
234
|
+
* Execute a user context menu command.
|
|
235
|
+
*/
|
|
236
|
+
async executeUserContextMenu(commandName, interaction, client) {
|
|
237
|
+
const command = this.userContextMenuCommands.get(commandName);
|
|
238
|
+
if (!command) {
|
|
239
|
+
throw new Error(`User context menu command not found: ${commandName}`);
|
|
240
|
+
}
|
|
241
|
+
await command.execute(interaction, client);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Execute a message context menu command.
|
|
245
|
+
*/
|
|
246
|
+
async executeMessageContextMenu(commandName, interaction, client) {
|
|
247
|
+
const command = this.messageContextMenuCommands.get(commandName);
|
|
248
|
+
if (!command) {
|
|
249
|
+
throw new Error(`Message context menu command not found: ${commandName}`);
|
|
250
|
+
}
|
|
251
|
+
await command.execute(interaction, client);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get total number of registered slash commands
|
|
116
255
|
*/
|
|
117
256
|
get size() {
|
|
118
257
|
return this.commands.size;
|
|
119
258
|
}
|
|
120
259
|
/**
|
|
121
|
-
*
|
|
260
|
+
* Get total number of all registered commands
|
|
261
|
+
*/
|
|
262
|
+
get totalSize() {
|
|
263
|
+
return this.commands.size +
|
|
264
|
+
this.userContextMenuCommands.size +
|
|
265
|
+
this.messageContextMenuCommands.size;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Check if a slash command exists
|
|
122
269
|
*/
|
|
123
270
|
has(name) {
|
|
124
271
|
return this.commands.has(name);
|
|
125
272
|
}
|
|
126
273
|
/**
|
|
127
|
-
*
|
|
274
|
+
* Check if a user context menu command exists
|
|
275
|
+
*/
|
|
276
|
+
hasUserContextMenu(name) {
|
|
277
|
+
return this.userContextMenuCommands.has(name);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Check if a message context menu command exists
|
|
281
|
+
*/
|
|
282
|
+
hasMessageContextMenu(name) {
|
|
283
|
+
return this.messageContextMenuCommands.has(name);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Check if any command (slash, user context, or message context) exists
|
|
287
|
+
*/
|
|
288
|
+
hasAny(name) {
|
|
289
|
+
return this.commands.has(name) ||
|
|
290
|
+
this.userContextMenuCommands.has(name) ||
|
|
291
|
+
this.messageContextMenuCommands.has(name);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Remove a slash command (useful for hot-reloading in dev)
|
|
128
295
|
*/
|
|
129
296
|
unregister(name) {
|
|
130
297
|
return this.commands.delete(name);
|
|
131
298
|
}
|
|
132
299
|
/**
|
|
133
|
-
*
|
|
300
|
+
* Remove a user context menu command
|
|
301
|
+
*/
|
|
302
|
+
unregisterUserContextMenu(name) {
|
|
303
|
+
return this.userContextMenuCommands.delete(name);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Remove a message context menu command
|
|
307
|
+
*/
|
|
308
|
+
unregisterMessageContextMenu(name) {
|
|
309
|
+
return this.messageContextMenuCommands.delete(name);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Clear all commands (slash commands, user context menus, and message context menus)
|
|
134
313
|
*/
|
|
135
314
|
clear() {
|
|
136
315
|
this.commands.clear();
|
|
316
|
+
this.userContextMenuCommands.clear();
|
|
317
|
+
this.messageContextMenuCommands.clear();
|
|
137
318
|
}
|
|
138
319
|
/**
|
|
139
|
-
* Get command names as an array
|
|
320
|
+
* Get slash command names as an array
|
|
140
321
|
*/
|
|
141
322
|
getCommandNames() {
|
|
142
323
|
return Array.from(this.commands.keys());
|
|
143
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Get user context menu command names as an array
|
|
327
|
+
*/
|
|
328
|
+
getUserContextMenuNames() {
|
|
329
|
+
return Array.from(this.userContextMenuCommands.keys());
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Get message context menu command names as an array
|
|
333
|
+
*/
|
|
334
|
+
getMessageContextMenuNames() {
|
|
335
|
+
return Array.from(this.messageContextMenuCommands.keys());
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get all command names (slash + context menus) as an array
|
|
339
|
+
*/
|
|
340
|
+
getAllCommandNames() {
|
|
341
|
+
return [
|
|
342
|
+
...this.getCommandNames(),
|
|
343
|
+
...this.getUserContextMenuNames(),
|
|
344
|
+
...this.getMessageContextMenuNames(),
|
|
345
|
+
];
|
|
346
|
+
}
|
|
144
347
|
setLogger(logger) {
|
|
145
348
|
this.logger = logger;
|
|
146
349
|
// Inject logger into all already-registered commands
|
|
147
350
|
for (const command of this.commands.values()) {
|
|
148
351
|
command.setLogger(logger);
|
|
149
352
|
}
|
|
353
|
+
for (const command of this.userContextMenuCommands.values()) {
|
|
354
|
+
command.setLogger(logger);
|
|
355
|
+
}
|
|
356
|
+
for (const command of this.messageContextMenuCommands.values()) {
|
|
357
|
+
command.setLogger(logger);
|
|
358
|
+
}
|
|
150
359
|
return this;
|
|
151
360
|
}
|
|
152
361
|
/**
|
|
@@ -157,6 +366,12 @@ export class CommandManager {
|
|
|
157
366
|
for (const command of this.commands.values()) {
|
|
158
367
|
command.setErrorReporter(reporter);
|
|
159
368
|
}
|
|
369
|
+
for (const command of this.userContextMenuCommands.values()) {
|
|
370
|
+
command.setErrorReporter(reporter);
|
|
371
|
+
}
|
|
372
|
+
for (const command of this.messageContextMenuCommands.values()) {
|
|
373
|
+
command.setErrorReporter(reporter);
|
|
374
|
+
}
|
|
160
375
|
return this;
|
|
161
376
|
}
|
|
162
377
|
}
|