@julanzw/ttoolbox-discordjs-framework 1.0.4 → 1.0.5

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.
@@ -1,6 +1,7 @@
1
1
  import { ChatInputCommandInteraction, Client, SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js';
2
2
  import { PermissionLevel } from '../types/permission.js';
3
3
  import { ILogger } from '../types/logger.js';
4
+ import { ErrorReporter } from '../utils/ErrorReporter.js';
4
5
  /**
5
6
  * Abstract base class for Discord slash commands.
6
7
  *
@@ -35,6 +36,8 @@ export declare abstract class Command {
35
36
  cooldown?: number;
36
37
  /** Logger instance to use inside the command */
37
38
  protected logger?: ILogger;
39
+ /** ErrorReporter instance to use inside the command */
40
+ protected errorReporter?: ErrorReporter;
38
41
  /**
39
42
  * Validates whether the command can be executed in the current context.
40
43
  *
@@ -166,4 +169,8 @@ export declare abstract class Command {
166
169
  * @param logToConsole - Whether to also log to console
167
170
  */
168
171
  protected log(message: string, level: string, scope: string, logToConsole?: boolean): void;
172
+ /**
173
+ * Set the error reporter for this command
174
+ */
175
+ setErrorReporter(reporter: ErrorReporter): void;
169
176
  }
@@ -85,13 +85,19 @@ export class Command {
85
85
  const scope = `${commandName}_EXECUTION`;
86
86
  try {
87
87
  await fn();
88
- const subcommandName = interaction.options.getSubcommand(false);
89
- this.logger?.log(`${commandName} ${subcommandName ? `(${subcommandName}) ` : ``}command executed`, 'info', scope);
88
+ this.logger?.log(`${commandName} command executed`, 'info', scope);
90
89
  }
91
90
  catch (err) {
92
- this.logger?.log(
93
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
94
- `An Error occurred: ${err.message ?? err}`, 'error', scope, true);
91
+ this.logger?.log(`An Error occurred: ${err.message ?? err}`, 'error', scope, true);
92
+ if (this.errorReporter) {
93
+ await this.errorReporter.reportError(err, `Command: ${commandName}`, {
94
+ user: interaction.user.tag,
95
+ userId: interaction.user.id,
96
+ guild: interaction.guild?.name,
97
+ guildId: interaction.guildId,
98
+ channel: interaction.channel?.id,
99
+ });
100
+ }
95
101
  return await safeReply(interaction, 'An unexpected error occurred.');
96
102
  }
97
103
  }
@@ -153,4 +159,10 @@ export class Command {
153
159
  log(message, level, scope, logToConsole = false) {
154
160
  this.logger?.log(message, level, scope, logToConsole);
155
161
  }
162
+ /**
163
+ * Set the error reporter for this command
164
+ */
165
+ setErrorReporter(reporter) {
166
+ this.errorReporter = reporter;
167
+ }
156
168
  }
@@ -2,9 +2,11 @@ import { ChatInputCommandInteraction, Client, RESTPostAPIChatInputApplicationCom
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
+ import { ErrorReporter } from '../utils/ErrorReporter.js';
5
6
  export declare class CommandManager {
6
7
  private commands;
7
8
  protected logger?: ILogger;
9
+ protected errorReporter?: ErrorReporter;
8
10
  /**
9
11
  * Register a single command or subcommand group
10
12
  */
@@ -66,4 +68,8 @@ export declare class CommandManager {
66
68
  */
67
69
  getCommandNames(): string[];
68
70
  setLogger(logger: ILogger): this;
71
+ /**
72
+ * Set the error reporter for all commands
73
+ */
74
+ setErrorReporter(reporter: ErrorReporter): this;
69
75
  }
@@ -10,6 +10,9 @@ export class CommandManager {
10
10
  if (this.logger) {
11
11
  command.setLogger(this.logger);
12
12
  }
13
+ if (this.errorReporter) {
14
+ command.setErrorReporter(this.errorReporter);
15
+ }
13
16
  this.commands.set(command.name, command);
14
17
  return this;
15
18
  }
@@ -146,4 +149,14 @@ export class CommandManager {
146
149
  }
147
150
  return this;
148
151
  }
152
+ /**
153
+ * Set the error reporter for all commands
154
+ */
155
+ setErrorReporter(reporter) {
156
+ this.errorReporter = reporter;
157
+ for (const command of this.commands.values()) {
158
+ command.setErrorReporter(reporter);
159
+ }
160
+ return this;
161
+ }
149
162
  }
