From 101d7c5ffa03edcf8cb8a0647b77d5c9a38e4bdd Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni <77477100+suneettipirneni@users.noreply.github.com> Date: Mon, 17 Jan 2022 07:13:48 -0500 Subject: [PATCH] refactor: remove djs components and use /builders components instead (#7252) Co-authored-by: Vlad Frangu --- packages/discord.js/src/index.js | 8 +- .../src/structures/BaseMessageComponent.js | 104 --------- packages/discord.js/src/structures/Message.js | 6 +- .../src/structures/MessageActionRow.js | 101 --------- .../src/structures/MessageButton.js | 165 -------------- .../src/structures/MessagePayload.js | 4 +- .../src/structures/MessageSelectMenu.js | 212 ------------------ packages/discord.js/typings/index.d.ts | 120 +++------- packages/discord.js/typings/index.test-d.ts | 35 +-- 9 files changed, 58 insertions(+), 697 deletions(-) delete mode 100644 packages/discord.js/src/structures/BaseMessageComponent.js delete mode 100644 packages/discord.js/src/structures/MessageActionRow.js delete mode 100644 packages/discord.js/src/structures/MessageButton.js delete mode 100644 packages/discord.js/src/structures/MessageSelectMenu.js diff --git a/packages/discord.js/src/index.js b/packages/discord.js/src/index.js index 6390956da..b06c7fa18 100644 --- a/packages/discord.js/src/index.js +++ b/packages/discord.js/src/index.js @@ -77,7 +77,6 @@ exports.BaseGuild = require('./structures/BaseGuild'); exports.BaseGuildEmoji = require('./structures/BaseGuildEmoji'); exports.BaseGuildTextChannel = require('./structures/BaseGuildTextChannel'); exports.BaseGuildVoiceChannel = require('./structures/BaseGuildVoiceChannel'); -exports.BaseMessageComponent = require('./structures/BaseMessageComponent'); exports.ButtonInteraction = require('./structures/ButtonInteraction'); exports.CategoryChannel = require('./structures/CategoryChannel'); exports.Channel = require('./structures/Channel').Channel; @@ -111,9 +110,7 @@ exports.Invite = require('./structures/Invite'); exports.InviteStageInstance = require('./structures/InviteStageInstance'); exports.InviteGuild = require('./structures/InviteGuild'); exports.Message = require('./structures/Message').Message; -exports.MessageActionRow = require('./structures/MessageActionRow'); exports.MessageAttachment = require('./structures/MessageAttachment'); -exports.MessageButton = require('./structures/MessageButton'); exports.MessageCollector = require('./structures/MessageCollector'); exports.MessageComponentInteraction = require('./structures/MessageComponentInteraction'); exports.MessageContextMenuCommandInteraction = require('./structures/MessageContextMenuCommandInteraction'); @@ -121,7 +118,6 @@ exports.MessageEmbed = require('./structures/MessageEmbed'); exports.MessageMentions = require('./structures/MessageMentions'); exports.MessagePayload = require('./structures/MessagePayload'); exports.MessageReaction = require('./structures/MessageReaction'); -exports.MessageSelectMenu = require('./structures/MessageSelectMenu'); exports.NewsChannel = require('./structures/NewsChannel'); exports.OAuth2Guild = require('./structures/OAuth2Guild'); exports.PartialGroupDMChannel = require('./structures/PartialGroupDMChannel'); @@ -180,3 +176,7 @@ exports.StageInstancePrivacyLevel = require('discord-api-types/v9').StageInstanc exports.StickerType = require('discord-api-types/v9').StickerType; exports.StickerFormatType = require('discord-api-types/v9').StickerFormatType; exports.WebhookType = require('discord-api-types/v9').WebhookType; +exports.ActionRow = require('@discordjs/builders').ActionRow; +exports.ButtonComponent = require('@discordjs/builders').ButtonComponent; +exports.SelectMenuComponent = require('@discordjs/builders').SelectMenuComponent; +exports.SelectMenuOption = require('@discordjs/builders').SelectMenuOption; diff --git a/packages/discord.js/src/structures/BaseMessageComponent.js b/packages/discord.js/src/structures/BaseMessageComponent.js deleted file mode 100644 index aa873fab6..000000000 --- a/packages/discord.js/src/structures/BaseMessageComponent.js +++ /dev/null @@ -1,104 +0,0 @@ -'use strict'; - -const { ComponentType } = require('discord-api-types/v9'); -const { TypeError } = require('../errors'); -const { Events } = require('../util/Constants'); - -/** - * Represents an interactive component of a Message. It should not be necessary to construct this directly. - * See {@link MessageComponent} - */ -class BaseMessageComponent { - /** - * Options for a BaseMessageComponent - * @typedef {Object} BaseMessageComponentOptions - * @property {MessageComponentTypeResolvable} type The type of this component - */ - - /** - * Data that can be resolved into options for a MessageComponent. This can be: - * * MessageActionRowOptions - * * MessageButtonOptions - * * MessageSelectMenuOptions - * @typedef {MessageActionRowOptions|MessageButtonOptions|MessageSelectMenuOptions} MessageComponentOptions - */ - - /** - * Components that can be sent in a message. These can be: - * * MessageActionRow - * * MessageButton - * * MessageSelectMenu - * @typedef {MessageActionRow|MessageButton|MessageSelectMenu} MessageComponent - * @see {@link https://discord.com/developers/docs/interactions/message-components#component-object-component-types} - */ - - /** - * Data that can be resolved to a MessageComponentType. This can be: - * * MessageComponentType - * * string - * * number - * @typedef {string|number|MessageComponentType} MessageComponentTypeResolvable - */ - - /** - * @param {BaseMessageComponent|BaseMessageComponentOptions} [data={}] The options for this component - */ - constructor(data) { - /** - * The type of this component - * @type {?MessageComponentType} - */ - this.type = 'type' in data ? BaseMessageComponent.resolveType(data.type) : null; - } - - /** - * Constructs a MessageComponent based on the type of the incoming data - * @param {MessageComponentOptions} data Data for a MessageComponent - * @param {Client|WebhookClient} [client] Client constructing this component - * @returns {?MessageComponent} - * @private - */ - static create(data, client) { - let component; - let type = data.type; - - if (typeof type === 'string') type = ComponentType[type]; - - switch (type) { - case ComponentType.ActionRow: { - const MessageActionRow = require('./MessageActionRow'); - component = data instanceof MessageActionRow ? data : new MessageActionRow(data, client); - break; - } - case ComponentType.Button: { - const MessageButton = require('./MessageButton'); - component = data instanceof MessageButton ? data : new MessageButton(data); - break; - } - case ComponentType.SelectMenu: { - const MessageSelectMenu = require('./MessageSelectMenu'); - component = data instanceof MessageSelectMenu ? data : new MessageSelectMenu(data); - break; - } - default: - if (client) { - client.emit(Events.DEBUG, `[BaseMessageComponent] Received component with unknown type: ${data.type}`); - } else { - throw new TypeError('INVALID_TYPE', 'data.type', 'valid MessageComponentType'); - } - } - return component; - } - - /** - * Resolves the type of a MessageComponent - * @param {MessageComponentTypeResolvable} type The type to resolve - * @returns {MessageComponentType} - * @private - */ - static resolveType(type) { - return typeof type === 'string' ? type : ComponentType[type]; - } -} - -module.exports = BaseMessageComponent; diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 7da715aa1..720085f40 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -1,10 +1,10 @@ 'use strict'; +const { createComponent } = require('@discordjs/builders'); const { Collection } = require('@discordjs/collection'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const { MessageType, InteractionType } = require('discord-api-types/v9'); const Base = require('./Base'); -const BaseMessageComponent = require('./BaseMessageComponent'); const ClientApplication = require('./ClientApplication'); const InteractionCollector = require('./InteractionCollector'); const MessageAttachment = require('./MessageAttachment'); @@ -138,9 +138,9 @@ class Message extends Base { if ('components' in data) { /** * A list of MessageActionRows in the message - * @type {MessageActionRow[]} + * @type {ActionRow[]} */ - this.components = data.components.map(c => BaseMessageComponent.create(c, this.client)); + this.components = data.components.map(c => createComponent(c)); } else { this.components = this.components?.slice() ?? []; } diff --git a/packages/discord.js/src/structures/MessageActionRow.js b/packages/discord.js/src/structures/MessageActionRow.js deleted file mode 100644 index 8ad93f082..000000000 --- a/packages/discord.js/src/structures/MessageActionRow.js +++ /dev/null @@ -1,101 +0,0 @@ -'use strict'; - -const { ComponentType } = require('discord-api-types/v9'); -const BaseMessageComponent = require('./BaseMessageComponent'); - -/** - * Represents an action row containing message components. - * @extends {BaseMessageComponent} - */ -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 - */ - - /** - * Data that can be resolved into components that can be placed in an action row - * * MessageActionRowComponent - * * MessageActionRowComponentOptions - * @typedef {MessageActionRowComponent|MessageActionRowComponentOptions} MessageActionRowComponentResolvable - */ - - /** - * @typedef {BaseMessageComponentOptions} MessageActionRowOptions - * @property {MessageActionRowComponentResolvable[]} [components] - * The components to place in this action row - */ - - /** - * @param {MessageActionRow|MessageActionRowOptions} [data={}] MessageActionRow to clone or raw data - * @param {Client} [client] The client constructing this MessageActionRow, if provided - */ - constructor(data = {}, client = null) { - super({ type: 'ACTION_ROW' }); - - /** - * The components in this action row - * @type {MessageActionRowComponent[]} - */ - this.components = data.components?.map(c => BaseMessageComponent.create(c, client)) ?? []; - } - - /** - * Adds components to the action row. - * @param {...MessageActionRowComponentResolvable[]} components The components to add - * @returns {MessageActionRow} - */ - addComponents(...components) { - this.components.push(...components.flat(Infinity).map(c => BaseMessageComponent.create(c))); - return this; - } - - /** - * Sets the components of the action row. - * @param {...MessageActionRowComponentResolvable[]} components The components to set - * @returns {MessageActionRow} - */ - setComponents(...components) { - this.spliceComponents(0, this.components.length, components); - return this; - } - - /** - * Removes, replaces, and inserts components in the action row. - * @param {number} index The index to start at - * @param {number} deleteCount The number of components to remove - * @param {...MessageActionRowComponentResolvable[]} [components] The replacing components - * @returns {MessageActionRow} - */ - spliceComponents(index, deleteCount, ...components) { - this.components.splice(index, deleteCount, ...components.flat(Infinity).map(c => BaseMessageComponent.create(c))); - return this; - } - - /** - * Transforms the action row to a plain object. - * @returns {APIMessageComponent} The raw data of this action row - */ - toJSON() { - return { - components: this.components.map(c => c.toJSON()), - type: ComponentType[this.type], - }; - } -} - -module.exports = MessageActionRow; - -/** - * @external APIMessageComponent - * @see {@link https://discord.com/developers/docs/interactions/message-components#component-object} - */ diff --git a/packages/discord.js/src/structures/MessageButton.js b/packages/discord.js/src/structures/MessageButton.js deleted file mode 100644 index 0eaad484b..000000000 --- a/packages/discord.js/src/structures/MessageButton.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; - -const { ButtonStyle, ComponentType } = require('discord-api-types/v9'); -const BaseMessageComponent = require('./BaseMessageComponent'); -const { RangeError } = require('../errors'); -const Util = require('../util/Util'); - -/** - * Represents a button message component. - * @extends {BaseMessageComponent} - */ -class MessageButton extends BaseMessageComponent { - /** - * @typedef {BaseMessageComponentOptions} MessageButtonOptions - * @property {string} [label] The text to be displayed on this button - * @property {string} [customId] A unique string to be sent in the interaction when clicked - * @property {MessageButtonStyleResolvable} [style] The style of this button - * @property {EmojiIdentifierResolvable} [emoji] The emoji to be displayed to the left of the text - * @property {string} [url] Optional URL for link-style buttons - * @property {boolean} [disabled=false] Disables the button to prevent interactions - */ - - /** - * @param {MessageButton|MessageButtonOptions} [data={}] MessageButton to clone or raw data - */ - constructor(data = {}) { - super({ type: 'BUTTON' }); - - this.setup(data); - } - - setup(data) { - /** - * The text to be displayed on this button - * @type {?string} - */ - this.label = data.label ?? null; - - /** - * A unique string to be sent in the interaction when clicked - * @type {?string} - */ - this.customId = data.custom_id ?? data.customId ?? null; - - /** - * The style of this button - * @type {?MessageButtonStyle} - */ - this.style = data.style ? MessageButton.resolveStyle(data.style) : null; - - /** - * Emoji for this button - * @type {?RawEmoji} - */ - this.emoji = data.emoji ? Util.resolvePartialEmoji(data.emoji) : null; - - /** - * The URL this button links to, if it is a Link style button - * @type {?string} - */ - this.url = data.url ?? null; - - /** - * Whether this button is currently disabled - * @type {boolean} - */ - this.disabled = data.disabled ?? false; - } - - /** - * Sets the custom id for this button - * @param {string} customId A unique string to be sent in the interaction when clicked - * @returns {MessageButton} - */ - setCustomId(customId) { - this.customId = Util.verifyString(customId, RangeError, 'BUTTON_CUSTOM_ID'); - return this; - } - - /** - * Sets the interactive status of the button - * @param {boolean} [disabled=true] Whether this button should be disabled - * @returns {MessageButton} - */ - setDisabled(disabled = true) { - this.disabled = disabled; - return this; - } - - /** - * Set the emoji of this button - * @param {EmojiIdentifierResolvable} emoji The emoji to be displayed on this button - * @returns {MessageButton} - */ - setEmoji(emoji) { - this.emoji = Util.resolvePartialEmoji(emoji); - return this; - } - - /** - * Sets the label of this button - * @param {string} label The text to be displayed on this button - * @returns {MessageButton} - */ - setLabel(label) { - this.label = Util.verifyString(label, RangeError, 'BUTTON_LABEL'); - return this; - } - - /** - * Sets the style of this button - * @param {MessageButtonStyleResolvable} style The style of this button - * @returns {MessageButton} - */ - setStyle(style) { - this.style = MessageButton.resolveStyle(style); - return this; - } - - /** - * Sets the URL of this button. - * MessageButton#style must be LINK when setting a URL - * @param {string} url The URL of this button - * @returns {MessageButton} - */ - setURL(url) { - this.url = Util.verifyString(url, RangeError, 'BUTTON_URL'); - return this; - } - - /** - * Transforms the button to a plain object. - * @returns {APIMessageButton} The raw data of this button - */ - toJSON() { - return { - custom_id: this.customId, - disabled: this.disabled, - emoji: this.emoji, - label: this.label, - style: ButtonStyle[this.style], - type: ComponentType[this.type], - url: this.url, - }; - } - - /** - * Data that can be resolved to a MessageButtonStyle. This can be - * * MessageButtonStyle - * * number - * @typedef {number|MessageButtonStyle} MessageButtonStyleResolvable - */ - - /** - * Resolves the style of a button - * @param {MessageButtonStyleResolvable} style The style to resolve - * @returns {MessageButtonStyle} - * @private - */ - static resolveStyle(style) { - return typeof style === 'string' ? style : ButtonStyle[style]; - } -} - -module.exports = MessageButton; diff --git a/packages/discord.js/src/structures/MessagePayload.js b/packages/discord.js/src/structures/MessagePayload.js index 7b6034d2b..2ee34f771 100644 --- a/packages/discord.js/src/structures/MessagePayload.js +++ b/packages/discord.js/src/structures/MessagePayload.js @@ -1,7 +1,7 @@ 'use strict'; const { Buffer } = require('node:buffer'); -const BaseMessageComponent = require('./BaseMessageComponent'); +const { createComponent } = require('@discordjs/builders'); const MessageEmbed = require('./MessageEmbed'); const { RangeError } = require('../errors'); const DataResolver = require('../util/DataResolver'); @@ -138,7 +138,7 @@ class MessagePayload { } } - const components = this.options.components?.map(c => BaseMessageComponent.create(c).toJSON()); + const components = this.options.components?.map(c => createComponent(c).toJSON()); let username; let avatarURL; diff --git a/packages/discord.js/src/structures/MessageSelectMenu.js b/packages/discord.js/src/structures/MessageSelectMenu.js deleted file mode 100644 index cab6c9a01..000000000 --- a/packages/discord.js/src/structures/MessageSelectMenu.js +++ /dev/null @@ -1,212 +0,0 @@ -'use strict'; - -const { ComponentType } = require('discord-api-types/v9'); -const BaseMessageComponent = require('./BaseMessageComponent'); -const Util = require('../util/Util'); - -/** - * Represents a select menu message component - * @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 {boolean} [disabled=false] Disables the select menu to prevent interactions - */ - - /** - * @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 {EmojiIdentifierResolvable} [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); - } - - setup(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 = this.constructor.normalizeOptions(data.options ?? []); - - /** - * Whether this select menu is currently disabled - * @type {boolean} - */ - this.disabled = data.disabled ?? false; - } - - /** - * 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.verifyString(customId, RangeError, 'SELECT_MENU_CUSTOM_ID'); - return this; - } - - /** - * Sets the interactive status of the select menu - * @param {boolean} [disabled=true] Whether this select menu should be disabled - * @returns {MessageSelectMenu} - */ - setDisabled(disabled = true) { - 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 - * @returns {MessageSelectMenu} - */ - setMaxValues(maxValues) { - this.maxValues = maxValues; - return this; - } - - /** - * 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} - */ - 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.verifyString(placeholder, RangeError, 'SELECT_MENU_PLACEHOLDER'); - return this; - } - - /** - * Adds options to the select menu. - * @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The options to add - * @returns {MessageSelectMenu} - */ - addOptions(...options) { - this.options.push(...this.constructor.normalizeOptions(options)); - return this; - } - - /** - * Sets the options of the select menu. - * @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The options to set - * @returns {MessageSelectMenu} - */ - setOptions(...options) { - this.spliceOptions(0, this.options.length, options); - return this; - } - - /** - * 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 {...MessageSelectOptionData|MessageSelectOptionData[]} [options] The replacing option objects - * @returns {MessageSelectMenu} - */ - spliceOptions(index, deleteCount, ...options) { - this.options.splice(index, deleteCount, ...this.constructor.normalizeOptions(...options)); - return this; - } - - /** - * Transforms the select menu into a plain object - * @returns {APIMessageSelectMenu} The raw data of this select menu - */ - 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), - options: this.options, - type: typeof this.type === 'string' ? ComponentType[this.type] : this.type, - }; - } - - /** - * Normalizes option input and resolves strings and emojis. - * @param {MessageSelectOptionData} option The select menu option to normalize - * @returns {MessageSelectOption} - */ - static normalizeOption(option) { - let { label, value, description, emoji } = option; - - label = Util.verifyString(label, RangeError, 'SELECT_OPTION_LABEL'); - value = Util.verifyString(value, RangeError, 'SELECT_OPTION_VALUE'); - 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 ?? false }; - } - - /** - * Normalizes option input and resolves strings and emojis. - * @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The select menu options to normalize - * @returns {MessageSelectOption[]} - */ - static normalizeOptions(...options) { - return options.flat(Infinity).map(option => this.normalizeOption(option)); - } -} - -module.exports = MessageSelectMenu; diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 49ee82fe6..47d702c7a 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -1,8 +1,12 @@ import { + ActionRow, + ActionRowComponent, blockQuote, bold, + ButtonComponent, channelMention, codeBlock, + Component, formatEmoji, hideLinkEmbed, hyperlink, @@ -11,6 +15,7 @@ import { memberNicknameMention, quote, roleMention, + SelectMenuComponent, spoiler, strikethrough, time, @@ -424,13 +429,6 @@ export class BaseGuildVoiceChannel extends GuildChannel { public fetchInvites(cache?: boolean): Promise>; } -export class BaseMessageComponent { - protected constructor(data?: BaseMessageComponent | BaseMessageComponentOptions); - public type: MessageComponentTypeKey | null; - private static create(data: MessageComponentOptions, client?: Client | WebhookClient): MessageComponent | undefined; - private static resolveType(type: MessageComponentTypeResolvable): MessageComponentTypeKey; -} - export class BitField { public constructor(bits?: BitFieldResolvable); public bitfield: N; @@ -454,10 +452,10 @@ export class ButtonInteraction extends Mes private constructor(client: Client, data: RawMessageButtonInteractionData); public readonly component: CacheTypeReducer< Cached, - MessageButton, + ButtonComponent, APIButtonComponent, - MessageButton | APIButtonComponent, - MessageButton | APIButtonComponent + ButtonComponent | APIButtonComponent, + ButtonComponent | APIButtonComponent >; public componentType: 'Button'; public inGuild(): this is ButtonInteraction<'raw' | 'cached'>; @@ -1476,7 +1474,7 @@ export class Message extends Base { public readonly channel: If; public channelId: Snowflake; public readonly cleanContent: string; - public components: MessageActionRow[]; + public components: ActionRow[]; public content: string; public readonly createdAt: Date; public createdTimestamp: number; @@ -1527,7 +1525,7 @@ export class Message extends Base { public react(emoji: EmojiIdentifierResolvable): Promise; public removeAttachments(): Promise; public reply(options: string | MessagePayload | ReplyMessageOptions): Promise; - public resolveComponent(customId: string): MessageActionRowComponent | null; + public resolveComponent(customId: string): ActionRowComponent | null; public startThread(options: StartThreadOptions): Promise; public suppressEmbeds(suppress?: boolean): Promise; public toJSON(): unknown; @@ -1536,24 +1534,6 @@ export class Message extends Base { public inGuild(): this is Message & this; } -export class MessageActionRow extends BaseMessageComponent { - public constructor(data?: MessageActionRow | MessageActionRowOptions | APIActionRowComponent); - public type: 'ActionRow'; - public components: MessageActionRowComponent[]; - public addComponents( - ...components: MessageActionRowComponentResolvable[] | MessageActionRowComponentResolvable[][] - ): this; - public setComponents( - ...components: MessageActionRowComponentResolvable[] | MessageActionRowComponentResolvable[][] - ): this; - public spliceComponents( - index: number, - deleteCount: number, - ...components: MessageActionRowComponentResolvable[] | MessageActionRowComponentResolvable[][] - ): this; - public toJSON(): APIActionRowComponent; -} - export class MessageAttachment { public constructor(attachment: BufferResolvable | Stream, name?: string, data?: RawMessageAttachmentData); @@ -1576,25 +1556,6 @@ export class MessageAttachment { public toJSON(): unknown; } -export class MessageButton extends BaseMessageComponent { - public constructor(data?: MessageButton | MessageButtonOptions | APIButtonComponent); - public customId: string | null; - public disabled: boolean; - public emoji: APIPartialEmoji | null; - public label: string | null; - public style: ButtonStyleKey | null; - public type: 'Button'; - public url: string | null; - public setCustomId(customId: string): this; - public setDisabled(disabled?: boolean): this; - public setEmoji(emoji: EmojiIdentifierResolvable): this; - public setLabel(label: string): this; - public setStyle(style: MessageButtonStyleResolvable): this; - public setURL(url: string): this; - public toJSON(): APIButtonComponent; - private static resolveStyle(style: MessageButtonStyleResolvable): ButtonStyleKey; -} - export class MessageCollector extends Collector { public constructor(channel: TextBasedChannel, options?: MessageCollectorOptions); private _handleChannelDeletion(channel: NonThreadGuildBasedChannel): void; @@ -1613,10 +1574,10 @@ export class MessageComponentInteraction e protected constructor(client: Client, data: RawMessageComponentInteractionData); public readonly component: CacheTypeReducer< Cached, - MessageActionRowComponent, + ActionRowComponent, Exclude, - MessageActionRowComponent | Exclude, - MessageActionRowComponent | Exclude + ActionRowComponent | Exclude, + ActionRowComponent | Exclude >; public componentType: Exclude; public customId: string; @@ -1776,30 +1737,6 @@ export class MessageReaction { public toJSON(): unknown; } -export class MessageSelectMenu extends BaseMessageComponent { - public constructor(data?: MessageSelectMenu | MessageSelectMenuOptions | APISelectMenuComponent); - public customId: string | null; - public disabled: boolean; - public maxValues: number | null; - public minValues: number | null; - public options: MessageSelectOption[]; - public placeholder: string | null; - public type: 'SelectMenu'; - public addOptions(...options: MessageSelectOptionData[] | MessageSelectOptionData[][]): this; - public setOptions(...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; - public spliceOptions( - index: number, - deleteCount: number, - ...options: MessageSelectOptionData[] | MessageSelectOptionData[][] - ): this; - public toJSON(): APISelectMenuComponent; -} - export class NewsChannel extends BaseGuildTextChannel { public threads: ThreadManager; public type: 'GuildNews'; @@ -1951,10 +1888,10 @@ export class SelectMenuInteraction extends public constructor(client: Client, data: RawMessageSelectMenuInteractionData); public readonly component: CacheTypeReducer< Cached, - MessageSelectMenu, + SelectMenuComponent, APISelectMenuComponent, - MessageSelectMenu | APISelectMenuComponent, - MessageSelectMenu | APISelectMenuComponent + SelectMenuComponent | APISelectMenuComponent, + SelectMenuComponent | APISelectMenuComponent >; public componentType: 'SelectMenu'; public values: string[]; @@ -4696,16 +4633,14 @@ export type MemberMention = UserMention | `<@!${Snowflake}>`; export type TeamMemberMembershipStateKey = keyof typeof TeamMemberMembershipState; -export type MessageActionRowComponent = MessageButton | MessageSelectMenu; - -export type MessageActionRowComponentOptions = +export type ActionRowComponentOptions = | (Required & MessageButtonOptions) | (Required & MessageSelectMenuOptions); -export type MessageActionRowComponentResolvable = MessageActionRowComponent | MessageActionRowComponentOptions; +export type MessageActionRowComponentResolvable = ActionRowComponent | ActionRowComponentOptions; -export interface MessageActionRowOptions extends BaseMessageComponentOptions { - components: MessageActionRowComponentResolvable[]; +export interface ActionRowOptions extends BaseMessageComponentOptions { + components: ActionRowComponent[]; } export interface MessageActivity { @@ -4740,7 +4675,7 @@ export interface MessageCollectorOptions extends CollectorOptions<[Message]> { maxProcessed?: number; } -export type MessageComponent = BaseMessageComponent | MessageActionRow | MessageButton | MessageSelectMenu; +export type MessageComponent = Component | ActionRow | ButtonComponent | SelectMenuComponent; export type MessageComponentCollectorOptions = Omit< InteractionCollectorOptions, @@ -4754,7 +4689,7 @@ export type MessageChannelComponentCollectorOptions; allowedMentions?: MessageMentionOptions; - components?: (MessageActionRow | (Required & MessageActionRowOptions))[]; + components?: (ActionRow | (Required & ActionRowOptions))[]; } export interface MessageEmbedAuthor { @@ -4868,7 +4803,7 @@ export interface MessageOptions { nonce?: string | number; content?: string | null; embeds?: (MessageEmbed | MessageEmbedOptions | APIEmbed)[]; - components?: (MessageActionRow | (Required & MessageActionRowOptions))[]; + components?: (ActionRow | (Required & ActionRowOptions))[]; allowedMentions?: MessageMentionOptions; files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[]; reply?: ReplyOptions; @@ -5545,3 +5480,10 @@ export { StickerFormatType, WebhookType, } from 'discord-api-types/v9'; +export { + ActionRow, + ButtonComponent, + SelectMenuComponent, + SelectMenuOption, + ActionRowComponent, +} from '@discordjs/builders'; diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 786b40e21..0fba5b6df 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -1,5 +1,5 @@ import type { ChildProcess } from 'child_process'; -import type { +import { APIInteractionGuildMember, APIMessage, APIPartialChannel, @@ -10,6 +10,7 @@ import type { APIButtonComponent, APISelectMenuComponent, ApplicationCommandOptionType, + ComponentType, } from 'discord-api-types/v9'; import { AuditLogEvent } from 'discord-api-types/v9'; import { @@ -47,9 +48,7 @@ import { Interaction, InteractionCollector, Message, - MessageActionRow, MessageAttachment, - MessageButton, MessageCollector, MessageComponentInteraction, MessageEmbed, @@ -88,9 +87,11 @@ import { GuildAuditLogsEntry, GuildAuditLogs, StageInstance, - MessageActionRowComponent, - MessageSelectMenu, PartialDMChannel, + ActionRow, + ButtonComponent, + SelectMenuComponent, + ActionRowComponent, } from '.'; import { expectAssignable, expectDeprecated, expectNotAssignable, expectNotType, expectType } from 'tsd'; @@ -668,11 +669,11 @@ client.on('interactionCreate', async interaction => { if (!interaction.isCommand()) return; - void new MessageActionRow(); + void new ActionRow(); - const button = new MessageButton(); + const button = new ButtonComponent(); - const actionRow = new MessageActionRow({ components: [button] }); + const actionRow = new ActionRow({ type: ComponentType.ActionRow, components: [button] }); await interaction.reply({ content: 'Hi!', components: [actionRow] }); @@ -993,11 +994,11 @@ client.on('interactionCreate', async interaction => { if (interaction.isButton()) { expectType(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType(interaction.message); if (interaction.inCachedGuild()) { expectAssignable(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType>(interaction.message); expectType(interaction.guild); expectAssignable>(interaction.reply({ fetchReply: true })); @@ -1009,7 +1010,7 @@ client.on('interactionCreate', async interaction => { expectType>(interaction.reply({ fetchReply: true })); } else if (interaction.inGuild()) { expectAssignable(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType(interaction.message); expectAssignable(interaction.guild); expectType>(interaction.reply({ fetchReply: true })); @@ -1018,11 +1019,11 @@ client.on('interactionCreate', async interaction => { if (interaction.isMessageComponent()) { expectType(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType(interaction.message); if (interaction.inCachedGuild()) { expectAssignable(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType>(interaction.message); expectType(interaction.guild); expectAssignable>(interaction.reply({ fetchReply: true })); @@ -1034,7 +1035,7 @@ client.on('interactionCreate', async interaction => { expectType>(interaction.reply({ fetchReply: true })); } else if (interaction.inGuild()) { expectAssignable(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType(interaction.message); expectType(interaction.guild); expectType>(interaction.reply({ fetchReply: true })); @@ -1043,11 +1044,11 @@ client.on('interactionCreate', async interaction => { if (interaction.isSelectMenu()) { expectType(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType(interaction.message); if (interaction.inCachedGuild()) { expectAssignable(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType>(interaction.message); expectType(interaction.guild); expectType>>(interaction.reply({ fetchReply: true })); @@ -1059,7 +1060,7 @@ client.on('interactionCreate', async interaction => { expectType>(interaction.reply({ fetchReply: true })); } else if (interaction.inGuild()) { expectAssignable(interaction); - expectType(interaction.component); + expectType(interaction.component); expectType(interaction.message); expectType(interaction.guild); expectType>(interaction.reply({ fetchReply: true }));