@minesa-org/mini-interaction 0.2.3 → 0.2.4

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.
@@ -49,15 +49,6 @@ export type InteractionTimeoutConfig = {
49
49
  /** Whether to enable debug logging for interaction responses (default: false) */
50
50
  enableResponseDebugLogging?: boolean;
51
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;
60
- };
61
52
  /** Handler signature invoked for Discord button interactions. */
62
53
  export type ButtonComponentHandler = (interaction: ButtonInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
63
54
  /** Handler signature invoked for Discord string select menu interactions. */
@@ -184,46 +175,15 @@ export declare class MiniInteraction {
184
175
  * Creates a new MiniInteraction client with optional command auto-loading and custom runtime hooks.
185
176
  */
186
177
  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
178
  private trackInteractionState;
191
179
  /**
192
180
  * Checks if an interaction can still respond (not expired and not already responded).
193
181
  */
194
182
  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
183
  /**
212
184
  * Gets the current state of an interaction.
213
185
  */
214
186
  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
187
  /**
228
188
  * Clears expired interaction states to prevent memory leaks.
229
189
  * Call this periodically to clean up old interaction data.
@@ -78,27 +78,16 @@ export class MiniInteraction {
78
78
  enableTimeoutWarnings: true,
79
79
  autoDeferSlowOperations: true,
80
80
  enableResponseDebugLogging: false,
81
- responseAcknowledgmentTimeout: 500, // 500ms to wait for acknowledgment
82
- responseAcknowledgmentRetries: 2,
83
- responseAcknowledgmentRetryDelay: 100,
84
81
  ...timeoutConfig,
85
82
  };
86
83
  }
87
- /**
88
- * Tracks the state of an interaction to prevent race conditions and double responses.
89
- */
90
84
  trackInteractionState(interactionId, token, state) {
91
- const existing = this.interactionStates.get(interactionId);
92
85
  const now = Date.now();
93
86
  this.interactionStates.set(interactionId, {
94
87
  state,
95
88
  timestamp: now,
96
89
  token,
97
- responseCount: existing ? existing.responseCount + 1 : 1,
98
90
  });
99
- if (this.timeoutConfig.enableResponseDebugLogging) {
100
- console.log(`[MiniInteraction:DEBUG] Interaction ${interactionId} state: ${state} (${existing ? existing.responseCount + 1 : 1} responses)`);
101
- }
102
91
  }
103
92
  /**
104
93
  * Checks if an interaction can still respond (not expired and not already responded).
@@ -118,68 +107,12 @@ export class MiniInteraction {
118
107
  }
119
108
  return true;
120
109
  }
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
110
  /**
168
111
  * Gets the current state of an interaction.
169
112
  */
170
113
  getInteractionState(interactionId) {
171
114
  return this.interactionStates.get(interactionId);
172
115
  }
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
116
  /**
184
117
  * Clears expired interaction states to prevent memory leaks.
185
118
  * Call this periodically to clean up old interaction data.
@@ -194,9 +127,6 @@ export class MiniInteraction {
194
127
  cleaned++;
195
128
  }
196
129
  }
197
- if (cleaned > 0) {
198
- console.log(`[MiniInteraction] Cleaned up ${cleaned} expired interactions`);
199
- }
200
130
  return cleaned;
201
131
  }
202
132
  normalizeCommandData(data) {
@@ -530,10 +460,6 @@ export class MiniInteraction {
530
460
  `This may cause "didn't respond in time" errors. ` +
531
461
  `Consider optimizing or using deferReply() for slow operations.`);
532
462
  }
533
- // Log successful response timing for debugging
534
- if (this.timeoutConfig.enableResponseDebugLogging) {
535
- console.log(`[MiniInteraction:DEBUG] Request completed in ${totalProcessingTime}ms`);
536
- }
537
463
  return {
538
464
  status: 400,
539
465
  body: {
@@ -1194,8 +1120,15 @@ export class MiniInteraction {
1194
1120
  };
1195
1121
  }
1196
1122
  try {
1197
- const interactionWithHelpers = createMessageComponentInteraction(interaction);
1198
- // Wrap component handler with timeout
1123
+ // Create an acknowledgment promise that resolves when the handler calls reply() or deferReply()
1124
+ let ackResolver = null;
1125
+ const ackPromise = new Promise((resolve) => {
1126
+ ackResolver = resolve;
1127
+ });
1128
+ const interactionWithHelpers = createMessageComponentInteraction(interaction, {
1129
+ onAck: (response) => ackResolver?.(response),
1130
+ });
1131
+ // Wrap component handler with timeout and acknowledgment
1199
1132
  const timeoutWrapper = createTimeoutWrapper(async () => {
1200
1133
  const response = await handler(interactionWithHelpers);
1201
1134
  const resolvedResponse = response ?? interactionWithHelpers.getResponse();
@@ -1204,7 +1137,7 @@ export class MiniInteraction {
1204
1137
  "Return an APIInteractionResponse to acknowledge the interaction.");
1205
1138
  }
1206
1139
  return resolvedResponse;
1207
- }, this.timeoutConfig.initialResponseTimeout, `Component "${customId}"`, this.timeoutConfig.enableTimeoutWarnings);
1140
+ }, this.timeoutConfig.initialResponseTimeout, `Component "${customId}"`, this.timeoutConfig.enableTimeoutWarnings, ackPromise);
1208
1141
  const resolvedResponse = await timeoutWrapper();
1209
1142
  return {
1210
1143
  status: 200,
@@ -1249,8 +1182,15 @@ export class MiniInteraction {
1249
1182
  };
1250
1183
  }
1251
1184
  try {
1252
- const interactionWithHelpers = createModalSubmitInteraction(interaction);
1253
- // Wrap modal handler with timeout
1185
+ // Create an acknowledgment promise for modals
1186
+ let ackResolver = null;
1187
+ const ackPromise = new Promise((resolve) => {
1188
+ ackResolver = resolve;
1189
+ });
1190
+ const interactionWithHelpers = createModalSubmitInteraction(interaction, {
1191
+ onAck: (response) => ackResolver?.(response),
1192
+ });
1193
+ // Wrap modal handler with timeout and acknowledgment
1254
1194
  const timeoutWrapper = createTimeoutWrapper(async () => {
1255
1195
  const response = await handler(interactionWithHelpers);
1256
1196
  const resolvedResponse = response ?? interactionWithHelpers.getResponse();
@@ -1259,7 +1199,7 @@ export class MiniInteraction {
1259
1199
  "Return an APIInteractionResponse to acknowledge the interaction.");
1260
1200
  }
1261
1201
  return resolvedResponse;
1262
- }, this.timeoutConfig.initialResponseTimeout, `Modal "${customId}"`, this.timeoutConfig.enableTimeoutWarnings);
1202
+ }, this.timeoutConfig.initialResponseTimeout, `Modal "${customId}"`, this.timeoutConfig.enableTimeoutWarnings, ackPromise);
1263
1203
  const resolvedResponse = await timeoutWrapper();
1264
1204
  return {
1265
1205
  status: 200,
@@ -1307,6 +1247,11 @@ export class MiniInteraction {
1307
1247
  try {
1308
1248
  let response;
1309
1249
  let resolvedResponse = null;
1250
+ // Create an acknowledgment promise for application commands
1251
+ let ackResolver = null;
1252
+ const ackPromise = new Promise((resolve) => {
1253
+ ackResolver = resolve;
1254
+ });
1310
1255
  // Create a timeout wrapper for the command handler
1311
1256
  const timeoutWrapper = createTimeoutWrapper(async () => {
1312
1257
  // Check if it's a chat input (slash) command
@@ -1315,7 +1260,7 @@ export class MiniInteraction {
1315
1260
  const interactionWithHelpers = createCommandInteraction(commandInteraction, {
1316
1261
  canRespond: (id) => this.canRespond(id),
1317
1262
  trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
1318
- logTiming: (id, op, start, success) => this.logResponseTiming(id, op, start, success),
1263
+ onAck: (response) => ackResolver?.(response),
1319
1264
  });
1320
1265
  response = await command.handler(interactionWithHelpers);
1321
1266
  resolvedResponse =
@@ -1324,14 +1269,18 @@ export class MiniInteraction {
1324
1269
  else if (commandInteraction.data.type ===
1325
1270
  ApplicationCommandType.User) {
1326
1271
  // User context menu command
1327
- const interactionWithHelpers = createUserContextMenuInteraction(commandInteraction);
1272
+ const interactionWithHelpers = createUserContextMenuInteraction(commandInteraction, {
1273
+ onAck: (response) => ackResolver?.(response),
1274
+ });
1328
1275
  response = await command.handler(interactionWithHelpers);
1329
1276
  resolvedResponse =
1330
1277
  response ?? interactionWithHelpers.getResponse();
1331
1278
  }
1332
1279
  else if (commandInteraction.data.type ===
1333
1280
  ApplicationCommandType.PrimaryEntryPoint) {
1334
- const interactionWithHelpers = createAppCommandInteraction(commandInteraction);
1281
+ const interactionWithHelpers = createAppCommandInteraction(commandInteraction, {
1282
+ onAck: (response) => ackResolver?.(response),
1283
+ });
1335
1284
  response = await command.handler(interactionWithHelpers);
1336
1285
  resolvedResponse =
1337
1286
  response ?? interactionWithHelpers.getResponse();
@@ -1339,7 +1288,9 @@ export class MiniInteraction {
1339
1288
  else if (commandInteraction.data.type ===
1340
1289
  ApplicationCommandType.Message) {
1341
1290
  // Message context menu command
1342
- const interactionWithHelpers = createMessageContextMenuInteraction(commandInteraction);
1291
+ const interactionWithHelpers = createMessageContextMenuInteraction(commandInteraction, {
1292
+ onAck: (response) => ackResolver?.(response),
1293
+ });
1343
1294
  response = await command.handler(interactionWithHelpers);
1344
1295
  resolvedResponse =
1345
1296
  response ?? interactionWithHelpers.getResponse();
@@ -1349,9 +1300,10 @@ export class MiniInteraction {
1349
1300
  response = await command.handler(commandInteraction);
1350
1301
  resolvedResponse = response ?? null;
1351
1302
  }
1352
- }, this.timeoutConfig.initialResponseTimeout, `Command "${commandName}"`, this.timeoutConfig.enableTimeoutWarnings);
1353
- await timeoutWrapper();
1354
- if (!resolvedResponse) {
1303
+ return resolvedResponse;
1304
+ }, this.timeoutConfig.initialResponseTimeout, `Command "${commandName}"`, this.timeoutConfig.enableTimeoutWarnings, ackPromise);
1305
+ const finalResponse = await timeoutWrapper();
1306
+ if (!finalResponse) {
1355
1307
  console.error(`[MiniInteraction] Command "${commandName}" did not return a response. ` +
1356
1308
  "This indicates the handler completed but no response was generated. " +
1357
1309
  "Check that deferReply(), reply(), showModal(), or a direct response is returned.");
@@ -1366,7 +1318,7 @@ export class MiniInteraction {
1366
1318
  }
1367
1319
  return {
1368
1320
  status: 200,
1369
- body: resolvedResponse,
1321
+ body: finalResponse,
1370
1322
  };
1371
1323
  }
1372
1324
  catch (error) {
@@ -1554,7 +1506,7 @@ function resolveOAuthConfig(provided) {
1554
1506
  /**
1555
1507
  * Wraps a handler function with timeout detection and error handling.
1556
1508
  */
1557
- function createTimeoutWrapper(handler, timeoutMs, handlerName, enableWarnings = true) {
1509
+ function createTimeoutWrapper(handler, timeoutMs, handlerName, enableWarnings = true, ackPromise) {
1558
1510
  return async (...args) => {
1559
1511
  const startTime = Date.now();
1560
1512
  let timeoutId;
@@ -1566,10 +1518,14 @@ function createTimeoutWrapper(handler, timeoutMs, handlerName, enableWarnings =
1566
1518
  }, timeoutMs);
1567
1519
  });
1568
1520
  try {
1569
- const result = await Promise.race([
1521
+ const promises = [
1570
1522
  Promise.resolve(handler(...args)),
1571
1523
  timeoutPromise,
1572
- ]);
1524
+ ];
1525
+ if (ackPromise) {
1526
+ promises.push(ackPromise);
1527
+ }
1528
+ const result = await Promise.race(promises);
1573
1529
  if (timeoutId) {
1574
1530
  clearTimeout(timeoutId);
1575
1531
  }
@@ -77,7 +77,6 @@ export class MiniDatabase {
77
77
  await this.mongoClient.connect();
78
78
  this.mongoDb = this.mongoClient.db(this.config.dbName || "minidb");
79
79
  this.mongoCollection = this.mongoDb.collection(this.config.collectionName || "data");
80
- console.log("✅ [MiniDatabase] Connected to MongoDB");
81
80
  }
82
81
  catch (err) {
83
82
  console.error("❌ [MiniDatabase] Failed to connect to MongoDB:", err);
@@ -136,7 +135,6 @@ export class MiniDatabase {
136
135
  createdAt: new Date(),
137
136
  },
138
137
  }, { upsert: true });
139
- console.log(`✅ [MiniDatabase] Saved data for key "${key}"`);
140
138
  return true;
141
139
  }
142
140
  catch (err) {
@@ -175,7 +173,6 @@ export class MiniDatabase {
175
173
  createdAt: new Date(),
176
174
  },
177
175
  }, { upsert: true });