@@ -1,6 +1,7 @@
1
1
  import { ChatInputCommandInteraction, Client, RESTPostAPIChatInputApplicationCommandsJSONBody } from 'discord.js';
2
2
  import { ILogger } from '../types/logger.js';
3
3
  import { Command } from './Command.class.js';
4
+ import { ErrorReporter } from '../utils/ErrorReporter.js';
4
5
  /**
5
6
  * Abstract base class for Discord slash command groups with subcommands.
6
7
  *
@@ -32,6 +33,8 @@ export declare abstract class SubcommandGroup {
32
33
  protected abstract subcommands: Map<string, Command>;
33
34
  /** The logger instance used in the subcommand group */
34
35
  protected logger?: ILogger;
36
+ /** ErrorReporter instance to use inside the subcommand group */
37
+ private errorReporter?;
35
38
  /**
36
39
  * Safely executes a function with error handling and logging.
37
40
  *
@@ -39,7 +42,7 @@ export declare abstract class SubcommandGroup {
39
42
  * and automatically handles errors by logging them and sending a user-friendly
40
43
  * error message.
41
44
  *
42
- * @param commandName - The name of the parent command
45
+ * @param subcommandName - The name of the subcommand
43
46
  * @param scope - The logging scope for this execution
44
47
  * @param interaction - The command interaction
45
48
  * @param fn - The function to execute
@@ -124,4 +127,8 @@ export declare abstract class SubcommandGroup {
124
127
  * @param logToConsole - Whether to also log to console
125
128
  */
126
129
  protected log(message: string, level: string, scope: string, logToConsole?: boolean): void;
130
+ /**
131
+ * Set the error reporter for this subcommand group.
132
+ */
133
+ setErrorReporter(reporter: ErrorReporter): this;
127
134
  }
@@ -30,20 +30,26 @@ export class SubcommandGroup {
30
30
  * and automatically handles errors by logging them and sending a user-friendly
31
31
  * error message.
32
32
  *
33
- * @param commandName - The name of the parent command
33
+ * @param subcommandName - The name of the subcommand
34
34
  * @param scope - The logging scope for this execution
35
35
  * @param interaction - The command interaction
36
36
  * @param fn - The function to execute
37
37
  * @private
38
38
  */
39
- async safeExecute(commandName, scope, interaction, fn) {
39
+ async safeExecute(subcommandName, scope, interaction, fn) {
40
40
  try {
41
41
  await fn();
42
- const subcommandName = interaction.options.getSubcommand(false);
43
- this.logger?.log(`${commandName} ${subcommandName ? `(${subcommandName}) ` : ``}command executed`, 'info', scope);
42
+ this.logger?.log(`${this.name} (${subcommandName}) command executed`, 'info', scope);
44
43
  }
45
44
  catch (err) {
46
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
+ });
47
53
  return await safeReply(interaction, 'An unexpected error occurred.');
48
54
  }
49
55
  }
@@ -74,7 +80,7 @@ export class SubcommandGroup {
74
80
  throw new Error(`Unknown subcommand: ${subcommandName}`);
75
81
  }
76
82
  const scope = `${subcommand.name}_EXECUTION`;
77
- await this.safeExecute(this.name, scope, interaction, () => subcommand.execute(interaction, client));
83
+ await this.safeExecute(subcommandName, scope, interaction, () => subcommand.execute(interaction, client));
78
84
  }
79
85
  /**
80
86
  * Converts the command group to Discord API JSON format for registration.
@@ -153,4 +159,14 @@ export class SubcommandGroup {
153
159
  log(message, level, scope, logToConsole = false) {
154
160
  this.logger?.log(message, level, scope, logToConsole);
155
161
  }
162
+ /**
163
+ * Set the error reporter for this subcommand group.
164
+ */
165
+ setErrorReporter(reporter) {
166
+ this.errorReporter = reporter;
167
+ for (const command of this.subcommands.values()) {
168
+ command.setErrorReporter(reporter);
169
+ }
170
+ return this;
171
+ }
156
172
  }
