@minesa-org/mini-interaction 0.4.1 → 0.4.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.
- package/dist/builders/ActionRowBuilder.d.ts +4 -5
- package/dist/builders/CheckboxBuilder.d.ts +17 -0
- package/dist/builders/CheckboxBuilder.js +24 -0
- package/dist/builders/index.d.ts +2 -0
- package/dist/builders/index.js +1 -0
- package/dist/compat/MiniInteraction.d.ts +64 -0
- package/dist/compat/MiniInteraction.js +336 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +3 -1
- package/dist/types/Commands.d.ts +14 -0
- package/dist/types/ComponentTypes.d.ts +17 -3
- package/dist/types/checkbox.d.ts +23 -0
- package/dist/types/checkbox.js +5 -0
- package/dist/utils/MessageComponentInteraction.d.ts +38 -0
- package/dist/utils/MessageComponentInteraction.js +2 -0
- package/dist/utils/interactionMessageHelpers.d.ts +3 -2
- package/dist/utils/interactionMessageHelpers.js +2 -0
- package/package.json +1 -1
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import type { APIActionRowComponent } from "discord-api-types/v10";
|
|
2
1
|
import type { JSONEncodable } from "./shared.js";
|
|
3
|
-
import type { ActionRowComponent } from "../types/ComponentTypes.js";
|
|
2
|
+
import type { ActionRowComponent, MiniActionRow } from "../types/ComponentTypes.js";
|
|
4
3
|
/** Values accepted when composing component action rows. */
|
|
5
4
|
export type ActionRowComponentLike<T extends ActionRowComponent> = JSONEncodable<T> | T;
|
|
6
5
|
/** Builder for creating Action Row components. */
|
|
7
|
-
export declare class ActionRowBuilder<T extends ActionRowComponent> implements JSONEncodable<
|
|
6
|
+
export declare class ActionRowBuilder<T extends ActionRowComponent> implements JSONEncodable<MiniActionRow<T>> {
|
|
8
7
|
private components;
|
|
9
|
-
constructor(data?: Partial<
|
|
8
|
+
constructor(data?: Partial<MiniActionRow<T>>);
|
|
10
9
|
/**
|
|
11
10
|
* Adds components to this action row.
|
|
12
11
|
*
|
|
@@ -19,5 +18,5 @@ export declare class ActionRowBuilder<T extends ActionRowComponent> implements J
|
|
|
19
18
|
* @param components - The new components to set.
|
|
20
19
|
*/
|
|
21
20
|
setComponents(...components: (T | JSONEncodable<T>)[]): this;
|
|
22
|
-
toJSON():
|
|
21
|
+
toJSON(): MiniActionRow<T>;
|
|
23
22
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JSONEncodable } from './shared.js';
|
|
2
|
+
import { APICheckboxComponent, APICheckboxOption } from '../types/checkbox.js';
|
|
3
|
+
export type CheckboxBuilderData = {
|
|
4
|
+
customId?: string;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
options?: APICheckboxOption[];
|
|
8
|
+
};
|
|
9
|
+
export declare class CheckboxBuilder implements JSONEncodable<APICheckboxComponent> {
|
|
10
|
+
private readonly data;
|
|
11
|
+
constructor(data?: CheckboxBuilderData);
|
|
12
|
+
setCustomId(customId: string): this;
|
|
13
|
+
setRequired(required: boolean): this;
|
|
14
|
+
setDisabled(disabled: boolean): this;
|
|
15
|
+
addOptions(...options: APICheckboxOption[]): this;
|
|
16
|
+
toJSON(): APICheckboxComponent;
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CHECKBOX_COMPONENT_TYPE } from '../types/checkbox.js';
|
|
2
|
+
import { assertDefined, assertStringLength, ValidationError } from '../types/validation.js';
|
|
3
|
+
export class CheckboxBuilder {
|
|
4
|
+
data;
|
|
5
|
+
constructor(data = {}) { this.data = { ...data, options: data.options ? [...data.options] : [] }; }
|
|
6
|
+
setCustomId(customId) { this.data.customId = customId; return this; }
|
|
7
|
+
setRequired(required) { this.data.required = required; return this; }
|
|
8
|
+
setDisabled(disabled) { this.data.disabled = disabled; return this; }
|
|
9
|
+
addOptions(...options) { this.data.options = [...(this.data.options ?? []), ...options]; return this; }
|
|
10
|
+
toJSON() {
|
|
11
|
+
const customId = assertDefined('CheckboxBuilder', 'custom_id', this.data.customId);
|
|
12
|
+
assertStringLength('CheckboxBuilder', 'custom_id', customId, 1, 100);
|
|
13
|
+
const options = [...(this.data.options ?? [])];
|
|
14
|
+
if (options.length === 0 || options.length > 25)
|
|
15
|
+
throw new ValidationError('CheckboxBuilder', 'options', 'must contain 1-25 options');
|
|
16
|
+
for (const [index, option] of options.entries()) {
|
|
17
|
+
assertStringLength('CheckboxBuilder', `options[${index}].label`, option.label, 1, 100);
|
|
18
|
+
assertStringLength('CheckboxBuilder', `options[${index}].value`, option.value, 1, 100);
|
|
19
|
+
if (option.description)
|
|
20
|
+
assertStringLength('CheckboxBuilder', `options[${index}].description`, option.description, 1, 100);
|
|
21
|
+
}
|
|
22
|
+
return { type: CHECKBOX_COMPONENT_TYPE, custom_id: customId, disabled: this.data.disabled, required: this.data.required, options };
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/builders/index.d.ts
CHANGED
|
@@ -38,3 +38,5 @@ export type { ContainerBuilderData, SectionBuilderData, TextDisplayBuilderData,
|
|
|
38
38
|
export type { JSONEncodable } from "./shared.js";
|
|
39
39
|
export { RadioBuilder } from "./RadioBuilder.js";
|
|
40
40
|
export type { RadioBuilderData } from "./RadioBuilder.js";
|
|
41
|
+
export { CheckboxBuilder } from "./CheckboxBuilder.js";
|
|
42
|
+
export type { CheckboxBuilderData } from "./CheckboxBuilder.js";
|
package/dist/builders/index.js
CHANGED
|
@@ -18,3 +18,4 @@ export { AutomodRuleBuilder } from "./AutomodRuleBuilder.js";
|
|
|
18
18
|
export { EmbedBuilder } from "./EmbedBuilder.js";
|
|
19
19
|
export { ContainerBuilder, SectionBuilder, TextDisplayBuilder, SeparatorBuilder, GalleryBuilder, GalleryItemBuilder, ThumbnailBuilder, } from "./MiniContainerBuilder.js";
|
|
20
20
|
export { RadioBuilder } from "./RadioBuilder.js";
|
|
21
|
+
export { CheckboxBuilder } from "./CheckboxBuilder.js";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
type TimeoutConfig = {
|
|
2
|
+
initialResponseTimeout?: number;
|
|
3
|
+
autoDeferSlowOperations?: boolean;
|
|
4
|
+
enableTimeoutWarnings?: boolean;
|
|
5
|
+
enableResponseDebugLogging?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type MiniInteractionOptions = {
|
|
8
|
+
commandsDirectory?: string;
|
|
9
|
+
componentsDirectory?: string;
|
|
10
|
+
utilsDirectory?: string;
|
|
11
|
+
timeoutConfig?: TimeoutConfig;
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
cwd?: string;
|
|
14
|
+
publicKey?: string;
|
|
15
|
+
applicationId?: string;
|
|
16
|
+
token?: string;
|
|
17
|
+
};
|
|
18
|
+
type NodeRequest = {
|
|
19
|
+
body?: unknown;
|
|
20
|
+
rawBody?: string | Uint8Array | Buffer;
|
|
21
|
+
headers: Record<string, string | string[] | undefined> | {
|
|
22
|
+
get(name: string): string | null;
|
|
23
|
+
};
|
|
24
|
+
method?: string;
|
|
25
|
+
[Symbol.asyncIterator]?: () => AsyncIterableIterator<Uint8Array>;
|
|
26
|
+
on?: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
27
|
+
};
|
|
28
|
+
type NodeResponse = {
|
|
29
|
+
statusCode?: number;
|
|
30
|
+
setHeader?: (name: string, value: string) => void;
|
|
31
|
+
end: (body?: string) => void;
|
|
32
|
+
status?: (code: number) => NodeResponse;
|
|
33
|
+
json?: (body: unknown) => void;
|
|
34
|
+
};
|
|
35
|
+
export declare class MiniInteraction {
|
|
36
|
+
private readonly options;
|
|
37
|
+
private readonly projectRoot;
|
|
38
|
+
private readonly rest;
|
|
39
|
+
private readonly responseStates;
|
|
40
|
+
private loadedModulesPromise?;
|
|
41
|
+
constructor(options?: MiniInteractionOptions);
|
|
42
|
+
createNodeHandler(): (req: NodeRequest, res: NodeResponse) => Promise<void>;
|
|
43
|
+
private dispatch;
|
|
44
|
+
private executeCommandHandler;
|
|
45
|
+
private executeComponentHandler;
|
|
46
|
+
private executeModalHandler;
|
|
47
|
+
private runWithResponseLifecycle;
|
|
48
|
+
private loadModules;
|
|
49
|
+
private discoverModules;
|
|
50
|
+
private loadDirectory;
|
|
51
|
+
private normalizeModuleExports;
|
|
52
|
+
private normalizeExportValue;
|
|
53
|
+
private walkFiles;
|
|
54
|
+
private isImportableModule;
|
|
55
|
+
private isInteractionCommand;
|
|
56
|
+
private getCommandName;
|
|
57
|
+
private isCustomIdHandler;
|
|
58
|
+
private looksLikeModalFile;
|
|
59
|
+
private readRawBody;
|
|
60
|
+
private getHeader;
|
|
61
|
+
private sendJson;
|
|
62
|
+
}
|
|
63
|
+
export declare const LegacyMiniInteractionAdapter: typeof MiniInteraction;
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { ApplicationCommandType, InteractionResponseType, InteractionType, } from "discord-api-types/v10";
|
|
5
|
+
import { createCommandInteraction } from "../utils/CommandInteractionOptions.js";
|
|
6
|
+
import { createAppCommandInteraction, createMessageContextMenuInteraction, createUserContextMenuInteraction, } from "../utils/ContextMenuInteraction.js";
|
|
7
|
+
import { createMessageComponentInteraction } from "../utils/MessageComponentInteraction.js";
|
|
8
|
+
import { createModalSubmitInteraction } from "../utils/ModalSubmitInteraction.js";
|
|
9
|
+
import { DiscordRestClient } from "../core/http/DiscordRestClient.js";
|
|
10
|
+
import { verifyAndParseInteraction } from "../core/interactions/InteractionVerifier.js";
|
|
11
|
+
export class MiniInteraction {
|
|
12
|
+
options;
|
|
13
|
+
projectRoot;
|
|
14
|
+
rest;
|
|
15
|
+
responseStates = new Map();
|
|
16
|
+
loadedModulesPromise;
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
this.projectRoot = path.resolve(options.cwd ?? process.cwd());
|
|
20
|
+
const applicationId = options.applicationId ??
|
|
21
|
+
process.env.DISCORD_APPLICATION_ID ??
|
|
22
|
+
process.env.DISCORD_APP_ID;
|
|
23
|
+
const token = options.token ??
|
|
24
|
+
process.env.DISCORD_BOT_TOKEN ??
|
|
25
|
+
process.env.DISCORD_TOKEN;
|
|
26
|
+
if (!applicationId || !token) {
|
|
27
|
+
throw new Error("[MiniInteraction] Missing Discord REST credentials. Set applicationId/token or DISCORD_APPLICATION_ID + DISCORD_BOT_TOKEN.");
|
|
28
|
+
}
|
|
29
|
+
this.rest = new DiscordRestClient({ applicationId, token });
|
|
30
|
+
}
|
|
31
|
+
createNodeHandler() {
|
|
32
|
+
return async (req, res) => {
|
|
33
|
+
try {
|
|
34
|
+
const body = await this.readRawBody(req);
|
|
35
|
+
const signature = this.getHeader(req.headers, "x-signature-ed25519");
|
|
36
|
+
const timestamp = this.getHeader(req.headers, "x-signature-timestamp");
|
|
37
|
+
const publicKey = this.options.publicKey ?? process.env.DISCORD_PUBLIC_KEY;
|
|
38
|
+
if (!publicKey) {
|
|
39
|
+
this.sendJson(res, 500, {
|
|
40
|
+
error: "[MiniInteraction] Missing DISCORD_PUBLIC_KEY.",
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (!signature || !timestamp) {
|
|
45
|
+
this.sendJson(res, 401, {
|
|
46
|
+
error: "[MiniInteraction] Missing Discord signature headers.",
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const interaction = await verifyAndParseInteraction({
|
|
51
|
+
body,
|
|
52
|
+
signature,
|
|
53
|
+
timestamp,
|
|
54
|
+
publicKey,
|
|
55
|
+
});
|
|
56
|
+
if (interaction.type === InteractionType.Ping) {
|
|
57
|
+
this.sendJson(res, 200, { type: InteractionResponseType.Pong });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const response = await this.dispatch(interaction);
|
|
61
|
+
this.sendJson(res, 200, response ?? {
|
|
62
|
+
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const message = error instanceof Error ? error.message : "[MiniInteraction] Unknown error";
|
|
67
|
+
if (this.options.debug) {
|
|
68
|
+
console.error("[MiniInteraction] createNodeHandler failed", error);
|
|
69
|
+
}
|
|
70
|
+
this.sendJson(res, 500, { error: message });
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async dispatch(interaction) {
|
|
75
|
+
const modules = await this.loadModules();
|
|
76
|
+
if (interaction.type === InteractionType.ApplicationCommand) {
|
|
77
|
+
const command = modules.commands.find((candidate) => this.getCommandName(candidate) === interaction.data.name);
|
|
78
|
+
if (!command)
|
|
79
|
+
return undefined;
|
|
80
|
+
return this.executeCommandHandler(command.handler, interaction);
|
|
81
|
+
}
|
|
82
|
+
if (interaction.type === InteractionType.MessageComponent) {
|
|
83
|
+
const component = modules.components.find((candidate) => candidate.customId === interaction.data.custom_id);
|
|
84
|
+
if (!component)
|
|
85
|
+
return undefined;
|
|
86
|
+
return this.executeComponentHandler(component.handler, interaction);
|
|
87
|
+
}
|
|
88
|
+
if (interaction.type === InteractionType.ModalSubmit) {
|
|
89
|
+
const modal = modules.modals.find((candidate) => candidate.customId === interaction.data.custom_id);
|
|
90
|
+
if (!modal)
|
|
91
|
+
return undefined;
|
|
92
|
+
return this.executeModalHandler(modal.handler, interaction);
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
async executeCommandHandler(handler, interaction) {
|
|
97
|
+
return this.runWithResponseLifecycle(interaction, async (helpers) => {
|
|
98
|
+
switch (interaction.data.type) {
|
|
99
|
+
case ApplicationCommandType.ChatInput:
|
|
100
|
+
return handler(createCommandInteraction(interaction, helpers));
|
|
101
|
+
case ApplicationCommandType.User:
|
|
102
|
+
return handler(createUserContextMenuInteraction(interaction, helpers));
|
|
103
|
+
case ApplicationCommandType.Message:
|
|
104
|
+
return handler(createMessageContextMenuInteraction(interaction, helpers));
|
|
105
|
+
default:
|
|
106
|
+
if (interaction.data.type === ApplicationCommandType.PrimaryEntryPoint) {
|
|
107
|
+
return handler(createAppCommandInteraction(interaction, helpers));
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`[MiniInteraction] Unsupported application command type: ${String(interaction.data.type)}`);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async executeComponentHandler(handler, interaction) {
|
|
114
|
+
return this.runWithResponseLifecycle(interaction, async (helpers) => handler(createMessageComponentInteraction(interaction, helpers)));
|
|
115
|
+
}
|
|
116
|
+
async executeModalHandler(handler, interaction) {
|
|
117
|
+
return this.runWithResponseLifecycle(interaction, async (helpers) => handler(createModalSubmitInteraction(interaction, helpers)));
|
|
118
|
+
}
|
|
119
|
+
async runWithResponseLifecycle(interaction, executor) {
|
|
120
|
+
let ackResponse;
|
|
121
|
+
const helpers = {
|
|
122
|
+
canRespond: (interactionId) => (this.responseStates.get(interactionId) ?? "pending") === "pending",
|
|
123
|
+
trackResponse: (interactionId, _token, state) => {
|
|
124
|
+
this.responseStates.set(interactionId, state);
|
|
125
|
+
},
|
|
126
|
+
onAck: (response) => {
|
|
127
|
+
ackResponse = response;
|
|
128
|
+
},
|
|
129
|
+
sendFollowUp: async (token, response, messageId) => {
|
|
130
|
+
const responseData = "data" in response ? response.data ?? {} : {};
|
|
131
|
+
if (messageId === "@original") {
|
|
132
|
+
await this.rest.editOriginal(token, responseData);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await this.rest.createFollowup(token, responseData);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
const autoDeferMs = Math.min(2500, this.options.timeoutConfig?.initialResponseTimeout ?? 2500);
|
|
139
|
+
const autoDeferTimer = this.options.timeoutConfig?.autoDeferSlowOperations === true
|
|
140
|
+
? setTimeout(() => {
|
|
141
|
+
if (!helpers.canRespond(interaction.id))
|
|
142
|
+
return;
|
|
143
|
+
if (this.options.debug || this.options.timeoutConfig?.enableResponseDebugLogging) {
|
|
144
|
+
console.warn(`[MiniInteraction] Auto-deferred interaction ${interaction.id} after ${autoDeferMs}ms.`);
|
|
145
|
+
}
|
|
146
|
+
ackResponse = {
|
|
147
|
+
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
|
148
|
+
};
|
|
149
|
+
helpers.trackResponse(interaction.id, interaction.token, "deferred");
|
|
150
|
+
}, autoDeferMs)
|
|
151
|
+
: undefined;
|
|
152
|
+
const timeoutWarningMs = this.options.timeoutConfig?.initialResponseTimeout;
|
|
153
|
+
const timeoutWarningTimer = this.options.timeoutConfig?.enableTimeoutWarnings && timeoutWarningMs
|
|
154
|
+
? setTimeout(() => {
|
|
155
|
+
if (this.responseStates.get(interaction.id))
|
|
156
|
+
return;
|
|
157
|
+
console.warn(`[MiniInteraction] Interaction ${interaction.id} exceeded ${timeoutWarningMs}ms without a response.`);
|
|
158
|
+
}, timeoutWarningMs)
|
|
159
|
+
: undefined;
|
|
160
|
+
try {
|
|
161
|
+
const result = await executor(helpers);
|
|
162
|
+
if (this.options.debug || this.options.timeoutConfig?.enableResponseDebugLogging) {
|
|
163
|
+
console.debug(`[MiniInteraction] Interaction ${interaction.id} completed with ${result ? "explicit" : "fallback"} response.`);
|
|
164
|
+
}
|
|
165
|
+
return result ?? ackResponse;
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
if (autoDeferTimer)
|
|
169
|
+
clearTimeout(autoDeferTimer);
|
|
170
|
+
if (timeoutWarningTimer)
|
|
171
|
+
clearTimeout(timeoutWarningTimer);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async loadModules() {
|
|
175
|
+
if (!this.loadedModulesPromise) {
|
|
176
|
+
this.loadedModulesPromise = this.discoverModules();
|
|
177
|
+
}
|
|
178
|
+
return this.loadedModulesPromise;
|
|
179
|
+
}
|
|
180
|
+
async discoverModules() {
|
|
181
|
+
const commands = this.options.commandsDirectory
|
|
182
|
+
? await this.loadDirectory(this.options.commandsDirectory)
|
|
183
|
+
: [];
|
|
184
|
+
const components = this.options.componentsDirectory
|
|
185
|
+
? await this.loadDirectory(this.options.componentsDirectory)
|
|
186
|
+
: [];
|
|
187
|
+
const loaded = {
|
|
188
|
+
commands: [],
|
|
189
|
+
components: [],
|
|
190
|
+
modals: [],
|
|
191
|
+
};
|
|
192
|
+
for (const { filePath, value } of commands) {
|
|
193
|
+
if (this.isInteractionCommand(value)) {
|
|
194
|
+
loaded.commands.push(value);
|
|
195
|
+
}
|
|
196
|
+
else if (this.options.debug) {
|
|
197
|
+
console.warn(`[MiniInteraction] Ignored non-command module: ${filePath}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (const { filePath, value } of components) {
|
|
201
|
+
if (!this.isCustomIdHandler(value)) {
|
|
202
|
+
if (this.options.debug) {
|
|
203
|
+
console.warn(`[MiniInteraction] Ignored non-component module: ${filePath}`);
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (this.looksLikeModalFile(filePath)) {
|
|
208
|
+
loaded.modals.push(value);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
loaded.components.push(value);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return loaded;
|
|
215
|
+
}
|
|
216
|
+
async loadDirectory(directory) {
|
|
217
|
+
const absoluteDirectory = path.resolve(this.projectRoot, directory);
|
|
218
|
+
const files = await this.walkFiles(absoluteDirectory);
|
|
219
|
+
const loaded = await Promise.all(files
|
|
220
|
+
.filter((filePath) => this.isImportableModule(filePath))
|
|
221
|
+
.map(async (filePath) => ({
|
|
222
|
+
filePath,
|
|
223
|
+
values: this.normalizeModuleExports(await import(pathToFileURL(filePath).href)),
|
|
224
|
+
})));
|
|
225
|
+
return loaded.flatMap(({ filePath, values }) => values.map((value) => ({ filePath, value })));
|
|
226
|
+
}
|
|
227
|
+
normalizeModuleExports(moduleValue) {
|
|
228
|
+
const values = [];
|
|
229
|
+
if ("default" in moduleValue) {
|
|
230
|
+
values.push(...this.normalizeExportValue(moduleValue.default));
|
|
231
|
+
}
|
|
232
|
+
for (const [key, value] of Object.entries(moduleValue)) {
|
|
233
|
+
if (key === "default")
|
|
234
|
+
continue;
|
|
235
|
+
values.push(...this.normalizeExportValue(value));
|
|
236
|
+
}
|
|
237
|
+
return values;
|
|
238
|
+
}
|
|
239
|
+
normalizeExportValue(value) {
|
|
240
|
+
if (Array.isArray(value))
|
|
241
|
+
return value;
|
|
242
|
+
return [value];
|
|
243
|
+
}
|
|
244
|
+
async walkFiles(directory) {
|
|
245
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
246
|
+
const results = await Promise.all(entries.map(async (entry) => {
|
|
247
|
+
const resolvedPath = path.join(directory, entry.name);
|
|
248
|
+
if (entry.isDirectory()) {
|
|
249
|
+
return this.walkFiles(resolvedPath);
|
|
250
|
+
}
|
|
251
|
+
return [resolvedPath];
|
|
252
|
+
}));
|
|
253
|
+
return results.flat();
|
|
254
|
+
}
|
|
255
|
+
isImportableModule(filePath) {
|
|
256
|
+
if (filePath.endsWith(".d.ts"))
|
|
257
|
+
return false;
|
|
258
|
+
return /\.(ts|mts|js|mjs|cjs)$/i.test(filePath);
|
|
259
|
+
}
|
|
260
|
+
isInteractionCommand(value) {
|
|
261
|
+
return (typeof value === "object" &&
|
|
262
|
+
value !== null &&
|
|
263
|
+
"data" in value &&
|
|
264
|
+
"handler" in value &&
|
|
265
|
+
typeof value.handler === "function");
|
|
266
|
+
}
|
|
267
|
+
getCommandName(command) {
|
|
268
|
+
const data = command.data;
|
|
269
|
+
if (typeof data.toJSON === "function") {
|
|
270
|
+
return data.toJSON().name;
|
|
271
|
+
}
|
|
272
|
+
return data.name;
|
|
273
|
+
}
|
|
274
|
+
isCustomIdHandler(value) {
|
|
275
|
+
return (typeof value === "object" &&
|
|
276
|
+
value !== null &&
|
|
277
|
+
"customId" in value &&
|
|
278
|
+
"handler" in value &&
|
|
279
|
+
typeof value.customId === "string" &&
|
|
280
|
+
typeof value.handler === "function");
|
|
281
|
+
}
|
|
282
|
+
looksLikeModalFile(filePath) {
|
|
283
|
+
const normalized = filePath.toLowerCase();
|
|
284
|
+
return (normalized.includes(`${path.sep}modals${path.sep}`) ||
|
|
285
|
+
normalized.endsWith(".modal.ts") ||
|
|
286
|
+
normalized.endsWith(".modal.js") ||
|
|
287
|
+
normalized.includes("_modal.") ||
|
|
288
|
+
normalized.includes("-modal."));
|
|
289
|
+
}
|
|
290
|
+
async readRawBody(req) {
|
|
291
|
+
if (typeof req.rawBody === "string")
|
|
292
|
+
return req.rawBody;
|
|
293
|
+
if (req.rawBody instanceof Uint8Array) {
|
|
294
|
+
return Buffer.from(req.rawBody).toString("utf8");
|
|
295
|
+
}
|
|
296
|
+
if (typeof req.body === "string")
|
|
297
|
+
return req.body;
|
|
298
|
+
if (req.body instanceof Uint8Array) {
|
|
299
|
+
return Buffer.from(req.body).toString("utf8");
|
|
300
|
+
}
|
|
301
|
+
if (req.body && typeof req.body === "object") {
|
|
302
|
+
return JSON.stringify(req.body);
|
|
303
|
+
}
|
|
304
|
+
if (typeof req[Symbol.asyncIterator] === "function") {
|
|
305
|
+
const chunks = [];
|
|
306
|
+
for await (const chunk of req) {
|
|
307
|
+
chunks.push(Buffer.from(chunk));
|
|
308
|
+
}
|
|
309
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
310
|
+
}
|
|
311
|
+
return "";
|
|
312
|
+
}
|
|
313
|
+
getHeader(headers, name) {
|
|
314
|
+
if (typeof headers.get === "function") {
|
|
315
|
+
return headers.get(name) ?? undefined;
|
|
316
|
+
}
|
|
317
|
+
const recordHeaders = headers;
|
|
318
|
+
const direct = recordHeaders[name] ??
|
|
319
|
+
recordHeaders[name.toLowerCase()] ??
|
|
320
|
+
recordHeaders[name.toUpperCase()];
|
|
321
|
+
if (Array.isArray(direct))
|
|
322
|
+
return direct[0];
|
|
323
|
+
return direct;
|
|
324
|
+
}
|
|
325
|
+
sendJson(res, statusCode, body) {
|
|
326
|
+
if (typeof res.status === "function" && typeof res.json === "function") {
|
|
327
|
+
const response = res.status(statusCode);
|
|
328
|
+
response.json?.(body);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
res.statusCode = statusCode;
|
|
332
|
+
res.setHeader?.("Content-Type", "application/json; charset=utf-8");
|
|
333
|
+
res.end(JSON.stringify(body));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
export const LegacyMiniInteractionAdapter = MiniInteraction;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ export type { AttachmentOptionBuilder, ChannelOptionBuilder, MentionableOptionBu
|
|
|
4
4
|
export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
|
|
5
5
|
export { CommandInteraction, MentionableOption, ResolvedUserOption, } from "./utils/CommandInteractionOptions.js";
|
|
6
6
|
export { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction, } from "./utils/ContextMenuInteraction.js";
|
|
7
|
-
export type { InteractionCommand, SlashCommandHandler, UserCommandHandler, MessageCommandHandler, AppCommandHandler, CommandHandler, } from "./types/Commands.js";
|
|
8
|
-
export { MessageComponentInteraction, ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
|
|
7
|
+
export type { InteractionCommand, SlashCommandHandler, UserCommandHandler, MessageCommandHandler, AppCommandHandler, CommandHandler, ComponentInteraction, InteractionComponent, InteractionModal, } from "./types/Commands.js";
|
|
8
|
+
export { MessageComponentInteraction, ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, RadioInteraction, CheckboxInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
|
|
9
9
|
export { ModalSubmitInteraction } from "./utils/ModalSubmitInteraction.js";
|
|
10
10
|
export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
|
|
11
11
|
export { ChannelType } from "./types/ChannelType.js";
|
|
@@ -14,7 +14,7 @@ export { ButtonStyle } from "./types/ButtonStyle.js";
|
|
|
14
14
|
export { SeparatorSpacingSize } from "./types/SeparatorSpacingSize.js";
|
|
15
15
|
export { TextInputStyle } from "discord-api-types/v10";
|
|
16
16
|
export { MiniPermFlags } from "./types/PermissionFlags.js";
|
|
17
|
-
export type { ActionRowComponent, MessageActionRowComponent, } from "./types/ComponentTypes.js";
|
|
17
|
+
export type { ActionRowComponent, MessageActionRowComponent, InteractionComponentData, } from "./types/ComponentTypes.js";
|
|
18
18
|
export * from "./builders/index.js";
|
|
19
19
|
export { MiniDataBuilder } from "./database/MiniDataBuilder.js";
|
|
20
20
|
export type { DataField } from "./database/MiniDataBuilder.js";
|
|
@@ -30,6 +30,10 @@ export { InteractionContext } from "./core/interactions/InteractionContext.js";
|
|
|
30
30
|
export type { InteractionContextOptions } from "./core/interactions/InteractionContext.js";
|
|
31
31
|
export { verifyAndParseInteraction } from "./core/interactions/InteractionVerifier.js";
|
|
32
32
|
export { InteractionRouter } from "./router/InteractionRouter.js";
|
|
33
|
+
export { MiniInteraction, LegacyMiniInteractionAdapter, } from "./compat/MiniInteraction.js";
|
|
34
|
+
export type { MiniInteractionOptions } from "./compat/MiniInteraction.js";
|
|
33
35
|
export type { APIRadioComponent, APIRadioOption } from "./types/radio.js";
|
|
34
36
|
export { RADIO_COMPONENT_TYPE } from "./types/radio.js";
|
|
37
|
+
export type { APICheckboxComponent, APICheckboxOption } from "./types/checkbox.js";
|
|
38
|
+
export { CHECKBOX_COMPONENT_TYPE } from "./types/checkbox.js";
|
|
35
39
|
export { ValidationError } from "./types/validation.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { UserCommandBuilder, MessageCommandBuilder, AppCommandBuilder, } from ".
|
|
|
3
3
|
export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
|
|
4
4
|
export { CommandInteraction, MentionableOption, ResolvedUserOption, } from "./utils/CommandInteractionOptions.js";
|
|
5
5
|
export { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction, } from "./utils/ContextMenuInteraction.js";
|
|
6
|
-
export { MessageComponentInteraction, ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
|
|
6
|
+
export { MessageComponentInteraction, ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, RadioInteraction, CheckboxInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
|
|
7
7
|
export { ModalSubmitInteraction } from "./utils/ModalSubmitInteraction.js";
|
|
8
8
|
export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
|
|
9
9
|
export { ChannelType } from "./types/ChannelType.js";
|
|
@@ -23,5 +23,7 @@ export { DiscordRestClient } from "./core/http/DiscordRestClient.js";
|
|
|
23
23
|
export { InteractionContext } from "./core/interactions/InteractionContext.js";
|
|
24
24
|
export { verifyAndParseInteraction } from "./core/interactions/InteractionVerifier.js";
|
|
25
25
|
export { InteractionRouter } from "./router/InteractionRouter.js";
|
|
26
|
+
export { MiniInteraction, LegacyMiniInteractionAdapter, } from "./compat/MiniInteraction.js";
|
|
26
27
|
export { RADIO_COMPONENT_TYPE } from "./types/radio.js";
|
|
28
|
+
export { CHECKBOX_COMPONENT_TYPE } from "./types/checkbox.js";
|
|
27
29
|
export { ValidationError } from "./types/validation.js";
|
package/dist/types/Commands.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIContextMenuApplicationCommandsJSONBody, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody } from "discord-api-types/v10";
|
|
2
2
|
import type { CommandInteraction } from "../utils/CommandInteractionOptions.js";
|
|
3
3
|
import type { UserContextMenuInteraction, MessageContextMenuInteraction, AppCommandInteraction } from "../utils/ContextMenuInteraction.js";
|
|
4
|
+
import type { ButtonInteraction, StringSelectInteraction, RoleSelectInteraction, UserSelectInteraction, ChannelSelectInteraction, MentionableSelectInteraction, RadioInteraction, CheckboxInteraction } from "../utils/MessageComponentInteraction.js";
|
|
5
|
+
import type { ModalSubmitInteraction } from "../utils/ModalSubmitInteraction.js";
|
|
4
6
|
import type { JSONEncodable } from "../builders/shared.js";
|
|
5
7
|
import type { CommandBuilder } from "../commands/CommandBuilder.js";
|
|
6
8
|
import type { MessageCommandBuilder, UserCommandBuilder, AppCommandBuilder } from "../commands/ContextMenuCommandBuilder.js";
|
|
@@ -19,5 +21,17 @@ export type InteractionCommand = {
|
|
|
19
21
|
data: RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody | CommandBuilder | UserCommandBuilder | MessageCommandBuilder | AppCommandBuilder | JSONEncodable<RESTPostAPIChatInputApplicationCommandsJSONBody | RESTPostAPIContextMenuApplicationCommandsJSONBody | RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody>;
|
|
20
22
|
handler: CommandHandler;
|
|
21
23
|
};
|
|
24
|
+
/** Handler for any message component interaction */
|
|
25
|
+
export type ComponentInteraction = ButtonInteraction | StringSelectInteraction | RoleSelectInteraction | UserSelectInteraction | ChannelSelectInteraction | MentionableSelectInteraction | RadioInteraction | CheckboxInteraction;
|
|
26
|
+
/** Structure for a standalone component handler */
|
|
27
|
+
export type InteractionComponent = {
|
|
28
|
+
customId: string;
|
|
29
|
+
handler: (interaction: ComponentInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
30
|
+
};
|
|
31
|
+
/** Structure for a standalone modal handler */
|
|
32
|
+
export type InteractionModal = {
|
|
33
|
+
customId: string;
|
|
34
|
+
handler: (interaction: ModalSubmitInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
35
|
+
};
|
|
22
36
|
/** Map of command names to their registered MiniInteraction command definitions. */
|
|
23
37
|
export type InteractionCommandsMap = Map<string, InteractionCommand>;
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import type { APIComponentInActionRow, APIComponentInMessageActionRow } from "discord-api-types/v10";
|
|
1
|
+
import type { APIComponentInActionRow, APIComponentInMessageActionRow, APIModalInteractionResponseCallbackComponent } from "discord-api-types/v10";
|
|
2
|
+
import type { APIRadioComponent } from "./radio.js";
|
|
3
|
+
import type { APICheckboxComponent } from "./checkbox.js";
|
|
2
4
|
/** Defines a component structure for use in ActionRow builders. */
|
|
3
|
-
export type ActionRowComponent = APIComponentInActionRow;
|
|
5
|
+
export type ActionRowComponent = APIComponentInActionRow | APIRadioComponent | APICheckboxComponent;
|
|
4
6
|
/** Defines a message component structure for use in message builders. */
|
|
5
|
-
export type MessageActionRowComponent = APIComponentInMessageActionRow;
|
|
7
|
+
export type MessageActionRowComponent = APIComponentInMessageActionRow | APIRadioComponent | APICheckboxComponent;
|
|
8
|
+
/** Structure for an action row containing mini-interaction components. */
|
|
9
|
+
export interface MiniActionRow<T extends ActionRowComponent = ActionRowComponent> {
|
|
10
|
+
type: 1;
|
|
11
|
+
components: T[];
|
|
12
|
+
}
|
|
13
|
+
/** Structure for a message action row containing mini-interaction components. */
|
|
14
|
+
export interface MessageMiniActionRow<T extends MessageActionRowComponent = MessageActionRowComponent> {
|
|
15
|
+
type: 1;
|
|
16
|
+
components: T[];
|
|
17
|
+
}
|
|
18
|
+
/** Generic type for any supported interaction component data. */
|
|
19
|
+
export type InteractionComponentData = MessageActionRowComponent | APIModalInteractionResponseCallbackComponent;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord API support for checkbox components may lag behind discord-api-types releases.
|
|
3
|
+
* These local contracts are runtime-validated and serialized as raw component payloads.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CHECKBOX_COMPONENT_TYPE: 2002;
|
|
6
|
+
export type APICheckboxOption = {
|
|
7
|
+
label: string;
|
|
8
|
+
value: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
emoji?: {
|
|
11
|
+
id?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
animated?: boolean;
|
|
14
|
+
};
|
|
15
|
+
default?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type APICheckboxComponent = {
|
|
18
|
+
type: typeof CHECKBOX_COMPONENT_TYPE;
|
|
19
|
+
custom_id: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
required?: boolean;
|
|
22
|
+
options: APICheckboxOption[];
|
|
23
|
+
};
|
|
@@ -138,6 +138,44 @@ export interface MentionableSelectInteraction extends Omit<APIMessageComponentIn
|
|
|
138
138
|
trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
|
|
139
139
|
}
|
|
140
140
|
export declare const MentionableSelectInteraction: {};
|
|
141
|
+
/**
|
|
142
|
+
* Radio interaction with helper methods.
|
|
143
|
+
*/
|
|
144
|
+
export interface RadioInteraction extends Omit<APIMessageComponentInteraction, "data"> {
|
|
145
|
+
data: APIMessageStringSelectInteractionData;
|
|
146
|
+
values: string[];
|
|
147
|
+
getStringValues: () => string[];
|
|
148
|
+
getResponse: () => APIInteractionResponse | null;
|
|
149
|
+
reply: (data: InteractionMessageData) => Promise<APIInteractionResponseChannelMessageWithSource>;
|
|
150
|
+
deferReply: (options?: DeferReplyOptions) => APIInteractionResponseDeferredChannelMessageWithSource;
|
|
151
|
+
update: (data?: InteractionMessageData) => Promise<APIInteractionResponseUpdateMessage>;
|
|
152
|
+
deferUpdate: () => APIInteractionResponseDeferredMessageUpdate;
|
|
153
|
+
showModal: (data: APIModalInteractionResponseCallbackData | {
|
|
154
|
+
toJSON(): APIModalInteractionResponseCallbackData;
|
|
155
|
+
}) => APIModalInteractionResponse;
|
|
156
|
+
canRespond?: (interactionId: string) => boolean;
|
|
157
|
+
trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
|
|
158
|
+
}
|
|
159
|
+
export declare const RadioInteraction: {};
|
|
160
|
+
/**
|
|
161
|
+
* Checkbox interaction with helper methods.
|
|
162
|
+
*/
|
|
163
|
+
export interface CheckboxInteraction extends Omit<APIMessageComponentInteraction, "data"> {
|
|
164
|
+
data: APIMessageStringSelectInteractionData;
|
|
165
|
+
values: string[];
|
|
166
|
+
getStringValues: () => string[];
|
|
167
|
+
getResponse: () => APIInteractionResponse | null;
|
|
168
|
+
reply: (data: InteractionMessageData) => Promise<APIInteractionResponseChannelMessageWithSource>;
|
|
169
|
+
deferReply: (options?: DeferReplyOptions) => APIInteractionResponseDeferredChannelMessageWithSource;
|
|
170
|
+
update: (data?: InteractionMessageData) => Promise<APIInteractionResponseUpdateMessage>;
|
|
171
|
+
deferUpdate: () => APIInteractionResponseDeferredMessageUpdate;
|
|
172
|
+
showModal: (data: APIModalInteractionResponseCallbackData | {
|
|
173
|
+
toJSON(): APIModalInteractionResponseCallbackData;
|
|
174
|
+
}) => APIModalInteractionResponse;
|
|
175
|
+
canRespond?: (interactionId: string) => boolean;
|
|
176
|
+
trackResponse?: (interactionId: string, token: string, state: 'responded' | 'deferred') => void;
|
|
177
|
+
}
|
|
178
|
+
export declare const CheckboxInteraction: {};
|
|
141
179
|
/**
|
|
142
180
|
* Represents a component interaction augmented with helper response methods.
|
|
143
181
|
*
|
|
@@ -8,6 +8,8 @@ export const RoleSelectInteraction = {};
|
|
|
8
8
|
export const UserSelectInteraction = {};
|
|
9
9
|
export const ChannelSelectInteraction = {};
|
|
10
10
|
export const MentionableSelectInteraction = {};
|
|
11
|
+
export const RadioInteraction = {};
|
|
12
|
+
export const CheckboxInteraction = {};
|
|
11
13
|
export const MessageComponentInteraction = {};
|
|
12
14
|
/**
|
|
13
15
|
* Wraps a raw component interaction with helper methods mirroring Discord's expected responses.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { APIInteractionResponseCallbackData, MessageFlags } from "discord-api-types/v10";
|
|
2
|
-
import type {
|
|
2
|
+
import type { APIContainerComponent } from "discord-api-types/v10";
|
|
3
3
|
import type { JSONEncodable } from "../builders/shared.js";
|
|
4
4
|
import { InteractionFlags } from "../types/InteractionFlags.js";
|
|
5
|
+
import type { MessageMiniActionRow } from "../types/ComponentTypes.js";
|
|
5
6
|
/** Union of helper flag enums and raw Discord message flags. */
|
|
6
7
|
export type MessageFlagLike = MessageFlags | InteractionFlags;
|
|
7
8
|
/** Top-level components allowed in messages (ActionRows or Containers) */
|
|
8
|
-
export type TopLevelComponent =
|
|
9
|
+
export type TopLevelComponent = MessageMiniActionRow | APIContainerComponent;
|
|
9
10
|
/** Message payload accepted by helper reply/edit functions. */
|
|
10
11
|
export type InteractionMessageData = {
|
|
11
12
|
content?: string;
|
package/package.json
CHANGED