178
- console.log(`✅ [MiniDatabase] Updated data for key "${key}"`);
179
176
  return true;
180
177
  }
181
178
  catch (err) {
@@ -194,7 +191,6 @@ export class MiniDatabase {
194
191
  throw new Error("MongoDB collection is not initialized");
195
192
  }
196
193
  await collection.deleteOne({ _id: key });
197
- console.log(`✅ [MiniDatabase] Deleted data for key "${key}"`);
198
194
  return true;
199
195
  }
200
196
  catch (err) {
@@ -208,7 +204,6 @@ export class MiniDatabase {
208
204
  async close() {
209
205
  if (this.mongoClient) {
210
206
  await this.mongoClient.close();
211
- console.log("✅ [MiniDatabase] MongoDB connection closed");
212
207
  }
213
208
  }
214
209
  }
@@ -154,7 +154,7 @@ export interface CommandInteraction extends Omit<APIChatInputApplicationCommandI
154
154
  withTimeoutProtection<T>(operation: () => Promise<T>, deferOptions?: DeferReplyOptions): Promise<T>;
155
155
  canRespond?(interactionId: string): boolean;
156
156
  trackResponse?(interactionId: string, token: string, state: 'responded' | 'deferred'): void;
157
- logTiming?(interactionId: string, operation: string, startTime: number, success: boolean): void;
157
+ onAck?(response: APIInteractionResponse): void;
158
158
  }
