@minesa-org/mini-interaction 0.2.8 → 0.2.10

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.
@@ -1278,6 +1278,8 @@ export class MiniInteraction {
1278
1278
  const interactionWithHelpers = createUserContextMenuInteraction(commandInteraction, {
1279
1279
  onAck: (response) => ackResolver?.(response),
1280
1280
  sendFollowUp,
1281
+ canRespond: (id) => this.canRespond(id),
1282
+ trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1281
1283
  });
1282
1284
  response = await command.handler(interactionWithHelpers);
1283
1285
  resolvedResponse =
@@ -1288,6 +1290,8 @@ export class MiniInteraction {
1288
1290
  const interactionWithHelpers = createAppCommandInteraction(commandInteraction, {
1289
1291
  onAck: (response) => ackResolver?.(response),
1290
1292
  sendFollowUp,
1293
+ canRespond: (id) => this.canRespond(id),
1294
+ trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1291
1295
  });
1292
1296
  response = await command.handler(interactionWithHelpers);
1293
1297
  resolvedResponse =
@@ -1299,6 +1303,8 @@ export class MiniInteraction {
1299
1303
  const interactionWithHelpers = createMessageContextMenuInteraction(commandInteraction, {
1300
1304
  onAck: (response) => ackResolver?.(response),
1301
1305
  sendFollowUp,
1306
+ canRespond: (id) => this.canRespond(id),
1307
+ trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1302
1308
  });
1303
1309
  response = await command.handler(interactionWithHelpers);
1304
1310
  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');
@@ -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;
@@ -28,8 +28,12 @@ function createContextMenuInteractionHelpers(interaction, helpers) {
28
28
  return captureResponse({ type });
29
29
  }
30
30
  const reply = (data) => {
31
+ if (helpers?.canRespond && !helpers.canRespond(interaction.id)) {
32
+ throw new Error("[MiniInteraction] Interaction cannot respond: already responded or expired");
33
+ }
31
34
  const response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
32
35
  hasResponded = true;
36
+ helpers?.trackResponse?.(interaction.id, interaction.token, 'responded');
33
37
  helpers?.onAck?.(response);
34
38
  return response;
35
39
  };
@@ -41,20 +45,31 @@ function createContextMenuInteractionHelpers(interaction, helpers) {
41
45
  return response;
42
46
  };
43
47
  const editReply = async (data) => {
44
- const response = createMessageResponse(InteractionResponseType.UpdateMessage, data);
48
+ if (helpers?.canRespond && !helpers.canRespond(interaction.id)) {
49
+ throw new Error("[MiniInteraction] Interaction cannot edit reply: expired");
50
+ }
51
+ // Context menu commands (User/Message) MUST use ChannelMessageWithSource (4)
52
+ // for their initial response if it's the first response.
53
+ const response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
45
54
  if (helpers?.sendFollowUp && (isDeferred || hasResponded)) {
46
55
  await helpers.sendFollowUp(interaction.token, response, '@original');
56
+ return capturedResponse;
47
57
  }
48
58
  hasResponded = true;
59
+ helpers?.trackResponse?.(interaction.id, interaction.token, 'responded');
49
60
  return response;
50
61
  };
51
62
  const deferReply = (options = {}) => {
63
+ if (helpers?.canRespond && !helpers.canRespond(interaction.id)) {
64
+ throw new Error("[MiniInteraction] Interaction cannot defer: already responded or expired");
65
+ }
52
66
  const flags = normaliseMessageFlags(options.flags);
53
67
  const response = captureResponse({
54
68
  type: InteractionResponseType.DeferredChannelMessageWithSource,
55
69
  data: flags ? { flags } : undefined,
56
70
  });
57
71
  isDeferred = true;
72
+ helpers?.trackResponse?.(interaction.id, interaction.token, 'deferred');
58
73
  helpers?.onAck?.(response);
59
74
  return response;
60
75
  };
@@ -70,10 +85,13 @@ function createContextMenuInteractionHelpers(interaction, helpers) {
70
85
  getResponse,
71
86
  reply,
72
87
  followUp,
73
- editReply,
88
+ editReply: editReply,
74
89
  deferReply,
75
90
  showModal,
91
+ onAck: helpers?.onAck,
76
92
  sendFollowUp: helpers?.sendFollowUp,
93
+ canRespond: helpers?.canRespond,
94
+ trackResponse: helpers?.trackResponse,
77
95
  };
78
96
  }
79
97
  /**
@@ -24,6 +24,9 @@ export function createMessageComponentInteraction(interaction, helpers) {
24
24
  return response;
25
25
  };
26
26
  const reply = async (data) => {
27
+ if (helpers?.canRespond && !helpers.canRespond(interaction.id)) {
28
+ throw new Error("[MiniInteraction] Interaction cannot respond: already responded or expired");
29
+ }
27
30
  const normalisedData = normaliseInteractionMessageData(data);
28
31
  if (!normalisedData) {
29
32
  throw new Error("[MiniInteraction] Component replies require response data to be provided.");
@@ -38,9 +41,13 @@ export function createMessageComponentInteraction(interaction, helpers) {
38
41
  else {
39
42
  helpers?.onAck?.(response);
40
43
  }
44
+ helpers?.trackResponse?.(interaction.id, interaction.token, 'responded');
41
45
  return response;
42
46
  };
43
47
  const deferReply = (options) => {
48
+ if (helpers?.canRespond && !helpers.canRespond(interaction.id)) {
49
+ throw new Error("[MiniInteraction] Interaction cannot defer: already responded or expired");
50
+ }
44
51
  const flags = normaliseMessageFlags(options?.flags);
45
52
  const response = flags !== undefined
46
53
  ? {
@@ -52,10 +59,14 @@ export function createMessageComponentInteraction(interaction, helpers) {
52
59
  };
53
60
  captureResponse(response);
54
61
  isDeferred = true;
62
+ helpers?.trackResponse?.(interaction.id, interaction.token, 'deferred');
55
63
  helpers?.onAck?.(response);
56
64
  return response;
57
65
  };
58
66
  const update = async (data) => {
67
+ if (helpers?.canRespond && !helpers.canRespond(interaction.id)) {
68
+ throw new Error("[MiniInteraction] Interaction cannot update: already responded or expired");
69
+ }
59
70
  const normalisedData = normaliseInteractionMessageData(data);
60
71
  const response = captureResponse(normalisedData
61
72
  ? {
@@ -71,13 +82,18 @@ export function createMessageComponentInteraction(interaction, helpers) {
71
82
  else {
72
83
  helpers?.onAck?.(response);
73
84
  }
85
+ helpers?.trackResponse?.(interaction.id, interaction.token, 'responded');
74
86
  return response;
75
87
  };
76
88
  const deferUpdate = () => {
89
+ if (helpers?.canRespond && !helpers.canRespond(interaction.id)) {
90
+ throw new Error("[MiniInteraction] Interaction cannot defer update: already responded or expired");
91
+ }
77
92
  const response = captureResponse({
78
93
  type: InteractionResponseType.DeferredMessageUpdate,
79
94
  });
80
95
  isDeferred = true;
96
+ helpers?.trackResponse?.(interaction.id, interaction.token, 'deferred');
81
97
  helpers?.onAck?.(response);
82
98
  return response;
83
99
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
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",