@minesa-org/mini-interaction 0.1.13 → 0.2.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/builders/ActionRowBuilder.d.ts +13 -15
- package/dist/builders/ActionRowBuilder.js +14 -16
- package/dist/clients/MiniInteraction.d.ts +88 -31
- package/dist/clients/MiniInteraction.js +140 -7
- package/dist/index.d.ts +9 -9
- package/dist/index.js +5 -1
- package/dist/types/Commands.d.ts +7 -6
- package/dist/types/ComponentTypes.d.ts +4 -4
- package/dist/types/InteractionFlags.d.ts +6 -7
- package/dist/types/InteractionFlags.js +10 -12
- package/dist/utils/CommandInteractionOptions.d.ts +12 -1
- package/dist/utils/CommandInteractionOptions.js +42 -4
- package/dist/utils/ContextMenuInteraction.d.ts +3 -0
- package/dist/utils/ContextMenuInteraction.js +3 -0
- package/dist/utils/MessageComponentInteraction.d.ts +9 -0
- package/dist/utils/MessageComponentInteraction.js +9 -0
- package/dist/utils/ModalSubmitInteraction.d.ts +1 -0
- package/dist/utils/ModalSubmitInteraction.js +1 -0
- package/dist/utils/interactionMessageHelpers.d.ts +11 -4
- package/dist/utils/interactionMessageHelpers.js +13 -30
- package/package.json +1 -1
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import type { APIActionRowComponent } from "discord-api-types/v10";
|
|
2
2
|
import type { JSONEncodable } from "./shared.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ActionRowComponent } from "../types/ComponentTypes.js";
|
|
4
4
|
/** Values accepted when composing component action rows. */
|
|
5
|
-
export type ActionRowComponentLike<T extends
|
|
6
|
-
/** Builder for
|
|
7
|
-
export declare class ActionRowBuilder<T extends
|
|
5
|
+
export type ActionRowComponentLike<T extends ActionRowComponent> = JSONEncodable<T> | T;
|
|
6
|
+
/** Builder for creating Action Row components. */
|
|
7
|
+
export declare class ActionRowBuilder<T extends ActionRowComponent> implements JSONEncodable<APIActionRowComponent<T>> {
|
|
8
8
|
private components;
|
|
9
|
+
constructor(data?: Partial<APIActionRowComponent<T>>);
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Adds components to this action row.
|
|
12
|
+
*
|
|
13
|
+
* @param components - The components to add (can be builders or raw objects).
|
|
11
14
|
*/
|
|
12
|
-
|
|
15
|
+
addComponents(...components: (T | JSONEncodable<T>)[]): this;
|
|
13
16
|
/**
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Replaces the current action row contents.
|
|
19
|
-
*/
|
|
20
|
-
setComponents(components: Iterable<ActionRowComponentLike<T>>): this;
|
|
21
|
-
/**
|
|
22
|
-
* Serialises the builder into an API compatible action row payload.
|
|
17
|
+
* Sets the components for this action row, replacing any existing ones.
|
|
18
|
+
*
|
|
19
|
+
* @param components - The new components to set.
|
|
23
20
|
*/
|
|
21
|
+
setComponents(...components: (T | JSONEncodable<T>)[]): this;
|
|
24
22
|
toJSON(): APIActionRowComponent<T>;
|
|
25
23
|
}
|
|
@@ -1,35 +1,33 @@
|
|
|
1
1
|
import { ComponentType } from "discord-api-types/v10";
|
|
2
2
|
import { resolveJSONEncodable } from "./shared.js";
|
|
3
|
-
/** Builder for
|
|
3
|
+
/** Builder for creating Action Row components. */
|
|
4
4
|
export class ActionRowBuilder {
|
|
5
|
-
components;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/
|
|
9
|
-
constructor(components = []) {
|
|
10
|
-
this.components = Array.from(components);
|
|
5
|
+
components = [];
|
|
6
|
+
constructor(data = {}) {
|
|
7
|
+
this.components = [...(data.components ?? [])];
|
|
11
8
|
}
|
|
12
9
|
/**
|
|
13
|
-
*
|
|
10
|
+
* Adds components to this action row.
|
|
11
|
+
*
|
|
12
|
+
* @param components - The components to add (can be builders or raw objects).
|
|
14
13
|
*/
|
|
15
14
|
addComponents(...components) {
|
|
16
|
-
this.components.push(...components);
|
|
15
|
+
this.components.push(...components.map((c) => resolveJSONEncodable(c)));
|
|
17
16
|
return this;
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
20
|
-
*
|
|
19
|
+
* Sets the components for this action row, replacing any existing ones.
|
|
20
|
+
*
|
|
21
|
+
* @param components - The new components to set.
|
|
21
22
|
*/
|
|
22
|
-
setComponents(components) {
|
|
23
|
-
this.components =
|
|
23
|
+
setComponents(...components) {
|
|
24
|
+
this.components = components.map((c) => resolveJSONEncodable(c));
|
|
24
25
|
return this;
|
|
25
26
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Serialises the builder into an API compatible action row payload.
|
|
28
|
-
*/
|
|
29
27
|
toJSON() {
|
|
30
28
|
return {
|
|
31
29
|
type: ComponentType.ActionRow,
|
|
32
|
-
components: this.components
|
|
30
|
+
components: [...this.components],
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
33
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody } from "discord-api-types/v10";
|
|
3
|
-
import type {
|
|
3
|
+
import type { InteractionCommand } from "../types/Commands.js";
|
|
4
4
|
import { RoleConnectionMetadataTypes } from "../types/RoleConnectionMetadataTypes.js";
|
|
5
5
|
import { type MessageComponentInteraction, type ButtonInteraction, type StringSelectInteraction, type RoleSelectInteraction, type UserSelectInteraction, type ChannelSelectInteraction, type MentionableSelectInteraction } from "../utils/MessageComponentInteraction.js";
|
|
6
6
|
import { type ModalSubmitInteraction } from "../utils/ModalSubmitInteraction.js";
|
|
7
7
|
import { type OAuthConfig, type OAuthTokens, type DiscordUser } from "../oauth/DiscordOAuth.js";
|
|
8
8
|
/** Configuration parameters for the MiniInteraction client. */
|
|
9
|
-
export type
|
|
9
|
+
export type InteractionClientOptions = {
|
|
10
10
|
applicationId: string;
|
|
11
11
|
publicKey: string;
|
|
12
12
|
commandsDirectory?: string | false;
|
|
@@ -26,13 +26,13 @@ export type RoleConnectionMetadataField = {
|
|
|
26
26
|
/**
|
|
27
27
|
* HTTP request information needed to validate and handle Discord interaction payloads.
|
|
28
28
|
*/
|
|
29
|
-
export type
|
|
29
|
+
export type InteractionRequest = {
|
|
30
30
|
body: string | Uint8Array;
|
|
31
31
|
signature?: string;
|
|
32
32
|
timestamp?: string;
|
|
33
33
|
};
|
|
34
34
|
/** Result payload returned by request handlers when processing an interaction. */
|
|
35
|
-
export type
|
|
35
|
+
export type InteractionHandlerResult = {
|
|
36
36
|
status: number;
|
|
37
37
|
body: APIInteractionResponse | {
|
|
38
38
|
error: string;
|
|
@@ -46,25 +46,36 @@ export type InteractionTimeoutConfig = {
|
|
|
46
46
|
enableTimeoutWarnings?: boolean;
|
|
47
47
|
/** Whether to force deferReply for slow operations (default: true) */
|
|
48
48
|
autoDeferSlowOperations?: boolean;
|
|
49
|
+
/** Whether to enable debug logging for interaction responses (default: false) */
|
|
50
|
+
enableResponseDebugLogging?: boolean;
|
|
51
|
+
};
|
|
52
|
+
/** Enhanced timeout configuration with response acknowledgment settings. */
|
|
53
|
+
export type InteractionTimeoutConfigV2 = InteractionTimeoutConfig & {
|
|
54
|
+
/** Time to wait for Discord acknowledgment after sending response (default: 500ms) */
|
|
55
|
+
responseAcknowledgmentTimeout?: number;
|
|
56
|
+
/** Maximum retries for response acknowledgment (default: 2) */
|
|
57
|
+
responseAcknowledgmentRetries?: number;
|
|
58
|
+
/** Delay between acknowledgment retries (default: 100ms) */
|
|
59
|
+
responseAcknowledgmentRetryDelay?: number;
|
|
49
60
|
};
|
|
50
61
|
/** Handler signature invoked for Discord button interactions. */
|
|
51
|
-
export type
|
|
62
|
+
export type ButtonComponentHandler = (interaction: ButtonInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
52
63
|
/** Handler signature invoked for Discord string select menu interactions. */
|
|
53
|
-
export type
|
|
64
|
+
export type StringSelectComponentHandler = (interaction: StringSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
54
65
|
/** Handler signature invoked for Discord role select menu interactions. */
|
|
55
|
-
export type
|
|
66
|
+
export type RoleSelectComponentHandler = (interaction: RoleSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
56
67
|
/** Handler signature invoked for Discord user select menu interactions. */
|
|
57
|
-
export type
|
|
68
|
+
export type UserSelectComponentHandler = (interaction: UserSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
58
69
|
/** Handler signature invoked for Discord channel select menu interactions. */
|
|
59
|
-
export type
|
|
70
|
+
export type ChannelSelectComponentHandler = (interaction: ChannelSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
60
71
|
/** Handler signature invoked for Discord mentionable select menu interactions. */
|
|
61
|
-
export type
|
|
72
|
+
export type MentionableSelectComponentHandler = (interaction: MentionableSelectInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
62
73
|
/** Handler signature invoked for Discord message component interactions (generic). */
|
|
63
|
-
export type
|
|
74
|
+
export type ComponentHandler = (interaction: MessageComponentInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
64
75
|
/** Handler signature invoked for Discord modal submit interactions. */
|
|
65
|
-
export type
|
|
76
|
+
export type ModalHandler = (interaction: ModalSubmitInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
66
77
|
/** Unified handler signature that accepts any component or modal interaction. */
|
|
67
|
-
export type
|
|
78
|
+
export type InteractionHandler = ButtonComponentHandler | StringSelectComponentHandler | RoleSelectComponentHandler | UserSelectComponentHandler | ChannelSelectComponentHandler | MentionableSelectComponentHandler | ComponentHandler | ModalHandler;
|
|
68
79
|
type CommandDataPayload = RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody;
|
|
69
80
|
/**
|
|
70
81
|
* Structure describing a component or modal handler mapped to a custom id.
|
|
@@ -73,19 +84,19 @@ type CommandDataPayload = RESTPostAPIChatInputApplicationCommandsJSONBody | REST
|
|
|
73
84
|
* - Other files are treated as component handlers
|
|
74
85
|
* You can use this type for both - the system will figure out which one it is.
|
|
75
86
|
*/
|
|
76
|
-
export type
|
|
87
|
+
export type ComponentCommand = {
|
|
77
88
|
customId: string;
|
|
78
|
-
handler:
|
|
89
|
+
handler: InteractionHandler;
|
|
79
90
|
};
|
|
80
91
|
/** Structure describing a modal handler mapped to a custom id. */
|
|
81
|
-
export type
|
|
92
|
+
export type ModalCommand = {
|
|
82
93
|
customId: string;
|
|
83
|
-
handler:
|
|
94
|
+
handler: ModalHandler;
|
|
84
95
|
};
|
|
85
96
|
/** Node.js HTTP handler compatible with frameworks like Express or Next.js API routes. */
|
|
86
|
-
export type
|
|
97
|
+
export type InteractionNodeHandler = (request: IncomingMessage, response: ServerResponse) => void;
|
|
87
98
|
/** Web Fetch API compatible request handler for platforms such as Cloudflare Workers. */
|
|
88
|
-
export type
|
|
99
|
+
export type InteractionFetchHandler = (request: Request) => Promise<Response>;
|
|
89
100
|
/** Context passed to OAuth success handlers and templates. */
|
|
90
101
|
export type DiscordOAuthAuthorizeContext = {
|
|
91
102
|
tokens: OAuthTokens;
|
|
@@ -162,6 +173,7 @@ export declare class MiniInteraction {
|
|
|
162
173
|
private readonly componentHandlers;
|
|
163
174
|
private readonly modalHandlers;
|
|
164
175
|
private readonly htmlTemplateCache;
|
|
176
|
+
private readonly interactionStates;
|
|
165
177
|
private commandsLoaded;
|
|
166
178
|
private loadCommandsPromise;
|
|
167
179
|
private componentsLoaded;
|
|
@@ -171,7 +183,52 @@ export declare class MiniInteraction {
|
|
|
171
183
|
/**
|
|
172
184
|
* Creates a new MiniInteraction client with optional command auto-loading and custom runtime hooks.
|
|
173
185
|
*/
|
|
174
|
-
constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, utilsDirectory, fetchImplementation, verifyKeyImplementation, timeoutConfig, }:
|
|
186
|
+
constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, utilsDirectory, fetchImplementation, verifyKeyImplementation, timeoutConfig, }: InteractionClientOptions);
|
|
187
|
+
/**
|
|
188
|
+
* Tracks the state of an interaction to prevent race conditions and double responses.
|
|
189
|
+
*/
|
|
190
|
+
private trackInteractionState;
|
|
191
|
+
/**
|
|
192
|
+
* Checks if an interaction can still respond (not expired and not already responded).
|
|
193
|
+
*/
|
|
194
|
+
private canRespond;
|
|
195
|
+
/**
|
|
196
|
+
* Logs response timing and acknowledgment for debugging.
|
|
197
|
+
*/
|
|
198
|
+
private logResponseTiming;
|
|
199
|
+
/**
|
|
200
|
+
* Simulates waiting for Discord acknowledgment to help debug timing issues.
|
|
201
|
+
* Note: This is a best-effort simulation since actual acknowledgment happens at the HTTP level.
|
|
202
|
+
*/
|
|
203
|
+
private simulateAcknowledgmentWait;
|
|
204
|
+
/**
|
|
205
|
+
* Enables or disables debug logging for interaction responses and timing.
|
|
206
|
+
* Useful for troubleshooting "didn't respond in time" errors.
|
|
207
|
+
*
|
|
208
|
+
* @param enabled - Whether to enable debug logging
|
|
209
|
+
*/
|
|
210
|
+
setResponseDebugLogging(enabled: boolean): void;
|
|
211
|
+
/**
|
|
212
|
+
* Gets the current state of an interaction.
|
|
213
|
+
*/
|
|
214
|
+
private getInteractionState;
|
|
215
|
+
/**
|
|
216
|
+
* Gets the current state of an interaction for debugging purposes.
|
|
217
|
+
*
|
|
218
|
+
* @param interactionId - The interaction ID to check
|
|
219
|
+
* @returns The current interaction state or null if not found
|
|
220
|
+
*/
|
|
221
|
+
getInteractionStateInfo(interactionId: string): {
|
|
222
|
+
state: "pending" | "deferred" | "responded" | "expired";
|
|
223
|
+
timestamp: number;
|
|
224
|
+
token: string;
|
|
225
|
+
responseCount: number;
|
|
226
|
+
} | null;
|
|
227
|
+
/**
|
|
228
|
+
* Clears expired interaction states to prevent memory leaks.
|
|
229
|
+
* Call this periodically to clean up old interaction data.
|
|
230
|
+
*/
|
|
231
|
+
cleanupExpiredInteractions(): number;
|
|
175
232
|
private normalizeCommandData;
|
|
176
233
|
private registerCommand;
|
|
177
234
|
/**
|
|
@@ -179,37 +236,37 @@ export declare class MiniInteraction {
|
|
|
179
236
|
*
|
|
180
237
|
* @param command - The command definition to register.
|
|
181
238
|
*/
|
|
182
|
-
useCommand(command:
|
|
239
|
+
useCommand(command: InteractionCommand): this;
|
|
183
240
|
/**
|
|
184
241
|
* Registers multiple command handlers with the client.
|
|
185
242
|
*
|
|
186
243
|
* @param commands - The command definitions to register.
|
|
187
244
|
*/
|
|
188
|
-
useCommands(commands:
|
|
245
|
+
useCommands(commands: InteractionCommand[]): this;
|
|
189
246
|
/**
|
|
190
247
|
* Registers a single component handler mapped to a custom identifier.
|
|
191
248
|
*
|
|
192
249
|
* @param component - The component definition to register.
|
|
193
250
|
*/
|
|
194
|
-
useComponent(component:
|
|
251
|
+
useComponent(component: ComponentCommand): this;
|
|
195
252
|
/**
|
|
196
253
|
* Registers multiple component handlers in a single call.
|
|
197
254
|
*
|
|
198
255
|
* @param components - The component definitions to register.
|
|
199
256
|
*/
|
|
200
|
-
useComponents(components:
|
|
257
|
+
useComponents(components: ComponentCommand[]): this;
|
|
201
258
|
/**
|
|
202
259
|
* Registers a single modal handler mapped to a custom identifier.
|
|
203
260
|
*
|
|
204
261
|
* @param modal - The modal definition to register.
|
|
205
262
|
*/
|
|
206
|
-
useModal(modal:
|
|
263
|
+
useModal(modal: ModalCommand): this;
|
|
207
264
|
/**
|
|
208
265
|
* Registers multiple modal handlers in a single call.
|
|
209
266
|
*
|
|
210
267
|
* @param modals - The modal definitions to register.
|
|
211
268
|
*/
|
|
212
|
-
useModals(modals:
|
|
269
|
+
useModals(modals: ModalCommand[]): this;
|
|
213
270
|
/**
|
|
214
271
|
* Recursively loads components from the configured components directory.
|
|
215
272
|
*
|
|
@@ -245,12 +302,12 @@ export declare class MiniInteraction {
|
|
|
245
302
|
*
|
|
246
303
|
* @param request - The request payload containing headers and body data.
|
|
247
304
|
*/
|
|
248
|
-
handleRequest(request:
|
|
305
|
+
handleRequest(request: InteractionRequest): Promise<InteractionHandlerResult>;
|
|
249
306
|
/**
|
|
250
307
|
* Creates a Node.js style request handler compatible with Express, Next.js API routes,
|
|
251
308
|
* Vercel serverless functions, and any runtime that expects a `(req, res)` listener.
|
|
252
309
|
*/
|
|
253
|
-
createNodeHandler():
|
|
310
|
+
createNodeHandler(): InteractionNodeHandler;
|
|
254
311
|
/**
|
|
255
312
|
* Generates a lightweight verification handler that serves an HTML page with an embedded OAuth link.
|
|
256
313
|
*
|
|
@@ -264,7 +321,7 @@ export declare class MiniInteraction {
|
|
|
264
321
|
* - `{{OAUTH_STATE}}` - HTML-escaped OAuth state value.
|
|
265
322
|
* - Custom placeholder name (from {@link DiscordOAuthVerificationPageOptions.placeholder}) and its `_RAW` variant.
|
|
266
323
|
*/
|
|
267
|
-
discordOAuthVerificationPage(options?: DiscordOAuthVerificationPageOptions):
|
|
324
|
+
discordOAuthVerificationPage(options?: DiscordOAuthVerificationPageOptions): InteractionNodeHandler;
|
|
268
325
|
/**
|
|
269
326
|
* Loads an HTML file and returns a success template that replaces useful placeholders.
|
|
270
327
|
*
|
|
@@ -300,11 +357,11 @@ export declare class MiniInteraction {
|
|
|
300
357
|
* This helper keeps the user-side implementation tiny while still exposing hooks for
|
|
301
358
|
* storing metadata or validating the OAuth state value.
|
|
302
359
|
*/
|
|
303
|
-
discordOAuthCallback(options: DiscordOAuthCallbackOptions):
|
|
360
|
+
discordOAuthCallback(options: DiscordOAuthCallbackOptions): InteractionNodeHandler;
|
|
304
361
|
/**
|
|
305
362
|
* Creates a Fetch API compatible handler for runtimes like Workers or Deno.
|
|
306
363
|
*/
|
|
307
|
-
createFetchHandler():
|
|
364
|
+
createFetchHandler(): InteractionFetchHandler;
|
|
308
365
|
/**
|
|
309
366
|
* Checks if the provided directory path exists on disk.
|
|
310
367
|
*/
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { ApplicationCommandType, InteractionResponseType, InteractionType, } from "discord-api-types/v10";
|
|
6
6
|
import { verifyKey } from "discord-interactions";
|
|
7
|
+
import { resolveJSONEncodable } from "../builders/shared.js";
|
|
7
8
|
import { DISCORD_BASE_URL } from "../utils/constants.js";
|
|
8
9
|
import { createCommandInteraction } from "../utils/CommandInteractionOptions.js";
|
|
9
10
|
import { createMessageComponentInteraction, } from "../utils/MessageComponentInteraction.js";
|
|
@@ -35,6 +36,7 @@ export class MiniInteraction {
|
|
|
35
36
|
componentHandlers = new Map();
|
|
36
37
|
modalHandlers = new Map();
|
|
37
38
|
htmlTemplateCache = new Map();
|
|
39
|
+
interactionStates = new Map();
|
|
38
40
|
commandsLoaded = false;
|
|
39
41
|
loadCommandsPromise = null;
|
|
40
42
|
componentsLoaded = false;
|
|
@@ -75,15 +77,131 @@ export class MiniInteraction {
|
|
|
75
77
|
initialResponseTimeout: 2800, // Leave 200ms buffer before Discord's 3s limit
|
|
76
78
|
enableTimeoutWarnings: true,
|
|
77
79
|
autoDeferSlowOperations: true,
|
|
80
|
+
enableResponseDebugLogging: false,
|
|
81
|
+
responseAcknowledgmentTimeout: 500, // 500ms to wait for acknowledgment
|
|
82
|
+
responseAcknowledgmentRetries: 2,
|
|
83
|
+
responseAcknowledgmentRetryDelay: 100,
|
|
78
84
|
...timeoutConfig,
|
|
79
85
|
};
|
|
80
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Tracks the state of an interaction to prevent race conditions and double responses.
|
|
89
|
+
*/
|
|
90
|
+
trackInteractionState(interactionId, token, state) {
|
|
91
|
+
const existing = this.interactionStates.get(interactionId);
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
this.interactionStates.set(interactionId, {
|
|
94
|
+
state,
|
|
95
|
+
timestamp: now,
|
|
96
|
+
token,
|
|
97
|
+
responseCount: existing ? existing.responseCount + 1 : 1,
|
|
98
|
+
});
|
|
99
|
+
if (this.timeoutConfig.enableResponseDebugLogging) {
|
|
100
|
+
console.log(`[MiniInteraction:DEBUG] Interaction ${interactionId} state: ${state} (${existing ? existing.responseCount + 1 : 1} responses)`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Checks if an interaction can still respond (not expired and not already responded).
|
|
105
|
+
*/
|
|
106
|
+
canRespond(interactionId) {
|
|
107
|
+
const state = this.getInteractionState(interactionId);
|
|
108
|
+
if (!state)
|
|
109
|
+
return true; // New interaction
|
|
110
|
+
// Check if expired (15 minutes)
|
|
111
|
+
if (Date.now() - state.timestamp > 900000) {
|
|
112
|
+
this.trackInteractionState(interactionId, state.token, 'expired');
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
// Check if already responded
|
|
116
|
+
if (state.state === 'responded') {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Logs response timing and acknowledgment for debugging.
|
|
123
|
+
*/
|
|
124
|
+
logResponseTiming(interactionId, operation, startTime, success) {
|
|
125
|
+
if (!this.timeoutConfig.enableResponseDebugLogging)
|
|
126
|
+
return;
|
|
127
|
+
const elapsed = Date.now() - startTime;
|
|
128
|
+
const status = success ? 'SUCCESS' : 'FAILED';
|
|
129
|
+
console.log(`[MiniInteraction:DEBUG] ${operation} for interaction ${interactionId}: ${status} (${elapsed}ms)`);
|
|
130
|
+
if (success && elapsed > 2000) {
|
|
131
|
+
console.warn(`[MiniInteraction:WARN] ${operation} took ${elapsed}ms - consider using deferReply() for slow operations`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Simulates waiting for Discord acknowledgment to help debug timing issues.
|
|
136
|
+
* Note: This is a best-effort simulation since actual acknowledgment happens at the HTTP level.
|
|
137
|
+
*/
|
|
138
|
+
async simulateAcknowledgmentWait(interactionId, operation) {
|
|
139
|
+
if (!this.timeoutConfig.enableResponseDebugLogging)
|
|
140
|
+
return;
|
|
141
|
+
const timeout = this.timeoutConfig.responseAcknowledgmentTimeout || 500;
|
|
142
|
+
const retries = this.timeoutConfig.responseAcknowledgmentRetries || 2;
|
|
143
|
+
const retryDelay = this.timeoutConfig.responseAcknowledgmentRetryDelay || 100;
|
|
144
|
+
console.log(`[MiniInteraction:DEBUG] Waiting for acknowledgment of ${operation} for interaction ${interactionId}...`);
|
|
145
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
146
|
+
await new Promise(resolve => setTimeout(resolve, attempt === 0 ? timeout : retryDelay));
|
|
147
|
+
// In a real implementation, this would verify Discord actually received the response
|
|
148
|
+
// For now, we just simulate the wait time
|
|
149
|
+
const state = this.getInteractionState(interactionId);
|
|
150
|
+
if (state?.state === 'responded' || state?.state === 'deferred') {
|
|
151
|
+
console.log(`[MiniInteraction:DEBUG] Acknowledgment confirmed for ${operation} after ${attempt + 1} attempts`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
console.warn(`[MiniInteraction:WARN] Acknowledgment timeout for ${operation} on interaction ${interactionId}`);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Enables or disables debug logging for interaction responses and timing.
|
|
159
|
+
* Useful for troubleshooting "didn't respond in time" errors.
|
|
160
|
+
*
|
|
161
|
+
* @param enabled - Whether to enable debug logging
|
|
162
|
+
*/
|
|
163
|
+
setResponseDebugLogging(enabled) {
|
|
164
|
+
this.timeoutConfig.enableResponseDebugLogging = enabled;
|
|
165
|
+
console.log(`[MiniInteraction] Response debug logging ${enabled ? 'enabled' : 'disabled'}`);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Gets the current state of an interaction.
|
|
169
|
+
*/
|
|
170
|
+
getInteractionState(interactionId) {
|
|
171
|
+
return this.interactionStates.get(interactionId);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Gets the current state of an interaction for debugging purposes.
|
|
175
|
+
*
|
|
176
|
+
* @param interactionId - The interaction ID to check
|
|
177
|
+
* @returns The current interaction state or null if not found
|
|
178
|
+
*/
|
|
179
|
+
getInteractionStateInfo(interactionId) {
|
|
180
|
+
const state = this.interactionStates.get(interactionId);
|
|
181
|
+
return state ? { ...state } : null; // Return a copy to prevent external modification
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Clears expired interaction states to prevent memory leaks.
|
|
185
|
+
* Call this periodically to clean up old interaction data.
|
|
186
|
+
*/
|
|
187
|
+
cleanupExpiredInteractions() {
|
|
188
|
+
const now = Date.now();
|
|
189
|
+
let cleaned = 0;
|
|
190
|
+
for (const [id, state] of this.interactionStates.entries()) {
|
|
191
|
+
// Remove interactions older than 15 minutes
|
|
192
|
+
if (now - state.timestamp > 900000) {
|
|
193
|
+
this.interactionStates.delete(id);
|
|
194
|
+
cleaned++;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (cleaned > 0) {
|
|
198
|
+
console.log(`[MiniInteraction] Cleaned up ${cleaned} expired interactions`);
|
|
199
|
+
}
|
|
200
|
+
return cleaned;
|
|
201
|
+
}
|
|
81
202
|
normalizeCommandData(data) {
|
|
82
203
|
if (typeof data === "object" && data !== null) {
|
|
83
|
-
|
|
84
|
-
if (typeof toJSON === "function") {
|
|
85
|
-
return toJSON.call(data);
|
|
86
|
-
}
|
|
204
|
+
return resolveJSONEncodable(data);
|
|
87
205
|
}
|
|
88
206
|
return data;
|
|
89
207
|
}
|
|
@@ -400,23 +518,34 @@ export class MiniInteraction {
|
|
|
400
518
|
};
|
|
401
519
|
}
|
|
402
520
|
if (interaction.type === InteractionType.ApplicationCommand) {
|
|
521
|
+
// Track interaction start
|
|
522
|
+
this.trackInteractionState(interaction.id, interaction.token, 'pending');
|
|
403
523
|
return this.handleApplicationCommand(interaction);
|
|
404
524
|
}
|
|
405
525
|
if (interaction.type === InteractionType.MessageComponent) {
|
|
526
|
+
// Track interaction start
|
|
527
|
+
this.trackInteractionState(interaction.id, interaction.token, 'pending');
|
|
406
528
|
return this.handleMessageComponent(interaction);
|
|
407
529
|
}
|
|
408
530
|
if (interaction.type === InteractionType.ModalSubmit) {
|
|
531
|
+
// Track interaction start
|
|
532
|
+
this.trackInteractionState(interaction.id, interaction.token, 'pending');
|
|
409
533
|
return this.handleModalSubmit(interaction);
|
|
410
534
|
}
|
|
411
|
-
// Check total processing time
|
|
535
|
+
// Check total processing time and log potential timeout issues
|
|
412
536
|
const totalProcessingTime = Date.now() - requestStartTime;
|
|
413
537
|
if (this.timeoutConfig.enableTimeoutWarnings &&
|
|
414
538
|
totalProcessingTime >
|
|
415
539
|
this.timeoutConfig.initialResponseTimeout * 0.9) {
|
|
416
|
-
console.warn(`[MiniInteraction]
|
|
540
|
+
console.warn(`[MiniInteraction] CRITICAL: Interaction processing took ${totalProcessingTime}ms ` +
|
|
417
541
|
`(${Math.round((totalProcessingTime / 3000) * 100)}% of Discord's 3-second limit). ` +
|
|
542
|
+
`This may cause "didn't respond in time" errors. ` +
|
|
418
543
|
`Consider optimizing or using deferReply() for slow operations.`);
|
|
419
544
|
}
|
|
545
|
+
// Log successful response timing for debugging
|
|
546
|
+
if (this.timeoutConfig.enableResponseDebugLogging) {
|
|
547
|
+
console.log(`[MiniInteraction:DEBUG] Request completed in ${totalProcessingTime}ms`);
|
|
548
|
+
}
|
|
420
549
|
return {
|
|
421
550
|
status: 400,
|
|
422
551
|
body: {
|
|
@@ -1195,7 +1324,11 @@ export class MiniInteraction {
|
|
|
1195
1324
|
// Check if it's a chat input (slash) command
|
|
1196
1325
|
if (commandInteraction.data.type ===
|
|
1197
1326
|
ApplicationCommandType.ChatInput) {
|
|
1198
|
-
const interactionWithHelpers = createCommandInteraction(commandInteraction
|
|
1327
|
+
const interactionWithHelpers = createCommandInteraction(commandInteraction, {
|
|
1328
|
+
canRespond: (id) => this.canRespond(id),
|
|
1329
|
+
trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
|
|
1330
|
+
logTiming: (id, op, start, success) => this.logResponseTiming(id, op, start, success),
|
|
1331
|
+
});
|
|
1199
1332
|
response = await command.handler(interactionWithHelpers);
|
|
1200
1333
|
resolvedResponse =
|
|
1201
1334
|
response ?? interactionWithHelpers.getResponse();
|
package/dist/index.d.ts
CHANGED
|
@@ -5,21 +5,21 @@ export { CommandBuilder, CommandContext, IntegrationType, } from "./commands/Com
|
|
|
5
5
|
export { UserCommandBuilder, MessageCommandBuilder, AppCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
|
|
6
6
|
export type { AttachmentOptionBuilder, ChannelOptionBuilder, MentionableOptionBuilder, NumberOptionBuilder, RoleOptionBuilder, StringOptionBuilder, SubcommandBuilder, SubcommandGroupBuilder, UserOptionBuilder, } from "./commands/CommandBuilder.js";
|
|
7
7
|
export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export type {
|
|
11
|
-
export type {
|
|
12
|
-
export type {
|
|
13
|
-
export
|
|
14
|
-
export
|
|
8
|
+
export { CommandInteraction, MentionableOption, ResolvedUserOption, } from "./utils/CommandInteractionOptions.js";
|
|
9
|
+
export { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction, } from "./utils/ContextMenuInteraction.js";
|
|
10
|
+
export type { InteractionFetchHandler, InteractionNodeHandler, InteractionHandlerResult, InteractionRequest, InteractionClientOptions, DiscordOAuthAuthorizeContext, DiscordOAuthCallbackOptions, DiscordOAuthCallbackTemplates, DiscordOAuthErrorTemplateContext, DiscordOAuthServerErrorTemplateContext, DiscordOAuthStateTemplateContext, DiscordOAuthSuccessTemplateContext, DiscordOAuthVerificationPageOptions, } from "./clients/MiniInteraction.js";
|
|
11
|
+
export type { InteractionCommand, SlashCommandHandler, UserCommandHandler, MessageCommandHandler, AppCommandHandler, CommandHandler, } from "./types/Commands.js";
|
|
12
|
+
export type { ComponentCommand, ButtonComponentHandler, StringSelectComponentHandler, RoleSelectComponentHandler, UserSelectComponentHandler, ChannelSelectComponentHandler, MentionableSelectComponentHandler, ComponentHandler, ModalCommand, ModalHandler, InteractionHandler, } from "./clients/MiniInteraction.js";
|
|
13
|
+
export { MessageComponentInteraction, ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
|
|
14
|
+
export { ModalSubmitInteraction } from "./utils/ModalSubmitInteraction.js";
|
|
15
15
|
export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
|
|
16
16
|
export { ChannelType } from "./types/ChannelType.js";
|
|
17
|
-
export { InteractionFollowUpFlags, InteractionReplyFlags, } from "./types/InteractionFlags.js";
|
|
17
|
+
export { InteractionFollowUpFlags, InteractionReplyFlags, InteractionFlags, } from "./types/InteractionFlags.js";
|
|
18
18
|
export { ButtonStyle } from "./types/ButtonStyle.js";
|
|
19
19
|
export { SeparatorSpacingSize } from "./types/SeparatorSpacingSize.js";
|
|
20
20
|
export { TextInputStyle } from "discord-api-types/v10";
|
|
21
21
|
export { MiniPermFlags } from "./types/PermissionFlags.js";
|
|
22
|
-
export type {
|
|
22
|
+
export type { ActionRowComponent, MessageActionRowComponent, } from "./types/ComponentTypes.js";
|
|
23
23
|
export * from "./builders/index.js";
|
|
24
24
|
export { MiniDataBuilder } from "./database/MiniDataBuilder.js";
|
|
25
25
|
export type { DataField } from "./database/MiniDataBuilder.js";
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,13 @@ export { MiniInteraction } from "./clients/MiniInteraction.js";
|
|
|
3
3
|
export { CommandBuilder, CommandContext, IntegrationType, } from "./commands/CommandBuilder.js";
|
|
4
4
|
export { UserCommandBuilder, MessageCommandBuilder, AppCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
|
|
5
5
|
export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
|
|
6
|
+
export { CommandInteraction, MentionableOption, ResolvedUserOption, } from "./utils/CommandInteractionOptions.js";
|
|
7
|
+
export { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction, } from "./utils/ContextMenuInteraction.js";
|
|
8
|
+
export { MessageComponentInteraction, ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
|
|
9
|
+
export { ModalSubmitInteraction } from "./utils/ModalSubmitInteraction.js";
|
|
6
10
|
export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
|
|
7
11
|
export { ChannelType } from "./types/ChannelType.js";
|
|
8
|
-
export { InteractionFollowUpFlags, InteractionReplyFlags, } from "./types/InteractionFlags.js";
|
|
12
|
+
export { InteractionFollowUpFlags, InteractionReplyFlags, InteractionFlags, } from "./types/InteractionFlags.js";
|
|
9
13
|
export { ButtonStyle } from "./types/ButtonStyle.js";
|
|
10
14
|
export { SeparatorSpacingSize } from "./types/SeparatorSpacingSize.js";
|
|
11
15
|
export { TextInputStyle } from "discord-api-types/v10";
|
package/dist/types/Commands.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody } from "discord-api-types/v10";
|
|
2
2
|
import type { CommandInteraction } from "../utils/CommandInteractionOptions.js";
|
|
3
3
|
import type { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction } from "../utils/ContextMenuInteraction.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { JSONEncodable } from "../builders/shared.js";
|
|
5
|
+
import type { ComponentCommand, ModalCommand } from "../clients/MiniInteraction.js";
|
|
5
6
|
import type { CommandBuilder } from "../commands/CommandBuilder.js";
|
|
6
7
|
import type { MessageCommandBuilder, UserCommandBuilder, AppCommandBuilder } from "../commands/ContextMenuCommandBuilder.js";
|
|
7
8
|
/** Handler signature for slash command executions within MiniInteraction. */
|
|
@@ -15,19 +16,19 @@ export type AppCommandHandler = (interaction: AppCommandInteraction) => Promise<
|
|
|
15
16
|
/** Union of all command handler types. */
|
|
16
17
|
export type CommandHandler = SlashCommandHandler | UserCommandHandler | MessageCommandHandler | AppCommandHandler;
|
|
17
18
|
/** Structure representing a slash command definition and its runtime handler. */
|
|
18
|
-
export type
|
|
19
|
-
data: RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody | CommandBuilder | UserCommandBuilder | MessageCommandBuilder | AppCommandBuilder
|
|
19
|
+
export type InteractionCommand = {
|
|
20
|
+
data: RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody | CommandBuilder | UserCommandBuilder | MessageCommandBuilder | AppCommandBuilder | JSONEncodable<RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody>;
|
|
20
21
|
handler: CommandHandler;
|
|
21
22
|
/**
|
|
22
23
|
* Optional array of component handlers related to this command.
|
|
23
24
|
* These will be automatically registered when the command is loaded.
|
|
24
25
|
*/
|
|
25
|
-
components?:
|
|
26
|
+
components?: ComponentCommand[];
|
|
26
27
|
/**
|
|
27
28
|
* Optional array of modal handlers related to this command.
|
|
28
29
|
* These will be automatically registered when the command is loaded.
|
|
29
30
|
*/
|
|
30
|
-
modals?:
|
|
31
|
+
modals?: ModalCommand[];
|
|
31
32
|
};
|
|
32
33
|
/** Map of command names to their registered MiniInteraction command definitions. */
|
|
33
|
-
export type
|
|
34
|
+
export type InteractionCommandsMap = Map<string, InteractionCommand>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { APIComponentInActionRow, APIComponentInMessageActionRow } from "discord-api-types/v10";
|
|
2
|
-
/**
|
|
3
|
-
export type
|
|
4
|
-
/**
|
|
5
|
-
export type
|
|
2
|
+
/** Defines a component structure for use in ActionRow builders. */
|
|
3
|
+
export type ActionRowComponent = APIComponentInActionRow;
|
|
4
|
+
/** Defines a message component structure for use in message builders. */
|
|
5
|
+
export type MessageActionRowComponent = APIComponentInMessageActionRow;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
/** Flags available
|
|
2
|
-
export declare enum
|
|
3
|
-
Ephemeral = 64,
|
|
4
|
-
IsComponentsV2 = 32768
|
|
5
|
-
}
|
|
6
|
-
/** Flags available when sending a follow-up message for an interaction. */
|
|
7
|
-
export declare enum InteractionFollowUpFlags {
|
|
1
|
+
/** Flags available for interaction responses. */
|
|
2
|
+
export declare enum InteractionFlags {
|
|
8
3
|
Ephemeral = 64,
|
|
9
4
|
IsComponentsV2 = 32768
|
|
10
5
|
}
|
|
6
|
+
/** @deprecated Use InteractionFlags instead. */
|
|
7
|
+
export { InteractionFlags as InteractionReplyFlags };
|
|
8
|
+
/** @deprecated Use InteractionFlags instead. */
|
|
9
|
+
export { InteractionFlags as InteractionFollowUpFlags };
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
/** Flags available
|
|
2
|
-
export var
|
|
3
|
-
(function (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
})(
|
|
7
|
-
/**
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
InteractionFollowUpFlags[InteractionFollowUpFlags["IsComponentsV2"] = 32768] = "IsComponentsV2";
|
|
12
|
-
})(InteractionFollowUpFlags || (InteractionFollowUpFlags = {}));
|
|
1
|
+
/** Flags available for interaction responses. */
|
|
2
|
+
export var InteractionFlags;
|
|
3
|
+
(function (InteractionFlags) {
|
|
4
|
+
InteractionFlags[InteractionFlags["Ephemeral"] = 64] = "Ephemeral";
|
|
5
|
+
InteractionFlags[InteractionFlags["IsComponentsV2"] = 32768] = "IsComponentsV2";
|
|
6
|
+
})(InteractionFlags || (InteractionFlags = {}));
|
|
7
|
+
/** @deprecated Use InteractionFlags instead. */
|
|
8
|
+
export { InteractionFlags as InteractionReplyFlags };
|
|
9
|
+
/** @deprecated Use InteractionFlags instead. */
|
|
10
|
+
export { InteractionFlags as InteractionFollowUpFlags };
|
|
@@ -4,6 +4,7 @@ export type ResolvedUserOption = {
|
|
|
4
4
|
user: APIUser;
|
|
5
5
|
member?: APIInteractionDataResolvedGuildMember;
|
|
6
6
|
};
|
|
7
|
+
export declare const ResolvedUserOption: {};
|
|
7
8
|
export type MentionableOption = {
|
|
8
9
|
type: "user";
|
|
9
10
|
value: ResolvedUserOption;
|
|
@@ -11,6 +12,7 @@ export type MentionableOption = {
|
|
|
11
12
|
type: "role";
|
|
12
13
|
value: APIRole;
|
|
13
14
|
};
|
|
15
|
+
export declare const MentionableOption: {};
|
|
14
16
|
/**
|
|
15
17
|
* Provides ergonomic helpers for extracting typed command options from Discord interactions.
|
|
16
18
|
*/
|
|
@@ -150,11 +152,20 @@ export interface CommandInteraction extends Omit<APIChatInputApplicationCommandI
|
|
|
150
152
|
toJSON(): APIModalInteractionResponseCallbackData;
|
|
151
153
|
}): APIModalInteractionResponse;
|
|
152
154
|
withTimeoutProtection<T>(operation: () => Promise<T>, deferOptions?: DeferReplyOptions): Promise<T>;
|
|
155
|
+
canRespond?(interactionId: string): boolean;
|
|
156
|
+
trackResponse?(interactionId: string, token: string, state: 'responded' | 'deferred'): void;
|
|
157
|
+
logTiming?(interactionId: string, operation: string, startTime: number, success: boolean): void;
|
|
153
158
|
}
|
|
159
|
+
export declare const CommandInteraction: {};
|
|
154
160
|
/**
|
|
155
161
|
* Wraps a raw application command interaction with helper methods and option resolvers.
|
|
156
162
|
*
|
|
157
163
|
* @param interaction - The raw interaction payload from Discord.
|
|
164
|
+
* @param helpers - Optional helper methods for state management and logging.
|
|
158
165
|
* @returns A helper-augmented interaction object.
|
|
159
166
|
*/
|
|
160
|
-
export declare function createCommandInteraction(interaction: APIChatInputApplicationCommandInteraction
|
|
167
|
+
export declare function createCommandInteraction(interaction: APIChatInputApplicationCommandInteraction, helpers?: {
|
|
168
|
+
canRespond?: (interactionId: string) => boolean;
|
|
169
|
+
trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
|
|
170
|
+
logTiming?: (interactionId: string, operation: string, startTime: number, success: boolean) => void;
|
|
171
|
+
}): CommandInteraction;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ApplicationCommandOptionType, InteractionResponseType, } from "discord-api-types/v10";
|
|
2
2
|
import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
|
|
3
|
+
export const ResolvedUserOption = {};
|
|
4
|
+
export const MentionableOption = {};
|
|
3
5
|
/** Maps application command option types to human-readable labels for error messages. */
|
|
4
6
|
const OPTION_TYPE_LABEL = {
|
|
5
7
|
[ApplicationCommandOptionType.Subcommand]: "subcommand",
|
|
@@ -314,13 +316,15 @@ export class CommandInteractionOptionResolver {
|
|
|
314
316
|
return records?.[id];
|
|
315
317
|
}
|
|
316
318
|
}
|
|
319
|
+
export const CommandInteraction = {};
|
|
317
320
|
/**
|
|
318
321
|
* Wraps a raw application command interaction with helper methods and option resolvers.
|
|
319
322
|
*
|
|
320
323
|
* @param interaction - The raw interaction payload from Discord.
|
|
324
|
+
* @param helpers - Optional helper methods for state management and logging.
|
|
321
325
|
* @returns A helper-augmented interaction object.
|
|
322
326
|
*/
|
|
323
|
-
export function createCommandInteraction(interaction) {
|
|
327
|
+
export function createCommandInteraction(interaction, helpers) {
|
|
324
328
|
const options = new CommandInteractionOptionResolver(interaction.data.options, interaction.data.resolved);
|
|
325
329
|
let capturedResponse = null;
|
|
326
330
|
/**
|
|
@@ -371,7 +375,17 @@ export function createCommandInteraction(interaction) {
|
|
|
371
375
|
return capturedResponse;
|
|
372
376
|
},
|
|
373
377
|
reply(data) {
|
|
374
|
-
|
|
378
|
+
// Validate interaction can respond
|
|
379
|
+
if (!this.canRespond?.(this.id)) {
|
|
380
|
+
throw new Error('Interaction cannot respond: already responded or expired');
|
|
381
|
+
}
|
|
382
|
+
const startTime = Date.now();
|
|
383
|
+
const response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
|
|
384
|
+
// Track response
|
|
385
|
+
this.trackResponse?.(this.id, this.token, 'responded');
|
|
386
|
+
// Log timing if debug enabled
|
|
387
|
+
this.logTiming?.(this.id, 'reply', startTime, true);
|
|
388
|
+
return response;
|
|
375
389
|
},
|
|
376
390
|
followUp(data) {
|
|
377
391
|
return createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
|
|
@@ -380,12 +394,32 @@ export function createCommandInteraction(interaction) {
|
|
|
380
394
|
return createMessageResponse(InteractionResponseType.UpdateMessage, data);
|
|
381
395
|
},
|
|
382
396
|
editReply(data) {
|
|
383
|
-
|
|
397
|
+
// Validate interaction can respond
|
|
398
|
+
if (!this.canRespond?.(this.id)) {
|
|
399
|
+
throw new Error('Interaction cannot edit reply: already responded, expired, or not deferred');
|
|
400
|
+
}
|
|
401
|
+
const startTime = Date.now();
|
|
402
|
+
const response = createMessageResponse(InteractionResponseType.UpdateMessage, data);
|
|
403
|
+
// Track response
|
|
404
|
+
this.trackResponse?.(this.id, this.token, 'responded');
|
|
405
|
+
// Log timing if debug enabled
|
|
406
|
+
this.logTiming?.(this.id, 'editReply', startTime, true);
|
|
407
|
+
return response;
|
|
384
408
|
},
|
|
385
409
|
deferReply(options) {
|
|
386
|
-
|
|
410
|
+
// Validate interaction can respond
|
|
411
|
+
if (!this.canRespond?.(this.id)) {
|
|
412
|
+
throw new Error('Interaction cannot defer: already responded or expired');
|
|
413
|
+
}
|
|
414
|
+
const startTime = Date.now();
|
|
415
|
+
const response = createDeferredResponse(options?.flags !== undefined
|
|
387
416
|
? { flags: options.flags }
|
|
388
417
|
: undefined);
|
|
418
|
+
// Track deferred state
|
|
419
|
+
this.trackResponse?.(this.id, this.token, 'deferred');
|
|
420
|
+
// Log timing if debug enabled
|
|
421
|
+
this.logTiming?.(this.id, 'deferReply', startTime, true);
|
|
422
|
+
return response;
|
|
389
423
|
},
|
|
390
424
|
showModal(data) {
|
|
391
425
|
const resolvedData = typeof data === "object" &&
|
|
@@ -432,6 +466,10 @@ export function createCommandInteraction(interaction) {
|
|
|
432
466
|
throw error;
|
|
433
467
|
}
|
|
434
468
|
},
|
|
469
|
+
// Helper methods for state management
|
|
470
|
+
canRespond: helpers?.canRespond,
|
|
471
|
+
trackResponse: helpers?.trackResponse,
|
|
472
|
+
logTiming: helpers?.logTiming,
|
|
435
473
|
};
|
|
436
474
|
return commandInteraction;
|
|
437
475
|
}
|
|
@@ -20,6 +20,7 @@ export type UserContextMenuInteraction = APIUserApplicationCommandInteraction &
|
|
|
20
20
|
/** Resolved user targeted by this user context menu command. */
|
|
21
21
|
targetUser?: APIUser;
|
|
22
22
|
};
|
|
23
|
+
export declare const UserContextMenuInteraction: {};
|
|
23
24
|
/**
|
|
24
25
|
* Message context menu interaction with helper methods.
|
|
25
26
|
*/
|
|
@@ -27,10 +28,12 @@ export type MessageContextMenuInteraction = APIMessageApplicationCommandInteract
|
|
|
27
28
|
/** Resolved message targeted by this message context menu command. */
|
|
28
29
|
targetMessage?: APIMessage;
|
|
29
30
|
};
|
|
31
|
+
export declare const MessageContextMenuInteraction: {};
|
|
30
32
|
/**
|
|
31
33
|
* Primary entry point interaction with helper methods.
|
|
32
34
|
*/
|
|
33
35
|
export type AppCommandInteraction = APIPrimaryEntryPointCommandInteraction & ContextMenuInteractionHelpers;
|
|
36
|
+
export declare const AppCommandInteraction: {};
|
|
34
37
|
/**
|
|
35
38
|
* Wraps a raw user context menu interaction with helper methods.
|
|
36
39
|
*
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { InteractionResponseType, } from "discord-api-types/v10";
|
|
2
2
|
import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
|
|
3
|
+
export const UserContextMenuInteraction = {};
|
|
4
|
+
export const MessageContextMenuInteraction = {};
|
|
5
|
+
export const AppCommandInteraction = {};
|
|
3
6
|
function createContextMenuInteractionHelpers() {
|
|
4
7
|
let capturedResponse = null;
|
|
5
8
|
const captureResponse = (response) => {
|
|
@@ -5,6 +5,7 @@ export type ResolvedUserOption = {
|
|
|
5
5
|
user: APIUser;
|
|
6
6
|
member?: APIInteractionDataResolvedGuildMember;
|
|
7
7
|
};
|
|
8
|
+
export declare const ResolvedUserOption: {};
|
|
8
9
|
/** Resolved mentionable option (either a user or role). */
|
|
9
10
|
export type ResolvedMentionableOption = {
|
|
10
11
|
type: "user";
|
|
@@ -13,6 +14,7 @@ export type ResolvedMentionableOption = {
|
|
|
13
14
|
type: "role";
|
|
14
15
|
value: APIRole;
|
|
15
16
|
};
|
|
17
|
+
export declare const ResolvedMentionableOption: {};
|
|
16
18
|
/**
|
|
17
19
|
* Base helper methods available on all component interactions.
|
|
18
20
|
*/
|
|
@@ -33,6 +35,7 @@ type BaseComponentInteractionHelpers = {
|
|
|
33
35
|
export interface ButtonInteraction extends Omit<APIMessageComponentInteraction, "data">, BaseComponentInteractionHelpers {
|
|
34
36
|
data: APIMessageButtonInteractionData;
|
|
35
37
|
}
|
|
38
|
+
export declare const ButtonInteraction: {};
|
|
36
39
|
/**
|
|
37
40
|
* String select menu interaction with helper methods.
|
|
38
41
|
*/
|
|
@@ -41,6 +44,7 @@ export interface StringSelectInteraction extends Omit<APIMessageComponentInterac
|
|
|
41
44
|
values: string[];
|
|
42
45
|
getStringValues: () => string[];
|
|
43
46
|
}
|
|
47
|
+
export declare const StringSelectInteraction: {};
|
|
44
48
|
/**
|
|
45
49
|
* Role select menu interaction with helper methods.
|
|
46
50
|
*/
|
|
@@ -49,6 +53,7 @@ export interface RoleSelectInteraction extends Omit<APIMessageComponentInteracti
|
|
|
49
53
|
values: string[];
|
|
50
54
|
getRoles: () => APIRole[];
|
|
51
55
|
}
|
|
56
|
+
export declare const RoleSelectInteraction: {};
|
|
52
57
|
/**
|
|
53
58
|
* User select menu interaction with helper methods.
|
|
54
59
|
*/
|
|
@@ -57,6 +62,7 @@ export interface UserSelectInteraction extends Omit<APIMessageComponentInteracti
|
|
|
57
62
|
values: string[];
|
|
58
63
|
getUsers: () => ResolvedUserOption[];
|
|
59
64
|
}
|
|
65
|
+
export declare const UserSelectInteraction: {};
|
|
60
66
|
/**
|
|
61
67
|
* Channel select menu interaction with helper methods.
|
|
62
68
|
*/
|
|
@@ -65,6 +71,7 @@ export interface ChannelSelectInteraction extends Omit<APIMessageComponentIntera
|
|
|
65
71
|
values: string[];
|
|
66
72
|
getChannels: () => APIInteractionDataResolvedChannel[];
|
|
67
73
|
}
|
|
74
|
+
export declare const ChannelSelectInteraction: {};
|
|
68
75
|
/**
|
|
69
76
|
* Mentionable select menu interaction with helper methods.
|
|
70
77
|
*/
|
|
@@ -73,6 +80,7 @@ export interface MentionableSelectInteraction extends Omit<APIMessageComponentIn
|
|
|
73
80
|
values: string[];
|
|
74
81
|
getMentionables: () => ResolvedMentionableOption[];
|
|
75
82
|
}
|
|
83
|
+
export declare const MentionableSelectInteraction: {};
|
|
76
84
|
/**
|
|
77
85
|
* Represents a component interaction augmented with helper response methods.
|
|
78
86
|
*
|
|
@@ -120,6 +128,7 @@ export type MessageComponentInteraction = APIMessageComponentInteraction & {
|
|
|
120
128
|
*/
|
|
121
129
|
getMentionables: () => ResolvedMentionableOption[];
|
|
122
130
|
};
|
|
131
|
+
export declare const MessageComponentInteraction: {};
|
|
123
132
|
/**
|
|
124
133
|
* Wraps a raw component interaction with helper methods mirroring Discord's expected responses.
|
|
125
134
|
*
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { InteractionResponseType, } from "discord-api-types/v10";
|
|
2
2
|
import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
|
|
3
|
+
export const ResolvedUserOption = {};
|
|
4
|
+
export const ResolvedMentionableOption = {};
|
|
5
|
+
export const ButtonInteraction = {};
|
|
6
|
+
export const StringSelectInteraction = {};
|
|
7
|
+
export const RoleSelectInteraction = {};
|
|
8
|
+
export const UserSelectInteraction = {};
|
|
9
|
+
export const ChannelSelectInteraction = {};
|
|
10
|
+
export const MentionableSelectInteraction = {};
|
|
11
|
+
export const MessageComponentInteraction = {};
|
|
3
12
|
/**
|
|
4
13
|
* Wraps a raw component interaction with helper methods mirroring Discord's expected responses.
|
|
5
14
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InteractionResponseType, } from "discord-api-types/v10";
|
|
2
2
|
import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
|
|
3
|
+
export const ModalSubmitInteraction = {};
|
|
3
4
|
/**
|
|
4
5
|
* Wraps a raw modal submit interaction with helper methods.
|
|
5
6
|
*
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import type { APIInteractionResponseCallbackData, MessageFlags } from "discord-api-types/v10";
|
|
2
|
-
import type {
|
|
2
|
+
import type { APIActionRowComponent, APIComponentInMessageActionRow, APIContainerComponent } from "discord-api-types/v10";
|
|
3
|
+
import type { JSONEncodable } from "../builders/shared.js";
|
|
4
|
+
import type { InteractionFlags } from "../types/InteractionFlags.js";
|
|
3
5
|
/** Union of helper flag enums and raw Discord message flags. */
|
|
4
|
-
export type MessageFlagLike = MessageFlags |
|
|
6
|
+
export type MessageFlagLike = MessageFlags | InteractionFlags;
|
|
7
|
+
/** Top-level components allowed in messages (ActionRows or Containers) */
|
|
8
|
+
export type TopLevelComponent = APIActionRowComponent<APIComponentInMessageActionRow> | APIContainerComponent;
|
|
5
9
|
/** Message payload accepted by helper reply/edit functions. */
|
|
6
|
-
export type InteractionMessageData =
|
|
7
|
-
|
|
10
|
+
export type InteractionMessageData = {
|
|
11
|
+
content?: string;
|
|
12
|
+
components?: TopLevelComponent[] | JSONEncodable<TopLevelComponent>[] | JSONEncodable<TopLevelComponent[]>;
|
|
13
|
+
embeds?: unknown[];
|
|
14
|
+
flags?: InteractionFlags;
|
|
8
15
|
};
|
|
9
16
|
/** Deferred response payload recognised by helper methods. */
|
|
10
17
|
export type DeferredResponseData = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveJSONEncodable } from "../builders/shared.js";
|
|
2
|
+
import { ComponentType, } from "discord-api-types/v10";
|
|
2
3
|
const COMPONENTS_V2_TYPES = new Set([
|
|
3
4
|
ComponentType.Container,
|
|
4
5
|
ComponentType.Section,
|
|
@@ -37,36 +38,18 @@ export function normaliseInteractionMessageData(data) {
|
|
|
37
38
|
if (!data) {
|
|
38
39
|
return undefined;
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const normalisedFlags = normaliseMessageFlags(data.flags);
|
|
51
|
-
const finalFlags = usesComponentsV2
|
|
52
|
-
? ((normalisedFlags ?? 0) |
|
|
53
|
-
RawMessageFlags.IsComponentsV2)
|
|
54
|
-
: normalisedFlags;
|
|
55
|
-
const needsNormalisation = finalFlags !== data.flags ||
|
|
56
|
-
normalisedComponents !== data.components ||
|
|
57
|
-
normalisedEmbeds !== data.embeds;
|
|
58
|
-
if (!needsNormalisation) {
|
|
59
|
-
return data;
|
|
41
|
+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
42
|
+
const responseData = { ...data };
|
|
43
|
+
if (data.components) {
|
|
44
|
+
const components = resolveJSONEncodable(data.components);
|
|
45
|
+
if (Array.isArray(components)) {
|
|
46
|
+
responseData.components = components.map((c) => resolveJSONEncodable(c));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (data.embeds) {
|
|
50
|
+
responseData.embeds = data.embeds.map((e) => resolveJSONEncodable(e));
|
|
60
51
|
}
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
...rest,
|
|
64
|
-
...(normalisedComponents !== undefined
|
|
65
|
-
? { components: normalisedComponents }
|
|
66
|
-
: {}),
|
|
67
|
-
...(normalisedEmbeds !== undefined ? { embeds: normalisedEmbeds } : {}),
|
|
68
|
-
...(finalFlags !== undefined ? { flags: finalFlags } : {}),
|
|
69
|
-
};
|
|
52
|
+
return responseData;
|
|
70
53
|
}
|
|
71
54
|
function containsComponentsV2(components) {
|
|
72
55
|
return components.some((component) => componentUsesComponentsV2(component));
|
package/package.json
CHANGED