159
159
  export declare const CommandInteraction: {};
160
160
  /**
@@ -168,4 +168,5 @@ export declare function createCommandInteraction(interaction: APIChatInputApplic
168
168
  canRespond?: (interactionId: string) => boolean;
169
169
  trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
170
170
  logTiming?: (interactionId: string, operation: string, startTime: number, success: boolean) => void;
171
+ onAck?: (response: APIInteractionResponse) => void;
171
172
  }): CommandInteraction;
@@ -383,8 +383,9 @@ export function createCommandInteraction(interaction, helpers) {
383
383
  const response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
384
384
  // Track response
385
385
  this.trackResponse?.(this.id, this.token, 'responded');
386
+ // Notify acknowledgment
387
+ this.onAck?.(response);
386
388
  // Log timing if debug enabled
387
- this.logTiming?.(this.id, 'reply', startTime, true);
388
389
  return response;
389
390
  },
390
391
  followUp(data) {
@@ -403,7 +404,6 @@ export function createCommandInteraction(interaction, helpers) {
403
404
  // Track response
404
405
  this.trackResponse?.(this.id, this.token, 'responded');
405
406
  // Log timing if debug enabled
406
- this.logTiming?.(this.id, 'editReply', startTime, true);
407
407
  return response;
408
408
  },
409
409
  deferReply(options) {
@@ -417,8 +417,9 @@ export function createCommandInteraction(interaction, helpers) {
417
417
  : undefined);
418
418
  // Track deferred state
419
419
  this.trackResponse?.(this.id, this.token, 'deferred');
420
+ // Notify acknowledgment
421
+ this.onAck?.(response);
420
422
  // Log timing if debug enabled
421
- this.logTiming?.(this.id, 'deferReply', startTime, true);
422
423
  return response;
423
424
  },
424
425
  showModal(data) {
@@ -469,7 +470,7 @@ export function createCommandInteraction(interaction, helpers) {
469
470
  // Helper methods for state management
470
471
  canRespond: helpers?.canRespond,
471
472
  trackResponse: helpers?.trackResponse,
472
- logTiming: helpers?.logTiming,
473
+ onAck: helpers?.onAck,
473
474
  };
474
475
  return commandInteraction;
475
476
  }
@@ -12,6 +12,7 @@ type ContextMenuInteractionHelpers = {
12
12
  showModal: (data: APIModalInteractionResponseCallbackData | {
13
13
  toJSON(): APIModalInteractionResponseCallbackData;
14
14
  }) => APIModalInteractionResponse;
15
+ onAck?: (response: APIInteractionResponse) => void;
15
16
  };
16
17
  /**
17
18
  * User context menu interaction with helper methods.
@@ -40,19 +41,25 @@ export declare const AppCommandInteraction: {};
40
41
  * @param interaction - The raw user context menu interaction payload from Discord.
41
42
  * @returns A helper-augmented interaction object.
42
43
  */
