@minesa-org/mini-interaction 0.2.7 → 0.2.9

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.
@@ -1126,14 +1126,15 @@ export class MiniInteraction {
1126
1126
  const interactionWithHelpers = createMessageComponentInteraction(interaction, {
1127
1127
  onAck: (response) => ackResolver?.(response),
1128
1128
  sendFollowUp,
1129
+ trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1130
+ canRespond: (id) => this.canRespond(id),
1129
1131
  });
1130
1132
  // Wrap component handler with timeout and acknowledgment
1131
1133
  const timeoutWrapper = createTimeoutWrapper(async () => {
1132
1134
  const response = await handler(interactionWithHelpers);
1133
1135
  const resolvedResponse = response ?? interactionWithHelpers.getResponse();
1134
- if (!resolvedResponse) {
1135
- throw new Error(`Component "${customId}" did not return a response. ` +
1136
- "Return an APIInteractionResponse to acknowledge the interaction.");
1136
+ if (this.timeoutConfig.enableResponseDebugLogging) {
1137
+ console.log(`[MiniInteraction] Component handler finished: "${customId}"`);
1137
1138
  }
1138
1139
  return resolvedResponse;
1139
1140
  }, this.timeoutConfig.initialResponseTimeout, `Component "${customId}"`, this.timeoutConfig.enableTimeoutWarnings, ackPromise);
@@ -1191,14 +1192,15 @@ export class MiniInteraction {
1191
1192
  const interactionWithHelpers = createModalSubmitInteraction(interaction, {
1192
1193
  onAck: (response) => ackResolver?.(response),
1193
1194
  sendFollowUp,
1195
+ trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1196
+ canRespond: (id) => this.canRespond(id),
1194
1197
  });
1195
1198
  // Wrap modal handler with timeout and acknowledgment
1196
1199
  const timeoutWrapper = createTimeoutWrapper(async () => {
1197
1200
  const response = await handler(interactionWithHelpers);
1198
1201
  const resolvedResponse = response ?? interactionWithHelpers.getResponse();
1199
- if (!resolvedResponse) {
1200
- throw new Error(`Modal "${customId}" did not return a response. ` +
1201
- "Return an APIInteractionResponse to acknowledge the interaction.");
1202
+ if (this.timeoutConfig.enableResponseDebugLogging) {
1203
+ console.log(`[MiniInteraction] Modal handler finished: "${customId}"`);
1202
1204
  }
1203
1205
  return resolvedResponse;
1204
1206
  }, this.timeoutConfig.initialResponseTimeout, `Modal "${customId}"`, this.timeoutConfig.enableTimeoutWarnings, ackPromise);
@@ -1276,6 +1278,8 @@ export class MiniInteraction {
1276
1278
  const interactionWithHelpers = createUserContextMenuInteraction(commandInteraction, {
1277
1279
  onAck: (response) => ackResolver?.(response),
1278
1280
  sendFollowUp,
1281
+ canRespond: (id) => this.canRespond(id),
1282
+ trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1279
1283
  });
1280
1284
  response = await command.handler(interactionWithHelpers);
1281
1285
  resolvedResponse =
@@ -1286,6 +1290,8 @@ export class MiniInteraction {
1286
1290
  const interactionWithHelpers = createAppCommandInteraction(commandInteraction, {
1287
1291
  onAck: (response) => ackResolver?.(response),
1288
1292
  sendFollowUp,
1293
+ canRespond: (id) => this.canRespond(id),
1294
+ trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1289
1295
  });
1290
1296
  response = await command.handler(interactionWithHelpers);
1291
1297
  resolvedResponse =
@@ -146,7 +146,7 @@ export interface CommandInteraction extends Omit<APIChatInputApplicationCommandI
146
146
  reply(data: InteractionMessageData): APIInteractionResponseChannelMessageWithSource;
147
147
  followUp(data: InteractionMessageData): Promise<APIInteractionResponseChannelMessageWithSource>;
148
148
  edit(data?: InteractionMessageData): APIInteractionResponseUpdateMessage;
149
- editReply(data?: InteractionMessageData): Promise<APIInteractionResponseUpdateMessage>;
149
+ editReply(data?: InteractionMessageData): Promise<APIInteractionResponseChannelMessageWithSource | APIInteractionResponseUpdateMessage>;
150
150
  deferReply(options?: DeferReplyOptions): APIInteractionResponseDeferredChannelMessageWithSource;
151
151
  showModal(data: APIModalInteractionResponseCallbackData | {
152
152
  toJSON(): APIModalInteractionResponseCallbackData;
@@ -1,4 +1,4 @@
1
- import { ApplicationCommandOptionType, InteractionResponseType, } from "discord-api-types/v10";
1
+ import { ApplicationCommandOptionType, InteractionResponseType, InteractionType, } from "discord-api-types/v10";
2
2
  import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
3
3
  export const ResolvedUserOption = {};
4
4
  export const MentionableOption = {};
@@ -405,10 +405,24 @@ export function createCommandInteraction(interaction, helpers) {
405
405
  if (!this.canRespond?.(this.id)) {
406
406
  throw new Error('Interaction cannot edit reply: expired');
407
407
  }
408
- const response = createMessageResponse(InteractionResponseType.UpdateMessage, data);
408
+ // Slash commands (type 2) MUST use ChannelMessageWithSource (type 4) for their initial response,
409
+ // or UpdateMessage (type 7) if they are updating a component interaction message.
410
+ // However, for as-yet-unresponded slash commands, we need type 4.
411
+ const interactionAny = interaction;
412
+ const isComponent = interactionAny.type === InteractionType.MessageComponent;
413
+ let response;
414
+ if (isComponent) {
415
+ response = createMessageResponse(InteractionResponseType.UpdateMessage, data);
416
+ }
417
+ else {
418
+ response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
419
+ }
409
420
  // If it's already deferred or responded, we MUST use a webhook
410
421
  if (this.sendFollowUp && (isDeferred || hasResponded)) {
411
422
  await this.sendFollowUp(this.token, response, '@original');
423
+ // If we already sent an ACK (like a DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE),
424
+ // we return the original captured response to avoid sending type 4/7 back to the initial POST.
425
+ return capturedResponse;
412
426
  }
413
427
  // Track response
414
428
  this.trackResponse?.(this.id, this.token, 'responded');
@@ -479,6 +493,7 @@ export function createCommandInteraction(interaction, helpers) {
479
493
  canRespond: helpers?.canRespond,
480
494
  trackResponse: helpers?.trackResponse,
481
495
  onAck: helpers?.onAck,
496
+ sendFollowUp: helpers?.sendFollowUp,
482
497
  };
483
498
  return commandInteraction;
484
499
  }
@@ -14,6 +14,8 @@ export type ContextMenuInteractionHelpers = {
14
14
  }) => APIModalInteractionResponse;
15
15
  onAck?: (response: APIInteractionResponse) => void;
16
16
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
17
+ canRespond?: (interactionId: string) => boolean;
18
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
17
19
  };
18
20
  /**
19
21
  * User context menu interaction with helper methods.
@@ -46,6 +48,8 @@ export declare const AppCommandInteraction: {};
46
48
  export declare function createUserContextMenuInteraction(interaction: APIUserApplicationCommandInteraction, helpers?: {
47
49
  onAck?: (response: APIInteractionResponse) => void;
48
50
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
51
+ canRespond?: (interactionId: string) => boolean;
52
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
49
53
  }): UserContextMenuInteraction;
50
54
  /**
51
55
  * Wraps a raw message context menu interaction with helper methods.
@@ -57,6 +61,8 @@ export declare function createUserContextMenuInteraction(interaction: APIUserApp
57
61
  export declare function createMessageContextMenuInteraction(interaction: APIMessageApplicationCommandInteraction, helpers?: {
58
62
  onAck?: (response: APIInteractionResponse) => void;
59
63
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
64
+ canRespond?: (interactionId: string) => boolean;
65
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
60
66
  }): MessageContextMenuInteraction;
61
67
  /**
62
68
  * Wraps a raw primary entry point interaction with helper methods.
@@ -68,4 +74,6 @@ export declare function createMessageContextMenuInteraction(interaction: APIMess
68
74
  export declare function createAppCommandInteraction(interaction: APIPrimaryEntryPointCommandInteraction, helpers?: {
69
75
  onAck?: (response: APIInteractionResponse) => void;
70
76
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
77
+ canRespond?: (interactionId: string) => boolean;
78
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
71
79
  }): AppCommandInteraction;
@@ -44,6 +44,7 @@ function createContextMenuInteractionHelpers(interaction, helpers) {
44
44
  const response = createMessageResponse(InteractionResponseType.UpdateMessage, data);
45
45
  if (helpers?.sendFollowUp && (isDeferred || hasResponded)) {
46
46
  await helpers.sendFollowUp(interaction.token, response, '@original');
47
+ return capturedResponse;
47
48
  }
48
49
  hasResponded = true;
49
50
  return response;
@@ -73,7 +74,10 @@ function createContextMenuInteractionHelpers(interaction, helpers) {
73
74
  editReply,
74
75
  deferReply,
75
76
  showModal,
77
+ onAck: helpers?.onAck,
76
78
  sendFollowUp: helpers?.sendFollowUp,
79
+ canRespond: helpers?.canRespond,
80
+ trackResponse: helpers?.trackResponse,
77
81
  };
78
82
  }
79
83
  /**
@@ -22,6 +22,8 @@ export type BaseComponentInteractionHelpers = {
22
22
  trackTiming?: (interactionId: string, operation: string, startTime: number, success: boolean) => void;
23
23
  onAck?: (response: APIInteractionResponse) => void;
24
24
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
25
+ canRespond?: (interactionId: string) => boolean;
26
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
25
27
  };
26
28
  /**
27
29
  * Button interaction with helper methods.
@@ -37,6 +39,8 @@ export interface ButtonInteraction extends Omit<APIMessageComponentInteraction,
37
39
  showModal: (data: APIModalInteractionResponseCallbackData | {
38
40
  toJSON(): APIModalInteractionResponseCallbackData;
39
41
  }) => APIModalInteractionResponse;
42
+ canRespond?: (interactionId: string) => boolean;
43
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
40
44
  }
41
45
  export declare const ButtonInteraction: {};
42
46
  /**
@@ -54,6 +58,8 @@ export interface StringSelectInteraction extends Omit<APIMessageComponentInterac
54
58
  showModal: (data: APIModalInteractionResponseCallbackData | {
55
59
  toJSON(): APIModalInteractionResponseCallbackData;
56
60
  }) => APIModalInteractionResponse;
61
+ canRespond?: (interactionId: string) => boolean;
62
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
57
63
  }
58
64
  export declare const StringSelectInteraction: {};
59
65
  /**
@@ -71,6 +77,8 @@ export interface RoleSelectInteraction extends Omit<APIMessageComponentInteracti
71
77
  showModal: (data: APIModalInteractionResponseCallbackData | {
72
78
  toJSON(): APIModalInteractionResponseCallbackData;
73
79
  }) => APIModalInteractionResponse;
80
+ canRespond?: (interactionId: string) => boolean;
81
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
74
82
  }
75
83
  export declare const RoleSelectInteraction: {};
76
84
  /**
@@ -88,6 +96,8 @@ export interface UserSelectInteraction extends Omit<APIMessageComponentInteracti
88
96
  showModal: (data: APIModalInteractionResponseCallbackData | {
89
97
  toJSON(): APIModalInteractionResponseCallbackData;
90
98
  }) => APIModalInteractionResponse;
99
+ canRespond?: (interactionId: string) => boolean;
100
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
91
101
  }
92
102
  export declare const UserSelectInteraction: {};
93
103
  /**
@@ -105,6 +115,8 @@ export interface ChannelSelectInteraction extends Omit<APIMessageComponentIntera
105
115
  showModal: (data: APIModalInteractionResponseCallbackData | {
106
116
  toJSON(): APIModalInteractionResponseCallbackData;
107
117
  }) => APIModalInteractionResponse;
118
+ canRespond?: (interactionId: string) => boolean;
119
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
108
120
  }
109
121
  export declare const ChannelSelectInteraction: {};
110
122
  /**
@@ -122,6 +134,8 @@ export interface MentionableSelectInteraction extends Omit<APIMessageComponentIn
122
134
  showModal: (data: APIModalInteractionResponseCallbackData | {
123
135
  toJSON(): APIModalInteractionResponseCallbackData;
124
136
  }) => APIModalInteractionResponse;
137
+ canRespond?: (interactionId: string) => boolean;
138
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
125
139
  }
126
140
  export declare const MentionableSelectInteraction: {};
127
141
  /**
@@ -144,6 +158,11 @@ export type MessageComponentInteraction = APIMessageComponentInteraction & {
144
158
  * This is automatically called by reply() and update() if the interaction is deferred.
145
159
  */
146
160
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
161
+ /**
162
+ * Optional state management helpers.
163
+ */
164
+ canRespond?: (interactionId: string) => boolean;
165
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
147
166
  /**
148
167
  * The selected values from a select menu interaction.
149
168
  * This property is only present for select menu interactions.
@@ -203,5 +203,7 @@ export function createMessageComponentInteraction(interaction, helpers) {
203
203
  getMentionables,
204
204
  onAck: helpers?.onAck,
205
205
  sendFollowUp: helpers?.sendFollowUp,
206
+ canRespond: helpers?.canRespond,
207
+ trackResponse: helpers?.trackResponse,
206
208
  });
207
209
  }
@@ -16,6 +16,11 @@ export type ModalSubmitInteraction = APIModalSubmitInteraction & {
16
16
  * This is automatically called by reply() if the interaction is deferred.
17
17
  */
18
18
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
19
+ /**
20
+ * Optional state management helpers.
21
+ */
22
+ canRespond?: (interactionId: string) => boolean;
23
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
19
24
  };
20
25
  export declare const ModalSubmitInteraction: {};
21
26
  /**
@@ -28,4 +33,6 @@ export declare const ModalSubmitInteraction: {};
28
33
  export declare function createModalSubmitInteraction(interaction: APIModalSubmitInteraction, helpers?: {
29
34
  onAck?: (response: APIInteractionResponse) => void;
30
35
  sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
36
+ canRespond?: (interactionId: string) => boolean;
37
+ trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
31
38
  }): ModalSubmitInteraction;
@@ -67,5 +67,7 @@ export function createModalSubmitInteraction(interaction, helpers) {
67
67
  getResponse,
68
68
  getTextFieldValue,
69
69
  sendFollowUp: helpers?.sendFollowUp,
70
+ canRespond: helpers?.canRespond,
71
+ trackResponse: helpers?.trackResponse,
70
72
  });
71
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Mini interaction, connecting your app with Discord via HTTP-interaction (Vercel support).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",