@julanzw/ttoolbox-discordjs-framework 1.0.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.
Files changed (45) hide show
  1. package/LICENSE +675 -0
  2. package/README.md +77 -0
  3. package/dist/classes/Command.class.d.ts +169 -0
  4. package/dist/classes/Command.class.js +156 -0
  5. package/dist/classes/CommandManager.class.d.ts +69 -0
  6. package/dist/classes/CommandManager.class.js +149 -0
  7. package/dist/classes/DiscordHandler.class.d.ts +241 -0
  8. package/dist/classes/DiscordHandler.class.js +222 -0
  9. package/dist/classes/InteractionError.class.d.ts +8 -0
  10. package/dist/classes/InteractionError.class.js +11 -0
  11. package/dist/classes/ModalManager.class.d.ts +154 -0
  12. package/dist/classes/ModalManager.class.js +205 -0
  13. package/dist/classes/SubcommandGroup.class.d.ts +127 -0
  14. package/dist/classes/SubcommandGroup.class.js +156 -0
  15. package/dist/index.d.ts +18 -0
  16. package/dist/index.js +17 -0
  17. package/dist/types/button.d.ts +2 -0
  18. package/dist/types/button.js +1 -0
  19. package/dist/types/channel.d.ts +2 -0
  20. package/dist/types/channel.js +1 -0
  21. package/dist/types/logger.d.ts +37 -0
  22. package/dist/types/logger.js +1 -0
  23. package/dist/types/modal.d.ts +77 -0
  24. package/dist/types/modal.js +1 -0
  25. package/dist/types/permission.d.ts +2 -0
  26. package/dist/types/permission.js +1 -0
  27. package/dist/utils/PaginatedEmbed.class.d.ts +25 -0
  28. package/dist/utils/PaginatedEmbed.class.js +102 -0
  29. package/dist/utils/TToolboxLogger.class.d.ts +176 -0
  30. package/dist/utils/TToolboxLogger.class.js +252 -0
  31. package/dist/utils/cooldown.d.ts +13 -0
  32. package/dist/utils/cooldown.js +43 -0
  33. package/dist/utils/editAndReply.d.ts +37 -0
  34. package/dist/utils/editAndReply.js +85 -0
  35. package/dist/utils/embeds.d.ts +55 -0
  36. package/dist/utils/embeds.js +94 -0
  37. package/dist/utils/formatting.d.ts +44 -0
  38. package/dist/utils/formatting.js +87 -0
  39. package/dist/utils/miliseconds.d.ts +10 -0
  40. package/dist/utils/miliseconds.js +11 -0
  41. package/dist/utils/permissions.d.ts +8 -0
  42. package/dist/utils/permissions.js +24 -0
  43. package/dist/utils/slashCommandOptions.d.ts +8 -0
  44. package/dist/utils/slashCommandOptions.js +11 -0
  45. package/package.json +50 -0