43
- export declare function createUserContextMenuInteraction(interaction: APIUserApplicationCommandInteraction): UserContextMenuInteraction;
44
+ export declare function createUserContextMenuInteraction(interaction: APIUserApplicationCommandInteraction, helpers?: {
45
+ onAck?: (response: APIInteractionResponse) => void;
46
+ }): UserContextMenuInteraction;
44
47
  /**
45
48
  * Wraps a raw message context menu interaction with helper methods.
46
49
  *
47
50
  * @param interaction - The raw message context menu interaction payload from Discord.
48
51
  * @returns A helper-augmented interaction object.
49
52
  */
50
- export declare function createMessageContextMenuInteraction(interaction: APIMessageApplicationCommandInteraction): MessageContextMenuInteraction;
53
+ export declare function createMessageContextMenuInteraction(interaction: APIMessageApplicationCommandInteraction, helpers?: {
54
+ onAck?: (response: APIInteractionResponse) => void;
55
+ }): MessageContextMenuInteraction;
51
56
  /**
52
57
  * Wraps a raw primary entry point interaction with helper methods.
53
58
  *
54
59
  * @param interaction - The raw primary entry point interaction payload from Discord.
55
60
  * @returns A helper-augmented interaction object.
56
61
  */
57
- export declare function createAppCommandInteraction(interaction: APIPrimaryEntryPointCommandInteraction): AppCommandInteraction;
62
+ export declare function createAppCommandInteraction(interaction: APIPrimaryEntryPointCommandInteraction, helpers?: {
63
+ onAck?: (response: APIInteractionResponse) => void;
64
+ }): AppCommandInteraction;
58
65
  export {};
