@minesa-org/mini-interaction 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,13 +37,21 @@ export type MiniInteractionHandlerResult = {
37
37
  };
38
38
  /** Handler signature invoked for Discord message component interactions. */
39
39
  export type MiniInteractionComponentHandler = (interaction: MessageComponentInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
40
- /** Structure describing a component handler mapped to a custom id. */
40
+ /** Handler signature invoked for Discord modal submit interactions. */
41
+ export type MiniInteractionModalHandler = (interaction: ModalSubmitInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
42
+ /** Unified handler signature that accepts both component and modal interactions. */
43
+ export type MiniInteractionHandler = MiniInteractionComponentHandler | MiniInteractionModalHandler;
44
+ /**
45
+ * Structure describing a component or modal handler mapped to a custom id.
46
+ * When auto-loading from the components directory:
47
+ * - Files in `components/modals/` are treated as modal handlers
48
+ * - Other files are treated as component handlers
49
+ * You can use this type for both - the system will figure out which one it is.
50
+ */
41
51
  export type MiniInteractionComponent = {
42
52
  customId: string;
43
- handler: MiniInteractionComponentHandler;
53
+ handler: MiniInteractionHandler;
44
54
  };
45
- /** Handler signature invoked for Discord modal submit interactions. */
46
- export type MiniInteractionModalHandler = (interaction: ModalSubmitInteraction) => Promise<APIInteractionResponse | void> | APIInteractionResponse | void;
47
55
  /** Structure describing a modal handler mapped to a custom id. */
48
56
  export type MiniInteractionModal = {
49
57
  customId: string;
@@ -0,0 +1,83 @@
1
+ import { ApplicationCommandType, type LocalizationMap, type Permissions, type RESTPostAPIContextMenuApplicationCommandsJSONBody } from "discord-api-types/v10";
2
+ import { CommandContext, IntegrationType } from "./CommandBuilder.js";
3
+ /**
4
+ * Base data structure for context menu commands.
5
+ */
6
+ type ContextMenuCommandData = Partial<Omit<RESTPostAPIContextMenuApplicationCommandsJSONBody, "contexts" | "integration_types" | "type">> & {
7
+ type: ApplicationCommandType.User | ApplicationCommandType.Message;
8
+ contexts?: CommandContext[] | null;
9
+ integration_types?: IntegrationType[];
10
+ };
11
+ /**
12
+ * Base builder for context menu commands (User and Message commands).
13
+ */
14
+ declare abstract class BaseContextMenuCommandBuilder<T extends ApplicationCommandType.User | ApplicationCommandType.Message> {
15
+ protected readonly data: ContextMenuCommandData;
16
+ constructor(type: T);
17
+ /**
18
+ * Sets the command name.
19
+ * Context menu command names can be up to 32 characters and are displayed in the UI.
20
+ */
21
+ setName(name: string): this;
22
+ /**
23
+ * Sets the localized names for the command.
24
+ */
25
+ setNameLocalizations(nameLocalizations: LocalizationMap | null): this;
26
+ /**
27
+ * Sets the default member permissions required to use this command.
28
+ */
29
+ setDefaultMemberPermissions(permissions: Permissions | null): this;
30
+ /**
31
+ * Sets whether the command is available in direct messages.
32
+ */
33
+ setDMPermission(dmPermission: boolean): this;
34
+ /**
35
+ * Marks the command as not safe for work.
36
+ */
37
+ setNSFW(nsfw: boolean): this;
38
+ /**
39
+ * Limits the contexts in which the command can appear.
40
+ */
41
+ setContexts(contexts: CommandContext[] | null): this;
42
+ /**
43
+ * Specifies the integration types supported by the command.
44
+ */
45
+ setIntegrationTypes(integrationTypes: IntegrationType[]): this;
46
+ /**
47
+ * Produces the final REST payload for the configured command.
48
+ */
49
+ toJSON(): RESTPostAPIContextMenuApplicationCommandsJSONBody;
50
+ /**
51
+ * Allows the builder to be coerced into its JSON payload automatically.
52
+ */
53
+ valueOf(): RESTPostAPIContextMenuApplicationCommandsJSONBody;
54
+ }
55
+ /**
56
+ * Builder for User context menu commands.
57
+ * These commands appear when right-clicking on a user.
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const userCommand = new UserCommandBuilder()
62
+ * .setName('User Info')
63
+ * .toJSON();
64
+ * ```
65
+ */
66
+ export declare class UserCommandBuilder extends BaseContextMenuCommandBuilder<ApplicationCommandType.User> {
67
+ constructor();
68
+ }
69
+ /**
70
+ * Builder for Message context menu commands.
71
+ * These commands appear when right-clicking on a message.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const messageCommand = new MessageCommandBuilder()
76
+ * .setName('Report Message')
77
+ * .toJSON();
78
+ * ```
79
+ */
80
+ export declare class MessageCommandBuilder extends BaseContextMenuCommandBuilder<ApplicationCommandType.Message> {
81
+ constructor();
82
+ }
83
+ export {};
@@ -0,0 +1,137 @@
1
+ import { ApplicationCommandType, } from "discord-api-types/v10";
2
+ /**
3
+ * Base builder for context menu commands (User and Message commands).
4
+ */
5
+ class BaseContextMenuCommandBuilder {
6
+ data;
7
+ constructor(type) {
8
+ this.data = {
9
+ type,
10
+ };
11
+ }
12
+ /**
13
+ * Sets the command name.
14
+ * Context menu command names can be up to 32 characters and are displayed in the UI.
15
+ */
16
+ setName(name) {
17
+ if (!name || name.length === 0) {
18
+ throw new Error("Context menu command name cannot be empty");
19
+ }
20
+ if (name.length > 32) {
21
+ throw new RangeError("Context menu command name cannot exceed 32 characters");
22
+ }
23
+ this.data.name = name;
24
+ return this;
25
+ }
26
+ /**
27
+ * Sets the localized names for the command.
28
+ */
29
+ setNameLocalizations(nameLocalizations) {
30
+ this.data.name_localizations = nameLocalizations ?? undefined;
31
+ return this;
32
+ }
33
+ /**
34
+ * Sets the default member permissions required to use this command.
35
+ */
36
+ setDefaultMemberPermissions(permissions) {
37
+ this.data.default_member_permissions =
38
+ permissions === null ? null : String(permissions);
39
+ return this;
40
+ }
41
+ /**
42
+ * Sets whether the command is available in direct messages.
43
+ */
44
+ setDMPermission(dmPermission) {
45
+ this.data.dm_permission = dmPermission;
46
+ return this;
47
+ }
48
+ /**
49
+ * Marks the command as not safe for work.
50
+ */
51
+ setNSFW(nsfw) {
52
+ this.data.nsfw = nsfw;
53
+ return this;
54
+ }
55
+ /**
56
+ * Limits the contexts in which the command can appear.
57
+ */
58
+ setContexts(contexts) {
59
+ this.data.contexts = contexts ? [...contexts] : null;
60
+ return this;
61
+ }
62
+ /**
63
+ * Specifies the integration types supported by the command.
64
+ */
65
+ setIntegrationTypes(integrationTypes) {
66
+ this.data.integration_types = [...integrationTypes];
67
+ return this;
68
+ }
69
+ /**
70
+ * Produces the final REST payload for the configured command.
71
+ */
72
+ toJSON() {
73
+ const { name, type } = this.data;
74
+ if (!name) {
75
+ throw new Error("Context menu command name has not been set");
76
+ }
77
+ const contexts = this.data.contexts;
78
+ const integrationTypes = this.data.integration_types;
79
+ return {
80
+ ...this.data,
81
+ name,
82
+ type,
83
+ contexts: contexts === null
84
+ ? null
85
+ : Array.isArray(contexts)
86
+ ? [...contexts]
87
+ : undefined,
88
+ integration_types: Array.isArray(integrationTypes)
89
+ ? [...integrationTypes]
90
+ : integrationTypes ?? undefined,
91
+ };
92
+ }
93
+ /**
94
+ * Allows the builder to be coerced into its JSON payload automatically.
95
+ */
96
+ valueOf() {
97
+ return this.toJSON();
98
+ }
99
+ /**
100
+ * Formats the command as JSON when inspected in Node.js runtimes.
101
+ */
102
+ [Symbol.for("nodejs.util.inspect.custom")]() {
103
+ return this.toJSON();
104
+ }
105
+ }
106
+ /**
107
+ * Builder for User context menu commands.
108
+ * These commands appear when right-clicking on a user.
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const userCommand = new UserCommandBuilder()
113
+ * .setName('User Info')
114
+ * .toJSON();
115
+ * ```
116
+ */
117
+ export class UserCommandBuilder extends BaseContextMenuCommandBuilder {
118
+ constructor() {
119
+ super(ApplicationCommandType.User);
120
+ }
121
+ }
122
+ /**
123
+ * Builder for Message context menu commands.
124
+ * These commands appear when right-clicking on a message.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const messageCommand = new MessageCommandBuilder()
129
+ * .setName('Report Message')
130
+ * .toJSON();
131
+ * ```
132
+ */
133
+ export class MessageCommandBuilder extends BaseContextMenuCommandBuilder {
134
+ constructor() {
135
+ super(ApplicationCommandType.Message);
136
+ }
137
+ }
package/dist/index.d.ts CHANGED
@@ -2,13 +2,14 @@
2
2
  export { MiniInteraction } from "./clients/MiniInteraction.js";
3
3
  export type { RoleConnectionMetadataField } from "./clients/MiniInteraction.js";
4
4
  export { CommandBuilder, CommandContext, IntegrationType, } from "./commands/CommandBuilder.js";
5
+ export { UserCommandBuilder, MessageCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
5
6
  export type { AttachmentOptionBuilder, ChannelOptionBuilder, MentionableOptionBuilder, NumberOptionBuilder, RoleOptionBuilder, StringOptionBuilder, SubcommandBuilder, SubcommandGroupBuilder, UserOptionBuilder, } from "./commands/CommandBuilder.js";
6
7
  export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
7
8
  export type { CommandInteraction, MentionableOption, ResolvedUserOption, } from "./utils/CommandInteractionOptions.js";
8
9
  export type { MiniInteractionFetchHandler, MiniInteractionNodeHandler, MiniInteractionHandlerResult, MiniInteractionRequest, MiniInteractionOptions, } from "./clients/MiniInteraction.js";
9
10
  export type { MiniInteractionCommand, SlashCommandHandler, } from "./types/Commands.js";
10
- export type { MiniInteractionComponent, MiniInteractionComponentHandler, MiniInteractionModal, MiniInteractionModalHandler, } from "./clients/MiniInteraction.js";
11
- export type { MessageComponentInteraction } from "./utils/MessageComponentInteraction.js";
11
+ export type { MiniInteractionComponent, MiniInteractionComponentHandler, MiniInteractionModal, MiniInteractionModalHandler, MiniInteractionHandler, } from "./clients/MiniInteraction.js";
12
+ export type { MessageComponentInteraction, ResolvedUserOption as ComponentResolvedUserOption, ResolvedMentionableOption as ComponentResolvedMentionableOption, } from "./utils/MessageComponentInteraction.js";
12
13
  export type { ModalSubmitInteraction } from "./utils/ModalSubmitInteraction.js";
13
14
  export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
14
15
  export { ChannelType } from "./types/ChannelType.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /** Entry point re-exporting MiniInteraction helpers, builders, and shared types. */
2
2
  export { MiniInteraction } from "./clients/MiniInteraction.js";
3
3
  export { CommandBuilder, CommandContext, IntegrationType, } from "./commands/CommandBuilder.js";
4
+ export { UserCommandBuilder, MessageCommandBuilder, } from "./commands/ContextMenuCommandBuilder.js";
4
5
  export { CommandInteractionOptionResolver, createCommandInteraction, } from "./utils/CommandInteractionOptions.js";
5
6
  export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes.js";
6
7
  export { ChannelType } from "./types/ChannelType.js";
@@ -47,12 +47,25 @@ export declare class CommandInteractionOptionResolver {
47
47
  */
48
48
  getString(name: string, required?: boolean): string | null;
49
49
  /**
50
- * Looks up a numeric option by name.
50
+ * Looks up an integer option by name.
51
51
  *
52
52
  * @param name - The option name to resolve.
53
53
  * @param required - Whether to throw when the option is missing.
54
54
  */
55
+ getInteger(name: string, required?: boolean): number | null;
56
+ /**
57
+ * Looks up a numeric option by name.
58
+ *
59
+ * @param name - The option name to resolve.
60
+ */
55
61
  getNumber(name: string, required?: boolean): number | null;
62
+ /**
63
+ * Looks up a boolean option by name.
64
+ *
65
+ * @param name - The option name to resolve.
66
+ * @param required - Whether to throw when the option is missing.
67
+ */
68
+ getBoolean(name: string, required?: boolean): boolean | null;
56
69
  /**
57
70
  * Resolves a user option, including any guild member payload.
58
71
  *
@@ -131,15 +131,34 @@ export class CommandInteractionOptionResolver {
131
131
  return option ? option.value : null;
132
132
  }
133
133
  /**
134
- * Looks up a numeric option by name.
134
+ * Looks up an integer option by name.
135
135
  *
136
136
  * @param name - The option name to resolve.
137
137
  * @param required - Whether to throw when the option is missing.
138
138
  */
139
+ getInteger(name, required = false) {
140
+ const option = this.extractOption(name, ApplicationCommandOptionType.Integer, required);
141
+ return option ? option.value : null;
142
+ }
143
+ /**
144
+ * Looks up a numeric option by name.
145
+ *
146
+ * @param name - The option name to resolve.
147
+ */
139
148
  getNumber(name, required = false) {
140
149
  const option = this.extractOption(name, ApplicationCommandOptionType.Number, required);
141
150
  return option ? option.value : null;
142
151
  }
152
+ /**
153
+ * Looks up a boolean option by name.
154
+ *
155
+ * @param name - The option name to resolve.
156
+ * @param required - Whether to throw when the option is missing.
157
+ */
158
+ getBoolean(name, required = false) {
159
+ const option = this.extractOption(name, ApplicationCommandOptionType.Boolean, required);
160
+ return option ? option.value : null;
161
+ }
143
162
  /**
144
163
  * Resolves a user option, including any guild member payload.
145
164
  *
@@ -1,5 +1,18 @@
1
- import { type APIInteractionResponse, type APIInteractionResponseChannelMessageWithSource, type APIInteractionResponseDeferredChannelMessageWithSource, type APIInteractionResponseDeferredMessageUpdate, type APIInteractionResponseUpdateMessage, type APIMessageComponentInteraction, type APIModalInteractionResponse, type APIModalInteractionResponseCallbackData } from "discord-api-types/v10";
1
+ import { type APIInteractionDataResolvedChannel, type APIInteractionDataResolvedGuildMember, type APIInteractionResponse, type APIInteractionResponseChannelMessageWithSource, type APIInteractionResponseDeferredChannelMessageWithSource, type APIInteractionResponseDeferredMessageUpdate, type APIInteractionResponseUpdateMessage, type APIMessageComponentInteraction, type APIModalInteractionResponse, type APIModalInteractionResponseCallbackData, type APIRole, type APIUser } from "discord-api-types/v10";
2
2
  import { DeferReplyOptions, InteractionMessageData } from "./interactionMessageHelpers.js";
3
+ /** Resolved user option including optional guild member data. */
4
+ export type ResolvedUserOption = {
5
+ user: APIUser;
6
+ member?: APIInteractionDataResolvedGuildMember;
7
+ };
8
+ /** Resolved mentionable option (either a user or role). */
9
+ export type ResolvedMentionableOption = {
10
+ type: "user";
11
+ value: ResolvedUserOption;
12
+ } | {
13
+ type: "role";
14
+ value: APIRole;
15
+ };
3
16
  /**
4
17
  * Represents a component interaction augmented with helper response methods.
5
18
  *
@@ -21,6 +34,31 @@ export type MessageComponentInteraction = APIMessageComponentInteraction & {
21
34
  * For button interactions, this will be undefined.
22
35
  */
23
36
  values?: string[];
37
+ /**
38
+ * Helper method to get selected string values from a string select menu.
39
+ * @returns Array of selected string values, or empty array if not a string select menu
40
+ */
41
+ getStringValues: () => string[];
42
+ /**
43
+ * Helper method to get selected roles from a role select menu.
44
+ * @returns Array of resolved role objects, or empty array if not a role select menu
45
+ */
46
+ getRoles: () => APIRole[];
47
+ /**
48
+ * Helper method to get selected channels from a channel select menu.
49
+ * @returns Array of resolved channel objects, or empty array if not a channel select menu
50
+ */
51
+ getChannels: () => APIInteractionDataResolvedChannel[];
52
+ /**
53
+ * Helper method to get selected users from a user select menu.
54
+ * @returns Array of resolved user objects (with optional member data), or empty array if not a user select menu
55
+ */
56
+ getUsers: () => ResolvedUserOption[];
57
+ /**
58
+ * Helper method to get selected mentionables from a mentionable select menu.
59
+ * @returns Array of resolved mentionable objects (users or roles), or empty array if not a mentionable select menu
60
+ */
61
+ getMentionables: () => ResolvedMentionableOption[];
24
62
  };
25
63
  /**
26
64
  * Wraps a raw component interaction with helper methods mirroring Discord's expected responses.
@@ -63,6 +63,99 @@ export function createMessageComponentInteraction(interaction) {
63
63
  const getResponse = () => capturedResponse;
64
64
  // Extract values from select menu interactions
65
65
  const values = "values" in interaction.data ? interaction.data.values : undefined;
66
+ // Helper methods for select menu interactions
67
+ const getStringValues = () => {
68
+ return values ?? [];
69
+ };
70
+ const getRoles = () => {
71
+ if (!values) {
72
+ return [];
73
+ }
74
+ // Type guard: check if resolved data exists
75
+ const resolved = "resolved" in interaction.data
76
+ ? interaction.data.resolved
77
+ : undefined;
78
+ if (!resolved?.roles) {
79
+ return [];
80
+ }
81
+ const roles = [];
82
+ for (const roleId of values) {
83
+ const role = resolved.roles[roleId];
84
+ if (role) {
85
+ roles.push(role);
86
+ }
87
+ }
88
+ return roles;
89
+ };
90
+ const getChannels = () => {
91
+ if (!values) {
92
+ return [];
93
+ }
94
+ // Type guard: check if resolved data exists
95
+ const resolved = "resolved" in interaction.data
96
+ ? interaction.data.resolved
97
+ : undefined;
98
+ if (!resolved?.channels) {
99
+ return [];
100
+ }
101
+ const channels = [];
102
+ for (const channelId of values) {
103
+ const channel = resolved.channels[channelId];
104
+ if (channel) {
105
+ channels.push(channel);
106
+ }
107
+ }
108
+ return channels;
109
+ };
110
+ const getUsers = () => {
111
+ if (!values) {
112
+ return [];
113
+ }
114
+ // Type guard: check if resolved data exists
115
+ const resolved = "resolved" in interaction.data
116
+ ? interaction.data.resolved
117
+ : undefined;
118
+ if (!resolved?.users) {
119
+ return [];
120
+ }
121
+ const users = [];
122
+ for (const userId of values) {
123
+ const user = resolved.users[userId];
124
+ if (user) {
125
+ const member = resolved.members?.[userId];
126
+ users.push({ user, member });
127
+ }
128
+ }
129
+ return users;
130
+ };
131
+ const getMentionables = () => {
132
+ if (!values) {
133
+ return [];
134
+ }
135
+ // Type guard: check if resolved data exists
136
+ const resolved = "resolved" in interaction.data
137
+ ? interaction.data.resolved
138
+ : undefined;
139
+ if (!resolved) {
140
+ return [];
141
+ }
142
+ const mentionables = [];
143
+ for (const id of values) {
144
+ // Check if it's a role
145
+ const role = resolved.roles?.[id];
146
+ if (role) {
147
+ mentionables.push({ type: "role", value: role });
148
+ continue;
149
+ }
150
+ // Check if it's a user
151
+ const user = resolved.users?.[id];
152
+ if (user) {
153
+ const member = resolved.members?.[id];
154
+ mentionables.push({ type: "user", value: { user, member } });
155
+ }
156
+ }
157
+ return mentionables;
158
+ };
66
159
  return Object.assign(interaction, {
67
160
  reply,
68
161
  deferReply,
@@ -71,5 +164,10 @@ export function createMessageComponentInteraction(interaction) {
71
164
  showModal,
72
165
  getResponse,
73
166
  values,
167
+ getStringValues,
168
+ getRoles,
169
+ getChannels,
170
+ getUsers,
171
+ getMentionables,
74
172
  });
75
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "Mini interaction, connecting your app with Discord via HTTP-interaction (Vercel support).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -48,4 +48,4 @@
48
48
  "ts-node": "^10.9.2",
49
49
  "typescript": "^5.9.3"
50
50
  }
51
- }
51
+ }