@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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { MessageContextMenuCommandInteraction, Client } from 'discord.js';
|
|
2
|
+
import type { ILogger } from '../types/logger.js';
|
|
3
|
+
import type { ErrorReporter } from '../utils/ErrorReporter.js';
|
|
4
|
+
import { PermissionLevel } from '../types/permission.js';
|
|
5
|
+
/**
|
|
6
|
+
* Base class for Message Context Menu Commands.
|
|
7
|
+
*
|
|
8
|
+
* Message context menu commands appear when right-clicking on a message and
|
|
9
|
+
* selecting "Apps" in the context menu.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* export class ReportMessageCommand extends MessageContextMenuCommand {
|
|
14
|
+
* name = 'Report Message';
|
|
15
|
+
* guildOnly = true;
|
|
16
|
+
* permissionLevel = 'user' as const;
|
|
17
|
+
*
|
|
18
|
+
* protected async run(interaction: MessageContextMenuCommandInteraction) {
|
|
19
|
+
* const message = interaction.targetMessage;
|
|
20
|
+
* await reportMessage(message);
|
|
21
|
+
* await interaction.reply({ content: 'Message reported!', ephemeral: true });
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare abstract class MessageContextMenuCommand {
|
|
27
|
+
abstract name: string;
|
|
28
|
+
abstract guildOnly: boolean;
|
|
29
|
+
abstract permissionLevel: PermissionLevel;
|
|
30
|
+
protected logger?: ILogger;
|
|
31
|
+
protected errorReporter?: ErrorReporter;
|
|
32
|
+
/**
|
|
33
|
+
* The main execution method - implement your command logic here.
|
|
34
|
+
*/
|
|
35
|
+
protected abstract run(interaction: MessageContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Optional: Called before command execution.
|
|
38
|
+
* Return false to stop execution.
|
|
39
|
+
*/
|
|
40
|
+
protected beforeExecute?(interaction: MessageContextMenuCommandInteraction, client: Client): Promise<boolean | void>;
|
|
41
|
+
/**
|
|
42
|
+
* Optional: Called after successful command execution.
|
|
43
|
+
*/
|
|
44
|
+
protected afterExecute?(interaction: MessageContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Optional: Called when command execution fails.
|
|
47
|
+
*/
|
|
48
|
+
protected onError?(interaction: MessageContextMenuCommandInteraction, error: Error, client: Client): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Set the logger for this command.
|
|
51
|
+
*/
|
|
52
|
+
setLogger(logger: ILogger): void;
|
|
53
|
+
/**
|
|
54
|
+
* Set the error reporter for this command.
|
|
55
|
+
*/
|
|
56
|
+
setErrorReporter(reporter: ErrorReporter): void;
|
|
57
|
+
/**
|
|
58
|
+
* Log a message using the configured logger.
|
|
59
|
+
*/
|
|
60
|
+
protected log(message: string, level: string, scope: string, logToConsole?: boolean): void;
|
|
61
|
+
/**
|
|
62
|
+
* Validate the interaction (guild-only check).
|
|
63
|
+
*/
|
|
64
|
+
protected validate(interaction: MessageContextMenuCommandInteraction): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Check if the user has permission to use this command.
|
|
67
|
+
* Override this method to implement custom permission logic.
|
|
68
|
+
*/
|
|
69
|
+
protected hasPermission(interaction: MessageContextMenuCommandInteraction): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* Execute the command with validation and error handling.
|
|
72
|
+
*/
|
|
73
|
+
execute(interaction: MessageContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
74
|
+
private safeExecute;
|
|
75
|
+
/**
|
|
76
|
+
* Convert this command to Discord API JSON format.
|
|
77
|
+
*/
|
|
78
|
+
toJSON(): import("discord.js").RESTPostAPIContextMenuApplicationCommandsJSONBody;
|
|
79
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ContextMenuCommandBuilder, ApplicationCommandType, } from 'discord.js';
|
|
2
|
+
import { safeReply } from '../utils/editAndReply.js';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for Message Context Menu Commands.
|
|
5
|
+
*
|
|
6
|
+
* Message context menu commands appear when right-clicking on a message and
|
|
7
|
+
* selecting "Apps" in the context menu.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* export class ReportMessageCommand extends MessageContextMenuCommand {
|
|
12
|
+
* name = 'Report Message';
|
|
13
|
+
* guildOnly = true;
|
|
14
|
+
* permissionLevel = 'user' as const;
|
|
15
|
+
*
|
|
16
|
+
* protected async run(interaction: MessageContextMenuCommandInteraction) {
|
|
17
|
+
* const message = interaction.targetMessage;
|
|
18
|
+
* await reportMessage(message);
|
|
19
|
+
* await interaction.reply({ content: 'Message reported!', ephemeral: true });
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class MessageContextMenuCommand {
|
|
25
|
+
/**
|
|
26
|
+
* Set the logger for this command.
|
|
27
|
+
*/
|
|
28
|
+
setLogger(logger) {
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Set the error reporter for this command.
|
|
33
|
+
*/
|
|
34
|
+
setErrorReporter(reporter) {
|
|
35
|
+
this.errorReporter = reporter;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Log a message using the configured logger.
|
|
39
|
+
*/
|
|
40
|
+
log(message, level, scope, logToConsole = false) {
|
|
41
|
+
this.logger?.log(message, level, scope, logToConsole);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate the interaction (guild-only check).
|
|
45
|
+
*/
|
|
46
|
+
validate(interaction) {
|
|
47
|
+
if (this.guildOnly && !interaction.guildId) {
|
|
48
|
+
return 'This command can only be used in a server.';
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if the user has permission to use this command.
|
|
54
|
+
* Override this method to implement custom permission logic.
|
|
55
|
+
*/
|
|
56
|
+
async hasPermission(interaction) {
|
|
57
|
+
// Default: everyone has permission
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Execute the command with validation and error handling.
|
|
62
|
+
*/
|
|
63
|
+
async execute(interaction, client) {
|
|
64
|
+
await this.safeExecute(this.name, interaction, client, async () => {
|
|
65
|
+
const error = this.validate(interaction);
|
|
66
|
+
if (error)
|
|
67
|
+
return await safeReply(interaction, error, true);
|
|
68
|
+
if (!(await this.hasPermission(interaction))) {
|
|
69
|
+
return await safeReply(interaction, 'You do not have permission to use this command.', true);
|
|
70
|
+
}
|
|
71
|
+
if (this.beforeExecute) {
|
|
72
|
+
const shouldContinue = await this.beforeExecute(interaction, client);
|
|
73
|
+
if (shouldContinue === false)
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
await this.run(interaction, client);
|
|
78
|
+
if (this.afterExecute) {
|
|
79
|
+
await this.afterExecute(interaction, client);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (this.onError) {
|
|
84
|
+
await this.onError(interaction, err, client);
|
|
85
|
+
}
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async safeExecute(commandName, interaction, client, fn) {
|
|
91
|
+
const scope = `${commandName}_EXECUTION`;
|
|
92
|
+
try {
|
|
93
|
+
await fn();
|
|
94
|
+
this.log(`${commandName} context menu command executed`, 'info', scope);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
this.log('An Error occurred: ' + err, 'error', scope, true);
|
|
98
|
+
if (this.errorReporter) {
|
|
99
|
+
await this.errorReporter.reportError(err, `Context Menu: ${commandName}`, {
|
|
100
|
+
user: interaction.user.tag,
|
|
101
|
+
userId: interaction.user.id,
|
|
102
|
+
messageAuthor: interaction.targetMessage.author.tag,
|
|
103
|
+
messageContent: interaction.targetMessage.content.slice(0, 100),
|
|
104
|
+
guild: interaction.guild?.name,
|
|
105
|
+
guildId: interaction.guildId,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return await safeReply(interaction, 'An unexpected error occurred.');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Convert this command to Discord API JSON format.
|
|
113
|
+
*/
|
|
114
|
+
toJSON() {
|
|
115
|
+
return new ContextMenuCommandBuilder()
|
|
116
|
+
.setName(this.name)
|
|
117
|
+
.setType(ApplicationCommandType.Message)
|
|
118
|
+
.toJSON();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -35,20 +35,6 @@ export declare abstract class SubcommandGroup {
|
|
|
35
35
|
protected logger?: ILogger;
|
|
36
36
|
/** ErrorReporter instance to use inside the subcommand group */
|
|
37
37
|
private errorReporter?;
|
|
38
|
-
/**
|
|
39
|
-
* Safely executes a function with error handling and logging.
|
|
40
|
-
*
|
|
41
|
-
* Wraps the execution in a try-catch block, logs successful executions,
|
|
42
|
-
* and automatically handles errors by logging them and sending a user-friendly
|
|
43
|
-
* error message.
|
|
44
|
-
*
|
|
45
|
-
* @param subcommandName - The name of the subcommand
|
|
46
|
-
* @param scope - The logging scope for this execution
|
|
47
|
-
* @param interaction - The command interaction
|
|
48
|
-
* @param fn - The function to execute
|
|
49
|
-
* @private
|
|
50
|
-
*/
|
|
51
|
-
private safeExecute;
|
|
52
38
|
/**
|
|
53
39
|
* Executes the appropriate subcommand based on the user's interaction.
|
|
54
40
|
*
|
|
@@ -131,4 +117,19 @@ export declare abstract class SubcommandGroup {
|
|
|
131
117
|
* Set the error reporter for this subcommand group.
|
|
132
118
|
*/
|
|
133
119
|
setErrorReporter(reporter: ErrorReporter): this;
|
|
120
|
+
/**
|
|
121
|
+
* Lifecycle hook called before any subcommand in this group executes.
|
|
122
|
+
*
|
|
123
|
+
* @param interaction - The command interaction
|
|
124
|
+
* @returns true to continue, false to stop execution
|
|
125
|
+
*/
|
|
126
|
+
protected beforeExecute?(interaction: ChatInputCommandInteraction, client: Client): Promise<boolean | void>;
|
|
127
|
+
/**
|
|
128
|
+
* Lifecycle hook called after any subcommand in this group executes successfully.
|
|
129
|
+
*/
|
|
130
|
+
protected afterExecute?(interaction: ChatInputCommandInteraction, client: Client): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Lifecycle hook called when any subcommand in this group fails.
|
|
133
|
+
*/
|
|
134
|
+
protected onError?(interaction: ChatInputCommandInteraction, error: Error, client: Client): Promise<void>;
|
|
134
135
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { SlashCommandBuilder, } from 'discord.js';
|
|
2
|
-
import { safeReply } from '../utils/editAndReply.js';
|
|
3
2
|
/**
|
|
4
3
|
* Abstract base class for Discord slash command groups with subcommands.
|
|
5
4
|
*
|
|
@@ -23,36 +22,6 @@ import { safeReply } from '../utils/editAndReply.js';
|
|
|
23
22
|
* ```
|
|
24
23
|
*/
|
|
25
24
|
export class SubcommandGroup {
|
|
26
|
-
/**
|
|
27
|
-
* Safely executes a function with error handling and logging.
|
|
28
|
-
*
|
|
29
|
-
* Wraps the execution in a try-catch block, logs successful executions,
|
|
30
|
-
* and automatically handles errors by logging them and sending a user-friendly
|
|
31
|
-
* error message.
|
|
32
|
-
*
|
|
33
|
-
* @param subcommandName - The name of the subcommand
|
|
34
|
-
* @param scope - The logging scope for this execution
|
|
35
|
-
* @param interaction - The command interaction
|
|
36
|
-
* @param fn - The function to execute
|
|
37
|
-
* @private
|
|
38
|
-
*/
|
|
39
|
-
async safeExecute(subcommandName, scope, interaction, fn) {
|
|
40
|
-
try {
|
|
41
|
-
await fn();
|
|
42
|
-
this.logger?.log(`${this.name} (${subcommandName}) command executed`, 'info', scope);
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
this.logger?.log('An Error occured' + err, 'error', scope, true);
|
|
46
|
-
await this.errorReporter?.reportError(err, `Command: ${subcommandName} in SubcommandGroup: ${this.name}`, {
|
|
47
|
-
user: interaction.user.tag,
|
|
48
|
-
userId: interaction.user.id,
|
|
49
|
-
guild: interaction.guild?.name,
|
|
50
|
-
guildId: interaction.guildId,
|
|
51
|
-
channel: interaction.channel?.id,
|
|
52
|
-
});
|
|
53
|
-
return await safeReply(interaction, 'An unexpected error occurred.');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
25
|
/**
|
|
57
26
|
* Executes the appropriate subcommand based on the user's interaction.
|
|
58
27
|
*
|
|
@@ -79,8 +48,24 @@ export class SubcommandGroup {
|
|
|
79
48
|
if (!subcommand) {
|
|
80
49
|
throw new Error(`Unknown subcommand: ${subcommandName}`);
|
|
81
50
|
}
|
|
82
|
-
|
|
83
|
-
|
|
51
|
+
if (this.beforeExecute) {
|
|
52
|
+
const shouldContinue = await this.beforeExecute(interaction, client);
|
|
53
|
+
if (shouldContinue === false) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await subcommand.execute(interaction, client);
|
|
59
|
+
if (this.afterExecute) {
|
|
60
|
+
await this.afterExecute(interaction, client);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (this.onError) {
|
|
65
|
+
await this.onError(interaction, err, client);
|
|
66
|
+
}
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
84
69
|
}
|
|
85
70
|
/**
|
|
86
71
|
* Converts the command group to Discord API JSON format for registration.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { UserContextMenuCommandInteraction, Client } from 'discord.js';
|
|
2
|
+
import type { ILogger } from '../types/logger.js';
|
|
3
|
+
import type { ErrorReporter } from '../utils/ErrorReporter.js';
|
|
4
|
+
import { PermissionLevel } from '../types/permission.js';
|
|
5
|
+
/**
|
|
6
|
+
* Base class for User Context Menu Commands.
|
|
7
|
+
*
|
|
8
|
+
* User context menu commands appear when right-clicking on a user and
|
|
9
|
+
* selecting "Apps" in the context menu.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* export class GetUserInfoCommand extends UserContextMenuCommand {
|
|
14
|
+
* name = 'Get User Info';
|
|
15
|
+
* guildOnly = false;
|
|
16
|
+
* permissionLevel = 'user' as const;
|
|
17
|
+
*
|
|
18
|
+
* protected async run(interaction: UserContextMenuCommandInteraction) {
|
|
19
|
+
* const user = interaction.targetUser;
|
|
20
|
+
* await interaction.reply(`User: ${user.tag}\nID: ${user.id}`);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare abstract class UserContextMenuCommand {
|
|
26
|
+
abstract name: string;
|
|
27
|
+
abstract guildOnly: boolean;
|
|
28
|
+
abstract permissionLevel: PermissionLevel;
|
|
29
|
+
protected logger?: ILogger;
|
|
30
|
+
protected errorReporter?: ErrorReporter;
|
|
31
|
+
/**
|
|
32
|
+
* The main execution method - implement your command logic here.
|
|
33
|
+
*/
|
|
34
|
+
protected abstract run(interaction: UserContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Optional: Called before command execution.
|
|
37
|
+
* Return false to stop execution.
|
|
38
|
+
*/
|
|
39
|
+
protected beforeExecute?(interaction: UserContextMenuCommandInteraction, client: Client): Promise<boolean | void>;
|
|
40
|
+
/**
|
|
41
|
+
* Optional: Called after successful command execution.
|
|
42
|
+
*/
|
|
43
|
+
protected afterExecute?(interaction: UserContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Optional: Called when command execution fails.
|
|
46
|
+
*/
|
|
47
|
+
protected onError?(interaction: UserContextMenuCommandInteraction, error: Error, client: Client): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Set the logger for this command.
|
|
50
|
+
*/
|
|
51
|
+
setLogger(logger: ILogger): void;
|
|
52
|
+
/**
|
|
53
|
+
* Set the error reporter for this command.
|
|
54
|
+
*/
|
|
55
|
+
setErrorReporter(reporter: ErrorReporter): void;
|
|
56
|
+
/**
|
|
57
|
+
* Log a message using the configured logger.
|
|
58
|
+
*/
|
|
59
|
+
protected log(message: string, level: string, scope: string, logToConsole?: boolean): void;
|
|
60
|
+
/**
|
|
61
|
+
* Validate the interaction (guild-only check).
|
|
62
|
+
*/
|
|
63
|
+
protected validate(interaction: UserContextMenuCommandInteraction): string | null;
|
|
64
|
+
/**
|
|
65
|
+
* Check if the user has permission to use this command.
|
|
66
|
+
* Override this method to implement custom permission logic.
|
|
67
|
+
*/
|
|
68
|
+
protected hasPermission(interaction: UserContextMenuCommandInteraction): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Execute the command with validation and error handling.
|
|
71
|
+
*/
|
|
72
|
+
execute(interaction: UserContextMenuCommandInteraction, client: Client): Promise<void>;
|
|
73
|
+
private safeExecute;
|
|
74
|
+
/**
|
|
75
|
+
* Convert this command to Discord API JSON format.
|
|
76
|
+
*/
|
|
77
|
+
toJSON(): import("discord.js").RESTPostAPIContextMenuApplicationCommandsJSONBody;
|
|
78
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ContextMenuCommandBuilder, ApplicationCommandType, } from 'discord.js';
|
|
2
|
+
import { safeReply } from '../utils/editAndReply.js';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for User Context Menu Commands.
|
|
5
|
+
*
|
|
6
|
+
* User context menu commands appear when right-clicking on a user and
|
|
7
|
+
* selecting "Apps" in the context menu.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* export class GetUserInfoCommand extends UserContextMenuCommand {
|
|
12
|
+
* name = 'Get User Info';
|
|
13
|
+
* guildOnly = false;
|
|
14
|
+
* permissionLevel = 'user' as const;
|
|
15
|
+
*
|
|
16
|
+
* protected async run(interaction: UserContextMenuCommandInteraction) {
|
|
17
|
+
* const user = interaction.targetUser;
|
|
18
|
+
* await interaction.reply(`User: ${user.tag}\nID: ${user.id}`);
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export class UserContextMenuCommand {
|
|
24
|
+
/**
|
|
25
|
+
* Set the logger for this command.
|
|
26
|
+
*/
|
|
27
|
+
setLogger(logger) {
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Set the error reporter for this command.
|
|
32
|
+
*/
|
|
33
|
+
setErrorReporter(reporter) {
|
|
34
|
+
this.errorReporter = reporter;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Log a message using the configured logger.
|
|
38
|
+
*/
|
|
39
|
+
log(message, level, scope, logToConsole = false) {
|
|
40
|
+
this.logger?.log(message, level, scope, logToConsole);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Validate the interaction (guild-only check).
|
|
44
|
+
*/
|
|
45
|
+
validate(interaction) {
|
|
46
|
+
if (this.guildOnly && !interaction.guildId) {
|
|
47
|
+
return 'This command can only be used in a server.';
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if the user has permission to use this command.
|
|
53
|
+
* Override this method to implement custom permission logic.
|
|
54
|
+
*/
|
|
55
|
+
async hasPermission(interaction) {
|
|
56
|
+
// Default: everyone has permission
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Execute the command with validation and error handling.
|
|
61
|
+
*/
|
|
62
|
+
async execute(interaction, client) {
|
|
63
|
+
await this.safeExecute(this.name, interaction, client, async () => {
|
|
64
|
+
const error = this.validate(interaction);
|
|
65
|
+
if (error)
|
|
66
|
+
return await safeReply(interaction, error, true);
|
|
67
|
+
if (!(await this.hasPermission(interaction))) {
|
|
68
|
+
return await safeReply(interaction, 'You do not have permission to use this command.', true);
|
|
69
|
+
}
|
|
70
|
+
if (this.beforeExecute) {
|
|
71
|
+
const shouldContinue = await this.beforeExecute(interaction, client);
|
|
72
|
+
if (shouldContinue === false)
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
await this.run(interaction, client);
|
|
77
|
+
if (this.afterExecute) {
|
|
78
|
+
await this.afterExecute(interaction, client);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (this.onError) {
|
|
83
|
+
await this.onError(interaction, err, client);
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async safeExecute(commandName, interaction, client, fn) {
|
|
90
|
+
const scope = `${commandName}_EXECUTION`;
|
|
91
|
+
try {
|
|
92
|
+
await fn();
|
|
93
|
+
this.log(`${commandName} context menu command executed`, 'info', scope);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
this.log('An Error occurred: ' + err, 'error', scope, true);
|
|
97
|
+
if (this.errorReporter) {
|
|
98
|
+
await this.errorReporter.reportError(err, `Context Menu: ${commandName}`, {
|
|
99
|
+
user: interaction.user.tag,
|
|
100
|
+
userId: interaction.user.id,
|
|
101
|
+
targetUser: interaction.targetUser.tag,
|
|
102
|
+
targetUserId: interaction.targetUser.id,
|
|
103
|
+
guild: interaction.guild?.name,
|
|
104
|
+
guildId: interaction.guildId,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return await safeReply(interaction, 'An unexpected error occurred.');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Convert this command to Discord API JSON format.
|
|
112
|
+
*/
|
|
113
|
+
toJSON() {
|
|
114
|
+
return new ContextMenuCommandBuilder()
|
|
115
|
+
.setName(this.name)
|
|
116
|
+
.setType(ApplicationCommandType.User)
|
|
117
|
+
.toJSON();
|
|
118
|
+
}
|
|
119
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,21 +3,25 @@ export { SubcommandGroup } from './classes/SubcommandGroup.class.js';
|
|
|
3
3
|
export { CommandManager } from './classes/CommandManager.class.js';
|
|
4
4
|
export { DiscordHandler } from './classes/DiscordHandler.class.js';
|
|
5
5
|
export { ModalManager } from './classes/ModalManager.class.js';
|
|
6
|
-
export {
|
|
6
|
+
export { UserContextMenuCommand } from './classes/UserContextMenuCommand.class.js';
|
|
7
|
+
export { MessageContextMenuCommand } from './classes/MessageContextMenuCommand.class.js';
|
|
7
8
|
export type { PermissionLevel } from './types/permission.js';
|
|
8
9
|
export type { Modal, ModalField } from './types/modal.js';
|
|
9
10
|
export type { ButtonType } from './types/button.js';
|
|
10
11
|
export type { ILogger } from './types/logger.js';
|
|
11
12
|
export type { AnySelectMenuInteraction, ButtonHandler, SelectMenuHandler, ComponentConfig } from './types/component.js';
|
|
12
13
|
export type { AutocompleteHandler } from './types/autocomplete.js';
|
|
14
|
+
export type { LoadCommandsOptions } from './types/loadCommands.js';
|
|
13
15
|
export { getPermissionsForLevel } from './utils/permissions.js';
|
|
14
16
|
export { embedBuilder, createButton, createButtonsRow, createPaginationButtons, } from './utils/embeds.js';
|
|
15
17
|
export { stringOption, integerOption, booleanOption, userOption, channelOption, roleOption, } from './utils/slashCommandOptions.js';
|
|
16
18
|
export { safeReply, safeEdit } from './utils/editAndReply.js';
|
|
17
19
|
export { formatDuration, formatDateToString, formatDateToYYYYMMDDHHMMSS, formatDateToDDMMYYYY, getDaySuffix, capitalizeFirst, } from './utils/formatting.js';
|
|
20
|
+
export { loadCommands } from './utils/loadCommands.js';
|
|
18
21
|
export { TIMES_MILISECONDS } from './utils/miliseconds.js';
|
|
19
22
|
export { TToolboxLogger } from './utils/TToolboxLogger.class.js';
|
|
20
23
|
export { ErrorReporter } from './utils/ErrorReporter.js';
|
|
21
24
|
export { ComponentManager } from './utils/ComponentManager.class.js';
|
|
22
25
|
export { AutocompleteManager } from './utils/AutocompleteManager.class.js';
|
|
26
|
+
export { PaginatedEmbed } from './utils/PaginatedEmbed.class.js';
|
|
23
27
|
export { InteractionError } from './classes/InteractionError.class.js';
|
package/dist/index.js
CHANGED
|
@@ -4,17 +4,20 @@ export { SubcommandGroup } from './classes/SubcommandGroup.class.js';
|
|
|
4
4
|
export { CommandManager } from './classes/CommandManager.class.js';
|
|
5
5
|
export { DiscordHandler } from './classes/DiscordHandler.class.js';
|
|
6
6
|
export { ModalManager } from './classes/ModalManager.class.js';
|
|
7
|
-
export {
|
|
7
|
+
export { UserContextMenuCommand } from './classes/UserContextMenuCommand.class.js';
|
|
8
|
+
export { MessageContextMenuCommand } from './classes/MessageContextMenuCommand.class.js';
|
|
8
9
|
// Utilities
|
|
9
10
|
export { getPermissionsForLevel } from './utils/permissions.js';
|
|
10
11
|
export { embedBuilder, createButton, createButtonsRow, createPaginationButtons, } from './utils/embeds.js';
|
|
11
12
|
export { stringOption, integerOption, booleanOption, userOption, channelOption, roleOption, } from './utils/slashCommandOptions.js';
|
|
12
13
|
export { safeReply, safeEdit } from './utils/editAndReply.js';
|
|
13
14
|
export { formatDuration, formatDateToString, formatDateToYYYYMMDDHHMMSS, formatDateToDDMMYYYY, getDaySuffix, capitalizeFirst, } from './utils/formatting.js';
|
|
15
|
+
export { loadCommands } from './utils/loadCommands.js';
|
|
14
16
|
export { TIMES_MILISECONDS } from './utils/miliseconds.js';
|
|
15
17
|
export { TToolboxLogger } from './utils/TToolboxLogger.class.js';
|
|
16
18
|
export { ErrorReporter } from './utils/ErrorReporter.js';
|
|
17
19
|
export { ComponentManager } from './utils/ComponentManager.class.js';
|
|
18
20
|
export { AutocompleteManager } from './utils/AutocompleteManager.class.js';
|
|
21
|
+
export { PaginatedEmbed } from './utils/PaginatedEmbed.class.js';
|
|
19
22
|
// Errors
|
|
20
23
|
export { InteractionError } from './classes/InteractionError.class.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for loading commands from a directory
|
|
3
|
+
*/
|
|
4
|
+
export interface LoadCommandsOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Whether to recursively search subdirectories (default: true)
|
|
7
|
+
*/
|
|
8
|
+
recursive?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to log verbose output to console (default: false)
|
|
11
|
+
*/
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Directory names to skip (default: ['subcommands', 'utils', 'helpers', 'lib'])
|
|
15
|
+
* Case-insensitive partial matching
|
|
16
|
+
*/
|
|
17
|
+
skipDirs?: string[];
|
|
18
|
+
/**
|
|
19
|
+
* File name patterns to skip (default: ['Helper', 'Util', '.test', '.spec'])
|
|
20
|
+
* Case-sensitive partial matching
|
|
21
|
+
*/
|
|
22
|
+
skipFiles?: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Custom filter function for fine-grained control (optional)
|
|
25
|
+
* Return true to include the file, false to skip
|
|
26
|
+
* Runs AFTER skipDirs and skipFiles checks
|
|
27
|
+
*/
|
|
28
|
+
filter?: (filePath: string) => boolean;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Stream from 'stream';
|
|
2
|
-
import { ActionRowBuilder, APIAttachment, Attachment, AttachmentBuilder, AttachmentPayload, BufferResolvable, ButtonInteraction, ChannelSelectMenuInteraction, ChatInputCommandInteraction, EmbedBuilder, JSONEncodable, Message, ModalSubmitInteraction, StringSelectMenuInteraction } from 'discord.js';
|
|
2
|
+
import { ActionRowBuilder, APIAttachment, Attachment, AttachmentBuilder, AttachmentPayload, BufferResolvable, ButtonInteraction, ChannelSelectMenuInteraction, ChatInputCommandInteraction, ContextMenuCommandInteraction, EmbedBuilder, JSONEncodable, Message, ModalSubmitInteraction, StringSelectMenuInteraction } from 'discord.js';
|
|
3
3
|
/**
|
|
4
4
|
* Safely replies to an interaction, handling deferred/replied states.
|
|
5
5
|
*
|
|
@@ -23,7 +23,7 @@ import { ActionRowBuilder, APIAttachment, Attachment, AttachmentBuilder, Attachm
|
|
|
23
23
|
* }
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
-
export declare function safeReply(interaction: ChatInputCommandInteraction | ButtonInteraction | ModalSubmitInteraction | ChannelSelectMenuInteraction | StringSelectMenuInteraction, content: string, ephemeral?: boolean, embeds?: EmbedBuilder[], components?: ActionRowBuilder<any>[], files?: (BufferResolvable | Stream | JSONEncodable<APIAttachment> | Attachment | AttachmentBuilder | AttachmentPayload)[]): Promise<Message>;
|
|
26
|
+
export declare function safeReply(interaction: ChatInputCommandInteraction | ButtonInteraction | ModalSubmitInteraction | ChannelSelectMenuInteraction | StringSelectMenuInteraction | ContextMenuCommandInteraction, content: string, ephemeral?: boolean, embeds?: EmbedBuilder[], components?: ActionRowBuilder<any>[], files?: (BufferResolvable | Stream | JSONEncodable<APIAttachment> | Attachment | AttachmentBuilder | AttachmentPayload)[]): Promise<Message>;
|
|
27
27
|
/**
|
|
28
28
|
* Safely edits an interaction reply.
|
|
29
29
|
*
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { LoadCommandsOptions } from '../types/loadCommands';
|
|
2
|
+
/**
|
|
3
|
+
* @experimental
|
|
4
|
+
* Automatically discover and load all commands from a directory.
|
|
5
|
+
*
|
|
6
|
+
* Returns an array of command instances that can be registered with CommandManager.
|
|
7
|
+
*
|
|
8
|
+
* @param dirPath - Absolute path to the directory containing commands
|
|
9
|
+
* @param options - Optional configuration
|
|
10
|
+
* @returns Array of command instances
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { loadCommands } from '@julanzw/ttoolbox-discordjs-framework';
|
|
15
|
+
*
|
|
16
|
+
* // Load all commands
|
|
17
|
+
* const commands = await loadCommands('./src/commands', { verbose: true });
|
|
18
|
+
*
|
|
19
|
+
* // Register them
|
|
20
|
+
* commandManager.registerCommand(commands);
|
|
21
|
+
*
|
|
22
|
+
* // Or filter/process them first
|
|
23
|
+
* const adminCommands = commands.filter(cmd => cmd.name.startsWith('admin'));
|
|
24
|
+
* commandManager.registerCommand(adminCommands);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadCommands(dirPath: string, options?: LoadCommandsOptions): Promise<any[]>;
|