@minesa-org/mini-interaction 0.1.12 → 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 +102 -34
- package/dist/clients/MiniInteraction.js +293 -70
- 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 +13 -1
- package/dist/utils/CommandInteractionOptions.js +76 -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
|
@@ -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
|
*/
|
|
@@ -149,11 +151,21 @@ export interface CommandInteraction extends Omit<APIChatInputApplicationCommandI
|
|
|
149
151
|
showModal(data: APIModalInteractionResponseCallbackData | {
|
|
150
152
|
toJSON(): APIModalInteractionResponseCallbackData;
|
|
151
153
|
}): APIModalInteractionResponse;
|
|
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;
|
|
152
158
|
}
|
|
159
|
+
export declare const CommandInteraction: {};
|
|
153
160
|
/**
|
|
154
161
|
* Wraps a raw application command interaction with helper methods and option resolvers.
|
|
155
162
|
*
|
|
156
163
|
* @param interaction - The raw interaction payload from Discord.
|
|
164
|
+
* @param helpers - Optional helper methods for state management and logging.
|
|
157
165
|
* @returns A helper-augmented interaction object.
|
|
158
166
|
*/
|
|
159
|
-
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" &&
|
|
@@ -398,6 +432,44 @@ export function createCommandInteraction(interaction) {
|
|
|
398
432
|
data: resolvedData,
|
|
399
433
|
});
|
|
400
434
|
},
|
|
435
|
+
/**
|
|
436
|
+
* Creates a delayed response wrapper that automatically defers if the operation takes too long.
|
|
437
|
+
* Use this for operations that might exceed Discord's 3-second limit.
|
|
438
|
+
*
|
|
439
|
+
* @param operation - The async operation to perform
|
|
440
|
+
* @param deferOptions - Options for automatic deferral
|
|
441
|
+
*/
|
|
442
|
+
async withTimeoutProtection(operation, deferOptions) {
|
|
443
|
+
const startTime = Date.now();
|
|
444
|
+
let deferred = false;
|
|
445
|
+
// Set up a timer to auto-defer after 2.5 seconds
|
|
446
|
+
const deferTimer = setTimeout(async () => {
|
|
447
|
+
if (!deferred) {
|
|
448
|
+
console.warn("[MiniInteraction] Auto-deferring interaction due to slow operation. " +
|
|
449
|
+
"Consider using deferReply() explicitly for better user experience.");
|
|
450
|
+
this.deferReply(deferOptions);
|
|
451
|
+
deferred = true;
|
|
452
|
+
}
|
|
453
|
+
}, 2500);
|
|
454
|
+
try {
|
|
455
|
+
const result = await operation();
|
|
456
|
+
clearTimeout(deferTimer);
|
|
457
|
+
const elapsed = Date.now() - startTime;
|
|
458
|
+
if (elapsed > 2000 && !deferred) {
|
|
459
|
+
console.warn(`[MiniInteraction] Operation completed in ${elapsed}ms. ` +
|
|
460
|
+
"Consider using deferReply() for operations > 2 seconds.");
|
|
461
|
+
}
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
clearTimeout(deferTimer);
|
|
466
|
+
throw error;
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
// Helper methods for state management
|
|
470
|
+
canRespond: helpers?.canRespond,
|
|
471
|
+
trackResponse: helpers?.trackResponse,
|
|
472
|
+
logTiming: helpers?.logTiming,
|
|
401
473
|
};
|
|
402
474
|
return commandInteraction;
|
|
403
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