@@ -3,7 +3,7 @@ import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./inter
3
3
  export const UserContextMenuInteraction = {};
4
4
  export const MessageContextMenuInteraction = {};
5
5
  export const AppCommandInteraction = {};
6
- function createContextMenuInteractionHelpers() {
6
+ function createContextMenuInteractionHelpers(helpers) {
7
7
  let capturedResponse = null;
8
8
  const captureResponse = (response) => {
9
9
  capturedResponse = response;
@@ -25,15 +25,21 @@ function createContextMenuInteractionHelpers() {
25
25
  }
26
26
  return captureResponse({ type });
27
27
  }
28
- const reply = (data) => createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
28
+ const reply = (data) => {
29
+ const response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
30
+ helpers?.onAck?.(response);
31
+ return response;
32
+ };
29
33
  const followUp = (data) => createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
30
34
  const editReply = (data) => createMessageResponse(InteractionResponseType.UpdateMessage, data);
31
35
  const deferReply = (options = {}) => {
32
36
  const flags = normaliseMessageFlags(options.flags);
33
- return captureResponse({
37
+ const response = captureResponse({
34
38
  type: InteractionResponseType.DeferredChannelMessageWithSource,
35
39
  data: flags ? { flags } : undefined,
36
40
  });
41
+ helpers?.onAck?.(response);
42
+ return response;
37
43
  };
38
44
  const showModal = (data) => {
39
45
  const modalData = typeof data === "object" && "toJSON" in data ? data.toJSON() : data;
@@ -50,6 +56,7 @@ function createContextMenuInteractionHelpers() {
50
56
  editReply,
51
57
  deferReply,
52
58
  showModal,
59
+ onAck: helpers?.onAck,
53
60
  };
54
61
  }
55
62
  /**
@@ -58,8 +65,8 @@ function createContextMenuInteractionHelpers() {
58
65
  * @param interaction - The raw user context menu interaction payload from Discord.
59
66
  * @returns A helper-augmented interaction object.
60
67
  */
61
- export function createUserContextMenuInteraction(interaction) {
62
- return Object.assign(interaction, createContextMenuInteractionHelpers(), {
68
+ export function createUserContextMenuInteraction(interaction, helpers) {
69
+ return Object.assign(interaction, createContextMenuInteractionHelpers(helpers), {
63
70
  targetUser: resolveTargetUser(interaction),
64
71
  });
65
72
  }
@@ -69,8 +76,8 @@ export function createUserContextMenuInteraction(interaction) {
69
76
  * @param interaction - The raw message context menu interaction payload from Discord.
70
77
  * @returns A helper-augmented interaction object.
71
78
  */
72
- export function createMessageContextMenuInteraction(interaction) {
73
- return Object.assign(interaction, createContextMenuInteractionHelpers(), {
79
+ export function createMessageContextMenuInteraction(interaction, helpers) {
80
+ return Object.assign(interaction, createContextMenuInteractionHelpers(helpers), {
74
81
  targetMessage: resolveTargetMessage(interaction),
75
82
  });
76
83
  }
@@ -80,8 +87,8 @@ export function createMessageContextMenuInteraction(interaction) {
80
87
  * @param interaction - The raw primary entry point interaction payload from Discord.
81
88
  * @returns A helper-augmented interaction object.
82
89
  */
83
- export function createAppCommandInteraction(interaction) {
84
- return Object.assign(interaction, createContextMenuInteractionHelpers());
90
+ export function createAppCommandInteraction(interaction, helpers) {
91
+ return Object.assign(interaction, createContextMenuInteractionHelpers(helpers));
85
92
  }
86
93
  function resolveTargetMessage(interaction) {
87
94
  const targetId = interaction.data?.target_id;
@@ -27,6 +27,7 @@ type BaseComponentInteractionHelpers = {
27
27
  showModal: (data: APIModalInteractionResponseCallbackData | {
28
28
  toJSON(): APIModalInteractionResponseCallbackData;
29
29
  }) => APIModalInteractionResponse;
30
+ onAck?: (response: APIInteractionResponse) => void;
30
31
  };
31
32
  /**
32
33
  * Button interaction with helper methods.
@@ -135,5 +136,7 @@ export declare const MessageComponentInteraction: {};
135
136
  * @param interaction - The raw interaction payload from Discord.
136
137
  * @returns A helper-augmented interaction object.
137
138
  */
138
- export declare function createMessageComponentInteraction(interaction: APIMessageComponentInteraction): MessageComponentInteraction;
139
+ export declare function createMessageComponentInteraction(interaction: APIMessageComponentInteraction, helpers?: {
140
+ onAck?: (response: APIInteractionResponse) => void;
141
+ }): MessageComponentInteraction;
139
142
  export {};
@@ -15,7 +15,7 @@ export const MessageComponentInteraction = {};
15
15
  * @param interaction - The raw interaction payload from Discord.
16
16
  * @returns A helper-augmented interaction object.
17
17
  */
18
- export function createMessageComponentInteraction(interaction) {
18
+ export function createMessageComponentInteraction(interaction, helpers) {
19
19
  let capturedResponse = null;
20
20
  const captureResponse = (response) => {
21
21
  capturedResponse = response;
@@ -26,10 +26,12 @@ export function createMessageComponentInteraction(interaction) {
26
26
  if (!normalisedData) {
27
27
  throw new Error("[MiniInteraction] Component replies require response data to be provided.");
28
28
  }
29
- return captureResponse({
29
+ const response = captureResponse({
30
30
  type: InteractionResponseType.ChannelMessageWithSource,
31
31
  data: normalisedData,
32
32
  });
33
+ helpers?.onAck?.(response);
34
+ return response;
33
35
  };
34
36
  const deferReply = (options) => {
35
37
  const flags = normaliseMessageFlags(options?.flags);
@@ -41,7 +43,9 @@ export function createMessageComponentInteraction(interaction) {
41
43
  : {
42
44
  type: InteractionResponseType.DeferredChannelMessageWithSource,
43
45
  };
44
- return captureResponse(response);
46
+ captureResponse(response);
47
+ helpers?.onAck?.(response);
48
+ return response;
45
49
  };
46
50
  const update = (data) => {
47
51
  const normalisedData = normaliseInteractionMessageData(data);
@@ -178,5 +182,6 @@ export function createMessageComponentInteraction(interaction) {
178
182
  getChannels,
179
183
  getUsers,
180
184
  getMentionables,
185
+ onAck: helpers?.onAck,
181
186
  });
182
187
  }
@@ -7,6 +7,7 @@ export type ModalSubmitInteraction = APIModalSubmitInteraction & {
7
7
  getResponse: () => APIInteractionResponse | null;
8
8
  reply: (data: InteractionMessageData) => APIInteractionResponseChannelMessageWithSource;
9
9
  deferReply: (options?: DeferReplyOptions) => APIInteractionResponseDeferredChannelMessageWithSource;
10
+ onAck?: (response: APIInteractionResponse) => void;
10
11
  /**
11
12
  * Helper method to get the value of a text input component by custom_id.
12
13
  * @param customId - The custom_id of the text input component
@@ -32,4 +33,6 @@ export declare const ModalSubmitInteraction: {};
32
33
  * @param interaction - The raw interaction payload from Discord.
33
34
  * @returns A helper-augmented interaction object.
34
35
  */
35
- export declare function createModalSubmitInteraction(interaction: APIModalSubmitInteraction): ModalSubmitInteraction;
36
+ export declare function createModalSubmitInteraction(interaction: APIModalSubmitInteraction, helpers?: {
37
+ onAck?: (response: APIInteractionResponse) => void;
38
+ }): ModalSubmitInteraction;
@@ -7,7 +7,7 @@ export const ModalSubmitInteraction = {};
7
7
  * @param interaction - The raw interaction payload from Discord.
8
8
  * @returns A helper-augmented interaction object.
9
9
  */
10
- export function createModalSubmitInteraction(interaction) {
10
+ export function createModalSubmitInteraction(interaction, helpers) {
11
11
  let capturedResponse = null;
12
12
  const captureResponse = (response) => {
13
13
  capturedResponse = response;
@@ -18,10 +18,12 @@ export function createModalSubmitInteraction(interaction) {
18
18
  if (!normalisedData) {
19
19
  throw new Error("[MiniInteraction] Modal submit replies require response data to be provided.");
20
20
  }
21
- return captureResponse({
21
+ const response = captureResponse({
22
22
  type: InteractionResponseType.ChannelMessageWithSource,
23
23
  data: normalisedData,
24
24
  });
25
+ helpers?.onAck?.(response);
26
+ return response;
25
27
  };
26
28
  const deferReply = (options) => {
27
29
  const flags = normaliseMessageFlags(options?.flags);
@@ -33,7 +35,9 @@ export function createModalSubmitInteraction(interaction) {
33
35
  : {
34
36
  type: InteractionResponseType.DeferredChannelMessageWithSource,
35
37
  };
36
- return captureResponse(response);
38
+ captureResponse(response);
39
+ helpers?.onAck?.(response);
40
+ return response;
37
41
  };
38
42
  const getResponse = () => capturedResponse;
39
43
  // Helper to extract text input values from modal components
@@ -95,5 +99,6 @@ export function createModalSubmitInteraction(interaction) {
95
99
  getTextInputValue,
96
100
  getTextInputValues,
97
101
  getSelectMenuValues: (customId) => selectMenuValues.get(customId),
102
+ onAck: helpers?.onAck,
98
103
  });
99
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
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",