@minesa-org/mini-interaction 0.0.1
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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/builders/ActionRowBuilder.d.ts +25 -0
- package/dist/builders/ActionRowBuilder.js +35 -0
- package/dist/builders/ButtonBuilder.d.ts +53 -0
- package/dist/builders/ButtonBuilder.js +124 -0
- package/dist/builders/ChannelSelectMenuBuilder.d.ts +58 -0
- package/dist/builders/ChannelSelectMenuBuilder.js +111 -0
- package/dist/builders/ModalBuilder.d.ts +40 -0
- package/dist/builders/ModalBuilder.js +65 -0
- package/dist/builders/RoleSelectMenuBuilder.d.ts +52 -0
- package/dist/builders/RoleSelectMenuBuilder.js +98 -0
- package/dist/builders/StringSelectMenuBuilder.d.ts +56 -0
- package/dist/builders/StringSelectMenuBuilder.js +100 -0
- package/dist/builders/index.d.ts +14 -0
- package/dist/builders/index.js +7 -0
- package/dist/builders/shared.d.ts +8 -0
- package/dist/builders/shared.js +11 -0
- package/dist/clients/MiniInteraction.d.ts +176 -0
- package/dist/clients/MiniInteraction.js +578 -0
- package/dist/commands/CommandBuilder.d.ts +278 -0
- package/dist/commands/CommandBuilder.js +687 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/types/ButtonStyle.d.ts +16 -0
- package/dist/types/ButtonStyle.js +17 -0
- package/dist/types/ChannelType.d.ts +2 -0
- package/dist/types/ChannelType.js +2 -0
- package/dist/types/Commands.d.ts +11 -0
- package/dist/types/Commands.js +1 -0
- package/dist/types/ComponentTypes.d.ts +5 -0
- package/dist/types/ComponentTypes.js +1 -0
- package/dist/types/InteractionFlags.d.ts +10 -0
- package/dist/types/InteractionFlags.js +12 -0
- package/dist/types/RoleConnectionMetadataTypes.d.ts +11 -0
- package/dist/types/RoleConnectionMetadataTypes.js +12 -0
- package/dist/utils/CommandInteractionOptions.d.ts +143 -0
- package/dist/utils/CommandInteractionOptions.js +376 -0
- package/dist/utils/MessageComponentInteraction.d.ts +19 -0
- package/dist/utils/MessageComponentInteraction.js +60 -0
- package/dist/utils/constants.d.ts +16 -0
- package/dist/utils/constants.js +16 -0
- package/dist/utils/interactionMessageHelpers.d.ts +30 -0
- package/dist/utils/interactionMessageHelpers.js +32 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Entry point re-exporting MiniInteraction helpers, builders, and shared types. */
|
|
2
|
+
export { MiniInteraction } from "./clients/MiniInteraction.js";
|
|
3
|
+
export { CommandBuilder, CommandContext, IntegrationType, } from "./commands/CommandBuilder.js";
|
|
4
|
+
export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
|
|
5
|
+
export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
|
|
6
|
+
export { ChannelType } from "./types/ChannelType.js";
|
|
7
|
+
export { InteractionFollowUpFlags, InteractionReplyFlags, } from "./types/InteractionFlags.js";
|
|
8
|
+
export { ButtonStyle } from "./types/ButtonStyle.js";
|
|
9
|
+
export * from "./builders/index.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Visual styles for Discord buttons matching the public API values. */
|
|
2
|
+
export declare enum ButtonStyle {
|
|
3
|
+
/** The most prominent option in a set of actions. */
|
|
4
|
+
Primary = 1,
|
|
5
|
+
/** A neutral action that complements the primary action. */
|
|
6
|
+
Secondary = 2,
|
|
7
|
+
/** Indicates a successful or positive action. */
|
|
8
|
+
Success = 3,
|
|
9
|
+
/** Highlights a potentially destructive action. */
|
|
10
|
+
Danger = 4,
|
|
11
|
+
/** Navigates the user to an external URL. */
|
|
12
|
+
Link = 5,
|
|
13
|
+
/** Initiates a premium purchase flow. */
|
|
14
|
+
Premium = 6
|
|
15
|
+
}
|
|
16
|
+
export default ButtonStyle;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Visual styles for Discord buttons matching the public API values. */
|
|
2
|
+
export var ButtonStyle;
|
|
3
|
+
(function (ButtonStyle) {
|
|
4
|
+
/** The most prominent option in a set of actions. */
|
|
5
|
+
ButtonStyle[ButtonStyle["Primary"] = 1] = "Primary";
|
|
6
|
+
/** A neutral action that complements the primary action. */
|
|
7
|
+
ButtonStyle[ButtonStyle["Secondary"] = 2] = "Secondary";
|
|
8
|
+
/** Indicates a successful or positive action. */
|
|
9
|
+
ButtonStyle[ButtonStyle["Success"] = 3] = "Success";
|
|
10
|
+
/** Highlights a potentially destructive action. */
|
|
11
|
+
ButtonStyle[ButtonStyle["Danger"] = 4] = "Danger";
|
|
12
|
+
/** Navigates the user to an external URL. */
|
|
13
|
+
ButtonStyle[ButtonStyle["Link"] = 5] = "Link";
|
|
14
|
+
/** Initiates a premium purchase flow. */
|
|
15
|
+
ButtonStyle[ButtonStyle["Premium"] = 6] = "Premium";
|
|
16
|
+
})(ButtonStyle || (ButtonStyle = {}));
|
|
17
|
+
export default ButtonStyle;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { APIInteractionResponse, RESTPostAPIChatInputApplicationCommandsJSONBody } from "discord-api-types/v10";
|
|
2
|
+
import type { CommandInteraction } from "../utils/CommandInteractionOptions.js";
|
|
3
|
+
/** Handler signature for slash command executions within MiniInteraction. */
|
|
4
|
+
export type SlashCommandHandler = (interaction: CommandInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
|
|
5
|
+
/** Structure representing a slash command definition and its runtime handler. */
|
|
6
|
+
export type MiniInteractionCommand = {
|
|
7
|
+
data: RESTPostAPIChatInputApplicationCommandsJSONBody;
|
|
8
|
+
handler: SlashCommandHandler;
|
|
9
|
+
};
|
|
10
|
+
/** Map of command names to their registered MiniInteraction command definitions. */
|
|
11
|
+
export type MiniInteractionCommandsMap = Map<string, MiniInteractionCommand>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { APIComponentInActionRow, APIComponentInMessageActionRow } from "discord-api-types/v10";
|
|
2
|
+
/** * Components that can appear inside a Discord action row container. */
|
|
3
|
+
export type MiniComponentMessageActionRow = APIComponentInMessageActionRow;
|
|
4
|
+
/** Components that are valid inside message action rows. */
|
|
5
|
+
export type MiniComponentActionRow = APIComponentInActionRow;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Flags available when responding directly to an interaction. */
|
|
2
|
+
export declare enum InteractionReplyFlags {
|
|
3
|
+
Ephemeral = 64,
|
|
4
|
+
IsComponentsV2 = 32768
|
|
5
|
+
}
|
|
6
|
+
/** Flags available when sending a follow-up message for an interaction. */
|
|
7
|
+
export declare enum InteractionFollowUpFlags {
|
|
8
|
+
Ephemeral = 64,
|
|
9
|
+
IsComponentsV2 = 32768
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Flags available when responding directly to an interaction. */
|
|
2
|
+
export var InteractionReplyFlags;
|
|
3
|
+
(function (InteractionReplyFlags) {
|
|
4
|
+
InteractionReplyFlags[InteractionReplyFlags["Ephemeral"] = 64] = "Ephemeral";
|
|
5
|
+
InteractionReplyFlags[InteractionReplyFlags["IsComponentsV2"] = 32768] = "IsComponentsV2";
|
|
6
|
+
})(InteractionReplyFlags || (InteractionReplyFlags = {}));
|
|
7
|
+
/** Flags available when sending a follow-up message for an interaction. */
|
|
8
|
+
export var InteractionFollowUpFlags;
|
|
9
|
+
(function (InteractionFollowUpFlags) {
|
|
10
|
+
InteractionFollowUpFlags[InteractionFollowUpFlags["Ephemeral"] = 64] = "Ephemeral";
|
|
11
|
+
InteractionFollowUpFlags[InteractionFollowUpFlags["IsComponentsV2"] = 32768] = "IsComponentsV2";
|
|
12
|
+
})(InteractionFollowUpFlags || (InteractionFollowUpFlags = {}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Enumerates available role connection metadata comparison types supported by Discord. */
|
|
2
|
+
export declare enum RoleConnectionMetadataTypes {
|
|
3
|
+
IntegerLessThanOrEqual = 1,
|
|
4
|
+
IntegerGreaterThanOrEqual = 2,
|
|
5
|
+
IntegerEqual = 3,
|
|
6
|
+
IntegerNotEqual = 4,
|
|
7
|
+
DateTimeLessThanOrEqual = 5,
|
|
8
|
+
DateTimeGreaterThanOrEqual = 6,
|
|
9
|
+
BooleanEqual = 7,
|
|
10
|
+
BooleanNotEqual = 8
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Enumerates available role connection metadata comparison types supported by Discord. */
|
|
2
|
+
export var RoleConnectionMetadataTypes;
|
|
3
|
+
(function (RoleConnectionMetadataTypes) {
|
|
4
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["IntegerLessThanOrEqual"] = 1] = "IntegerLessThanOrEqual";
|
|
5
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["IntegerGreaterThanOrEqual"] = 2] = "IntegerGreaterThanOrEqual";
|
|
6
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["IntegerEqual"] = 3] = "IntegerEqual";
|
|
7
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["IntegerNotEqual"] = 4] = "IntegerNotEqual";
|
|
8
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["DateTimeLessThanOrEqual"] = 5] = "DateTimeLessThanOrEqual";
|
|
9
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["DateTimeGreaterThanOrEqual"] = 6] = "DateTimeGreaterThanOrEqual";
|
|
10
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["BooleanEqual"] = 7] = "BooleanEqual";
|
|
11
|
+
RoleConnectionMetadataTypes[RoleConnectionMetadataTypes["BooleanNotEqual"] = 8] = "BooleanNotEqual";
|
|
12
|
+
})(RoleConnectionMetadataTypes || (RoleConnectionMetadataTypes = {}));
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { type APIInteractionResponse, type APIApplicationCommandInteractionDataOption, type APIChatInputApplicationCommandInteraction, type APIInteractionDataResolved, type APIInteractionDataResolvedChannel, type APIInteractionDataResolvedGuildMember, type APIModalInteractionResponse, type APIModalInteractionResponseCallbackData, type APIInteractionResponseChannelMessageWithSource, type APIInteractionResponseDeferredChannelMessageWithSource, type APIInteractionResponseUpdateMessage, type APIRole, type APIUser, type APIAttachment } from "discord-api-types/v10";
|
|
2
|
+
import { DeferReplyOptions, InteractionMessageData } from "./interactionMessageHelpers.js";
|
|
3
|
+
export type ResolvedUserOption = {
|
|
4
|
+
user: APIUser;
|
|
5
|
+
member?: APIInteractionDataResolvedGuildMember;
|
|
6
|
+
};
|
|
7
|
+
export type MentionableOption = {
|
|
8
|
+
type: "user";
|
|
9
|
+
value: ResolvedUserOption;
|
|
10
|
+
} | {
|
|
11
|
+
type: "role";
|
|
12
|
+
value: APIRole;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Provides ergonomic helpers for extracting typed command options from Discord interactions.
|
|
16
|
+
*/
|
|
17
|
+
export declare class CommandInteractionOptionResolver {
|
|
18
|
+
readonly raw: ReadonlyArray<APIApplicationCommandInteractionDataOption>;
|
|
19
|
+
private readonly resolved;
|
|
20
|
+
private readonly focusedOptions;
|
|
21
|
+
private readonly subcommand;
|
|
22
|
+
private readonly subcommandGroup;
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new resolver from raw interaction options.
|
|
25
|
+
*
|
|
26
|
+
* @param options - The raw options provided by Discord.
|
|
27
|
+
* @param resolved - The resolved data map included with the interaction payload.
|
|
28
|
+
*/
|
|
29
|
+
constructor(options: APIApplicationCommandInteractionDataOption[] | undefined, resolved: APIInteractionDataResolved | undefined);
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves the subcommand invoked by the user.
|
|
32
|
+
*
|
|
33
|
+
* @param required - Whether to throw when the subcommand is missing.
|
|
34
|
+
*/
|
|
35
|
+
getSubcommand(required?: boolean): string | null;
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves the subcommand group invoked by the user.
|
|
38
|
+
*
|
|
39
|
+
* @param required - Whether to throw when the subcommand group is missing.
|
|
40
|
+
*/
|
|
41
|
+
getSubcommandGroup(required?: boolean): string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Looks up a string option by name.
|
|
44
|
+
*
|
|
45
|
+
* @param name - The option name to resolve.
|
|
46
|
+
* @param required - Whether to throw when the option is missing.
|
|
47
|
+
*/
|
|
48
|
+
getString(name: string, required?: boolean): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Looks up a numeric option by name.
|
|
51
|
+
*
|
|
52
|
+
* @param name - The option name to resolve.
|
|
53
|
+
* @param required - Whether to throw when the option is missing.
|
|
54
|
+
*/
|
|
55
|
+
getNumber(name: string, required?: boolean): number | null;
|
|
56
|
+
/**
|
|
57
|
+
* Resolves a user option, including any guild member payload.
|
|
58
|
+
*
|
|
59
|
+
* @param name - The option name to resolve.
|
|
60
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
61
|
+
*/
|
|
62
|
+
getUser(name: string, required?: boolean): ResolvedUserOption | null;
|
|
63
|
+
/**
|
|
64
|
+
* Resolves a role option to its resolved payload.
|
|
65
|
+
*
|
|
66
|
+
* @param name - The option name to resolve.
|
|
67
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
68
|
+
*/
|
|
69
|
+
getRole(name: string, required?: boolean): APIRole | null;
|
|
70
|
+
/**
|
|
71
|
+
* Resolves a channel option to its resolved payload.
|
|
72
|
+
*
|
|
73
|
+
* @param name - The option name to resolve.
|
|
74
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
75
|
+
*/
|
|
76
|
+
getChannel(name: string, required?: boolean): APIInteractionDataResolvedChannel | null;
|
|
77
|
+
/**
|
|
78
|
+
* Resolves an attachment option to its resolved payload.
|
|
79
|
+
*
|
|
80
|
+
* @param name - The option name to resolve.
|
|
81
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
82
|
+
*/
|
|
83
|
+
getAttachment(name: string, required?: boolean): APIAttachment | null;
|
|
84
|
+
/**
|
|
85
|
+
* Resolves a mentionable option to either a role or user payload.
|
|
86
|
+
*
|
|
87
|
+
* @param name - The option name to resolve.
|
|
88
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
89
|
+
*/
|
|
90
|
+
getMentionable(name: string, required?: boolean): MentionableOption | null;
|
|
91
|
+
/**
|
|
92
|
+
* Returns the raw option payload, bypassing type checks and casting.
|
|
93
|
+
*
|
|
94
|
+
* @param name - The option name to look up.
|
|
95
|
+
*/
|
|
96
|
+
getRawOption(name: string): APIApplicationCommandInteractionDataOption | null;
|
|
97
|
+
/**
|
|
98
|
+
* Extracts a strongly typed option, ensuring it exists and matches the expected type.
|
|
99
|
+
*/
|
|
100
|
+
private extractOption;
|
|
101
|
+
/**
|
|
102
|
+
* Resolves an attachment by identifier from the interaction's resolved payloads.
|
|
103
|
+
*/
|
|
104
|
+
private resolveAttachment;
|
|
105
|
+
/**
|
|
106
|
+
* Resolves a channel by identifier from the interaction's resolved payloads.
|
|
107
|
+
*/
|
|
108
|
+
private resolveChannel;
|
|
109
|
+
/**
|
|
110
|
+
* Resolves a role by identifier from the interaction's resolved payloads.
|
|
111
|
+
*/
|
|
112
|
+
private resolveRole;
|
|
113
|
+
/**
|
|
114
|
+
* Resolves a user and optional member by identifier from the interaction's resolved payloads.
|
|
115
|
+
*/
|
|
116
|
+
private resolveUser;
|
|
117
|
+
/**
|
|
118
|
+
* Retrieves a record from a resolved payload map by identifier.
|
|
119
|
+
*/
|
|
120
|
+
private getResolvedRecord;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Represents a command interaction augmented with helper methods for easy responses.
|
|
124
|
+
*/
|
|
125
|
+
export interface CommandInteraction extends Omit<APIChatInputApplicationCommandInteraction, "data"> {
|
|
126
|
+
data: Omit<APIChatInputApplicationCommandInteraction["data"], "options"> & {
|
|
127
|
+
options: CommandInteractionOptionResolver;
|
|
128
|
+
};
|
|
129
|
+
options: CommandInteractionOptionResolver;
|
|
130
|
+
getResponse(): APIInteractionResponse | null;
|
|
131
|
+
reply(data: InteractionMessageData): APIInteractionResponseChannelMessageWithSource;
|
|
132
|
+
followUp(data: InteractionMessageData): APIInteractionResponseChannelMessageWithSource;
|
|
133
|
+
edit(data?: InteractionMessageData): APIInteractionResponseUpdateMessage;
|
|
134
|
+
deferReply(options?: DeferReplyOptions): APIInteractionResponseDeferredChannelMessageWithSource;
|
|
135
|
+
showModal(data: APIModalInteractionResponseCallbackData): APIModalInteractionResponse;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Wraps a raw application command interaction with helper methods and option resolvers.
|
|
139
|
+
*
|
|
140
|
+
* @param interaction - The raw interaction payload from Discord.
|
|
141
|
+
* @returns A helper-augmented interaction object.
|
|
142
|
+
*/
|
|
143
|
+
export declare function createCommandInteraction(interaction: APIChatInputApplicationCommandInteraction): CommandInteraction;
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { ApplicationCommandOptionType, InteractionResponseType, } from "discord-api-types/v10";
|
|
2
|
+
import { normaliseInteractionMessageData, normaliseMessageFlags, } from "./interactionMessageHelpers.js";
|
|
3
|
+
/** Maps application command option types to human-readable labels for error messages. */
|
|
4
|
+
const OPTION_TYPE_LABEL = {
|
|
5
|
+
[ApplicationCommandOptionType.Subcommand]: "subcommand",
|
|
6
|
+
[ApplicationCommandOptionType.SubcommandGroup]: "subcommand group",
|
|
7
|
+
[ApplicationCommandOptionType.String]: "string",
|
|
8
|
+
[ApplicationCommandOptionType.Integer]: "integer",
|
|
9
|
+
[ApplicationCommandOptionType.Boolean]: "boolean",
|
|
10
|
+
[ApplicationCommandOptionType.User]: "user",
|
|
11
|
+
[ApplicationCommandOptionType.Channel]: "channel",
|
|
12
|
+
[ApplicationCommandOptionType.Role]: "role",
|
|
13
|
+
[ApplicationCommandOptionType.Mentionable]: "mentionable",
|
|
14
|
+
[ApplicationCommandOptionType.Number]: "number",
|
|
15
|
+
[ApplicationCommandOptionType.Attachment]: "attachment",
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Determines whether the provided option represents a subcommand.
|
|
19
|
+
*
|
|
20
|
+
* @param option - The option to inspect.
|
|
21
|
+
*/
|
|
22
|
+
function isSubcommandOption(option) {
|
|
23
|
+
return option.type === ApplicationCommandOptionType.Subcommand;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Determines whether the provided option represents a subcommand group.
|
|
27
|
+
*
|
|
28
|
+
* @param option - The option to inspect.
|
|
29
|
+
*/
|
|
30
|
+
function isSubcommandGroupOption(option) {
|
|
31
|
+
return option.type === ApplicationCommandOptionType.SubcommandGroup;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Extracts the most relevant option set from the raw command options hierarchy.
|
|
35
|
+
*
|
|
36
|
+
* @param options - The raw options array from the interaction payload.
|
|
37
|
+
* @returns The resolved subcommand context and focused options for convenience accessors.
|
|
38
|
+
*/
|
|
39
|
+
function resolveFocusedOptions(options) {
|
|
40
|
+
if (!options || options.length === 0) {
|
|
41
|
+
return { subcommandGroup: null, subcommand: null, options: [] };
|
|
42
|
+
}
|
|
43
|
+
const [firstOption] = options;
|
|
44
|
+
if (isSubcommandGroupOption(firstOption)) {
|
|
45
|
+
const [firstSubcommand] = firstOption.options ?? [];
|
|
46
|
+
if (firstSubcommand && isSubcommandOption(firstSubcommand)) {
|
|
47
|
+
return {
|
|
48
|
+
subcommandGroup: firstOption.name,
|
|
49
|
+
subcommand: firstSubcommand.name,
|
|
50
|
+
options: firstSubcommand.options ?? [],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
subcommandGroup: firstOption.name,
|
|
55
|
+
subcommand: null,
|
|
56
|
+
options: [],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (isSubcommandOption(firstOption)) {
|
|
60
|
+
return {
|
|
61
|
+
subcommandGroup: null,
|
|
62
|
+
subcommand: firstOption.name,
|
|
63
|
+
options: firstOption.options ?? [],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
subcommandGroup: null,
|
|
68
|
+
subcommand: null,
|
|
69
|
+
options: options,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Provides ergonomic helpers for extracting typed command options from Discord interactions.
|
|
74
|
+
*/
|
|
75
|
+
export class CommandInteractionOptionResolver {
|
|
76
|
+
raw;
|
|
77
|
+
resolved;
|
|
78
|
+
focusedOptions;
|
|
79
|
+
subcommand;
|
|
80
|
+
subcommandGroup;
|
|
81
|
+
/**
|
|
82
|
+
* Creates a new resolver from raw interaction options.
|
|
83
|
+
*
|
|
84
|
+
* @param options - The raw options provided by Discord.
|
|
85
|
+
* @param resolved - The resolved data map included with the interaction payload.
|
|
86
|
+
*/
|
|
87
|
+
constructor(options, resolved) {
|
|
88
|
+
this.raw = Object.freeze([...(options ?? [])]);
|
|
89
|
+
this.resolved = resolved;
|
|
90
|
+
const focused = resolveFocusedOptions(options);
|
|
91
|
+
this.focusedOptions = focused.options;
|
|
92
|
+
this.subcommand = focused.subcommand;
|
|
93
|
+
this.subcommandGroup = focused.subcommandGroup;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Retrieves the subcommand invoked by the user.
|
|
97
|
+
*
|
|
98
|
+
* @param required - Whether to throw when the subcommand is missing.
|
|
99
|
+
*/
|
|
100
|
+
getSubcommand(required = true) {
|
|
101
|
+
if (this.subcommand) {
|
|
102
|
+
return this.subcommand;
|
|
103
|
+
}
|
|
104
|
+
if (required) {
|
|
105
|
+
throw new Error("Expected a subcommand to be present on this interaction");
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Retrieves the subcommand group invoked by the user.
|
|
111
|
+
*
|
|
112
|
+
* @param required - Whether to throw when the subcommand group is missing.
|
|
113
|
+
*/
|
|
114
|
+
getSubcommandGroup(required = true) {
|
|
115
|
+
if (this.subcommandGroup) {
|
|
116
|
+
return this.subcommandGroup;
|
|
117
|
+
}
|
|
118
|
+
if (required) {
|
|
119
|
+
throw new Error("Expected a subcommand group to be present on this interaction");
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Looks up a string option by name.
|
|
125
|
+
*
|
|
126
|
+
* @param name - The option name to resolve.
|
|
127
|
+
* @param required - Whether to throw when the option is missing.
|
|
128
|
+
*/
|
|
129
|
+
getString(name, required = false) {
|
|
130
|
+
const option = this.extractOption(name, ApplicationCommandOptionType.String, required);
|
|
131
|
+
return option ? option.value : null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Looks up a numeric option by name.
|
|
135
|
+
*
|
|
136
|
+
* @param name - The option name to resolve.
|
|
137
|
+
* @param required - Whether to throw when the option is missing.
|
|
138
|
+
*/
|
|
139
|
+
getNumber(name, required = false) {
|
|
140
|
+
const option = this.extractOption(name, ApplicationCommandOptionType.Number, required);
|
|
141
|
+
return option ? option.value : null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Resolves a user option, including any guild member payload.
|
|
145
|
+
*
|
|
146
|
+
* @param name - The option name to resolve.
|
|
147
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
148
|
+
*/
|
|
149
|
+
getUser(name, required = false) {
|
|
150
|
+
const option = this.extractOption(name, ApplicationCommandOptionType.User, required);
|
|
151
|
+
if (!option) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const resolvedUser = this.resolveUser(option.value);
|
|
155
|
+
if (!resolvedUser) {
|
|
156
|
+
if (required) {
|
|
157
|
+
throw new Error(`Resolved data for user option "${name}" is missing`);
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return resolvedUser;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Resolves a role option to its resolved payload.
|
|
165
|
+
*
|
|
166
|
+
* @param name - The option name to resolve.
|
|
167
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
168
|
+
*/
|
|
169
|
+
getRole(name, required = false) {
|
|
170
|
+
const option = this.extractOption(name, ApplicationCommandOptionType.Role, required);
|
|
171
|
+
if (!option) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const role = this.resolveRole(option.value);
|
|
175
|
+
if (!role && required) {
|
|
176
|
+
throw new Error(`Resolved data for role option "${name}" is missing`);
|
|
177
|
+
}
|
|
178
|
+
return role ?? null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Resolves a channel option to its resolved payload.
|
|
182
|
+
*
|
|
183
|
+
* @param name - The option name to resolve.
|
|
184
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
185
|
+
*/
|
|
186
|
+
getChannel(name, required = false) {
|
|
187
|
+
const option = this.extractOption(name, ApplicationCommandOptionType.Channel, required);
|
|
188
|
+
if (!option) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const channel = this.resolveChannel(option.value);
|
|
192
|
+
if (!channel && required) {
|
|
193
|
+
throw new Error(`Resolved data for channel option "${name}" is missing`);
|
|
194
|
+
}
|
|
195
|
+
return channel ?? null;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Resolves an attachment option to its resolved payload.
|
|
199
|
+
*
|
|
200
|
+
* @param name - The option name to resolve.
|
|
201
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
202
|
+
*/
|
|
203
|
+
getAttachment(name, required = false) {
|
|
204
|
+
const option = this.extractOption(name, ApplicationCommandOptionType.Attachment, required);
|
|
205
|
+
if (!option) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const attachment = this.resolveAttachment(option.value);
|
|
209
|
+
if (!attachment && required) {
|
|
210
|
+
throw new Error(`Resolved data for attachment option "${name}" is missing`);
|
|
211
|
+
}
|
|
212
|
+
return attachment ?? null;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Resolves a mentionable option to either a role or user payload.
|
|
216
|
+
*
|
|
217
|
+
* @param name - The option name to resolve.
|
|
218
|
+
* @param required - Whether to throw when the option is missing or cannot be resolved.
|
|
219
|
+
*/
|
|
220
|
+
getMentionable(name, required = false) {
|
|
221
|
+
const option = this.extractOption(name, ApplicationCommandOptionType.Mentionable, required);
|
|
222
|
+
if (!option) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const role = this.resolveRole(option.value);
|
|
226
|
+
if (role) {
|
|
227
|
+
return { type: "role", value: role };
|
|
228
|
+
}
|
|
229
|
+
const user = this.resolveUser(option.value);
|
|
230
|
+
if (user) {
|
|
231
|
+
return { type: "user", value: user };
|
|
232
|
+
}
|
|
233
|
+
if (required) {
|
|
234
|
+
throw new Error(`Resolved data for mentionable option "${name}" is missing`);
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Returns the raw option payload, bypassing type checks and casting.
|
|
240
|
+
*
|
|
241
|
+
* @param name - The option name to look up.
|
|
242
|
+
*/
|
|
243
|
+
getRawOption(name) {
|
|
244
|
+
return (this.focusedOptions.find((option) => option.name === name) ?? null);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Extracts a strongly typed option, ensuring it exists and matches the expected type.
|
|
248
|
+
*/
|
|
249
|
+
extractOption(name, type, required) {
|
|
250
|
+
const option = this.focusedOptions.find((candidate) => candidate.name === name);
|
|
251
|
+
if (!option) {
|
|
252
|
+
if (required) {
|
|
253
|
+
throw new Error(`Required ${OPTION_TYPE_LABEL[type]} option "${name}" is missing`);
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
if (option.type !== type) {
|
|
258
|
+
throw new Error(`Option "${name}" is a ${OPTION_TYPE_LABEL[option.type]}, expected ${OPTION_TYPE_LABEL[type]}`);
|
|
259
|
+
}
|
|
260
|
+
return option;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Resolves an attachment by identifier from the interaction's resolved payloads.
|
|
264
|
+
*/
|
|
265
|
+
resolveAttachment(id) {
|
|
266
|
+
return this.getResolvedRecord(this.resolved?.attachments, id);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Resolves a channel by identifier from the interaction's resolved payloads.
|
|
270
|
+
*/
|
|
271
|
+
resolveChannel(id) {
|
|
272
|
+
return this.getResolvedRecord(this.resolved?.channels, id);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Resolves a role by identifier from the interaction's resolved payloads.
|
|
276
|
+
*/
|
|
277
|
+
resolveRole(id) {
|
|
278
|
+
return this.getResolvedRecord(this.resolved?.roles, id);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Resolves a user and optional member by identifier from the interaction's resolved payloads.
|
|
282
|
+
*/
|
|
283
|
+
resolveUser(id) {
|
|
284
|
+
const user = this.getResolvedRecord(this.resolved?.users, id);
|
|
285
|
+
if (!user) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
const member = this.getResolvedRecord(this.resolved?.members, id);
|
|
289
|
+
return { user, member: member ?? undefined };
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Retrieves a record from a resolved payload map by identifier.
|
|
293
|
+
*/
|
|
294
|
+
getResolvedRecord(records, id) {
|
|
295
|
+
return records?.[id];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Wraps a raw application command interaction with helper methods and option resolvers.
|
|
300
|
+
*
|
|
301
|
+
* @param interaction - The raw interaction payload from Discord.
|
|
302
|
+
* @returns A helper-augmented interaction object.
|
|
303
|
+
*/
|
|
304
|
+
export function createCommandInteraction(interaction) {
|
|
305
|
+
const options = new CommandInteractionOptionResolver(interaction.data.options, interaction.data.resolved);
|
|
306
|
+
let capturedResponse = null;
|
|
307
|
+
/**
|
|
308
|
+
* Stores the most recent response helper payload for later retrieval.
|
|
309
|
+
*/
|
|
310
|
+
const captureResponse = (response) => {
|
|
311
|
+
capturedResponse = response;
|
|
312
|
+
return response;
|
|
313
|
+
};
|
|
314
|
+
function createMessageResponse(type, data) {
|
|
315
|
+
const normalisedData = normaliseInteractionMessageData(data);
|
|
316
|
+
if (type === InteractionResponseType.ChannelMessageWithSource) {
|
|
317
|
+
if (!normalisedData) {
|
|
318
|
+
throw new Error("[MiniInteraction] Channel message responses require data to be provided.");
|
|
319
|
+
}
|
|
320
|
+
return captureResponse({
|
|
321
|
+
type,
|
|
322
|
+
data: normalisedData,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
if (normalisedData) {
|
|
326
|
+
return captureResponse({ type, data: normalisedData });
|
|
327
|
+
}
|
|
328
|
+
return captureResponse({ type });
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Creates a deferred response while normalising any helper flag values.
|
|
332
|
+
*/
|
|
333
|
+
const createDeferredResponse = (data) => {
|
|
334
|
+
if (!data) {
|
|
335
|
+
return captureResponse({
|
|
336
|
+
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return captureResponse({
|
|
340
|
+
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
|
341
|
+
data: { flags: normaliseMessageFlags(data.flags) },
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
const commandInteraction = {
|
|
345
|
+
...interaction,
|
|
346
|
+
data: {
|
|
347
|
+
...interaction.data,
|
|
348
|
+
options,
|
|
349
|
+
},
|
|
350
|
+
options,
|
|
351
|
+
getResponse() {
|
|
352
|
+
return capturedResponse;
|
|
353
|
+
},
|
|
354
|
+
reply(data) {
|
|
355
|
+
return createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
|
|
356
|
+
},
|
|
357
|
+
followUp(data) {
|
|
358
|
+
return createMessageResponse(InteractionResponseType.ChannelMessageWithSource, data);
|
|
359
|
+
},
|
|
360
|
+
edit(data) {
|
|
361
|
+
return createMessageResponse(InteractionResponseType.UpdateMessage, data);
|
|
362
|
+
},
|
|
363
|
+
deferReply(options) {
|
|
364
|
+
return createDeferredResponse(options?.flags !== undefined
|
|
365
|
+
? { flags: options.flags }
|
|
366
|
+
: undefined);
|
|
367
|
+
},
|
|
368
|
+
showModal(data) {
|
|
369
|
+
return captureResponse({
|
|
370
|
+
type: InteractionResponseType.Modal,
|
|
371
|
+
data,
|
|
372
|
+
});
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
return commandInteraction;
|
|
376
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type APIInteractionResponse, type APIInteractionResponseChannelMessageWithSource, type APIInteractionResponseDeferredChannelMessageWithSource, type APIInteractionResponseDeferredMessageUpdate, type APIInteractionResponseUpdateMessage, type APIMessageComponentInteraction } from "discord-api-types/v10";
|
|
2
|
+
import { DeferReplyOptions, InteractionMessageData } from "./interactionMessageHelpers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Represents a component interaction augmented with helper response methods.
|
|
5
|
+
*/
|
|
6
|
+
export type MessageComponentInteraction = APIMessageComponentInteraction & {
|
|
7
|
+
getResponse: () => APIInteractionResponse | null;
|
|
8
|
+
reply: (data: InteractionMessageData) => APIInteractionResponseChannelMessageWithSource;
|
|
9
|
+
deferReply: (options?: DeferReplyOptions) => APIInteractionResponseDeferredChannelMessageWithSource;
|
|
10
|
+
update: (data?: InteractionMessageData) => APIInteractionResponseUpdateMessage;
|
|
11
|
+
deferUpdate: () => APIInteractionResponseDeferredMessageUpdate;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Wraps a raw component interaction with helper methods mirroring Discord's expected responses.
|
|
15
|
+
*
|
|
16
|
+
* @param interaction - The raw interaction payload from Discord.
|
|
17
|
+
* @returns A helper-augmented interaction object.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createMessageComponentInteraction(interaction: APIMessageComponentInteraction): MessageComponentInteraction;
|