package/dist/index.d.ts CHANGED
@@ -15,4 +15,5 @@ export { safeReply, safeEdit } from './utils/editAndReply.js';
15
15
  export { formatDuration, formatDateToString, formatDateToYYYYMMDDHHMMSS, formatDateToDDMMYYYY, getDaySuffix, capitalizeFirst, } from './utils/formatting.js';
16
16
  export { TIMES_MILISECONDS } from './utils/miliseconds.js';
17
17
  export { TToolboxLogger } from './utils/TToolboxLogger.class.js';
18
+ export { ErrorReporter } from './utils/ErrorReporter.js';
18
19
  export { InteractionError } from './classes/InteractionError.class.js';
package/dist/index.js CHANGED
@@ -13,5 +13,6 @@ export { safeReply, safeEdit } from './utils/editAndReply.js';
13
13
  export { formatDuration, formatDateToString, formatDateToYYYYMMDDHHMMSS, formatDateToDDMMYYYY, getDaySuffix, capitalizeFirst, } from './utils/formatting.js';
14
14
  export { TIMES_MILISECONDS } from './utils/miliseconds.js';
15
15
  export { TToolboxLogger } from './utils/TToolboxLogger.class.js';
16
+ export { ErrorReporter } from './utils/ErrorReporter.js';
16
17
  // Errors
17
18
  export { InteractionError } from './classes/InteractionError.class.js';
