From 97701e7786a91686b9d81809de789e3d66c23b77 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 12:21:59 +1000 Subject: [PATCH 01/27] chore: initial structure for SelectMenu, with typings --- src/structures/MessageSelectMenu.js | 66 +++++++++++++++++++++++++++++ src/util/Constants.js | 2 +- typings/index.d.ts | 37 ++++++++++++++-- 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 src/structures/MessageSelectMenu.js diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js new file mode 100644 index 000000000000..01c65fbc8a34 --- /dev/null +++ b/src/structures/MessageSelectMenu.js @@ -0,0 +1,66 @@ +'use strict'; + +const BaseMessageComponent = require('./BaseMessageComponent'); + +class MessageSelectMenu extends BaseMessageComponent { + /** + * @typedef {BaseMessageComponentOptions} MessageSelectMenuOptions + * @property {?string} [customID] A unique string to be sent in the interaction when clicked + * @property {?string} [placeholder] Custom placeholder text to display when nothing is selected + * @property {?number} [minValues] The minimum number of selections required + * @property {?number} [maxValues] The maximum number of selections allowed + * @property {?MessageSelectOption[]} [options] Options for the select menu + */ + + /** + * @typedef {Object} MessageSelectOption + * @property {string} label The text to be displayed on this option + * @property {string} value The value to be sent for this option + * @property {?string} [description] Optional description to show for this option + * @property {?Emoji} [emoji] Emoji to display for this option + * @property {boolean} [default] Render this option as the default selection + */ + + /** + * @param {MessageSelectMenu|MessageSelectMenuOptions} [data={}] MessageSelectMenu to clone or raw data + */ + constructor(data = {}) { + super({ type: 'SELECT_MENU' }); + + this.setup(data); + } + + super(data) { + /** + * A unique string to be sent in the interaction when clicked + * @type {?string} + */ + this.customID = data.custom_id ?? data.customID ?? null; + + /** + * Custom placeholder text to display when nothing is selected + * @type {?string} + */ + this.placeholder = data.placeholder ?? null; + + /** + * The minimum number of selections required + * @type {?number} + */ + this.minValues = data.min_values ?? data.minValues ?? null; + + /** + * The maximum number of selections allowed + * @type {?number} + */ + this.maxValues = data.max_values ?? data.maxValues ?? null; + + /** + * Options for the select menu + * @type {MessageSelectOption[]} + */ + this.options = data.options ?? []; + } +} + +module.exports = MessageSelectMenu; diff --git a/src/util/Constants.js b/src/util/Constants.js index 27b9770502f7..86d5f556782a 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -946,7 +946,7 @@ exports.InteractionResponseTypes = createEnum([ * * BUTTON * @typedef {string} MessageComponentType */ -exports.MessageComponentTypes = createEnum([null, 'ACTION_ROW', 'BUTTON']); +exports.MessageComponentTypes = createEnum([null, 'ACTION_ROW', 'BUTTON', 'SELECT_MENU']); /** * The style of a message button diff --git a/typings/index.d.ts b/typings/index.d.ts index e9665f50d7eb..71985cab190b 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -82,6 +82,7 @@ declare enum MessageButtonStyles { declare enum MessageComponentTypes { ACTION_ROW = 1, BUTTON = 2, + SELECT_MENU = 3, } declare enum MFALevels { @@ -1531,6 +1532,16 @@ declare module 'discord.js' { public toJSON(): unknown; } + class MessageSelectMenu extends BaseMessageComponent { + constructor(data?: MessageSelectMenu | MessageSelectMenuOptions); + public customID: string | null; + public maxValues: number | null; + public minValues: number | null; + public options: MessageSelectOption[]; + public placeholder: string | null; + public type: 'SELECT_MENU'; + } + export class NewsChannel extends TextBasedChannel(GuildChannel) { constructor(guild: Guild, data?: unknown); public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration; @@ -3605,15 +3616,18 @@ declare module 'discord.js' { maxProcessed?: number; } - type MessageComponent = BaseMessageComponent | MessageActionRow | MessageButton; - + type MessageComponent = BaseMessageComponent | MessageActionRow | MessageButton | MessageSelectMenu; interface MessageComponentInteractionCollectorOptions extends CollectorOptions { max?: number; maxComponents?: number; maxUsers?: number; } - type MessageComponentOptions = BaseMessageComponentOptions | MessageActionRowOptions | MessageButtonOptions; + type MessageComponentOptions = + | BaseMessageComponentOptions + | MessageActionRowOptions + | MessageButtonOptions + | MessageSelectMenuOptions; type MessageComponentType = keyof typeof MessageComponentTypes; @@ -3750,6 +3764,23 @@ declare module 'discord.js' { type MessageResolvable = Message | Snowflake; + interface MessageSelectMenuOptions extends BaseMessageComponentOptions { + customID?: string; + maxValues?: number; + minValues?: number; + options?: MessageSelectOption[]; + placeholder?: string; + type: 'SELECT_MENU' | MessageComponentTypes.SELECT_MENU; + } + + interface MessageSelectOption { + default?: boolean; + description?: string; + emoji?: RawEmoji; + label: string; + value: string; + } + type MessageTarget = | Interaction | InteractionWebhook From 984839a0f7b5eccaf11046654f2d9b3d81d5775f Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 14:20:48 +1000 Subject: [PATCH 02/27] feat: set methods for MessageSelectMenu --- src/structures/MessageSelectMenu.js | 96 +++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 01c65fbc8a34..afdf1adda63e 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -1,6 +1,7 @@ 'use strict'; const BaseMessageComponent = require('./BaseMessageComponent'); +const Util = require('../util/Util'); class MessageSelectMenu extends BaseMessageComponent { /** @@ -61,6 +62,101 @@ class MessageSelectMenu extends BaseMessageComponent { */ this.options = data.options ?? []; } + + /** + * Sets the custom ID of this select menu + * @param {string} customID A unique string to be sent in the interaction when clicked + * @returns {MessageSelectMenu} + */ + setCustomID(customID) { + this.customID = Util.resolveString(customID); + return this; + } + + /** + * Sets the maximum number of selection allowed for this select menu + * @param {number} maxValues Number of selections to be allowed + * @returns {MessageSelectMenu} + */ + setMaxValues(maxValues) { + this.maxValues = maxValues; + return this; + } + + /** + * Sets the minimum number of selection required for this select menu + * @param {number} minValues Number of selections to be required + * @returns {MessageSelectMenu} + */ + setMinValues(minValues) { + this.minValues = minValues; + return this; + } + + /** + * Sets the placeholder of this select menu + * @param {string} placeholder Custom placeholder text to display when nothing is selected + * @returns {MessageSelectMenu} + */ + setPlaceholder(placeholder) { + this.placeholder = Util.resolveString(placeholder); + return this; + } + + /** + * Adds an option to the select menu (max 25) + * @param {MessageSelectOption} option The option to add + * @returns {MessageSelectMenu} + */ + addOption(option) { + return this.addOptions({ ...option }); + } + + /** + * Adds options to the select menu (max 5). + * @param {...(MessageSelectOption[]|MessageSelectOption[])} options The options to add + * @returns {MessageSelectMenu} + */ + addOptions(...options) { + this.options.push(...this.constructor.normalizeOptions(options)); + return this; + } + + /** + * Removes, replaces, and inserts options in the select menu (max 25). + * @param {number} index The index to start at + * @param {number} deleteCount The number of options to remove + * @param {...MessageSelectOption|MessageSelectOption[]} [options] The replacing option objects + * @returns {MessageSelectMenu} + */ + spliceFields(index, deleteCount, ...options) { + this.fields.splice(index, deleteCount, ...this.constructor.normalizeOptions(...options)); + return this; + } + + /** + * Normalizes option input and resolves strings. + * @param {MessageSelectOption} option The select menu option to normalize + * @returns {MessageSelectOption} + */ + static normalizeOption(option) { + let { label, value, description, emoji } = option; + + label = Util.resolveString(label); + value = Util.resolveString(value); + description = typeof description !== 'undefined' ? Util.resolveString(description) : undefined; + + return { label, value, description, emoji, default: option.default }; + } + + /** + * Normalizes option input and resolves strings. + * @param {...MessageSelectOption|MessageSelectOption[]} options The select menu options to normalize + * @returns {MessageSelectOption[]} + */ + static normalizeOptions(...options) { + return options.flat(2).map(option => this.normalizeOption(option)); + } } module.exports = MessageSelectMenu; From df2cb8a439c4c7d437101c47ca973bfad1b1d79b Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 15:14:32 +1000 Subject: [PATCH 03/27] feat: toJSON method --- src/structures/MessageSelectMenu.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index afdf1adda63e..7b2a7f43896f 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -134,6 +134,20 @@ class MessageSelectMenu extends BaseMessageComponent { return this; } + /** + * Transforms this select menu to a plain object + * @returns {Object} The raw data of this select menu + */ + toJSON() { + return { + custom_id: this.customID, + placeholder: this.placeholder, + min_values: this.minValues, + max_values: this.maxValues, + options: this.options, + }; + } + /** * Normalizes option input and resolves strings. * @param {MessageSelectOption} option The select menu option to normalize From c2d59f25f3b995d9ebec22bf030528fc39207767 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 15:41:25 +1000 Subject: [PATCH 04/27] fix: missing docs --- src/util/Constants.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/Constants.js b/src/util/Constants.js index 86d5f556782a..e4e158bc0ea7 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -944,6 +944,7 @@ exports.InteractionResponseTypes = createEnum([ * The type of a message component * * ACTION_ROW * * BUTTON + * * SELECT_MENU * @typedef {string} MessageComponentType */ exports.MessageComponentTypes = createEnum([null, 'ACTION_ROW', 'BUTTON', 'SELECT_MENU']); From 88e5dd4fda6e609bd142476ee4e945a005005a5e Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 15:41:57 +1000 Subject: [PATCH 05/27] feat: support creating select menus --- src/structures/BaseMessageComponent.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/structures/BaseMessageComponent.js b/src/structures/BaseMessageComponent.js index 0abb9568baed..943f01355e2f 100644 --- a/src/structures/BaseMessageComponent.js +++ b/src/structures/BaseMessageComponent.js @@ -18,7 +18,7 @@ class BaseMessageComponent { * Data that can be resolved into options for a MessageComponent. This can be: * * MessageActionRowOptions * * MessageButtonOptions - * @typedef {MessageActionRowOptions|MessageButtonOptions} MessageComponentOptions + * @typedef {MessageActionRowOptions|MessageButtonOptions|MessageSelectMenuOptions} MessageComponentOptions */ /** @@ -72,6 +72,11 @@ class BaseMessageComponent { component = new MessageButton(data); break; } + case MessageComponentTypes.SELECT_MENU: { + const MessageSelectMenu = require('./MessageSelectMenu'); + component = new MessageSelectMenu(data); + break; + } default: if (client) { client.emit(Events.DEBUG, `[BaseMessageComponent] Received component with unknown type: ${data.type}`); From 840f37fd2498a83a2257e098bf5ba631fdddcbb1 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 16:51:54 +1000 Subject: [PATCH 06/27] chore: transform min/max correctly --- src/index.js | 1 + src/structures/MessageComponentInteraction.js | 6 ++++++ src/structures/MessageSelectMenu.js | 9 ++++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 7fd16b9b6398..052f809f67f3 100644 --- a/src/index.js +++ b/src/index.js @@ -97,6 +97,7 @@ module.exports = { MessageEmbed: require('./structures/MessageEmbed'), MessageMentions: require('./structures/MessageMentions'), MessageReaction: require('./structures/MessageReaction'), + MessageSelectMenu: require('./structures/MessageSelectMenu'), NewsChannel: require('./structures/NewsChannel'), OAuth2Guild: require('./structures/OAuth2Guild'), PermissionOverwrites: require('./structures/PermissionOverwrites'), diff --git a/src/structures/MessageComponentInteraction.js b/src/structures/MessageComponentInteraction.js index 7dece90b5221..82df3e1a04de 100644 --- a/src/structures/MessageComponentInteraction.js +++ b/src/structures/MessageComponentInteraction.js @@ -55,6 +55,12 @@ class MessageComponentInteraction extends Interaction { * @type {InteractionWebhook} */ this.webhook = new InteractionWebhook(this.client, this.applicationID, this.token); + + /** + * The values selected in a MessageSelectMenu interaction + * @type {string[]} + */ + this.values = this.componentType === 'SELECT_MENU' ? data.data.values : null; } /** diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 7b2a7f43896f..3a7ad1561671 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -1,6 +1,7 @@ 'use strict'; const BaseMessageComponent = require('./BaseMessageComponent'); +const { MessageComponentTypes } = require('../util/Constants'); const Util = require('../util/Util'); class MessageSelectMenu extends BaseMessageComponent { @@ -31,7 +32,7 @@ class MessageSelectMenu extends BaseMessageComponent { this.setup(data); } - super(data) { + setup(data) { /** * A unique string to be sent in the interaction when clicked * @type {?string} @@ -84,7 +85,8 @@ class MessageSelectMenu extends BaseMessageComponent { } /** - * Sets the minimum number of selection required for this select menu + * Sets the minimum number of selections required for this select menu + * This will default the maxValues to the number of options, unless manually set * @param {number} minValues Number of selections to be required * @returns {MessageSelectMenu} */ @@ -143,8 +145,9 @@ class MessageSelectMenu extends BaseMessageComponent { custom_id: this.customID, placeholder: this.placeholder, min_values: this.minValues, - max_values: this.maxValues, + max_values: this.maxValues ?? this.minValues ? this.options.length : undefined, options: this.options, + type: typeof this.type === 'string' ? MessageComponentTypes[this.type] : this.type, }; } From b8d0e42f5cd2e3c2bd01e9f18f0f0bb8efbe5b69 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 16:54:05 +1000 Subject: [PATCH 07/27] types: done --- typings/index.d.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/typings/index.d.ts b/typings/index.d.ts index 71985cab190b..31d4cc2f6863 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1393,6 +1393,7 @@ declare module 'discord.js' { public ephemeral: boolean | null; public message: Message | RawMessage; public replied: boolean; + public values: string[] | null; public webhook: InteractionWebhook; public defer(options?: InteractionDeferOptions): Promise; public deferUpdate(): Promise; @@ -1540,6 +1541,13 @@ declare module 'discord.js' { public options: MessageSelectOption[]; public placeholder: string | null; public type: 'SELECT_MENU'; + public addOption(option: MessageSelectOption): this; + public addOptions(options: MessageSelectOption[] | MessageSelectOption[][]): this; + public setCustomID(customID: string): this; + public setMaxValues(maxValues: number): this; + public setMinValues(minValues: number): this; + public setPlaceholder(placeholder: string): this; + public toJSON(): object; } export class NewsChannel extends TextBasedChannel(GuildChannel) { From 0bb5e1ad27085310f464175cbb492d2a81940af3 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 28 May 2021 17:16:41 +1000 Subject: [PATCH 08/27] fix: emoji typings --- typings/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index 31d4cc2f6863..145ddcdeea99 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3784,7 +3784,7 @@ declare module 'discord.js' { interface MessageSelectOption { default?: boolean; description?: string; - emoji?: RawEmoji; + emoji?: Emoji | RawEmoji; label: string; value: string; } From b5f822fed5946415ea960817b3e471bee62e42e7 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sun, 30 May 2021 18:27:09 +1000 Subject: [PATCH 09/27] feat: stricter types for components --- typings/index.d.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index 145ddcdeea99..60e110fe1f9e 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3591,9 +3591,12 @@ declare module 'discord.js' { type MembershipState = keyof typeof MembershipStates; + type MessageActionRowComponent = MessageButton; - type MessageActionRowComponentOptions = MessageButtonOptions; + type MessageActionRowComponent = MessageButton | MessageSelectMenu; + + type MessageActionRowComponentOptions = MessageButtonOptions | MessageSelectMenuOptions; type MessageActionRowComponentResolvable = MessageActionRowComponent | MessageActionRowComponentOptions; @@ -3606,6 +3609,8 @@ declare module 'discord.js' { type: number; } + type MessageAdditions = MessageEmbed | MessageAttachment | (MessageEmbed | MessageAttachment)[]; + interface MessageButtonOptions extends BaseMessageComponentOptions { customID?: string; disabled?: boolean; @@ -3625,6 +3630,7 @@ declare module 'discord.js' { } type MessageComponent = BaseMessageComponent | MessageActionRow | MessageButton | MessageSelectMenu; + interface MessageComponentInteractionCollectorOptions extends CollectorOptions { max?: number; maxComponents?: number; From 320cb86d0ef6cd04281b9cd1d55297e7eda8903b Mon Sep 17 00:00:00 2001 From: Monbrey Date: Tue, 1 Jun 2021 09:35:14 +1000 Subject: [PATCH 10/27] fix: splice method --- src/structures/MessageSelectMenu.js | 5 +++-- typings/index.d.ts | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 3a7ad1561671..3a30075f7d83 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -131,8 +131,8 @@ class MessageSelectMenu extends BaseMessageComponent { * @param {...MessageSelectOption|MessageSelectOption[]} [options] The replacing option objects * @returns {MessageSelectMenu} */ - spliceFields(index, deleteCount, ...options) { - this.fields.splice(index, deleteCount, ...this.constructor.normalizeOptions(...options)); + spliceOptions(index, deleteCount, ...options) { + this.options.splice(index, deleteCount, ...this.constructor.normalizeOptions(...options)); return this; } @@ -161,6 +161,7 @@ class MessageSelectMenu extends BaseMessageComponent { label = Util.resolveString(label); value = Util.resolveString(value); + emoji = /^\d{17,19}$/.test(emoji) ? { id: value } : Util.parseEmoji(emoji); description = typeof description !== 'undefined' ? Util.resolveString(description) : undefined; return { label, value, description, emoji, default: option.default }; diff --git a/typings/index.d.ts b/typings/index.d.ts index 60e110fe1f9e..8c4e2cde6d03 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1547,6 +1547,11 @@ declare module 'discord.js' { public setMaxValues(maxValues: number): this; public setMinValues(minValues: number): this; public setPlaceholder(placeholder: string): this; + public spliceOptions( + index: number, + deleteCount: number, + ...options: MessageSelectOption[] | MessageSelectOption[][] + ): this; public toJSON(): object; } @@ -3784,7 +3789,6 @@ declare module 'discord.js' { minValues?: number; options?: MessageSelectOption[]; placeholder?: string; - type: 'SELECT_MENU' | MessageComponentTypes.SELECT_MENU; } interface MessageSelectOption { From f6bd2c3d2c8aa36cb72221081e7ea4adafd845ac Mon Sep 17 00:00:00 2001 From: Monbrey Date: Tue, 1 Jun 2021 10:00:30 +1000 Subject: [PATCH 11/27] docs: update after rebase --- src/structures/MessageActionRow.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structures/MessageActionRow.js b/src/structures/MessageActionRow.js index 450b387783c6..347958a33ced 100644 --- a/src/structures/MessageActionRow.js +++ b/src/structures/MessageActionRow.js @@ -11,13 +11,13 @@ class MessageActionRow extends BaseMessageComponent { /** * Components that can be placed in an action row * * MessageButton - * @typedef {MessageButton} MessageActionRowComponent + * @typedef {MessageButton|MessageSelectMenu} MessageActionRowComponent */ /** * Options for components that can be placed in an action row * * MessageButtonOptions - * @typedef {MessageButtonOptions} MessageActionRowComponentOptions + * @typedef {MessageButtonOptions|MessageSelectMenuOptions} MessageActionRowComponentOptions */ /** From 47e4b3fef91f9e94be5c444080978de14b7e88eb Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sat, 5 Jun 2021 08:19:08 +1000 Subject: [PATCH 12/27] docs: updates to MessageSelectMenu --- src/structures/MessageSelectMenu.js | 35 +++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 3a30075f7d83..40e9c9e667af 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -4,22 +4,26 @@ const BaseMessageComponent = require('./BaseMessageComponent'); const { MessageComponentTypes } = require('../util/Constants'); const Util = require('../util/Util'); +/** + * Represents a SelectMenu message components + * @extends {BaseMessageComponent} + */ class MessageSelectMenu extends BaseMessageComponent { /** * @typedef {BaseMessageComponentOptions} MessageSelectMenuOptions - * @property {?string} [customID] A unique string to be sent in the interaction when clicked - * @property {?string} [placeholder] Custom placeholder text to display when nothing is selected - * @property {?number} [minValues] The minimum number of selections required - * @property {?number} [maxValues] The maximum number of selections allowed - * @property {?MessageSelectOption[]} [options] Options for the select menu + * @property {string} [customID] A unique string to be sent in the interaction when clicked + * @property {string} [placeholder] Custom placeholder text to display when nothing is selected + * @property {number} [minValues] The minimum number of selections required + * @property {number} [maxValues] The maximum number of selections allowed + * @property {MessageSelectOption[]} [options] Options for the select menu */ /** * @typedef {Object} MessageSelectOption * @property {string} label The text to be displayed on this option * @property {string} value The value to be sent for this option - * @property {?string} [description] Optional description to show for this option - * @property {?Emoji} [emoji] Emoji to display for this option + * @property {string} [description] Optional description to show for this option + * @property {Emoji} [emoji] Emoji to display for this option * @property {boolean} [default] Render this option as the default selection */ @@ -106,16 +110,7 @@ class MessageSelectMenu extends BaseMessageComponent { } /** - * Adds an option to the select menu (max 25) - * @param {MessageSelectOption} option The option to add - * @returns {MessageSelectMenu} - */ - addOption(option) { - return this.addOptions({ ...option }); - } - - /** - * Adds options to the select menu (max 5). + * Adds options to the select menu. * @param {...(MessageSelectOption[]|MessageSelectOption[])} options The options to add * @returns {MessageSelectMenu} */ @@ -125,7 +120,7 @@ class MessageSelectMenu extends BaseMessageComponent { } /** - * Removes, replaces, and inserts options in the select menu (max 25). + * Removes, replaces, and inserts options in the select menu. * @param {number} index The index to start at * @param {number} deleteCount The number of options to remove * @param {...MessageSelectOption|MessageSelectOption[]} [options] The replacing option objects @@ -161,14 +156,14 @@ class MessageSelectMenu extends BaseMessageComponent { label = Util.resolveString(label); value = Util.resolveString(value); - emoji = /^\d{17,19}$/.test(emoji) ? { id: value } : Util.parseEmoji(emoji); + emoji = /^\d{17,19}$/.test(emoji) ? { id: emoji } : Util.parseEmoji(emoji); description = typeof description !== 'undefined' ? Util.resolveString(description) : undefined; return { label, value, description, emoji, default: option.default }; } /** - * Normalizes option input and resolves strings. + * Normalizes option input and resolves strings and emojis. * @param {...MessageSelectOption|MessageSelectOption[]} options The select menu options to normalize * @returns {MessageSelectOption[]} */ From 3088880bfca46c36aa708f03d57f46f01b40a83c Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sat, 5 Jun 2021 08:19:53 +1000 Subject: [PATCH 13/27] types: remove MessageSelectMenu#addOption --- typings/index.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index 8c4e2cde6d03..03ab44a6f2e7 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1541,7 +1541,6 @@ declare module 'discord.js' { public options: MessageSelectOption[]; public placeholder: string | null; public type: 'SELECT_MENU'; - public addOption(option: MessageSelectOption): this; public addOptions(options: MessageSelectOption[] | MessageSelectOption[][]): this; public setCustomID(customID: string): this; public setMaxValues(maxValues: number): this; @@ -3794,7 +3793,7 @@ declare module 'discord.js' { interface MessageSelectOption { default?: boolean; description?: string; - emoji?: Emoji | RawEmoji; + emoji?: GuildEmoji | RawEmoji; label: string; value: string; } From 41065062e88adef12b9d7a075b26abd1c345d48a Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sat, 5 Jun 2021 08:36:30 +1000 Subject: [PATCH 14/27] docs: updates and consistency --- src/structures/MessageActionRow.js | 2 ++ src/structures/MessageComponentInteraction.js | 2 +- src/structures/MessageSelectMenu.js | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/structures/MessageActionRow.js b/src/structures/MessageActionRow.js index 347958a33ced..0498b8f13bed 100644 --- a/src/structures/MessageActionRow.js +++ b/src/structures/MessageActionRow.js @@ -11,12 +11,14 @@ class MessageActionRow extends BaseMessageComponent { /** * Components that can be placed in an action row * * MessageButton + * * MessageSelectMenu * @typedef {MessageButton|MessageSelectMenu} MessageActionRowComponent */ /** * Options for components that can be placed in an action row * * MessageButtonOptions + * * MessageSelectMenuOptions * @typedef {MessageButtonOptions|MessageSelectMenuOptions} MessageActionRowComponentOptions */ diff --git a/src/structures/MessageComponentInteraction.js b/src/structures/MessageComponentInteraction.js index 82df3e1a04de..d8da5ab27112 100644 --- a/src/structures/MessageComponentInteraction.js +++ b/src/structures/MessageComponentInteraction.js @@ -57,7 +57,7 @@ class MessageComponentInteraction extends Interaction { this.webhook = new InteractionWebhook(this.client, this.applicationID, this.token); /** - * The values selected in a MessageSelectMenu interaction + * The values selected, if the component which was interacted with was a select menu * @type {string[]} */ this.values = this.componentType === 'SELECT_MENU' ? data.data.values : null; diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 40e9c9e667af..a57d53562372 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -5,7 +5,7 @@ const { MessageComponentTypes } = require('../util/Constants'); const Util = require('../util/Util'); /** - * Represents a SelectMenu message components + * Represents a select menu message component * @extends {BaseMessageComponent} */ class MessageSelectMenu extends BaseMessageComponent { @@ -79,7 +79,7 @@ class MessageSelectMenu extends BaseMessageComponent { } /** - * Sets the maximum number of selection allowed for this select menu + * Sets the maximum number of selections allowed for this select menu * @param {number} maxValues Number of selections to be allowed * @returns {MessageSelectMenu} */ From b751e82b66e65d072c37eaff0f34199a9fdce55c Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sat, 5 Jun 2021 08:54:00 +1000 Subject: [PATCH 15/27] docs: realised docgen does not need links here --- src/structures/BaseMessageComponent.js | 4 +++- src/structures/MessageActionRow.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/structures/BaseMessageComponent.js b/src/structures/BaseMessageComponent.js index 943f01355e2f..3124e34b4bcf 100644 --- a/src/structures/BaseMessageComponent.js +++ b/src/structures/BaseMessageComponent.js @@ -18,6 +18,7 @@ class BaseMessageComponent { * Data that can be resolved into options for a MessageComponent. This can be: * * MessageActionRowOptions * * MessageButtonOptions + * * MessageSelectMenuOptions * @typedef {MessageActionRowOptions|MessageButtonOptions|MessageSelectMenuOptions} MessageComponentOptions */ @@ -25,7 +26,8 @@ class BaseMessageComponent { * Components that can be sent in a message. This can be: * * MessageActionRow * * MessageButton - * @typedef {MessageActionRow|MessageButton} MessageComponent + * * MessageSelectMenu + * @typedef {MessageActionRow|MessageButton|MessageSelectMenu} MessageComponent */ /** diff --git a/src/structures/MessageActionRow.js b/src/structures/MessageActionRow.js index 0498b8f13bed..eea6f9f68fec 100644 --- a/src/structures/MessageActionRow.js +++ b/src/structures/MessageActionRow.js @@ -23,7 +23,7 @@ class MessageActionRow extends BaseMessageComponent { */ /** - * Data that can be resolved into a components that can be placed in an action row + * Data that can be resolved into components that can be placed in an action row * * MessageActionRowComponent * * MessageActionRowComponentOptions * @typedef {MessageActionRowComponent|MessageActionRowComponentOptions} MessageActionRowComponentResolvable From 993f70822ebf4027a6761ae9e9878d498ede2ac4 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sat, 5 Jun 2021 11:45:56 +1000 Subject: [PATCH 16/27] feat: verify strings --- src/errors/Messages.js | 6 ++++++ src/structures/MessageSelectMenu.js | 15 +++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 36b3a123ff11..2e04d168094d 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -50,6 +50,12 @@ const Messages = { BUTTON_URL: 'MessageButton url must be a string', BUTTON_CUSTOM_ID: 'MessageButton customID must be a string', + SELECT_MENU_CUSTOM_ID: 'MessageSelectMenu customID must be a string', + SELECT_MENU_PLACEHOLDER: 'MessageSelectMenu placeholder must be a string', + SELECT_OPTION_LABEL: 'MessageSelectOption label must be a string', + SELECT_OPTION_VALUE: 'MessageSelectOption value must be a string', + SELECT_OPTION_DESCRIPTION: 'MessageSelectOption description must be a string', + INTERACTION_COLLECTOR_ERROR: reason => `Collector received no interactions before ending with reason: ${reason}`, FILE_NOT_FOUND: file => `File could not be found: ${file}`, diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index a57d53562372..13a30ad22664 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -74,7 +74,7 @@ class MessageSelectMenu extends BaseMessageComponent { * @returns {MessageSelectMenu} */ setCustomID(customID) { - this.customID = Util.resolveString(customID); + this.customID = Util.verifyString(customID, RangeError, 'SELECT_MENU_CUSTOM_ID'); return this; } @@ -105,7 +105,7 @@ class MessageSelectMenu extends BaseMessageComponent { * @returns {MessageSelectMenu} */ setPlaceholder(placeholder) { - this.placeholder = Util.resolveString(placeholder); + this.placeholder = Util.verifyString(placeholder, RangeError, 'SELECT_MENU_PLACEHOLDER'); return this; } @@ -154,10 +154,13 @@ class MessageSelectMenu extends BaseMessageComponent { static normalizeOption(option) { let { label, value, description, emoji } = option; - label = Util.resolveString(label); - value = Util.resolveString(value); - emoji = /^\d{17,19}$/.test(emoji) ? { id: emoji } : Util.parseEmoji(emoji); - description = typeof description !== 'undefined' ? Util.resolveString(description) : undefined; + label = Util.verifyString(label, RangeError, 'SELECT_OPTION_LABEL'); + value = Util.verifyString(value, RangeError, 'SELECT_OPTION_VALUE'); + emoji = /^\d{17,19}$/.test(emoji) ? { id: value } : Util.parseEmoji(emoji); + description = + typeof description !== 'undefined' + ? Util.verifyString(description, RangeError, 'SELECT_OPTION_DESCRIPTION', true) + : undefined; return { label, value, description, emoji, default: option.default }; } From c3cab42f825074dd157255513ab1136968a82739 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sat, 5 Jun 2021 11:49:21 +1000 Subject: [PATCH 17/27] feat: improved emoji resolution for components --- src/structures/MessageSelectMenu.js | 26 ++++++++++++++++---------- typings/index.d.ts | 16 ++++++++++++---- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 13a30ad22664..c5c470d6d2b4 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -22,8 +22,17 @@ class MessageSelectMenu extends BaseMessageComponent { * @typedef {Object} MessageSelectOption * @property {string} label The text to be displayed on this option * @property {string} value The value to be sent for this option + * @property {?string} description Optional description to show for this option + * @property {?RawEmoji} emoji Emoji to display for this option + * @property {boolean} default Render this option as the default selection + */ + + /** + * @typedef {Object} MessageSelectOptionData + * @property {string} label The text to be displayed on this option + * @property {string} value The value to be sent for this option * @property {string} [description] Optional description to show for this option - * @property {Emoji} [emoji] Emoji to display for this option + * @property {EmojiIdentifierResolvable} [emoji] Emoji to display for this option * @property {boolean} [default] Render this option as the default selection */ @@ -65,7 +74,7 @@ class MessageSelectMenu extends BaseMessageComponent { * Options for the select menu * @type {MessageSelectOption[]} */ - this.options = data.options ?? []; + this.options = this.constructor.normalizeOptions(data.options ?? []); } /** @@ -148,7 +157,7 @@ class MessageSelectMenu extends BaseMessageComponent { /** * Normalizes option input and resolves strings. - * @param {MessageSelectOption} option The select menu option to normalize + * @param {MessageSelectOptionData} option The select menu option to normalize * @returns {MessageSelectOption} */ static normalizeOption(option) { @@ -156,18 +165,15 @@ class MessageSelectMenu extends BaseMessageComponent { label = Util.verifyString(label, RangeError, 'SELECT_OPTION_LABEL'); value = Util.verifyString(value, RangeError, 'SELECT_OPTION_VALUE'); - emoji = /^\d{17,19}$/.test(emoji) ? { id: value } : Util.parseEmoji(emoji); - description = - typeof description !== 'undefined' - ? Util.verifyString(description, RangeError, 'SELECT_OPTION_DESCRIPTION', true) - : undefined; + emoji = emoji ? Util.resolvePartialEmoji(emoji) : null; + description = description ? Util.verifyString(description, RangeError, 'SELECT_OPTION_DESCRIPTION', true) : null; - return { label, value, description, emoji, default: option.default }; + return { label, value, description, emoji, default: option.default ?? false }; } /** * Normalizes option input and resolves strings and emojis. - * @param {...MessageSelectOption|MessageSelectOption[]} options The select menu options to normalize + * @param {...MessageSelectOptionData|MessageSelectOption[]} options The select menu options to normalize * @returns {MessageSelectOption[]} */ static normalizeOptions(...options) { diff --git a/typings/index.d.ts b/typings/index.d.ts index 03ab44a6f2e7..05d57b646556 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1541,7 +1541,7 @@ declare module 'discord.js' { public options: MessageSelectOption[]; public placeholder: string | null; public type: 'SELECT_MENU'; - public addOptions(options: MessageSelectOption[] | MessageSelectOption[][]): this; + public addOptions(options: MessageSelectOptionData[] | MessageSelectOptionData[][]): this; public setCustomID(customID: string): this; public setMaxValues(maxValues: number): this; public setMinValues(minValues: number): this; @@ -1549,7 +1549,7 @@ declare module 'discord.js' { public spliceOptions( index: number, deleteCount: number, - ...options: MessageSelectOption[] | MessageSelectOption[][] + ...options: MessageSelectOptionData[] | MessageSelectOptionData[][] ): this; public toJSON(): object; } @@ -3786,14 +3786,22 @@ declare module 'discord.js' { customID?: string; maxValues?: number; minValues?: number; - options?: MessageSelectOption[]; + options?: MessageSelectOptionData[]; placeholder?: string; } interface MessageSelectOption { + default: boolean; + description: string | null; + emoji: RawEmoji | null; + label: string; + value: string; + } + + interface MessageSelectOptionData { default?: boolean; description?: string; - emoji?: GuildEmoji | RawEmoji; + emoji?: EmojiIdentifierResolvable; label: string; value: string; } From 3c35454dce2f3094217b7c862ff8a01277e39a04 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sun, 6 Jun 2021 08:09:44 +1000 Subject: [PATCH 18/27] fix: max_values binding order --- src/structures/MessageSelectMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index c5c470d6d2b4..27cd6bb95af7 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -149,7 +149,7 @@ class MessageSelectMenu extends BaseMessageComponent { custom_id: this.customID, placeholder: this.placeholder, min_values: this.minValues, - max_values: this.maxValues ?? this.minValues ? this.options.length : undefined, + max_values: this.maxValues ?? (this.minValues ? this.options.length : undefined), options: this.options, type: typeof this.type === 'string' ? MessageComponentTypes[this.type] : this.type, }; From a9b46a714ce2d90ce5374b81fed93820d487a994 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sun, 6 Jun 2021 09:24:05 +1000 Subject: [PATCH 19/27] feat: support for disabling --- src/structures/MessageSelectMenu.js | 18 ++++++++++++++++++ typings/index.d.ts | 3 +++ 2 files changed, 21 insertions(+) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 27cd6bb95af7..b18afcea2b7e 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -16,6 +16,7 @@ class MessageSelectMenu extends BaseMessageComponent { * @property {number} [minValues] The minimum number of selections required * @property {number} [maxValues] The maximum number of selections allowed * @property {MessageSelectOption[]} [options] Options for the select menu + * @property {boolean} [disabled=false] Disables the select menu to prevent interactions */ /** @@ -75,6 +76,12 @@ class MessageSelectMenu extends BaseMessageComponent { * @type {MessageSelectOption[]} */ this.options = this.constructor.normalizeOptions(data.options ?? []); + + /** + * Whether this select menu is currently disabled + * @type {?boolean} + */ + this.disabled = data.disabled ?? false; } /** @@ -87,6 +94,16 @@ class MessageSelectMenu extends BaseMessageComponent { return this; } + /** + * Sets the interactive status of the select menu + * @param {boolean} disabled Whether this select menu should be disabled + * @returns {MessageSelectMenu} + */ + setDisabled(disabled) { + this.disabled = disabled; + return this; + } + /** * Sets the maximum number of selections allowed for this select menu * @param {number} maxValues Number of selections to be allowed @@ -147,6 +164,7 @@ class MessageSelectMenu extends BaseMessageComponent { toJSON() { return { custom_id: this.customID, + disabled: this.disabled, placeholder: this.placeholder, min_values: this.minValues, max_values: this.maxValues ?? (this.minValues ? this.options.length : undefined), diff --git a/typings/index.d.ts b/typings/index.d.ts index 05d57b646556..b9c3e179843c 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1536,6 +1536,7 @@ declare module 'discord.js' { class MessageSelectMenu extends BaseMessageComponent { constructor(data?: MessageSelectMenu | MessageSelectMenuOptions); public customID: string | null; + public disabled: boolean; public maxValues: number | null; public minValues: number | null; public options: MessageSelectOption[]; @@ -1543,6 +1544,7 @@ declare module 'discord.js' { public type: 'SELECT_MENU'; public addOptions(options: MessageSelectOptionData[] | MessageSelectOptionData[][]): this; public setCustomID(customID: string): this; + public setDisabled(disabled: boolean): this; public setMaxValues(maxValues: number): this; public setMinValues(minValues: number): this; public setPlaceholder(placeholder: string): this; @@ -3784,6 +3786,7 @@ declare module 'discord.js' { interface MessageSelectMenuOptions extends BaseMessageComponentOptions { customID?: string; + disabled?: boolean; maxValues?: number; minValues?: number; options?: MessageSelectOptionData[]; From 221f5a3637c342a4a26dd3908f73df9d4152340d Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sun, 6 Jun 2021 18:02:00 +1000 Subject: [PATCH 20/27] docs: minor update --- src/structures/MessageSelectMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index b18afcea2b7e..fb5d513f758d 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -174,7 +174,7 @@ class MessageSelectMenu extends BaseMessageComponent { } /** - * Normalizes option input and resolves strings. + * Normalizes option input and resolves strings and emojis. * @param {MessageSelectOptionData} option The select menu option to normalize * @returns {MessageSelectOption} */ From c3c05946b0cbb7bb0ce43201316092d003a573b5 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sun, 6 Jun 2021 18:15:47 +1000 Subject: [PATCH 21/27] chore: final fixes --- src/structures/MessageSelectMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index fb5d513f758d..a79522cd3738 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -195,7 +195,7 @@ class MessageSelectMenu extends BaseMessageComponent { * @returns {MessageSelectOption[]} */ static normalizeOptions(...options) { - return options.flat(2).map(option => this.normalizeOption(option)); + return options.flat(Infinity).map(option => this.normalizeOption(option)); } } From 315e86d90cf43fd65b85575e2680083a3c189e69 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Tue, 8 Jun 2021 09:34:18 +1000 Subject: [PATCH 22/27] refactor: component interaction classes --- src/client/actions/InteractionCreate.js | 3 +++ src/index.js | 1 + src/structures/Interaction.js | 10 ++++++++- src/structures/MessageComponentInteraction.js | 6 ------ src/structures/SelectMenuInteraction.js | 21 +++++++++++++++++++ src/util/Structures.js | 2 ++ typings/index.d.ts | 7 ++++++- 7 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 src/structures/SelectMenuInteraction.js diff --git a/src/client/actions/InteractionCreate.js b/src/client/actions/InteractionCreate.js index 3559963efe2c..e30c3312e1f1 100644 --- a/src/client/actions/InteractionCreate.js +++ b/src/client/actions/InteractionCreate.js @@ -21,6 +21,9 @@ class InteractionCreateAction extends Action { case MessageComponentTypes.BUTTON: InteractionType = Structures.get('ButtonInteraction'); break; + case MessageComponentTypes.SELECT_MENU: + InteractionType = Structures.get('SelectMenuInteraction'); + break; default: client.emit( Events.DEBUG, diff --git a/src/index.js b/src/index.js index 052f809f67f3..ca84b6ea4587 100644 --- a/src/index.js +++ b/src/index.js @@ -107,6 +107,7 @@ module.exports = { ReactionEmoji: require('./structures/ReactionEmoji'), RichPresenceAssets: require('./structures/Presence').RichPresenceAssets, Role: require('./structures/Role'), + SelectMenuInteraction: require('./structures/SelectMenuInteraction'), Sticker: require('./structures/Sticker'), StoreChannel: require('./structures/StoreChannel'), StageChannel: require('./structures/StageChannel'), diff --git a/src/structures/Interaction.js b/src/structures/Interaction.js index 8fb11830ad78..fd76766fb8b9 100644 --- a/src/structures/Interaction.js +++ b/src/structures/Interaction.js @@ -114,7 +114,7 @@ class Interaction extends Base { } /** - * Indicates whether this interaction is a component interaction. + * Indicates whether this interaction is a message component interaction. * @returns {boolean} */ isMessageComponent() { @@ -128,6 +128,14 @@ class Interaction extends Base { isButton() { return InteractionTypes[this.type] === InteractionTypes.MESSAGE_COMPONENT && this.componentType === 'BUTTON'; } + + /** + * Indicates whether this interaction is a select menu interaction. + * @returns {boolean} + */ + isSelectMenu() { + return InteractionTypes[this.type] === InteractionTypes.MESSAGE_COMPONENT && this.componentType === 'SELECT_MENU'; + } } module.exports = Interaction; diff --git a/src/structures/MessageComponentInteraction.js b/src/structures/MessageComponentInteraction.js index d8da5ab27112..7dece90b5221 100644 --- a/src/structures/MessageComponentInteraction.js +++ b/src/structures/MessageComponentInteraction.js @@ -55,12 +55,6 @@ class MessageComponentInteraction extends Interaction { * @type {InteractionWebhook} */ this.webhook = new InteractionWebhook(this.client, this.applicationID, this.token); - - /** - * The values selected, if the component which was interacted with was a select menu - * @type {string[]} - */ - this.values = this.componentType === 'SELECT_MENU' ? data.data.values : null; } /** diff --git a/src/structures/SelectMenuInteraction.js b/src/structures/SelectMenuInteraction.js new file mode 100644 index 000000000000..be4f3a0ed649 --- /dev/null +++ b/src/structures/SelectMenuInteraction.js @@ -0,0 +1,21 @@ +'use strict'; + +const MessageComponentInteraction = require('./MessageComponentInteraction'); + +/** + * Represents a select menu interaction. + * @extends {MessageComponentInteraction} + */ +class SelectMenuInteraction extends MessageComponentInteraction { + constructor(client, data) { + super(client, data); + + /** + * The values selected, if the component which was interacted with was a select menu + * @type {string[]} + */ + this.values = this.componentType === 'SELECT_MENU' ? data.data.values : null; + } +} + +module.exports = SelectMenuInteraction; diff --git a/src/util/Structures.js b/src/util/Structures.js index 039a9ca51ee4..325b9e4e6b0d 100644 --- a/src/util/Structures.js +++ b/src/util/Structures.js @@ -24,6 +24,7 @@ * * **`CommandInteraction`** * * **`ButtonInteraction`** * * **`StageInstance`** + * * **`SelectMenuInteraction`** * @typedef {string} ExtendableStructure */ @@ -118,6 +119,7 @@ const structures = { User: require('../structures/User'), CommandInteraction: require('../structures/CommandInteraction'), ButtonInteraction: require('../structures/ButtonInteraction'), + SelectMenuInteraction: require('../structures/SelectMenuInteraction'), StageInstance: require('../structures/StageInstance'), }; diff --git a/typings/index.d.ts b/typings/index.d.ts index b9c3e179843c..6e52ae40de85 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1177,6 +1177,7 @@ declare module 'discord.js' { public isButton(): this is ButtonInteraction; public isCommand(): this is CommandInteraction; public isMessageComponent(): this is MessageComponentInteraction; + public isSelectMenu(): this is SelectMenuInteraction; } export class InteractionWebhook extends PartialWebhookMixin() { @@ -1393,7 +1394,6 @@ declare module 'discord.js' { public ephemeral: boolean | null; public message: Message | RawMessage; public replied: boolean; - public values: string[] | null; public webhook: InteractionWebhook; public defer(options?: InteractionDeferOptions): Promise; public deferUpdate(): Promise; @@ -1718,6 +1718,11 @@ declare module 'discord.js' { public static comparePositions(role1: Role, role2: Role): number; } + export class SelectMenuInteraction extends MessageComponentInteraction { + public componentType: 'SELECT_MENU'; + public values: string[] | null; + } + export class Shard extends EventEmitter { constructor(manager: ShardingManager, id: number); private _evals: Map>; From 66864699cb49779fa5622472558b9741ee1b8ef6 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Thu, 10 Jun 2021 04:16:59 +1000 Subject: [PATCH 23/27] chore: minor rebase cleanup --- typings/index.d.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index 6e52ae40de85..7070fadfdea8 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3602,9 +3602,6 @@ declare module 'discord.js' { type MembershipState = keyof typeof MembershipStates; - - type MessageActionRowComponent = MessageButton; - type MessageActionRowComponent = MessageButton | MessageSelectMenu; type MessageActionRowComponentOptions = MessageButtonOptions | MessageSelectMenuOptions; From d182d2ef9dc8a6095c0624492ba3b96bb29166d6 Mon Sep 17 00:00:00 2001 From: monbrey Date: Fri, 11 Jun 2021 07:44:06 +1000 Subject: [PATCH 24/27] fix; update typings/index.d.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antonio Román --- typings/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index 7070fadfdea8..b083f200652b 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1553,7 +1553,7 @@ declare module 'discord.js' { deleteCount: number, ...options: MessageSelectOptionData[] | MessageSelectOptionData[][] ): this; - public toJSON(): object; + public toJSON(): unknown; } export class NewsChannel extends TextBasedChannel(GuildChannel) { From 1c6ceb4a3cc4c93ad363e533b0c496afc9283e89 Mon Sep 17 00:00:00 2001 From: Monbrey Date: Fri, 11 Jun 2021 09:45:14 +1000 Subject: [PATCH 25/27] types: extendable --- typings/index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/typings/index.d.ts b/typings/index.d.ts index b083f200652b..197903987381 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3212,6 +3212,7 @@ declare module 'discord.js' { User: typeof User; CommandInteraction: typeof CommandInteraction; ButtonInteraction: typeof ButtonInteraction; + SelectMenuInteraction: typeof SelectMenuInteraction; } interface FetchApplicationCommandOptions extends BaseFetchOptions { From 8d242f287f0525f350444df20f24357930c5a07d Mon Sep 17 00:00:00 2001 From: Monbrey Date: Sat, 12 Jun 2021 15:14:49 +1000 Subject: [PATCH 26/27] docs: fix incorrect return type --- src/structures/MessageActionRow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/MessageActionRow.js b/src/structures/MessageActionRow.js index eea6f9f68fec..219e27e0825b 100644 --- a/src/structures/MessageActionRow.js +++ b/src/structures/MessageActionRow.js @@ -63,7 +63,7 @@ class MessageActionRow extends BaseMessageComponent { * @param {number} index The index to start at * @param {number} deleteCount The number of components to remove * @param {...MessageActionRowComponentResolvable[]} [components] The replacing components - * @returns {MessageSelectMenu} + * @returns {MessageActionRow} */ spliceComponents(index, deleteCount, ...components) { this.components.splice( From 33f284eed8816fc6a11f472047f0fe03f038a40e Mon Sep 17 00:00:00 2001 From: Monbrey Date: Mon, 21 Jun 2021 08:33:12 +1000 Subject: [PATCH 27/27] refactor(Interaction): use enum --- src/structures/Interaction.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/structures/Interaction.js b/src/structures/Interaction.js index fd76766fb8b9..189b3de03ec3 100644 --- a/src/structures/Interaction.js +++ b/src/structures/Interaction.js @@ -1,7 +1,7 @@ 'use strict'; const Base = require('./Base'); -const { InteractionTypes } = require('../util/Constants'); +const { InteractionTypes, MessageComponentTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); /** @@ -126,7 +126,10 @@ class Interaction extends Base { * @returns {boolean} */ isButton() { - return InteractionTypes[this.type] === InteractionTypes.MESSAGE_COMPONENT && this.componentType === 'BUTTON'; + return ( + InteractionTypes[this.type] === InteractionTypes.MESSAGE_COMPONENT && + MessageComponentTypes[this.componentType] === MessageComponentTypes.BUTTON + ); } /** @@ -134,7 +137,10 @@ class Interaction extends Base { * @returns {boolean} */ isSelectMenu() { - return InteractionTypes[this.type] === InteractionTypes.MESSAGE_COMPONENT && this.componentType === 'SELECT_MENU'; + return ( + InteractionTypes[this.type] === InteractionTypes.MESSAGE_COMPONENT && + MessageComponentTypes[this.componentType] === MessageComponentTypes.SELECT_MENU + ); } }