@@ -0,0 +1,241 @@
1
+ import { Client, Interaction, Message, MessageReaction, PartialMessageReaction, PartialUser, User, OmitPartialGroupDMChannel, PartialMessage, Awaitable, ClientEvents } from 'discord.js';
2
+ import { PrismaClient } from '@prisma/client';
3
+ import { TToolboxLogger } from '../utils/TToolboxLogger.class.js';
4
+ /**
5
+ * Abstract base class for Discord bot event handling.
6
+ *
7
+ * Provides a structured way to handle Discord.js events with automatic setup,
8
+ * error handling, and graceful shutdown capabilities. Child classes should
9
+ * override the protected handler methods for the events they want to handle.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * class MyBotHandler extends DiscordHandler {
14
+ * protected async handleInteractionCreation(interaction: Interaction) {
15
+ * if (interaction.isChatInputCommand()) {
16
+ * await executeCommand(interaction);
17
+ * }
18
+ * }
19
+ * }
20
+ *
21
+ * const handler = new MyBotHandler(client);
22
+ * handler.setupErrorHandlers();
23
+ * await handler.setupOtherHandlers();
24
+ * ```
25
+ */
26
+ export declare abstract class DiscordHandler {
27
+ /** The Discord.js client instance */
28
+ client: Client;
29
+ /** The Prisma database client instance, Optional */
30
+ private prisma?;
31
+ /** The TToolboxLogger instance used for logging */
32
+ private logger;
33
+ /**
34
+ * Creates a new DiscordHandler instance.
35
+ *
36
+ * @param client - The Discord.js client instance
37
+ * @param prisma - The Prisma database client instance
38
+ */
39
+ constructor(client: Client, logger: TToolboxLogger, prisma?: PrismaClient);
40
+ /**
41
+ * Sets up process-level error handlers and graceful shutdown handlers.
42
+ *
43
+ * This method registers handlers for:
44
+ * - Uncaught exceptions (exits process)
45
+ * - Unhandled promise rejections (logs warning)
46
+ * - SIGINT and SIGTERM signals (graceful shutdown)
47
+ *
48
+ * Should be called once during bot initialization.
49
+ */
50
+ setupErrorHandlers(): void;
51
+ /**
52
+ * Handles graceful shutdown of the bot.
53
+ *
54
+ * Properly cleans up resources by:
55
+ * 1. Destroying the Discord client connection
56
+ * 2. Disconnecting from the Prisma database
57
+ * 3. Exiting the process
58
+ *
59
+ * @param signal - The signal that triggered the shutdown (e.g., 'SIGINT', 'SIGTERM')
60
+ * @private
61
+ */
62
+ private gracefulShutdown;
63
+ /**
64
+ * Automatically sets up Discord event handlers for all implemented handler methods.
65
+ *
66
+ * This method checks which handler methods have been overridden in the child class
67
+ * and only registers event listeners for those handlers. This allows child classes
68
+ * to selectively implement only the events they need.
69
+ *
70
+ * Should be called once during bot initialization, after setupErrorHandlers().
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * const handler = new MyBotHandler(client, prisma);
75
+ * handler.setupErrorHandlers();
76
+ * await handler.setupOtherHandlers();
77
+ * ```
78
+ */
79
+ setupOtherHandlers(): Promise<void>;
80
+ /**
81
+ * Sets up the Discord MessageCreate event listener.
82
+ * @private
83
+ */
84
+ private setupMessageCreateHandler;
85
+ /**
86
+ * Handles the MessageCreate event.
87
+ *
88
+ * Override this method to implement custom message handling logic.
89
+ * This is called whenever a message is created in any channel the bot can see.
90
+ *
91
+ * @param message - The message that was created
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * protected async handleMessageCreation(message: Message) {
96
+ * if (message.content.startsWith('!ping')) {
97
+ * await message.reply('Pong!');
98
+ * }
99
+ * }
100
+ * ```
101
+ */
102
+ protected abstract handleMessageCreation(message: Message<boolean>): Promise<void>;
103
+ /**
104
+ * Sets up the Discord InteractionCreate event listener.
105
+ * @private
106
+ */
107
+ private setupInteractionCreateHandler;
108
+ /**
109
+ * Handles the InteractionCreate event.
110
+ *
111
+ * Override this method to implement custom interaction handling logic.
112
+ * This is called for all types of interactions: slash commands, buttons,
113
+ * select menus, modals, etc.
114
+ *
115
+ * @param interaction - The interaction that was created
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * protected async handleInteractionCreation(interaction: Interaction) {
120
+ * if (interaction.isChatInputCommand()) {
121
+ * await commandManager.executeCommand(interaction.commandName, interaction, client);
122
+ * }
123
+ * if (interaction.isButton()) {
124
+ * await handleButtonClick(interaction);
125
+ * }
126
+ * }
127
+ * ```
128
+ */
129
+ protected abstract handleInteractionCreation(interaction: Interaction): Promise<void>;
130
+ /**
131
+ * Sets up the Discord MessageReactionAdd event listener.
132
+ * @private
133
+ */
134
+ private setupMessageReactionAddHandler;
135
+ /**
136
+ * Handles the MessageReactionAdd event.
137
+ *
138
+ * Override this method to implement custom reaction handling logic.
139
+ * This is called whenever a user adds a reaction to a message.
140
+ *
141
+ * @param reaction - The reaction that was added (may be partial)
142
+ * @param user - The user who added the reaction (may be partial)
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * protected async handleReactionAdded(reaction: MessageReaction, user: User) {
147
+ * if (reaction.emoji.name === '⭐' && reaction.count >= 5) {
148
+ * await addToStarboard(reaction.message);
149
+ * }
150
+ * }
151
+ * ```
152
+ */
153
+ protected abstract handleReactionAdded(reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser): Promise<void>;
154
+ /**
155
+ * Sets up the Discord MessageReactionRemove event listener.
156
+ * @private
157
+ */
158
+ private setupMessageReactionRemoveHandler;
159
+ /**
160
+ * Handles the MessageReactionRemove event.
161
+ *
162
+ * Override this method to implement custom reaction removal handling logic.
163
+ * This is called whenever a user removes a reaction from a message.
164
+ *
165
+ * @param reaction - The reaction that was removed (may be partial)
166
+ * @param user - The user who removed the reaction (may be partial)
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * protected async handleReactionRemoval(reaction: MessageReaction, user: User) {
171
+ * if (reaction.emoji.name === '⭐' && reaction.count < 5) {
172
+ * await removeFromStarboard(reaction.message);
173
+ * }
174
+ * }
175
+ * ```
176
+ */
177
+ protected abstract handleReactionRemoval(reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser): Promise<void>;
178
+ /**
179
+ * Sets up the Discord MessageDelete event listener.
180
+ * @private
181
+ */
182
+ private setupMessageDeleteHandler;
183
+ /**
184
+ * Handles the MessageDelete event.
185
+ *
186
+ * Override this method to implement custom message deletion handling logic.
187
+ * This is called whenever a message is deleted.
188
+ *
189
+ * @param message - The message that was deleted (may be partial)
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * protected async handleMessageDeletion(message: Message) {
194
+ * await logDeletedMessage(message);
195
+ * }
196
+ * ```
197
+ */
198
+ protected abstract handleMessageDeletion(message: OmitPartialGroupDMChannel<Message<boolean> | PartialMessage>): Promise<void>;
199
+ /**
200
+ * Register a custom event handler for any Discord.js event not covered by the base handlers.
201
+ *
202
+ * This allows you to listen to any Discord.js event beyond the standard ones
203
+ * (MessageCreate, InteractionCreate, etc.) that are built into the base class.
204
+ *
205
+ * @param event - The Discord.js event name (e.g., Events.GuildMemberAdd)
206
+ * @param handler - The handler function to call when the event fires
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * this.registerCustomHandler(Events.GuildMemberAdd, async (member) => {
211
+ * await sendWelcomeMessage(member);
212
+ * });
213
+ * ```
214
+ */
215
+ protected registerCustomHandler<K extends keyof ClientEvents>(event: K, handler: (...args: ClientEvents[K]) => Awaitable<void>): void;
216
+ /**
217
+ * Register multiple custom event handlers at once.
218
+ *
219
+ * Convenience method for registering several custom event handlers in a single call.
220
+ *
221
+ * @param handlers - Array of event/handler pairs to register
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * this.registerCustomHandlers([
226
+ * {
227
+ * event: Events.GuildMemberAdd,
228
+ * handler: async (member) => await sendWelcomeMessage(member),
229
+ * },
230
+ * {
231
+ * event: Events.VoiceStateUpdate,
232
+ * handler: async (oldState, newState) => await handleVoiceChange(oldState, newState),
233
+ * },
234
+ * ]);
235
+ * ```
236
+ */
237
+ protected registerCustomHandlers(handlers: Array<{
238
+ event: keyof ClientEvents;
239
+ handler: (...args: any[]) => Awaitable<void>;
240
+ }>): void;
241
+ }
@@ -0,0 +1,222 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable @typescript-eslint/require-await */
3
+ /* eslint-disable @typescript-eslint/no-misused-promises */
4
+ import { Events, } from 'discord.js';
5
+ /**
6
+ * Abstract base class for Discord bot event handling.
7
+ *
8
+ * Provides a structured way to handle Discord.js events with automatic setup,
9
+ * error handling, and graceful shutdown capabilities. Child classes should
10
+ * override the protected handler methods for the events they want to handle.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * class MyBotHandler extends DiscordHandler {
15
+ * protected async handleInteractionCreation(interaction: Interaction) {
16
+ * if (interaction.isChatInputCommand()) {
17
+ * await executeCommand(interaction);
18
+ * }
19
+ * }
20
+ * }
21
+ *
22
+ * const handler = new MyBotHandler(client);
23
+ * handler.setupErrorHandlers();
24
+ * await handler.setupOtherHandlers();
25
+ * ```
26
+ */
27
+ export class DiscordHandler {
28
+ /**
29
+ * Creates a new DiscordHandler instance.
30
+ *
31
+ * @param client - The Discord.js client instance
32
+ * @param prisma - The Prisma database client instance
33
+ */
34
+ constructor(client, logger, prisma) {
35
+ this.client = client;
36
+ this.prisma = prisma;
37
+ this.logger = logger;
38
+ }
39
+ /**
40
+ * Sets up process-level error handlers and graceful shutdown handlers.
41
+ *
42
+ * This method registers handlers for:
43
+ * - Uncaught exceptions (exits process)
44
+ * - Unhandled promise rejections (logs warning)
45
+ * - SIGINT and SIGTERM signals (graceful shutdown)
46
+ *
47
+ * Should be called once during bot initialization.
48
+ */
49
+ setupErrorHandlers() {
50
+ process.on('uncaughtException', (err) => {
51
+ this.logger.error(`Uncaught Exception: ${err}`, 'errorhandler', true);
52
+ process.exit(1);
53
+ });
54
+ process.on('unhandledRejection', (reason) => {
55
+ this.logger.warn(`Unhandled Rejection: ${reason}`, 'errorhandler');
56
+ });
57
+ process.on('SIGINT', () => void this.gracefulShutdown('SIGINT'));
58
+ process.on('SIGTERM', () => void this.gracefulShutdown('SIGTERM'));
59
+ this.logger.info('Error handlers set up.', 'startup');
60
+ }
61
+ /**
62
+ * Handles graceful shutdown of the bot.
63
+ *
64
+ * Properly cleans up resources by:
65
+ * 1. Destroying the Discord client connection
66
+ * 2. Disconnecting from the Prisma database
67
+ * 3. Exiting the process
68
+ *
69
+ * @param signal - The signal that triggered the shutdown (e.g., 'SIGINT', 'SIGTERM')
70
+ * @private
71
+ */
72
+ async gracefulShutdown(signal) {
73
+ const scope = 'shutdown';
74
+ this.logger.info(`${signal} received. Cleaning up...`, scope);
75
+ try {
76
+ if (this.client.isReady()) {
77
+ this.logger.info(`Destroying Discord client...`, scope);
78
+ await this.client.destroy();
79
+ }
80
+ }
81
+ catch (err) {
82
+ this.logger.error(`Failed to destroy Discord client: ${err}`, scope, true);
83
+ }
84
+ if (this.prisma) {
85
+ try {
86
+ this.logger.info(`Disconnecting Prisma...`, scope);
87
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
88
+ await this.prisma.$disconnect();
89
+ }
90
+ catch (err) {
91
+ this.logger.error(`Failed to disconnect Prisma: ${err}`, scope, true);
92
+ }
93
+ }
94
+ process.exit(0);
95
+ }
96
+ /**
97
+ * Automatically sets up Discord event handlers for all implemented handler methods.
98
+ *
99
+ * This method checks which handler methods have been overridden in the child class
100
+ * and only registers event listeners for those handlers. This allows child classes
101
+ * to selectively implement only the events they need.
102
+ *
103
+ * Should be called once during bot initialization, after setupErrorHandlers().
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const handler = new MyBotHandler(client, prisma);
108
+ * handler.setupErrorHandlers();
109
+ * await handler.setupOtherHandlers();
110
+ * ```
111
+ */
112
+ async setupOtherHandlers() {
113
+ const baseClass = Object.getPrototypeOf(this.constructor);
114
+ if (this.handleMessageCreation !== baseClass.prototype.handleMessageCreation) {
115
+ await this.setupMessageCreateHandler();
116
+ }
117
+ if (this.handleInteractionCreation !==
118
+ baseClass.prototype.handleInteractionCreation) {
119
+ await this.setupInteractionCreateHandler();
120
+ }
121
+ if (this.handleReactionAdded !== baseClass.prototype.handleReactionAdded) {
122
+ await this.setupMessageReactionAddHandler();
123
+ }
124
+ if (this.handleReactionRemoval !== baseClass.prototype.handleReactionRemoval) {
125
+ await this.setupMessageReactionRemoveHandler();
126
+ }
127
+ if (this.handleMessageDeletion !== baseClass.prototype.handleMessageDeletion) {
128
+ await this.setupMessageDeleteHandler();
129
+ }
130
+ }
131
+ /**
132
+ * Sets up the Discord MessageCreate event listener.
133
+ * @private
134
+ */
135
+ async setupMessageCreateHandler() {
136
+ this.client.on(Events.MessageCreate, async (message) => {
137
+ await this.handleMessageCreation(message);
138
+ });
139
+ }
140
+ /**
141
+ * Sets up the Discord InteractionCreate event listener.
142
+ * @private
143
+ */
144
+ async setupInteractionCreateHandler() {
145
+ this.client.on(Events.InteractionCreate, async (interaction) => {
146
+ await this.handleInteractionCreation(interaction);
147
+ });
148
+ }
149
+ /**
150
+ * Sets up the Discord MessageReactionAdd event listener.
151
+ * @private
152
+ */
153
+ async setupMessageReactionAddHandler() {
154
+ this.client.on(Events.MessageReactionAdd, async (reaction, user) => {
155
+ await this.handleReactionAdded(reaction, user);
156
+ });
157
+ }
158
+ /**
159
+ * Sets up the Discord MessageReactionRemove event listener.
160
+ * @private
161
+ */
162
+ async setupMessageReactionRemoveHandler() {
163
+ this.client.on(Events.MessageReactionRemove, async (reaction, user) => {
164
+ await this.handleReactionRemoval(reaction, user);
165
+ });
166
+ }
167
+ /**
168
+ * Sets up the Discord MessageDelete event listener.
169
+ * @private
170
+ */
171
+ async setupMessageDeleteHandler() {
172
+ this.client.on(Events.MessageDelete, async (message) => {
173
+ await this.handleMessageDeletion(message);
174
+ });
175
+ }
176
+ /**
177
+ * Register a custom event handler for any Discord.js event not covered by the base handlers.
178
+ *
179
+ * This allows you to listen to any Discord.js event beyond the standard ones
180
+ * (MessageCreate, InteractionCreate, etc.) that are built into the base class.
181
+ *
182
+ * @param event - The Discord.js event name (e.g., Events.GuildMemberAdd)
183
+ * @param handler - The handler function to call when the event fires
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * this.registerCustomHandler(Events.GuildMemberAdd, async (member) => {
188
+ * await sendWelcomeMessage(member);
189
+ * });
190
+ * ```
191
+ */
192
+ registerCustomHandler(event, handler) {
193
+ this.client.on(event, handler);
194
+ this.logger.info(`Registered custom handler for event: ${event}`, 'handler');
195
+ }
196
+ /**
197
+ * Register multiple custom event handlers at once.
198
+ *
199
+ * Convenience method for registering several custom event handlers in a single call.
200
+ *
201
+ * @param handlers - Array of event/handler pairs to register
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * this.registerCustomHandlers([
206
+ * {
207
+ * event: Events.GuildMemberAdd,
208
+ * handler: async (member) => await sendWelcomeMessage(member),
209
+ * },
210
+ * {
211
+ * event: Events.VoiceStateUpdate,
212
+ * handler: async (oldState, newState) => await handleVoiceChange(oldState, newState),
213
+ * },
214
+ * ]);
215
+ * ```
216
+ */
217
+ registerCustomHandlers(handlers) {
218
+ for (const { event, handler } of handlers) {
219
+ this.registerCustomHandler(event, handler);
220
+ }
221
+ }
222
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Custom error for interaction failures
3
+ */
4
+ export declare class InteractionError extends Error {
5
+ readonly interactionId: string;
6
+ readonly reason: 'expired' | 'failed';
7
+ constructor(message: string, interactionId: string, reason: 'expired' | 'failed');
8
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Custom error for interaction failures
3
+ */
4
+ export class InteractionError extends Error {
5
+ constructor(message, interactionId, reason) {
6
+ super(message);
7
+ this.interactionId = interactionId;
8
+ this.reason = reason;
9
+ this.name = 'InteractionError';
10
+ }
11
+ }
@@ -0,0 +1,154 @@
1
+ import { ModalBuilder, ModalSubmitInteraction } from 'discord.js';
2
+ import { Modal } from '../types/modal.js';
3
+ /**
4
+ * Manages modal creation, registration, and submission handling.
5
+ *
6
+ * Provides a centralized way to create and track modals throughout the bot.
7
+ * Modals can be registered with their submission handlers and automatically
8
+ * cleaned up after use if marked as ephemeral.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const modalManager = new ModalManager();
13
+ *
14
+ * // Create and register a modal
15
+ * const modal = modalManager.buildAndRegister({
16
+ * id: 'feedback-modal',
17
+ * title: 'Submit Feedback',
18
+ * ephemeral: true,
19
+ * fields: [
20
+ * {
21
+ * customId: 'message',
22
+ * name: 'Your Feedback',
23
+ * style: TextInputStyle.Paragraph,
24
+ * required: true,
25
+ * }
26
+ * ],
27
+ * onSubmit: async (interaction) => {
28
+ * const feedback = interaction.fields.getTextInputValue('message');
29
+ * await interaction.reply('Thank you for your feedback!');
30
+ * }
31
+ * });
32
+ *
33
+ * await interaction.showModal(modal);
34
+ * ```
35
+ */
36
+ export declare class ModalManager {
37
+ private modals;
38
+ /**
39
+ * Builds a Discord ModalBuilder and registers the modal for submission handling.
40
+ *
41
+ * Creates a modal with the specified fields and registers it so that when
42
+ * a user submits it, the onSubmit handler will be called.
43
+ *
44
+ * @param data - The modal configuration including fields and submission handler
45
+ * @returns A ModalBuilder ready to be shown to the user
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const modal = modalManager.buildAndRegister({
50
+ * id: 'edit-reminder',
51
+ * title: 'Edit Reminder',
52
+ * ephemeral: true,
53
+ * fields: [
54
+ * {
55
+ * customId: 'message',
56
+ * name: 'Reminder Message',
57
+ * style: TextInputStyle.Short,
58
+ * required: true,
59
+ * value: existingMessage,
60
+ * }
61
+ * ],
62
+ * onSubmit: async (interaction) => {
63
+ * const newMessage = interaction.fields.getTextInputValue('message');
64
+ * await updateReminder(id, newMessage);
65
+ * }
66
+ * });
67
+ *
68
+ * await buttonInteraction.showModal(modal);
69
+ * ```
70
+ */
71
+ buildAndRegister(data: Modal): ModalBuilder;
72
+ /**
73
+ * Retrieves a registered modal by its ID.
74
+ *
75
+ * Supports dynamic IDs - if the exact ID isn't found, attempts to match
76
+ * using the base ID (before the first colon). This allows for modals with
77
+ * dynamic suffixes like "edit-reminder:123".
78
+ *
79
+ * @param id - The modal ID to look up
80
+ * @returns The modal configuration, or undefined if not found
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Register with base ID
85
+ * modalManager.buildAndRegister({ id: 'edit-reminder', ... });
86
+ *
87
+ * // Can retrieve with dynamic ID
88
+ * const modal = modalManager.get('edit-reminder:123'); // Works!
89
+ * ```
90
+ */
91
+ get(id: string): Modal | undefined;
92
+ /**
93
+ * Removes a modal from the registry.
94
+ *
95
+ * Useful for cleaning up ephemeral modals after they've been submitted,
96
+ * or for unregistering modals that are no longer needed.
97
+ *
98
+ * @param id - The modal ID to remove
99
+ * @returns true if the modal was removed, false if it didn't exist
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * // Clean up after submission
104
+ * await modalManager.handleSubmit(interaction);
105
+ * modalManager.remove(interaction.customId); // If ephemeral
106
+ * ```
107
+ */
108
+ remove(id: string): boolean;
109
+ /**
110
+ * Handles a modal submission by calling the registered onSubmit handler.
111
+ *
112
+ * Looks up the modal by ID, calls its onSubmit handler, and automatically
113
+ * removes ephemeral modals from the registry after submission.
114
+ *
115
+ * @param interaction - The modal submit interaction
116
+ * @throws {Error} If no modal is found for the interaction's custom ID
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * // In your interaction handler
121
+ * client.on('interactionCreate', async (interaction) => {
122
+ * if (interaction.isModalSubmit()) {
123
+ * await modalManager.handleSubmit(interaction);
124
+ * }
125
+ * });
126
+ * ```
127
+ */
128
+ handleSubmit(interaction: ModalSubmitInteraction): Promise<void>;
129
+ /**
130
+ * Checks if a modal with the given ID is registered.
131
+ *
132
+ * @param id - The modal ID to check
133
+ * @returns true if the modal exists in the registry
134
+ */
135
+ has(id: string): boolean;
136
+ /**
137
+ * Clears all registered modals from the registry.
138
+ *
139
+ * Useful for cleanup during bot shutdown or for testing.
140
+ */
141
+ clear(): void;
142
+ /**
143
+ * Gets the total number of registered modals.
144
+ *
145
+ * @returns The count of modals in the registry
146
+ */
147
+ get size(): number;
148
+ /**
149
+ * Gets all registered modal IDs.
150
+ *
151
+ * @returns Array of modal IDs currently in the registry
152
+ */
153
+ getModalIds(): string[];
154
+ }