@@ -0,0 +1,72 @@
1
+ import type { ChatInputCommandInteraction, User, Guild, TextChannel, Client } from 'discord.js';
2
+ /**
3
+ * Options for creating a mock interaction
4
+ */
5
+ export interface MockInteractionOptions {
6
+ commandName?: string;
7
+ user?: Partial<User>;
8
+ guild?: Partial<Guild>;
9
+ guildId?: string | null;
10
+ channelId?: string;
11
+ options?: Record<string, any>;
12
+ replied?: boolean;
13
+ deferred?: boolean;
14
+ }
15
+ /**
16
+ * Create a mock ChatInputCommandInteraction for testing.
17
+ *
18
+ * @param overrides - Optional overrides for interaction properties
19
+ * @returns A mocked interaction object
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { describe, it, expect, vi } from 'vitest';
24
+ * import { createMockInteraction } from '@julanzw/ttoolbox-discordjs-framework/testing';
25
+ *
26
+ * describe('PingCommand', () => {
27
+ * it('should reply with pong', async () => {
28
+ * const interaction = createMockInteraction({
29
+ * commandName: 'ping',
30
+ * });
31
+ *
32
+ * const command = new PingCommand();
33
+ * await command.execute(interaction, mockClient);
34
+ *
35
+ * expect(interaction.reply).toHaveBeenCalledWith('Pong!');
36
+ * });
37
+ * });
38
+ * ```
39
+ */
40
+ export declare function createMockInteraction(overrides?: MockInteractionOptions): ChatInputCommandInteraction;
41
+ /**
42
+ * Create a mock Discord client for testing.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const client = createMockClient();
47
+ * const command = new PingCommand();
48
+ * await command.execute(mockInteraction, client);
49
+ * ```
50
+ */
51
+ export declare function createMockClient(): Client;
52
+ /**
53
+ * Create a mock user for testing.
54
+ *
55
+ * @param overrides - Optional overrides for user properties
56
+ * @returns A mocked User object
57
+ */
58
+ export declare function createMockUser(overrides?: Partial<User>): User;
59
+ /**
60
+ * Create a mock guild for testing.
61
+ *
62
+ * @param overrides - Optional overrides for guild properties
63
+ * @returns A mocked Guild object
64
+ */
65
+ export declare function createMockGuild(overrides?: Partial<Guild>): Guild;
66
+ /**
67
+ * Create a mock text channel for testing.
68
+ *
69
+ * @param overrides - Optional overrides for channel properties
70
+ * @returns A mocked TextChannel object
71
+ */
72
+ export declare function createMockChannel(overrides?: Partial<TextChannel>): TextChannel;
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Create a mock ChatInputCommandInteraction for testing.
3
+ *
4
+ * @param overrides - Optional overrides for interaction properties
5
+ * @returns A mocked interaction object
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { describe, it, expect, vi } from 'vitest';
10
+ * import { createMockInteraction } from '@julanzw/ttoolbox-discordjs-framework/testing';
11
+ *
12
+ * describe('PingCommand', () => {
13
+ * it('should reply with pong', async () => {
14
+ * const interaction = createMockInteraction({
15
+ * commandName: 'ping',
16
+ * });
17
+ *
18
+ * const command = new PingCommand();
19
+ * await command.execute(interaction, mockClient);
20
+ *
21
+ * expect(interaction.reply).toHaveBeenCalledWith('Pong!');
22
+ * });
23
+ * });
24
+ * ```
25
+ */
26
+ export function createMockInteraction(overrides = {}) {
27
+ const mockUser = {
28
+ id: overrides.user?.id || '123456789',
29
+ username: overrides.user?.username || 'testuser',
30
+ discriminator: overrides.user?.discriminator || '0001',
31
+ tag: overrides.user?.tag || 'testuser#0001',
32
+ bot: overrides.user?.bot || false,
33
+ system: overrides.user?.system || false,
34
+ avatar: overrides.user?.avatar || null,
35
+ ...overrides.user,
36
+ };
37
+ const mockGuild = overrides.guildId === null ? null : {
38
+ id: overrides.guild?.id || overrides.guildId || '987654321',
39
+ name: overrides.guild?.name || 'Test Guild',
40
+ ...overrides.guild,
41
+ };
42
+ const mockChannel = {
43
+ id: overrides.channelId || '111222333',
44
+ type: 0, // TextChannel
45
+ isTextBased: () => true,
46
+ };
47
+ const mockMember = mockGuild ? {
48
+ id: mockUser.id,
49
+ user: mockUser,
50
+ guild: mockGuild,
51
+ roles: {
52
+ cache: new Map(),
53
+ highest: { position: 0 },
54
+ },
55
+ } : null;
56
+ // Create mock options resolver
57
+ const mockOptionsResolver = {
58
+ getString: vi.fn((name, required) => {
59
+ const value = overrides.options?.[name];
60
+ if (required && !value)
61
+ throw new Error(`Required option ${name} is missing`);
62
+ return value || null;
63
+ }),
64
+ getInteger: vi.fn((name, required) => {
65
+ const value = overrides.options?.[name];
66
+ if (required && value === undefined)
67
+ throw new Error(`Required option ${name} is missing`);
68
+ return value !== undefined ? value : null;
69
+ }),
70
+ getNumber: vi.fn((name, required) => {
71
+ const value = overrides.options?.[name];
72
+ if (required && value === undefined)
73
+ throw new Error(`Required option ${name} is missing`);
74
+ return value !== undefined ? value : null;
75
+ }),
76
+ getBoolean: vi.fn((name, required) => {
77
+ const value = overrides.options?.[name];
78
+ if (required && value === undefined)
79
+ throw new Error(`Required option ${name} is missing`);
80
+ return value !== undefined ? value : null;
81
+ }),
82
+ getUser: vi.fn((name) => overrides.options?.[name] || null),
83
+ getChannel: vi.fn((name) => overrides.options?.[name] || null),
84
+ getRole: vi.fn((name) => overrides.options?.[name] || null),
85
+ getMember: vi.fn((name) => overrides.options?.[name] || null),
86
+ getMentionable: vi.fn((name) => overrides.options?.[name] || null),
87
+ getAttachment: vi.fn((name) => overrides.options?.[name] || null),
88
+ getSubcommand: vi.fn((required) => {
89
+ const value = overrides.options?.['_subcommand'];
90
+ if (required && !value)
91
+ throw new Error('Subcommand is required');
92
+ return value || null;
93
+ }),
94
+ data: [],
95
+ };
96
+ const mockInteraction = {
97
+ id: 'mock-interaction-id',
98
+ type: 2, // ApplicationCommandType.ChatInput
99
+ user: mockUser,
100
+ member: mockMember,
101
+ guild: mockGuild,
102
+ guildId: mockGuild?.id || null,
103
+ channel: mockChannel,
104
+ channelId: mockChannel.id,
105
+ commandName: overrides.commandName || 'test',
106
+ options: mockOptionsResolver,
107
+ replied: overrides.replied || false,
108
+ deferred: overrides.deferred || false,
109
+ createdTimestamp: Date.now(),
110
+ // Mock methods
111
+ reply: vi.fn().mockResolvedValue(undefined),
112
+ editReply: vi.fn().mockResolvedValue(undefined),
113
+ followUp: vi.fn().mockResolvedValue(undefined),
114
+ deferReply: vi.fn().mockResolvedValue(undefined),
115
+ deleteReply: vi.fn().mockResolvedValue(undefined),
116
+ fetchReply: vi.fn().mockResolvedValue(undefined),
117
+ };
118
+ return mockInteraction;
119
+ }
120
+ /**
121
+ * Create a mock Discord client for testing.
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * const client = createMockClient();
126
+ * const command = new PingCommand();
127
+ * await command.execute(mockInteraction, client);
128
+ * ```
129
+ */
130
+ export function createMockClient() {
131
+ const mockClient = {
132
+ user: {
133
+ id: '999888777',
134
+ tag: 'TestBot#1234',
135
+ username: 'TestBot',
136
+ },
137
+ ws: {
138
+ ping: 42,
139
+ },
140
+ channels: {
141
+ fetch: vi.fn().mockResolvedValue({
142
+ id: '111222333',
143
+ isTextBased: () => true,
144
+ send: vi.fn().mockResolvedValue(undefined),
145
+ }),
146
+ },
147
+ };
148
+ return mockClient;
149
+ }
150
+ /**
151
+ * Create a mock user for testing.
152
+ *
153
+ * @param overrides - Optional overrides for user properties
154
+ * @returns A mocked User object
155
+ */
156
+ export function createMockUser(overrides = {}) {
157
+ return {
158
+ id: overrides.id || '123456789',
159
+ username: overrides.username || 'testuser',
160
+ discriminator: overrides.discriminator || '0001',
161
+ tag: overrides.tag || 'testuser#0001',
162
+ bot: overrides.bot || false,
163
+ system: overrides.system || false,
164
+ avatar: overrides.avatar || null,
165
+ ...overrides,
166
+ };
167
+ }
168
+ /**
169
+ * Create a mock guild for testing.
170
+ *
171
+ * @param overrides - Optional overrides for guild properties
172
+ * @returns A mocked Guild object
173
+ */
174
+ export function createMockGuild(overrides = {}) {
175
+ return {
176
+ id: overrides.id || '987654321',
177
+ name: overrides.name || 'Test Guild',
178
+ ownerId: '111111111',
179
+ ...overrides,
180
+ };
181
+ }
182
+ /**
183
+ * Create a mock text channel for testing.
184
+ *
185
+ * @param overrides - Optional overrides for channel properties
186
+ * @returns A mocked TextChannel object
187
+ */
188
+ export function createMockChannel(overrides = {}) {
189
+ return {
190
+ id: overrides.id || '111222333',
191
+ type: 0, // TextChannel
192
+ name: 'test-channel',
193
+ isTextBased: () => true,
194
+ send: vi.fn().mockResolvedValue(undefined),
195
+ ...overrides,
196
+ };
197
+ }
@@ -0,0 +1,54 @@
1
+ import { Client } from 'discord.js';
2
+ import type { ILogger } from '../types/logger.js';
3
+ /**
4
+ * Reports errors to a designated Discord channel.
5
+ *
6
+ * Used for monitoring production bots - get notified when errors occur
7
+ * without having to constantly checking logs.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const errorReporter = new ErrorReporter(client, '1234567890', logger);
12
+ *
13
+ * try {
14
+ * await riskyOperation();
15
+ * } catch (err) {
16
+ * await errorReporter.reportError(err, 'Processing user data');
17
+ * }
18
+ * ```
19
+ */
20
+ export declare class ErrorReporter {
21
+ private client;
22
+ private channelId;
23
+ private logger?;
24
+ constructor(client: Client, channelId: string, logger?: ILogger | undefined);
25
+ /**
26
+ * Report an error to the configured Discord channel.
27
+ *
28
+ * @param error - The error that occurred
29
+ * @param context - Context about where/why the error happened
30
+ * @param additionalInfo - Optional additional information to include
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * await errorReporter.reportError(
35
+ * new Error('Comparison went wrong'),
36
+ * 'data not equal to moreData',
37
+ * { data: '123', moreData: '456' }
38
+ * );
39
+ * ```
40
+ */
41
+ reportError(error: Error, context: string, additionalInfo?: Record<string, any>): Promise<void>;
42
+ /**
43
+ * Truncate text to a maximum length
44
+ */
45
+ private truncate;
46
+ /**
47
+ * Update the channel ID for error reporting
48
+ */
49
+ setChannel(channelId: string): void;
50
+ /**
51
+ * Get the current channel ID
52
+ */
53
+ getChannelId(): string;
54
+ }
@@ -0,0 +1,95 @@
1
+ import { EmbedBuilder } from 'discord.js';
2
+ /**
3
+ * Reports errors to a designated Discord channel.
4
+ *
5
+ * Used for monitoring production bots - get notified when errors occur
6
+ * without having to constantly checking logs.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const errorReporter = new ErrorReporter(client, '1234567890', logger);
11
+ *
12
+ * try {
13
+ * await riskyOperation();
14
+ * } catch (err) {
15
+ * await errorReporter.reportError(err, 'Processing user data');
16
+ * }
17
+ * ```
18
+ */
19
+ export class ErrorReporter {
20
+ constructor(client, channelId, logger) {
21
+ this.client = client;
22
+ this.channelId = channelId;
23
+ this.logger = logger;
24
+ }
25
+ /**
26
+ * Report an error to the configured Discord channel.
27
+ *
28
+ * @param error - The error that occurred
29
+ * @param context - Context about where/why the error happened
30
+ * @param additionalInfo - Optional additional information to include
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * await errorReporter.reportError(
35
+ * new Error('Comparison went wrong'),
36
+ * 'data not equal to moreData',
37
+ * { data: '123', moreData: '456' }
38
+ * );
39
+ * ```
40
+ */
41
+ async reportError(error, context, additionalInfo) {
42
+ try {
43
+ const channel = await this.client.channels.fetch(this.channelId);
44
+ if (!channel?.isTextBased()) {
45
+ this.logger?.warn(`Error reporter channel ${this.channelId} is not text-based`, 'error-reporter');
46
+ return;
47
+ }
48
+ const embed = new EmbedBuilder()
49
+ .setTitle('Error Occurred')
50
+ .setColor(0xed4245)
51
+ .addFields({ name: 'Context', value: context }, {
52
+ name: 'Error',
53
+ value: `\`\`\`${this.truncate(error.message, 1000)}\`\`\``
54
+ })
55
+ .setTimestamp();
56
+ if (error.stack) {
57
+ embed.addFields({
58
+ name: 'Stack Trace',
59
+ value: `\`\`\`${this.truncate(error.stack, 1000)}\`\`\``,
60
+ });
61
+ }
62
+ if (additionalInfo && Object.keys(additionalInfo).length > 0) {
63
+ embed.addFields({
64
+ name: 'Additional Info',
65
+ value: `\`\`\`json\n${JSON.stringify(additionalInfo, null, 2).slice(0, 1000)}\`\`\``,
66
+ });
67
+ }
68
+ await channel.send({ embeds: [embed] });
69
+ this.logger?.info(`Error reported to channel ${this.channelId}`, 'error-reporter');
70
+ }
71
+ catch (err) {
72
+ this.logger?.error(`Failed to report error to Discord: ${err.message}`, 'error-reporter');
73
+ }
74
+ }
75
+ /**
76
+ * Truncate text to a maximum length
77
+ */
78
+ truncate(text, maxLength) {
79
+ if (text.length <= maxLength)
80
+ return text;
81
+ return text.slice(0, maxLength - 3) + '...';
82
+ }
83
+ /**
84
+ * Update the channel ID for error reporting
85
+ */
86
+ setChannel(channelId) {
87
+ this.channelId = channelId;
88
+ }
89
+ /**
90
+ * Get the current channel ID
91
+ */
92
+ getChannelId() {
93
+ return this.channelId;
94
+ }
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@julanzw/ttoolbox-discordjs-framework",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "A Discord.js command framework with built-in handlers and utilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",