@minesa-org/mini-interaction 0.2.9 → 0.2.11
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.
|
@@ -1303,6 +1303,8 @@ export class MiniInteraction {
|
|
|
1303
1303
|
const interactionWithHelpers = createMessageContextMenuInteraction(commandInteraction, {
|
|
1304
1304
|
onAck: (response) => ackResolver?.(response),
|
|
1305
1305
|
sendFollowUp,
|
|
1306
|
+
canRespond: (id) => this.canRespond(id),
|
|
1307
|
+
trackResponse: (id, token, state) => this.trackInteractionState(id, token, state),
|
|
1306
1308
|
});
|
|
1307
1309
|
response = await command.handler(interactionWithHelpers);
|
|
1308
1310
|
resolvedResponse =
|
|
@@ -13,130 +13,32 @@ export type MentionableOption = {
|
|
|
13
13
|
value: APIRole;
|
|
14
14
|
};
|
|
15
15
|
export declare const MentionableOption: {};
|
|
16
|
-
/**
|
|
17
|
-
* Provides ergonomic helpers for extracting typed command options from Discord interactions.
|
|
18
|
-
*/
|
|
19
16
|
export declare class CommandInteractionOptionResolver {
|
|
20
17
|
readonly raw: ReadonlyArray<APIApplicationCommandInteractionDataOption>;
|
|
21
18
|
private readonly resolved;
|
|
22
19
|
private readonly focusedOptions;
|
|
23
20
|
private readonly subcommand;
|
|
24
21
|
private readonly subcommandGroup;
|
|
25
|
-
/**
|
|
26
|
-
* Creates a new resolver from raw interaction options.
|
|
27
|
-
*
|
|
28
|
-
* @param options - The raw options provided by Discord.
|
|
29
|
-
* @param resolved - The resolved data map included with the interaction payload.
|
|
30
|
-
*/
|
|
31
22
|
constructor(options: APIApplicationCommandInteractionDataOption[] | undefined, resolved: APIInteractionDataResolved | undefined);
|
|
32
|
-
/**
|
|
33
|
-
* Retrieves the subcommand invoked by the user.
|
|
34
|
-
*
|
|
35
|
-
* @param required - Whether to throw when the subcommand is missing.
|
|
36
|
-
*/
|
|
37
23
|
getSubcommand(required?: boolean): string | null;
|
|
38
|
-
/**
|
|
39
|
-
* Retrieves the subcommand group invoked by the user.
|
|
40
|
-
*
|
|
41
|
-
* @param required - Whether to throw when the subcommand group is missing.
|
|
42
|
-
*/
|
|
43
24
|
getSubcommandGroup(required?: boolean): string | null;
|
|
44
|
-
/**
|
|
45
|
-
* Looks up a string option by name.
|
|
46
|
-
*
|
|
47
|
-
* @param name - The option name to resolve.
|
|
48
|
-
* @param required - Whether to throw when the option is missing.
|
|
49
|
-
*/
|
|
50
25
|
getString(name: string, required?: boolean): string | null;
|
|
51
|
-
/**
|
|
52
|
-
* Looks up an integer option by name.
|
|
53
|
-
*
|
|
54
|
-
* @param name - The option name to resolve.
|
|
55
|
-
* @param required - Whether to throw when the option is missing.
|
|
56
|
-
*/
|
|
57
26
|
getInteger(name: string, required?: boolean): number | null;
|
|
58
|
-
/**
|
|
59
|
-
* Looks up a numeric option by name.
|
|
60
|
-
*
|
|
61
|
-
* @param name - The option name to resolve.
|
|
62
|
-
*/
|
|
63
27
|
getNumber(name: string, required?: boolean): number | null;
|
|
64
|
-
/**
|
|
65
|
-
* Looks up a boolean option by name.
|
|
66
|
-
*
|
|
67
|
-
* @param name - The option name to resolve.
|
|
68
|
-
* @param required - Whether to throw when the option is missing.
|
|
69
|
-
*/
|
|
70
28
|
getBoolean(name: string, required?: boolean): boolean | null;
|
|
71
|
-
/**
|
|
72
|
-
* Resolves a user option, including any guild member payload.
|
|
73
|
-
*
|
|
74
|
-
* @param name - The option name to resolve.
|
|
75
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
76
|
-
*/
|
|
77
29
|
getUser(name: string, required?: boolean): ResolvedUserOption | null;
|
|
78
|
-
/**
|
|
79
|
-
* Resolves a role option to its resolved payload.
|
|
80
|
-
*
|
|
81
|
-
* @param name - The option name to resolve.
|
|
82
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
83
|
-
*/
|
|
84
30
|
getRole(name: string, required?: boolean): APIRole | null;
|
|
85
|
-
/**
|
|
86
|
-
* Resolves a channel option to its resolved payload.
|
|
87
|
-
*
|
|
88
|
-
* @param name - The option name to resolve.
|
|
89
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
90
|
-
*/
|
|
91
31
|
getChannel(name: string, required?: boolean): APIInteractionDataResolvedChannel | null;
|
|
92
|
-
/**
|
|
93
|
-
* Resolves an attachment option to its resolved payload.
|
|
94
|
-
*
|
|
95
|
-
* @param name - The option name to resolve.
|
|
96
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
97
|
-
*/
|
|
98
32
|
getAttachment(name: string, required?: boolean): APIAttachment | null;
|
|
99
|
-
/**
|
|
100
|
-
* Resolves a mentionable option to either a role or user payload.
|
|
101
|
-
*
|
|
102
|
-
* @param name - The option name to resolve.
|
|
103
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
104
|
-
*/
|
|
105
33
|
getMentionable(name: string, required?: boolean): MentionableOption | null;
|
|
106
|
-
/**
|
|
107
|
-
* Returns the raw option payload, bypassing type checks and casting.
|
|
108
|
-
*
|
|
109
|
-
* @param name - The option name to look up.
|
|
110
|
-
*/
|
|
111
34
|
getRawOption(name: string): APIApplicationCommandInteractionDataOption | null;
|
|
112
|
-
/**
|
|
113
|
-
* Extracts a strongly typed option, ensuring it exists and matches the expected type.
|
|
114
|
-
*/
|
|
115
35
|
private extractOption;
|
|
116
|
-
/**
|
|
117
|
-
* Resolves an attachment by identifier from the interaction's resolved payloads.
|
|
118
|
-
*/
|
|
119
36
|
private resolveAttachment;
|
|
120
|
-
/**
|
|
121
|
-
* Resolves a channel by identifier from the interaction's resolved payloads.
|
|
122
|
-
*/
|
|
123
37
|
private resolveChannel;
|
|
124
|
-
/**
|
|
125
|
-
* Resolves a role by identifier from the interaction's resolved payloads.
|
|
126
|
-
*/
|
|
127
38
|
private resolveRole;
|
|
128
|
-
/**
|
|
129
|
-
* Resolves a user and optional member by identifier from the interaction's resolved payloads.
|
|
130
|
-
*/
|
|
131
39
|
private resolveUser;
|
|
132
|
-
/**
|
|
133
|
-
* Retrieves a record from a resolved payload map by identifier.
|
|
134
|
-
*/
|
|
135
40
|
private getResolvedRecord;
|
|
136
41
|
}
|
|
137
|
-
/**
|
|
138
|
-
* Represents a command interaction augmented with helper methods for easy responses.
|
|
139
|
-
*/
|
|
140
42
|
export interface CommandInteraction extends Omit<APIChatInputApplicationCommandInteraction, "data"> {
|
|
141
43
|
data: Omit<APIChatInputApplicationCommandInteraction["data"], "options"> & {
|
|
142
44
|
options: CommandInteractionOptionResolver;
|
|
@@ -153,21 +55,14 @@ export interface CommandInteraction extends Omit<APIChatInputApplicationCommandI
|
|
|
153
55
|
}): APIModalInteractionResponse;
|
|
154
56
|
withTimeoutProtection<T>(operation: () => Promise<T>, deferOptions?: DeferReplyOptions): Promise<T>;
|
|
155
57
|
canRespond?(interactionId: string): boolean;
|
|
156
|
-
trackResponse?(interactionId: string, token: string, state:
|
|
58
|
+
trackResponse?(interactionId: string, token: string, state: "responded" | "deferred"): void;
|
|
157
59
|
onAck?(response: APIInteractionResponse): void;
|
|
158
60
|
sendFollowUp?(token: string, response: APIInteractionResponse, messageId?: string): Promise<void>;
|
|
159
61
|
}
|
|
160
62
|
export declare const CommandInteraction: {};
|
|
161
|
-
/**
|
|
162
|
-
* Wraps a raw application command interaction with helper methods and option resolvers.
|
|
163
|
-
*
|
|
164
|
-
* @param interaction - The raw interaction payload from Discord.
|
|
165
|
-
* @param helpers - Optional helper methods for state management and logging.
|
|
166
|
-
* @returns A helper-augmented interaction object.
|
|
167
|
-
*/
|
|
168
63
|
export declare function createCommandInteraction(interaction: APIChatInputApplicationCommandInteraction, helpers?: {
|
|
169
64
|
canRespond?: (interactionId: string) => boolean;
|
|
170
|
-
trackResponse?: (interactionId: string, token: string, state:
|
|
65
|
+
trackResponse?: (interactionId: string, token: string, state: "responded" | "deferred") => void;
|
|
171
66
|
logTiming?: (interactionId: string, operation: string, startTime: number, success: boolean) => void;
|
|
172
67
|
onAck?: (response: APIInteractionResponse) => void;
|
|
173
68
|
sendFollowUp?: (token: string, response: APIInteractionResponse, messageId?: string) => Promise<void>;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { ApplicationCommandOptionType, InteractionResponseType,
|
|
1
|
+
import { ApplicationCommandOptionType, InteractionResponseType, } from "discord-api-types/v10";
|
|
2
2
|
import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
|
|
3
3
|
export const ResolvedUserOption = {};
|
|
4
4
|
export const MentionableOption = {};
|
|
5
|
-
/** Maps application command option types to human-readable labels for error messages. */
|
|
6
5
|
const OPTION_TYPE_LABEL = {
|
|
7
6
|
[ApplicationCommandOptionType.Subcommand]: "subcommand",
|
|
8
7
|
[ApplicationCommandOptionType.SubcommandGroup]: "subcommand group",
|
|
@@ -16,28 +15,12 @@ const OPTION_TYPE_LABEL = {
|
|
|
16
15
|
[ApplicationCommandOptionType.Number]: "number",
|
|
17
16
|
[ApplicationCommandOptionType.Attachment]: "attachment",
|
|
18
17
|
};
|
|
19
|
-
/**
|
|
20
|
-
* Determines whether the provided option represents a subcommand.
|
|
21
|
-
*
|
|
22
|
-
* @param option - The option to inspect.
|
|
23
|
-
*/
|
|
24
18
|
function isSubcommandOption(option) {
|
|
25
19
|
return option.type === ApplicationCommandOptionType.Subcommand;
|
|
26
20
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Determines whether the provided option represents a subcommand group.
|
|
29
|
-
*
|
|
30
|
-
* @param option - The option to inspect.
|
|
31
|
-
*/
|
|
32
21
|
function isSubcommandGroupOption(option) {
|
|
33
22
|
return option.type === ApplicationCommandOptionType.SubcommandGroup;
|
|
34
23
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Extracts the most relevant option set from the raw command options hierarchy.
|
|
37
|
-
*
|
|
38
|
-
* @param options - The raw options array from the interaction payload.
|
|
39
|
-
* @returns The resolved subcommand context and focused options for convenience accessors.
|
|
40
|
-
*/
|
|
41
24
|
function resolveFocusedOptions(options) {
|
|
42
25
|
if (!options || options.length === 0) {
|
|
43
26
|
return { subcommandGroup: null, subcommand: null, options: [] };
|
|
@@ -71,21 +54,12 @@ function resolveFocusedOptions(options) {
|
|
|
71
54
|
options: options,
|
|
72
55
|
};
|
|
73
56
|
}
|
|
74
|
-
/**
|
|
75
|
-
* Provides ergonomic helpers for extracting typed command options from Discord interactions.
|
|
76
|
-
*/
|
|
77
57
|
export class CommandInteractionOptionResolver {
|
|
78
58
|
raw;
|
|
79
59
|
resolved;
|
|
80
60
|
focusedOptions;
|
|
81
61
|
subcommand;
|
|
82
62
|
subcommandGroup;
|
|
83
|
-
/**
|
|
84
|
-
* Creates a new resolver from raw interaction options.
|
|
85
|
-
*
|
|
86
|
-
* @param options - The raw options provided by Discord.
|
|
87
|
-
* @param resolved - The resolved data map included with the interaction payload.
|
|
88
|
-
*/
|
|
89
63
|
constructor(options, resolved) {
|
|
90
64
|
this.raw = Object.freeze([...(options ?? [])]);
|
|
91
65
|
this.resolved = resolved;
|
|
@@ -94,11 +68,6 @@ export class CommandInteractionOptionResolver {
|
|
|
94
68
|
this.subcommand = focused.subcommand;
|
|
95
69
|
this.subcommandGroup = focused.subcommandGroup;
|
|
96
70
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Retrieves the subcommand invoked by the user.
|
|
99
|
-
*
|
|
100
|
-
* @param required - Whether to throw when the subcommand is missing.
|
|
101
|
-
*/
|
|
102
71
|
getSubcommand(required = true) {
|
|
103
72
|
if (this.subcommand) {
|
|
104
73
|
return this.subcommand;
|
|
@@ -108,11 +77,6 @@ export class CommandInteractionOptionResolver {
|
|
|
108
77
|
}
|
|
109
78
|
return null;
|
|
110
79
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Retrieves the subcommand group invoked by the user.
|
|
113
|
-
*
|
|
114
|
-
* @param required - Whether to throw when the subcommand group is missing.
|
|
115
|
-
*/
|
|
116
80
|
getSubcommandGroup(required = true) {
|
|
117
81
|
if (this.subcommandGroup) {
|
|
118
82
|
return this.subcommandGroup;
|
|
@@ -122,51 +86,22 @@ export class CommandInteractionOptionResolver {
|
|
|
122
86
|
}
|
|
123
87
|
return null;
|
|
124
88
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Looks up a string option by name.
|
|
127
|
-
*
|
|
128
|
-
* @param name - The option name to resolve.
|
|
129
|
-
* @param required - Whether to throw when the option is missing.
|
|
130
|
-
*/
|
|
131
89
|
getString(name, required = false) {
|
|
132
90
|
const option = this.extractOption(name, ApplicationCommandOptionType.String, required);
|
|
133
91
|
return option ? option.value : null;
|
|
134
92
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Looks up an integer option by name.
|
|
137
|
-
*
|
|
138
|
-
* @param name - The option name to resolve.
|
|
139
|
-
* @param required - Whether to throw when the option is missing.
|
|
140
|
-
*/
|
|
141
93
|
getInteger(name, required = false) {
|
|
142
94
|
const option = this.extractOption(name, ApplicationCommandOptionType.Integer, required);
|
|
143
95
|
return option ? option.value : null;
|
|
144
96
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Looks up a numeric option by name.
|
|
147
|
-
*
|
|
148
|
-
* @param name - The option name to resolve.
|
|
149
|
-
*/
|
|
150
97
|
getNumber(name, required = false) {
|
|
151
98
|
const option = this.extractOption(name, ApplicationCommandOptionType.Number, required);
|
|
152
99
|
return option ? option.value : null;
|
|
153
100
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Looks up a boolean option by name.
|
|
156
|
-
*
|
|
157
|
-
* @param name - The option name to resolve.
|
|
158
|
-
* @param required - Whether to throw when the option is missing.
|
|
159
|
-
*/
|
|
160
101
|
getBoolean(name, required = false) {
|
|
161
102
|
const option = this.extractOption(name, ApplicationCommandOptionType.Boolean, required);
|
|
162
103
|
return option ? option.value : null;
|
|
163
104
|
}
|
|
164
|
-
/**
|
|
165
|
-
* Resolves a user option, including any guild member payload.
|
|
166
|
-
*
|
|
167
|
-
* @param name - The option name to resolve.
|
|
168
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
169
|
-
*/
|
|
170
105
|
getUser(name, required = false) {
|
|
171
106
|
const option = this.extractOption(name, ApplicationCommandOptionType.User, required);
|
|
172
107
|
if (!option) {
|
|
@@ -181,12 +116,6 @@ export class CommandInteractionOptionResolver {
|
|
|
181
116
|
}
|
|
182
117
|
return resolvedUser;
|
|
183
118
|
}
|
|
184
|
-
/**
|
|
185
|
-
* Resolves a role option to its resolved payload.
|
|
186
|
-
*
|
|
187
|
-
* @param name - The option name to resolve.
|
|
188
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
189
|
-
*/
|
|
190
119
|
getRole(name, required = false) {
|
|
191
120
|
const option = this.extractOption(name, ApplicationCommandOptionType.Role, required);
|
|
192
121
|
if (!option) {
|
|
@@ -198,12 +127,6 @@ export class CommandInteractionOptionResolver {
|
|
|
198
127
|
}
|
|
199
128
|
return role ?? null;
|
|
200
129
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Resolves a channel option to its resolved payload.
|
|
203
|
-
*
|
|
204
|
-
* @param name - The option name to resolve.
|
|
205
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
206
|
-
*/
|
|
207
130
|
getChannel(name, required = false) {
|
|
208
131
|
const option = this.extractOption(name, ApplicationCommandOptionType.Channel, required);
|
|
209
132
|
if (!option) {
|
|
@@ -215,12 +138,6 @@ export class CommandInteractionOptionResolver {
|
|
|
215
138
|
}
|
|
216
139
|
return channel ?? null;
|
|
217
140
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Resolves an attachment option to its resolved payload.
|
|
220
|
-
*
|
|
221
|
-
* @param name - The option name to resolve.
|
|
222
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
223
|
-
*/
|
|
224
141
|
getAttachment(name, required = false) {
|
|
225
142
|
const option = this.extractOption(name, ApplicationCommandOptionType.Attachment, required);
|
|
226
143
|
if (!option) {
|
|
@@ -232,12 +149,6 @@ export class CommandInteractionOptionResolver {
|
|
|
232
149
|
}
|
|
233
150
|
return attachment ?? null;
|
|
234
151
|
}
|
|
235
|
-
/**
|
|
236
|
-
* Resolves a mentionable option to either a role or user payload.
|
|
237
|
-
*
|
|
238
|
-
* @param name - The option name to resolve.
|
|
239
|
-
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
240
|
-
*/
|
|
241
152
|
getMentionable(name, required = false) {
|
|
242
153
|
const option = this.extractOption(name, ApplicationCommandOptionType.Mentionable, required);
|
|
243
154
|
if (!option) {
|
|
@@ -256,17 +167,9 @@ export class CommandInteractionOptionResolver {
|
|
|
256
167
|
}
|
|
257
168
|
return null;
|
|
258
169
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Returns the raw option payload, bypassing type checks and casting.
|
|
261
|
-
*
|
|
262
|
-
* @param name - The option name to look up.
|
|
263
|
-
*/
|
|
264
170
|
getRawOption(name) {
|
|
265
171
|
return (this.focusedOptions.find((option) => option.name === name) ?? null);
|
|
266
172
|
}
|
|
267
|
-
/**
|
|
268
|
-
* Extracts a strongly typed option, ensuring it exists and matches the expected type.
|
|
269
|
-
*/
|
|
270
173
|
extractOption(name, type, required) {
|
|
271
174
|
const option = this.focusedOptions.find((candidate) => candidate.name === name);
|
|
272
175
|
if (!option) {
|
|
@@ -280,27 +183,15 @@ export class CommandInteractionOptionResolver {
|
|
|
280
183
|
}
|
|
281
184
|
return option;
|
|
282
185
|
}
|
|
283
|
-
/**
|
|
284
|
-
* Resolves an attachment by identifier from the interaction's resolved payloads.
|
|
285
|
-
*/
|
|
286
186
|
resolveAttachment(id) {
|
|
287
187
|
return this.getResolvedRecord(this.resolved?.attachments, id);
|
|
288
188
|
}
|
|
289
|
-
/**
|
|
290
|
-
* Resolves a channel by identifier from the interaction's resolved payloads.
|
|
291
|
-
*/
|
|
292
189
|
resolveChannel(id) {
|
|
293
190
|
return this.getResolvedRecord(this.resolved?.channels, id);
|
|
294
191
|
}
|
|
295
|
-
/**
|
|
296
|
-
* Resolves a role by identifier from the interaction's resolved payloads.
|
|
297
|
-
*/
|
|
298
192
|
resolveRole(id) {
|
|
299
193
|
return this.getResolvedRecord(this.resolved?.roles, id);
|
|
300
194
|
}
|
|
301
|
-
/**
|
|
302
|
-
* Resolves a user and optional member by identifier from the interaction's resolved payloads.
|
|
303
|
-
*/
|
|
304
195
|
resolveUser(id) {
|
|
305
196
|
const user = this.getResolvedRecord(this.resolved?.users, id);
|
|
306
197
|
if (!user) {
|
|
@@ -309,29 +200,16 @@ export class CommandInteractionOptionResolver {
|
|
|
309
200
|
const member = this.getResolvedRecord(this.resolved?.members, id);
|
|
310
201
|
return { user, member: member ?? undefined };
|
|
311
202
|
}
|
|
312
|
-
/**
|
|
313
|
-
* Retrieves a record from a resolved payload map by identifier.
|
|
314
|
-
*/
|
|
315
203
|
getResolvedRecord(records, id) {
|
|
316
204
|
return records?.[id];
|
|
317
205
|
}
|
|
318
206
|
}
|
|
319
207
|
export const CommandInteraction = {};
|
|
320
|
-
/**
|
|
321
|
-
* Wraps a raw application command interaction with helper methods and option resolvers.
|
|
322
|
-
*
|
|
323
|
-
* @param interaction - The raw interaction payload from Discord.
|
|
324
|
-
* @param helpers - Optional helper methods for state management and logging.
|
|
325
|
-
* @returns A helper-augmented interaction object.
|
|
326
|
-
*/
|
|
327
208
|
export function createCommandInteraction(interaction, helpers) {
|
|
328
209
|
const options = new CommandInteractionOptionResolver(interaction.data.options, interaction.data.resolved);
|
|
329
210
|
let capturedResponse = null;
|
|
330
211
|
let isDeferred = false;
|
|
331
212
|
let hasResponded = false;
|
|
332
|
-
/**
|
|
333
|
-
* Stores the most recent response helper payload for later retrieval.
|
|
334
|
-
*/
|
|
335
213
|
const captureResponse = (response) => {
|
|
336
214
|
capturedResponse = response;
|
|
337
215
|
return response;
|
|
@@ -352,9 +230,6 @@ export function createCommandInteraction(interaction, helpers) {
|
|
|
352
230
|
}
|
|
353
231
|
return captureResponse({ type });
|
|
354
232
|
}
|
|
355
|
-
/**
|
|
356
|
-
* Creates a deferred response while normalising any helper flag values.
|
|
357
|
-
*/
|
|
358
233
|
const createDeferredResponse = (data) => {
|
|
359
234
|
if (!data) {
|
|
360
235
|
return captureResponse({
|
|
@@ -377,23 +252,26 @@ export function createCommandInteraction(interaction, helpers) {
|
|
|
377
252
|
return capturedResponse;
|
|
378
253
|
},
|
|
379
254
|
reply(data) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
throw new Error('Interaction cannot respond: already responded or expired');
|
|
255
|
+
if (this.canRespond && !this.canRespond(this.id)) {
|
|
256
|
+
throw new Error("Interaction cannot respond: already responded or expired");
|
|
383
257
|
}
|
|
384
258
|
const response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
|
|
385
|
-
|
|
386
|
-
this.trackResponse?.(this.id, this.token, 'responded');
|
|
259
|
+
this.trackResponse?.(this.id, this.token, "responded");
|
|
387
260
|
hasResponded = true;
|
|
388
|
-
// Notify acknowledgment
|
|
389
261
|
this.onAck?.(response);
|
|
390
262
|
return response;
|
|
391
263
|
},
|
|
392
264
|
async followUp(data) {
|
|
393
|
-
const
|
|
265
|
+
const normalisedData = normaliseInteractionMessageData(data);
|
|
266
|
+
if (!normalisedData) {
|
|
267
|
+
throw new Error("[MiniInteraction] followUp requires data");
|
|
268
|
+
}
|
|
269
|
+
const response = {
|
|
270
|
+
type: InteractionResponseType.ChannelMessageWithSource,
|
|
271
|
+
data: normalisedData,
|
|
272
|
+
};
|
|
394
273
|
if (this.sendFollowUp) {
|
|
395
|
-
|
|
396
|
-
await this.sendFollowUp(this.token, response, '');
|
|
274
|
+
await this.sendFollowUp(this.token, response, "");
|
|
397
275
|
}
|
|
398
276
|
return response;
|
|
399
277
|
},
|
|
@@ -401,46 +279,39 @@ export function createCommandInteraction(interaction, helpers) {
|
|
|
401
279
|
return createMessageResponse(InteractionResponseType.UpdateMessage, data);
|
|
402
280
|
},
|
|
403
281
|
async editReply(data) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
throw new Error('Interaction cannot edit reply: expired');
|
|
282
|
+
if (this.canRespond && !this.canRespond(this.id)) {
|
|
283
|
+
throw new Error("Interaction cannot edit reply: expired");
|
|
407
284
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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;
|
|
285
|
+
if (isDeferred) {
|
|
286
|
+
const normalisedData = normaliseInteractionMessageData(data);
|
|
287
|
+
if (!normalisedData) {
|
|
288
|
+
throw new Error("[MiniInteraction] editReply requires data when deferred");
|
|
289
|
+
}
|
|
290
|
+
const response = {
|
|
291
|
+
type: InteractionResponseType.ChannelMessageWithSource,
|
|
292
|
+
data: normalisedData,
|
|
293
|
+
};
|
|
294
|
+
if (this.sendFollowUp) {
|
|
295
|
+
await this.sendFollowUp(this.token, response, "@original");
|
|
296
|
+
}
|
|
297
|
+
this.trackResponse?.(this.id, this.token, "responded");
|
|
298
|
+
hasResponded = true;
|
|
299
|
+
return response;
|
|
426
300
|
}
|
|
427
|
-
|
|
428
|
-
this.trackResponse?.(this.id, this.token,
|
|
301
|
+
const response = createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
|
|
302
|
+
this.trackResponse?.(this.id, this.token, "responded");
|
|
429
303
|
hasResponded = true;
|
|
430
304
|
return response;
|
|
431
305
|
},
|
|
432
306
|
deferReply(options) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
throw new Error('Interaction cannot defer: already responded or expired');
|
|
307
|
+
if (this.canRespond && !this.canRespond(this.id)) {
|
|
308
|
+
throw new Error("Interaction cannot defer: already responded or expired");
|
|
436
309
|
}
|
|
437
310
|
const response = createDeferredResponse(options?.flags !== undefined
|
|
438
311
|
? { flags: options.flags }
|
|
439
312
|
: undefined);
|
|
440
|
-
|
|
441
|
-
this.trackResponse?.(this.id, this.token, 'deferred');
|
|
313
|
+
this.trackResponse?.(this.id, this.token, "deferred");
|
|
442
314
|
isDeferred = true;
|
|
443
|
-
// Notify acknowledgment
|
|
444
315
|
this.onAck?.(response);
|
|
445
316
|
return response;
|
|
446
317
|
},
|
|
@@ -455,17 +326,9 @@ export function createCommandInteraction(interaction, helpers) {
|
|
|
455
326
|
data: resolvedData,
|
|
456
327
|
});
|
|
457
328
|
},
|
|
458
|
-
/**
|
|
459
|
-
* Creates a delayed response wrapper that automatically defers if the operation takes too long.
|
|
460
|
-
* Use this for operations that might exceed Discord's 3-second limit.
|
|
461
|
-
*
|
|
462
|
-
* @param operation - The async operation to perform
|
|
463
|
-
* @param deferOptions - Options for automatic deferral
|
|
464
|
-
*/
|
|
465
329
|
async withTimeoutProtection(operation, deferOptions) {
|
|
466
330
|
const startTime = Date.now();
|
|
467
331
|
let deferred = false;
|
|
468
|
-
// Set up a timer to auto-defer after 2.5 seconds
|
|
469
332
|
const deferTimer = setTimeout(async () => {
|
|
470
333
|
if (!deferred) {
|
|
471
334
|
console.warn("[MiniInteraction] Auto-deferring interaction due to slow operation. " +
|
|
@@ -489,7 +352,6 @@ export function createCommandInteraction(interaction, helpers) {
|
|
|
489
352
|
throw error;
|
|
490
353
|
}
|
|
491
354
|
},
|
|
492
|
-
// Helper methods for state management
|
|
493
355
|
canRespond: helpers?.canRespond,
|
|
494
356
|
trackResponse: helpers?.trackResponse,
|
|
495
357
|
onAck: helpers?.onAck,
|
|
@@ -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,21 +45,31 @@ function createContextMenuInteractionHelpers(interaction, helpers) {
|
|
|
41
45
|
return response;
|
|
42
46
|
};
|
|
43
47
|
const editReply = async (data) => {
|
|
44
|
-
|
|
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');
|
|
47
56
|
return capturedResponse;
|
|
48
57
|
}
|
|
49
58
|
hasResponded = true;
|
|
59
|
+
helpers?.trackResponse?.(interaction.id, interaction.token, 'responded');
|
|
50
60
|
return response;
|
|
51
61
|
};
|
|
52
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
|
+
}
|
|
53
66
|
const flags = normaliseMessageFlags(options.flags);
|
|
54
67
|
const response = captureResponse({
|
|
55
68
|
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
|
56
69
|
data: flags ? { flags } : undefined,
|
|
57
70
|
});
|
|
58
71
|
isDeferred = true;
|
|
72
|
+
helpers?.trackResponse?.(interaction.id, interaction.token, 'deferred');
|
|
59
73
|
helpers?.onAck?.(response);
|
|
60
74
|
return response;
|
|
61
75
|
};
|
|
@@ -71,7 +85,7 @@ function createContextMenuInteractionHelpers(interaction, helpers) {
|
|
|
71
85
|
getResponse,
|
|
72
86
|
reply,
|
|
73
87
|
followUp,
|
|
74
|
-
editReply,
|
|
88
|
+
editReply: editReply,
|
|
75
89
|
deferReply,
|
|
76
90
|
showModal,
|
|
77
91
|
onAck: helpers?.onAck,
|
|
@@ -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