mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-11 09:03:29 +01:00
feat(MessageComponents): clickybois (MessageButton, MessageActionRow, associated Collectors) (#5674)
Co-authored-by: Vicente <33096355+Vicente015@users.noreply.github.com> Co-authored-by: Shubham Parihar <shubhamparihar391@gmail.com> Co-authored-by: SpaceEEC <spaceeec@yahoo.com> Co-authored-by: BannerBomb <BannerBomb55@gmail.com> Co-authored-by: Arechi <22101241+Arechii@users.noreply.github.com> Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com> Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com> Co-authored-by: Antonio Román <kyradiscord@gmail.com>
This commit is contained in:
@@ -4,20 +4,31 @@ const { Events, InteractionTypes } = require('../../../util/Constants');
|
||||
let Structures;
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
if (data.type === InteractionTypes.APPLICATION_COMMAND) {
|
||||
if (!Structures) Structures = require('../../../util/Structures');
|
||||
const CommandInteraction = Structures.get('CommandInteraction');
|
||||
let interaction;
|
||||
switch (data.type) {
|
||||
case InteractionTypes.APPLICATION_COMMAND: {
|
||||
if (!Structures) Structures = require('../../../util/Structures');
|
||||
const CommandInteraction = Structures.get('CommandInteraction');
|
||||
|
||||
const interaction = new CommandInteraction(client, data);
|
||||
interaction = new CommandInteraction(client, data);
|
||||
break;
|
||||
}
|
||||
case InteractionTypes.MESSAGE_COMPONENT: {
|
||||
if (!Structures) Structures = require('../../../util/Structures');
|
||||
const MessageComponentInteraction = Structures.get('MessageComponentInteraction');
|
||||
|
||||
/**
|
||||
* Emitted when an interaction is created.
|
||||
* @event Client#interaction
|
||||
* @param {Interaction} interaction The interaction which was created
|
||||
*/
|
||||
client.emit(Events.INTERACTION_CREATE, interaction);
|
||||
return;
|
||||
interaction = new MessageComponentInteraction(client, data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
client.emit(Events.DEBUG, `[INTERACTION] Received interaction with unknown type: ${data.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
client.emit(Events.DEBUG, `[INTERACTION] Received interaction with unknown type: ${data.type}`);
|
||||
/**
|
||||
* Emitted when an interaction is created.
|
||||
* @event Client#interaction
|
||||
* @param {Interaction} interaction The interaction which was created
|
||||
*/
|
||||
client.emit(Events.INTERACTION_CREATE, interaction);
|
||||
};
|
||||
|
||||
@@ -44,6 +44,10 @@ const Messages = {
|
||||
EMBED_DESCRIPTION: 'MessageEmbed description must be a string.',
|
||||
EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.',
|
||||
|
||||
BUTTON_LABEL: 'MessageButton label must be a string',
|
||||
BUTTON_URL: 'MessageButton url must be a string',
|
||||
BUTTON_CUSTOM_ID: 'MessageButton customID must be a string',
|
||||
|
||||
FILE_NOT_FOUND: file => `File could not be found: ${file}`,
|
||||
|
||||
USER_NO_DMCHANNEL: 'No DM Channel exists!',
|
||||
|
||||
@@ -68,6 +68,7 @@ module.exports = {
|
||||
BaseGuild: require('./structures/BaseGuild'),
|
||||
BaseGuildEmoji: require('./structures/BaseGuildEmoji'),
|
||||
BaseGuildVoiceChannel: require('./structures/BaseGuildVoiceChannel'),
|
||||
BaseMessageComponent: require('./structures/BaseMessageComponent'),
|
||||
CategoryChannel: require('./structures/CategoryChannel'),
|
||||
Channel: require('./structures/Channel'),
|
||||
ClientApplication: require('./structures/ClientApplication'),
|
||||
@@ -92,8 +93,12 @@ module.exports = {
|
||||
Interaction: require('./structures/Interaction'),
|
||||
Invite: require('./structures/Invite'),
|
||||
Message: require('./structures/Message'),
|
||||
MessageActionRow: require('./structures/MessageActionRow'),
|
||||
MessageAttachment: require('./structures/MessageAttachment'),
|
||||
MessageButton: require('./structures/MessageButton'),
|
||||
MessageCollector: require('./structures/MessageCollector'),
|
||||
MessageComponentInteraction: require('./structures/MessageComponentInteraction'),
|
||||
MessageComponentInteractionCollector: require('./structures/MessageComponentInteractionCollector'),
|
||||
MessageEmbed: require('./structures/MessageEmbed'),
|
||||
MessageMentions: require('./structures/MessageMentions'),
|
||||
MessageReaction: require('./structures/MessageReaction'),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||
const MessageAttachment = require('./MessageAttachment');
|
||||
const MessageEmbed = require('./MessageEmbed');
|
||||
const { RangeError } = require('../errors');
|
||||
@@ -151,6 +152,8 @@ class APIMessage {
|
||||
}
|
||||
const embeds = embedLikes.map(e => new MessageEmbed(e).toJSON());
|
||||
|
||||
const components = this.options.components?.map(c => BaseMessageComponent.create(c).toJSON());
|
||||
|
||||
let username;
|
||||
let avatarURL;
|
||||
if (isWebhook) {
|
||||
@@ -196,6 +199,7 @@ class APIMessage {
|
||||
nonce,
|
||||
embed: !isWebhookLike ? (this.options.embed === null ? null : embeds[0]) : undefined,
|
||||
embeds: isWebhookLike ? embeds : undefined,
|
||||
components,
|
||||
username,
|
||||
avatar_url: avatarURL,
|
||||
allowed_mentions:
|
||||
|
||||
94
src/structures/BaseMessageComponent.js
Normal file
94
src/structures/BaseMessageComponent.js
Normal file
@@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
const { TypeError } = require('../errors');
|
||||
const { MessageComponentTypes, 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
|
||||
* @typedef {MessageActionRowOptions|MessageButtonOptions} MessageComponentOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Components that can be sent in a message
|
||||
* @typedef {MessageActionRow|MessageButton} MessageComponent
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data that can be resolved to a MessageComponentType. This can be:
|
||||
* * {@link 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
|
||||
* @param {boolean} [skipValidation=false] Whether or not to validate the component type
|
||||
* @returns {?MessageComponent}
|
||||
* @private
|
||||
*/
|
||||
static create(data, client, skipValidation = false) {
|
||||
let component;
|
||||
let type = data.type;
|
||||
|
||||
if (typeof type === 'string') type = MessageComponentTypes[type];
|
||||
|
||||
switch (type) {
|
||||
case MessageComponentTypes.ACTION_ROW: {
|
||||
const MessageActionRow = require('./MessageActionRow');
|
||||
component = new MessageActionRow(data);
|
||||
break;
|
||||
}
|
||||
case MessageComponentTypes.BUTTON: {
|
||||
const MessageButton = require('./MessageButton');
|
||||
component = new MessageButton(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (client) {
|
||||
client.emit(Events.DEBUG, `[BaseMessageComponent] Received component with unknown type: ${data.type}`);
|
||||
} else if (!skipValidation) {
|
||||
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 : MessageComponentTypes[type];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseMessageComponent;
|
||||
@@ -1,16 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const APIMessage = require('./APIMessage');
|
||||
const Interaction = require('./Interaction');
|
||||
const InteractionResponses = require('./interfaces/InteractionResponses');
|
||||
const WebhookClient = require('../client/WebhookClient');
|
||||
const { Error } = require('../errors');
|
||||
const Collection = require('../util/Collection');
|
||||
const { ApplicationCommandOptionTypes, InteractionResponseTypes } = require('../util/Constants');
|
||||
const MessageFlags = require('../util/MessageFlags');
|
||||
const { ApplicationCommandOptionTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents a command interaction.
|
||||
* @extends {Interaction}
|
||||
* @implements {InteractionResponses}
|
||||
*/
|
||||
class CommandInteraction extends Interaction {
|
||||
constructor(client, data) {
|
||||
@@ -69,126 +68,6 @@ class CommandInteraction extends Interaction {
|
||||
return this.guild?.commands.cache.get(id) ?? this.client.application.commands.cache.get(id) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for deferring the reply to a {@link CommandInteraction}.
|
||||
* @typedef {Object} InteractionDeferOptions
|
||||
* @property {boolean} [ephemeral] Whether the reply should be ephemeral
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defers the reply to this interaction.
|
||||
* @param {InteractionDeferOptions} [options] Options for deferring the reply to this interaction
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Defer the reply to this interaction
|
||||
* interaction.defer()
|
||||
* .then(console.log)
|
||||
* .catch(console.error)
|
||||
* @example
|
||||
* // Defer to send an ephemeral reply later
|
||||
* interaction.defer({ ephemeral: true })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async defer({ ephemeral } = {}) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: {
|
||||
flags: ephemeral ? MessageFlags.FLAGS.EPHEMERAL : undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
this.deferred = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for a reply to an interaction.
|
||||
* @typedef {BaseMessageOptions} InteractionReplyOptions
|
||||
* @property {boolean} [ephemeral] Whether the reply should be ephemeral
|
||||
* @property {MessageEmbed[]|Object[]} [embeds] An array of embeds for the message
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a reply to this interaction.
|
||||
* @param {string|APIMessage|MessageAdditions} content The content for the reply
|
||||
* @param {InteractionReplyOptions} [options] Additional options for the reply
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Reply to the interaction with an embed
|
||||
* const embed = new MessageEmbed().setDescription('Pong!');
|
||||
*
|
||||
* interaction.reply(embed)
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Create an ephemeral reply
|
||||
* interaction.reply('Pong!', { ephemeral: true })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async reply(content, options) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options);
|
||||
const { data, files } = await apiMessage.resolveData().resolveFiles();
|
||||
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data,
|
||||
},
|
||||
files,
|
||||
});
|
||||
this.replied = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the initial reply to this interaction.
|
||||
* @see Webhook#fetchMessage
|
||||
* @returns {Promise<Message|Object>}
|
||||
* @example
|
||||
* // Fetch the reply to this interaction
|
||||
* interaction.fetchReply()
|
||||
* .then(reply => console.log(`Replied with ${reply.content}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchReply() {
|
||||
const raw = await this.webhook.fetchMessage('@original');
|
||||
return this.channel?.messages.add(raw) ?? raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the initial reply to this interaction.
|
||||
* @see Webhook#editMessage
|
||||
* @param {string|APIMessage|MessageAdditions} content The new content for the message
|
||||
* @param {WebhookEditMessageOptions} [options] The options to provide
|
||||
* @returns {Promise<Message|Object>}
|
||||
* @example
|
||||
* // Edit the reply to this interaction
|
||||
* interaction.editReply('New content')
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async editReply(content, options) {
|
||||
const raw = await this.webhook.editMessage('@original', content, options);
|
||||
return this.channel?.messages.add(raw) ?? raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the initial reply to this interaction.
|
||||
* @see Webhook#deleteMessage
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Delete the reply to this interaction
|
||||
* interaction.deleteReply()
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async deleteReply() {
|
||||
await this.webhook.deleteMessage('@original');
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an option of a received command interaction.
|
||||
* @typedef {Object} CommandInteractionOption
|
||||
@@ -203,24 +82,6 @@ class CommandInteraction extends Interaction {
|
||||
* @property {Role|Object} [role] The resolved role
|
||||
*/
|
||||
|
||||
/**
|
||||
* Send a follow-up message to this interaction.
|
||||
* @param {string|APIMessage|MessageAdditions} content The content for the reply
|
||||
* @param {InteractionReplyOptions} [options] Additional options for the reply
|
||||
* @returns {Promise<Message|Object>}
|
||||
*/
|
||||
async followUp(content, options) {
|
||||
const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options);
|
||||
const { data, files } = await apiMessage.resolveData().resolveFiles();
|
||||
|
||||
const raw = await this.client.api.webhooks(this.applicationID, this.token).post({
|
||||
data,
|
||||
files,
|
||||
});
|
||||
|
||||
return this.channel?.messages.add(raw) ?? raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an option received from the API.
|
||||
* @param {Object} option The received option
|
||||
@@ -267,6 +128,17 @@ class CommandInteraction extends Interaction {
|
||||
}
|
||||
return optionsCollection;
|
||||
}
|
||||
|
||||
// These are here only for documentation purposes - they are implemented by InteractionResponses
|
||||
/* eslint-disable no-empty-function */
|
||||
defer() {}
|
||||
reply() {}
|
||||
fetchReply() {}
|
||||
editReply() {}
|
||||
deleteReply() {}
|
||||
followUp() {}
|
||||
}
|
||||
|
||||
InteractionResponses.applyToClass(CommandInteraction, ['deferUpdate', 'update']);
|
||||
|
||||
module.exports = CommandInteraction;
|
||||
|
||||
@@ -91,6 +91,8 @@ class DMChannel extends Channel {
|
||||
get typingCount() {}
|
||||
createMessageCollector() {}
|
||||
awaitMessages() {}
|
||||
createMessageComponentInteractionCollector() {}
|
||||
awaitMessageComponentInteractions() {}
|
||||
// Doesn't work on DM channels; bulkDelete() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,14 @@ class Interaction extends Base {
|
||||
isCommand() {
|
||||
return InteractionTypes[this.type] === InteractionTypes.APPLICATION_COMMAND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this interaction is a component interaction.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isMessageComponent() {
|
||||
return InteractionTypes[this.type] === InteractionTypes.MESSAGE_COMPONENT;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Interaction;
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
const APIMessage = require('./APIMessage');
|
||||
const Base = require('./Base');
|
||||
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||
const ClientApplication = require('./ClientApplication');
|
||||
const MessageAttachment = require('./MessageAttachment');
|
||||
const MessageComponentInteractionCollector = require('./MessageComponentInteractionCollector');
|
||||
const Embed = require('./MessageEmbed');
|
||||
const Mentions = require('./MessageMentions');
|
||||
const ReactionCollector = require('./ReactionCollector');
|
||||
@@ -123,6 +125,12 @@ class Message extends Base {
|
||||
*/
|
||||
this.embeds = (data.embeds || []).map(e => new Embed(e, true));
|
||||
|
||||
/**
|
||||
* A list of MessageActionRows in the message
|
||||
* @type {MessageActionRow[]}
|
||||
*/
|
||||
this.components = (data.components ?? []).map(c => BaseMessageComponent.create(c, this.client));
|
||||
|
||||
/**
|
||||
* A collection of attachments in the message - e.g. Pictures - mapped by their ID
|
||||
* @type {Collection<Snowflake, MessageAttachment>}
|
||||
@@ -282,6 +290,8 @@ class Message extends Base {
|
||||
if ('tts' in data) this.tts = data.tts;
|
||||
if ('embeds' in data) this.embeds = data.embeds.map(e => new Embed(e, true));
|
||||
else this.embeds = this.embeds.slice();
|
||||
if ('components' in data) this.components = data.components.map(c => BaseMessageComponent.create(c, this.client));
|
||||
else this.components = this.components.slice();
|
||||
|
||||
if ('attachments' in data) {
|
||||
this.attachments = new Collection();
|
||||
@@ -407,6 +417,51 @@ class Message extends Base {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a message component interaction collector.
|
||||
* @param {CollectorFilter} filter The filter to apply
|
||||
* @param {MessageComponentInteractionCollectorOptions} [options={}] Options to send to the collector
|
||||
* @returns {MessageComponentInteractionCollector}
|
||||
* @example
|
||||
* // Create a message component interaction collector
|
||||
* const filter = (interaction) => interaction.customID === 'button' && interaction.user.id === 'someID';
|
||||
* const collector = message.createMessageComponentInteractionCollector(filter, { time: 15000 });
|
||||
* collector.on('collect', i => console.log(`Collected ${i.customID}`));
|
||||
* collector.on('end', collected => console.log(`Collected ${collected.size} items`));
|
||||
*/
|
||||
createMessageComponentInteractionCollector(filter, options = {}) {
|
||||
return new MessageComponentInteractionCollector(this, filter, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing the same properties as CollectorOptions, but a few more:
|
||||
* @typedef {MessageComponentInteractionCollectorOptions} AwaitMessageComponentInteractionsOptions
|
||||
* @property {string[]} [errors] Stop/end reasons that cause the promise to reject
|
||||
*/
|
||||
|
||||
/**
|
||||
* Similar to createMessageComponentInteractionCollector but in promise form.
|
||||
* Resolves with a collection of interactions that pass the specified filter.
|
||||
* @param {CollectorFilter} filter The filter function to use
|
||||
* @param {AwaitMessageComponentInteractionsOptions} [options={}] Optional options to pass to the internal collector
|
||||
* @returns {Promise<Collection<string, MessageComponentInteraction>>}
|
||||
* @example
|
||||
* // Create a message component interaction collector
|
||||
* const filter = (interaction) => interaction.customID === 'button' && interaction.user.id === 'someID';
|
||||
* message.awaitMessageComponentInteractions(filter, { time: 15000 })
|
||||
* .then(collected => console.log(`Collected ${collected.size} interactions`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
awaitMessageComponentInteractions(filter, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const collector = this.createMessageComponentInteractionCollector(filter, options);
|
||||
collector.once('end', (interactions, reason) => {
|
||||
if (options.errors && options.errors.includes(reason)) reject(interactions);
|
||||
else resolve(interactions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the message is editable by the client user
|
||||
* @type {boolean}
|
||||
|
||||
87
src/structures/MessageActionRow.js
Normal file
87
src/structures/MessageActionRow.js
Normal file
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||
const { MessageComponentTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents an ActionRow containing message components.
|
||||
* @extends {BaseMessageComponent}
|
||||
*/
|
||||
class MessageActionRow extends BaseMessageComponent {
|
||||
/**
|
||||
* Components that can be placed in a MessageActionRow
|
||||
* * MessageButton
|
||||
* @typedef {MessageButton} MessageActionRowComponent
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for components that can be placed in a MessageActionRow
|
||||
* * MessageButtonOptions
|
||||
* @typedef {MessageButtonOptions} MessageActionRowComponentOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data that can be resolved into a components that can be placed in a MessageActionRow
|
||||
* * MessageActionRowComponent
|
||||
* * MessageActionRowComponentOptions
|
||||
* @typedef {MessageActionRowComponent|MessageActionRowComponentOptions} MessageActionRowComponentResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {BaseMessageComponentOptions} MessageActionRowOptions
|
||||
* @property {MessageActionRowComponentResolvable[]} [components]
|
||||
* The components to place in this ActionRow
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {MessageActionRow|MessageActionRowOptions} [data={}] MessageActionRow to clone or raw data
|
||||
*/
|
||||
constructor(data = {}) {
|
||||
super({ type: 'ACTION_ROW' });
|
||||
|
||||
/**
|
||||
* The components in this MessageActionRow
|
||||
* @type {MessageActionRowComponent[]}
|
||||
*/
|
||||
this.components = (data.components ?? []).map(c => BaseMessageComponent.create(c, null, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds components to the row.
|
||||
* @param {...MessageActionRowComponentResolvable[]} components The components to add
|
||||
* @returns {MessageActionRow}
|
||||
*/
|
||||
addComponents(...components) {
|
||||
this.components.push(...components.flat(Infinity).map(c => BaseMessageComponent.create(c, null, true)));
|
||||
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 {MessageSelectMenu}
|
||||
*/
|
||||
spliceComponents(index, deleteCount, ...components) {
|
||||
this.components.splice(
|
||||
index,
|
||||
deleteCount,
|
||||
...components.flat(Infinity).map(c => BaseMessageComponent.create(c, null, true)),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the action row to a plain object.
|
||||
* @returns {Object} The raw data of this action row
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
components: this.components.map(c => c.toJSON()),
|
||||
type: MessageComponentTypes[this.type],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageActionRow;
|
||||
166
src/structures/MessageButton.js
Normal file
166
src/structures/MessageButton.js
Normal file
@@ -0,0 +1,166 @@
|
||||
'use strict';
|
||||
|
||||
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||
const { RangeError } = require('../errors');
|
||||
const { MessageButtonStyles, MessageComponentTypes } = require('../util/Constants');
|
||||
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 {Emoji} [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 {?Emoji|string}
|
||||
*/
|
||||
this.emoji = 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 of 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 Whether this button should be disabled
|
||||
* @returns {MessageButton}
|
||||
*/
|
||||
setDisabled(disabled) {
|
||||
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) {
|
||||
if (/^\d{17,19}$/.test(emoji)) this.emoji = { id: emoji };
|
||||
else this.emoji = Util.parseEmoji(`${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 should be LINK
|
||||
* @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 {Object} The raw data of this button
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
custom_id: this.customID,
|
||||
disabled: this.disabled,
|
||||
emoji: this.emoji,
|
||||
label: this.label,
|
||||
style: MessageButtonStyles[this.style],
|
||||
type: MessageComponentTypes[this.type],
|
||||
url: this.url,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to a MessageButtonStyle. This can be
|
||||
* * {@link MessageButtonStyle}
|
||||
* * string
|
||||
* * number
|
||||
* @typedef {string|number|MessageButtonStyle} MessageButtonStyleResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves the style of a MessageButton
|
||||
* @param {MessageButtonStyleResolvable} style The style to resolve
|
||||
* @returns {MessageButtonStyle}
|
||||
* @private
|
||||
*/
|
||||
static resolveStyle(style) {
|
||||
return typeof style === 'string' ? style : MessageButtonStyles[style];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageButton;
|
||||
78
src/structures/MessageComponentInteraction.js
Normal file
78
src/structures/MessageComponentInteraction.js
Normal file
@@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
|
||||
const Interaction = require('./Interaction');
|
||||
const InteractionResponses = require('./interfaces/InteractionResponses');
|
||||
const WebhookClient = require('../client/WebhookClient');
|
||||
const { MessageComponentTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents a message component interaction.
|
||||
* @extends {Interaction}
|
||||
* @implements {InteractionResponses}
|
||||
*/
|
||||
class MessageComponentInteraction extends Interaction {
|
||||
constructor(client, data) {
|
||||
super(client, data);
|
||||
|
||||
/**
|
||||
* The message to which the component was attached
|
||||
* @type {?Message|Object}
|
||||
*/
|
||||
this.message = data.message ? this.channel?.messages.add(data.message) ?? data.message : null;
|
||||
|
||||
/**
|
||||
* The custom ID of the component which was clicked
|
||||
* @type {string}
|
||||
*/
|
||||
this.customID = data.data.custom_id;
|
||||
|
||||
/**
|
||||
* The type of component that was interacted with
|
||||
* @type {string}
|
||||
*/
|
||||
this.componentType = MessageComponentInteraction.resolveType(data.data.component_type);
|
||||
|
||||
/**
|
||||
* Whether the reply to this interaction has been deferred
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.deferred = false;
|
||||
|
||||
/**
|
||||
* Whether this interaction has already been replied to
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.replied = false;
|
||||
|
||||
/**
|
||||
* An associated webhook client, can be used to create deferred replies
|
||||
* @type {WebhookClient}
|
||||
*/
|
||||
this.webhook = new WebhookClient(this.applicationID, this.token, this.client.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the type of a MessageComponent
|
||||
* @param {MessageComponentTypeResolvable} type The type to resolve
|
||||
* @returns {MessageComponentType}
|
||||
* @private
|
||||
*/
|
||||
static resolveType(type) {
|
||||
return typeof type === 'string' ? type : MessageComponentTypes[type];
|
||||
}
|
||||
|
||||
// These are here only for documentation purposes - they are implemented by InteractionResponses
|
||||
/* eslint-disable no-empty-function */
|
||||
defer() {}
|
||||
reply() {}
|
||||
fetchReply() {}
|
||||
editReply() {}
|
||||
deleteReply() {}
|
||||
followUp() {}
|
||||
deferUpdate() {}
|
||||
update() {}
|
||||
}
|
||||
|
||||
InteractionResponses.applyToClass(MessageComponentInteraction);
|
||||
|
||||
module.exports = MessageComponentInteraction;
|
||||
178
src/structures/MessageComponentInteractionCollector.js
Normal file
178
src/structures/MessageComponentInteractionCollector.js
Normal file
@@ -0,0 +1,178 @@
|
||||
'use strict';
|
||||
|
||||
const Collector = require('./interfaces/Collector');
|
||||
const Collection = require('../util/Collection');
|
||||
const { Events } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* @typedef {CollectorOptions} MessageComponentInteractionCollectorOptions
|
||||
* @property {number} max The maximum total amount of interactions to collect
|
||||
* @property {number} maxComponents The maximum number of components to collect
|
||||
* @property {number} maxUsers The maximum number of users to interact
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collects interaction on message components.
|
||||
* Will automatically stop if the message (`'messageDelete'`),
|
||||
* channel (`'channelDelete'`), or guild (`'guildDelete'`) are deleted.
|
||||
* @extends {Collector}
|
||||
*/
|
||||
class MessageComponentInteractionCollector extends Collector {
|
||||
/**
|
||||
* @param {Message|TextChannel|DMChannel|NewsChannel} source
|
||||
* The source from which to collect message component interactions
|
||||
* @param {CollectorFilter} filter The filter to apply to this collector
|
||||
* @param {MessageComponentInteractionCollectorOptions} [options={}] The options to apply to this collector
|
||||
*/
|
||||
constructor(source, filter, options = {}) {
|
||||
super(source.client, filter, options);
|
||||
|
||||
/**
|
||||
* The message from which to collect message component interactions, if provided
|
||||
* @type {?Message}
|
||||
*/
|
||||
this.message = source instanceof require('./Message') ? source : null;
|
||||
|
||||
/**
|
||||
* The source channel from which to collect message component interactions
|
||||
* @type {TextChannel|DMChannel|NewsChannel}
|
||||
*/
|
||||
this.channel = this.message ? this.message.channel : source;
|
||||
|
||||
/**
|
||||
* The users which have interacted to buttons on this collector
|
||||
* @type {Collection}
|
||||
*/
|
||||
this.users = new Collection();
|
||||
|
||||
/**
|
||||
* The total number of interactions collected
|
||||
* @type {number}
|
||||
*/
|
||||
this.total = 0;
|
||||
|
||||
this.empty = this.empty.bind(this);
|
||||
this._handleChannelDeletion = this._handleChannelDeletion.bind(this);
|
||||
this._handleGuildDeletion = this._handleGuildDeletion.bind(this);
|
||||
this._handleMessageDeletion = this._handleMessageDeletion.bind(this);
|
||||
|
||||
this.client.incrementMaxListeners();
|
||||
this.client.on(Events.INTERACTION_CREATE, this.handleCollect);
|
||||
|
||||
if (this.message) this.client.on(Events.MESSAGE_DELETE, this._handleMessageDeletion);
|
||||
|
||||
this.client.on(Events.CHANNEL_DELETE, this._handleChannelDeletion);
|
||||
this.client.on(Events.GUILD_DELETE, this._handleGuildDeletion);
|
||||
|
||||
this.once('end', () => {
|
||||
this.client.removeListener(Events.INTERACTION_CREATE, this.handleCollect);
|
||||
|
||||
if (this.message) this.client.removeListener(Events.MESSAGE_DELETE, this._handleMessageDeletion);
|
||||
|
||||
this.client.removeListener(Events.CHANNEL_DELETE, this._handleChannelDeletion);
|
||||
this.client.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion);
|
||||
this.client.decrementMaxListeners();
|
||||
});
|
||||
|
||||
this.on('collect', interaction => {
|
||||
this.total++;
|
||||
this.users.set(interaction.user.id, interaction.user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an incoming interaction for possible collection.
|
||||
* @param {Interaction} interaction The interaction to possibly collect
|
||||
* @returns {?Snowflake|string}
|
||||
* @private
|
||||
*/
|
||||
collect(interaction) {
|
||||
/**
|
||||
* Emitted whenever a interaction is collected.
|
||||
* @event MessageComponentInteractionCollector#collect
|
||||
* @param {Interaction} interaction The interaction that was collected
|
||||
*/
|
||||
if (!interaction.isMessageComponent()) return null;
|
||||
|
||||
if (this.message) {
|
||||
return interaction.message.id === this.message.id ? interaction.id : null;
|
||||
}
|
||||
|
||||
return interaction.channel.id === this.channel.id ? interaction.id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an interaction for possible disposal.
|
||||
* @param {Interaction} interaction The interaction that could be disposed of
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
dispose(interaction) {
|
||||
/**
|
||||
* Emitted whenever an interaction is disposed of.
|
||||
* @event MessageComponentInteractionCollector#dispose
|
||||
* @param {Interaction} interaction The interaction that was disposed of
|
||||
*/
|
||||
if (!interaction.isMessageComponent()) return null;
|
||||
|
||||
if (this.message) {
|
||||
return interaction.message.id === this.message.id ? interaction.id : null;
|
||||
}
|
||||
|
||||
return interaction.channel.id === this.channel.id ? interaction.id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties this message component collector.
|
||||
*/
|
||||
empty() {
|
||||
this.total = 0;
|
||||
this.collected.clear();
|
||||
this.users.clear();
|
||||
this.checkEnd();
|
||||
}
|
||||
|
||||
get endReason() {
|
||||
if (this.options.max && this.total >= this.options.max) return 'limit';
|
||||
if (this.options.maxComponents && this.collected.size >= this.options.maxComponents) return 'componentLimit';
|
||||
if (this.options.maxUsers && this.users.size >= this.options.maxUsers) return 'userLimit';
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles checking if the message has been deleted, and if so, stops the collector with the reason 'messageDelete'.
|
||||
* @private
|
||||
* @param {Message} message The message that was deleted
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleMessageDeletion(message) {
|
||||
if (message.id === this.message?.id) {
|
||||
this.stop('messageDelete');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles checking if the channel has been deleted, and if so, stops the collector with the reason 'channelDelete'.
|
||||
* @private
|
||||
* @param {GuildChannel} channel The channel that was deleted
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleChannelDeletion(channel) {
|
||||
if (channel.id === this.channel.id) {
|
||||
this.stop('channelDelete');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'.
|
||||
* @private
|
||||
* @param {Guild} guild The guild that was deleted
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleGuildDeletion(guild) {
|
||||
if (guild.id === this.channel.guild?.id) {
|
||||
this.stop('guildDelete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageComponentInteractionCollector;
|
||||
@@ -157,6 +157,8 @@ class TextChannel extends GuildChannel {
|
||||
get typingCount() {}
|
||||
createMessageCollector() {}
|
||||
awaitMessages() {}
|
||||
createMessageComponentInteractionCollector() {}
|
||||
awaitMessageComponentInteractions() {}
|
||||
bulkDelete() {}
|
||||
}
|
||||
|
||||
|
||||
208
src/structures/interfaces/InteractionResponses.js
Normal file
208
src/structures/interfaces/InteractionResponses.js
Normal file
@@ -0,0 +1,208 @@
|
||||
'use strict';
|
||||
|
||||
const { InteractionResponseTypes } = require('../../util/Constants');
|
||||
const MessageFlags = require('../../util/MessageFlags');
|
||||
const APIMessage = require('../APIMessage');
|
||||
|
||||
/**
|
||||
* Interface for classes that support shared interaction response types.
|
||||
* @interface
|
||||
*/
|
||||
class InteractionResponses {
|
||||
/**
|
||||
* Options for deferring the reply to a {@link CommandInteraction}.
|
||||
* @typedef {InteractionDeferOptions}
|
||||
* @property {boolean} [ephemeral] Whether the reply should be ephemeral
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for a reply to an interaction.
|
||||
* @typedef {BaseMessageOptions} InteractionReplyOptions
|
||||
* @property {boolean} [ephemeral] Whether the reply should be ephemeral
|
||||
* @property {MessageEmbed[]|Object[]} [embeds] An array of embeds for the message
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defers the reply to this interaction.
|
||||
* @param {InteractionDeferOptions} [options] Options for deferring the reply to this interaction
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Defer the reply to this interaction
|
||||
* interaction.defer()
|
||||
* .then(console.log)
|
||||
* .catch(console.error)
|
||||
* @example
|
||||
* // Defer to send an ephemeral reply later
|
||||
* interaction.defer({ ephemeral: true })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async defer({ ephemeral } = {}) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: {
|
||||
flags: ephemeral ? MessageFlags.FLAGS.EPHEMERAL : undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
this.deferred = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reply to this interaction.
|
||||
* @param {string|APIMessage|MessageAdditions} content The content for the reply
|
||||
* @param {InteractionReplyOptions} [options] Additional options for the reply
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Reply to the interaction with an embed
|
||||
* const embed = new MessageEmbed().setDescription('Pong!');
|
||||
*
|
||||
* interaction.reply(embed)
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Create an ephemeral reply
|
||||
* interaction.reply('Pong!', { ephemeral: true })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async reply(content, options) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options);
|
||||
const { data, files } = await apiMessage.resolveData().resolveFiles();
|
||||
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data,
|
||||
},
|
||||
files,
|
||||
});
|
||||
this.replied = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the initial reply to this interaction.
|
||||
* @see Webhook#fetchMessage
|
||||
* @returns {Promise<Message|Object>}
|
||||
* @example
|
||||
* // Fetch the reply to this interaction
|
||||
* interaction.fetchReply()
|
||||
* .then(reply => console.log(`Replied with ${reply.content}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchReply() {
|
||||
const raw = await this.webhook.fetchMessage('@original');
|
||||
return this.channel?.messages.add(raw) ?? raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the initial reply to this interaction.
|
||||
* @see Webhook#editMessage
|
||||
* @param {string|APIMessage|MessageAdditions} content The new content for the message
|
||||
* @param {WebhookEditMessageOptions} [options] The options to provide
|
||||
* @returns {Promise<Message|Object>}
|
||||
* @example
|
||||
* // Edit the reply to this interaction
|
||||
* interaction.editReply('New content')
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async editReply(content, options) {
|
||||
const raw = await this.webhook.editMessage('@original', content, options);
|
||||
return this.channel?.messages.add(raw) ?? raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the initial reply to this interaction.
|
||||
* @see Webhook#deleteMessage
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Delete the reply to this interaction
|
||||
* interaction.deleteReply()
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async deleteReply() {
|
||||
await this.webhook.deleteMessage('@original');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a follow-up message to this interaction.
|
||||
* @param {string|APIMessage|MessageAdditions} content The content for the reply
|
||||
* @param {InteractionReplyOptions} [options] Additional options for the reply
|
||||
* @returns {Promise<Message|Object>}
|
||||
*/
|
||||
async followUp(content, options) {
|
||||
const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options);
|
||||
const { data, files } = await apiMessage.resolveData().resolveFiles();
|
||||
|
||||
const raw = await this.client.api.webhooks(this.applicationID, this.token).post({
|
||||
data,
|
||||
files,
|
||||
});
|
||||
|
||||
return this.channel?.messages.add(raw) ?? raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers an update to the message to which the button was attached
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Defer to update the button to a loading state
|
||||
* interaction.deferUpdate()
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async deferUpdate() {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.DEFERRED_MESSAGE_UPDATE,
|
||||
},
|
||||
});
|
||||
this.deferred = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the original message whose button was pressed
|
||||
* @param {string|APIMessage|MessageAdditions} content The content for the reply
|
||||
* @param {WebhookEditMessageOptions} [options] Additional options for the reply
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Remove the buttons from the message
|
||||
* interaction.update("A button was clicked", { components: [] })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async update(content, options) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
const apiMessage = content instanceof APIMessage ? content : APIMessage.create(this, content, options);
|
||||
const { data, files } = await apiMessage.resolveData().resolveFiles();
|
||||
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.UPDATE_MESSAGE,
|
||||
data,
|
||||
},
|
||||
files,
|
||||
});
|
||||
this.replied = true;
|
||||
}
|
||||
|
||||
static applyToClass(structure, ignore = []) {
|
||||
const props = ['defer', 'reply', 'fetchReply', 'editReply', 'deleteReply', 'followUp', 'deferUpdate', 'update'];
|
||||
for (const prop of props) {
|
||||
if (ignore.includes(prop)) continue;
|
||||
Object.defineProperty(
|
||||
structure.prototype,
|
||||
prop,
|
||||
Object.getOwnPropertyDescriptor(InteractionResponses.prototype, prop),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InteractionResponses;
|
||||
@@ -6,6 +6,7 @@ const APIMessage = require('../APIMessage');
|
||||
const SnowflakeUtil = require('../../util/SnowflakeUtil');
|
||||
const Collection = require('../../util/Collection');
|
||||
const { RangeError, TypeError } = require('../../errors');
|
||||
const MessageComponentInteractionCollector = require('../MessageComponentInteractionCollector');
|
||||
|
||||
/**
|
||||
* Interface for classes that have text-channel-like features.
|
||||
@@ -315,6 +316,45 @@ class TextBasedChannel {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a button interaction collector.
|
||||
* @param {CollectorFilter} filter The filter to apply
|
||||
* @param {MessageComponentInteractionCollectorOptions} [options={}] Options to send to the collector
|
||||
* @returns {MessageComponentInteractionCollector}
|
||||
* @example
|
||||
* // Create a button interaction collector
|
||||
* const filter = (interaction) => interaction.customID === 'button' && interaction.user.id === 'someID';
|
||||
* const collector = channel.createMessageComponentInteractionCollector(filter, { time: 15000 });
|
||||
* collector.on('collect', i => console.log(`Collected ${i.customID}`));
|
||||
* collector.on('end', collected => console.log(`Collected ${collected.size} items`));
|
||||
*/
|
||||
createMessageComponentInteractionCollector(filter, options = {}) {
|
||||
return new MessageComponentInteractionCollector(this, filter, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to createMessageComponentInteractionCollector but in promise form.
|
||||
* Resolves with a collection of interactions that pass the specified filter.
|
||||
* @param {CollectorFilter} filter The filter function to use
|
||||
* @param {AwaitMessageComponentInteractionsOptions} [options={}] Optional options to pass to the internal collector
|
||||
* @returns {Promise<Collection<string, MessageComponentInteraction>>}
|
||||
* @example
|
||||
* // Create a button interaction collector
|
||||
* const filter = (interaction) => interaction.customID === 'button' && interaction.user.id === 'someID';
|
||||
* channel.awaitMessageComponentInteractions(filter, { time: 15000 })
|
||||
* .then(collected => console.log(`Collected ${collected.size} interactions`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
awaitMessageComponentInteractions(filter, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const collector = this.createMessageComponentInteractionCollector(filter, options);
|
||||
collector.once('end', (interactions, reason) => {
|
||||
if (options.errors && options.errors.includes(reason)) reject(interactions);
|
||||
else resolve(interactions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk deletes given messages that are newer than two weeks.
|
||||
* @param {Collection<Snowflake, Message>|MessageResolvable[]|number} messages
|
||||
@@ -379,6 +419,8 @@ class TextBasedChannel {
|
||||
'typingCount',
|
||||
'createMessageCollector',
|
||||
'awaitMessages',
|
||||
'createMessageComponentInteractionCollector',
|
||||
'awaitMessageComponentInteractions',
|
||||
);
|
||||
}
|
||||
for (const prop of props) {
|
||||
|
||||
@@ -799,15 +799,18 @@ exports.ApplicationCommandPermissionTypes = createEnum([null, 'ROLE', 'USER']);
|
||||
* The type of an {@link Interaction} object:
|
||||
* * PING
|
||||
* * APPLICATION_COMMAND
|
||||
* * MESSAGE_COMPONENT
|
||||
* @typedef {string} InteractionType
|
||||
*/
|
||||
exports.InteractionTypes = createEnum([null, 'PING', 'APPLICATION_COMMAND']);
|
||||
exports.InteractionTypes = createEnum([null, 'PING', 'APPLICATION_COMMAND', 'MESSAGE_COMPONENT']);
|
||||
|
||||
/**
|
||||
* The type of an interaction response:
|
||||
* * PONG
|
||||
* * CHANNEL_MESSAGE_WITH_SOURCE
|
||||
* * DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
|
||||
* * DEFERRED_MESSAGE_UPDATE
|
||||
* * UPDATE_MESSAGE
|
||||
* @typedef {string} InteractionResponseType
|
||||
*/
|
||||
exports.InteractionResponseTypes = createEnum([
|
||||
@@ -817,8 +820,29 @@ exports.InteractionResponseTypes = createEnum([
|
||||
null,
|
||||
'CHANNEL_MESSAGE_WITH_SOURCE',
|
||||
'DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE',
|
||||
'DEFERRED_MESSAGE_UPDATE',
|
||||
'UPDATE_MESSAGE',
|
||||
]);
|
||||
|
||||
/**
|
||||
* The type of a message component
|
||||
* ACTION_ROW
|
||||
* BUTTON
|
||||
* @typedef {string} MessageComponentType
|
||||
*/
|
||||
exports.MessageComponentTypes = createEnum([null, 'ACTION_ROW', 'BUTTON']);
|
||||
|
||||
/**
|
||||
* The style of a message button
|
||||
* PRIMARY
|
||||
* SECONDARY
|
||||
* SUCCESS
|
||||
* DANGER
|
||||
* LINK
|
||||
* @typedef {string} MessageButtonStyle
|
||||
*/
|
||||
exports.MessageButtonStyles = createEnum([null, 'PRIMARY', 'SECONDARY', 'SUCCESS', 'DANGER', 'LINK']);
|
||||
|
||||
/**
|
||||
* NSFW level of a Guild
|
||||
* * DEFAULT
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
* * **`Role`**
|
||||
* * **`User`**
|
||||
* * **`CommandInteraction`**
|
||||
* * **`MessageComponentInteraction`**
|
||||
* @typedef {string} ExtendableStructure
|
||||
*/
|
||||
|
||||
@@ -111,6 +112,7 @@ const structures = {
|
||||
Role: require('../structures/Role'),
|
||||
User: require('../structures/User'),
|
||||
CommandInteraction: require('../structures/CommandInteraction'),
|
||||
MessageComponentInteraction: require('../structures/MessageComponentInteraction'),
|
||||
};
|
||||
|
||||
module.exports = Structures;
|
||||
|
||||
196
typings/index.d.ts
vendored
196
typings/index.d.ts
vendored
@@ -25,11 +25,14 @@ declare enum InteractionResponseTypes {
|
||||
PONG = 1,
|
||||
CHANNEL_MESSAGE_WITH_SOURCE = 4,
|
||||
DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5,
|
||||
DEFERRED_MESSAGE_UPDATE = 6,
|
||||
UPDATE_MESSAGE = 7,
|
||||
}
|
||||
|
||||
declare enum InteractionTypes {
|
||||
PING = 1,
|
||||
APPLICATION_COMMAND = 2,
|
||||
MESSAGE_COMPONENT = 3,
|
||||
}
|
||||
|
||||
declare enum InviteTargetType {
|
||||
@@ -37,6 +40,19 @@ declare enum InviteTargetType {
|
||||
EMBEDDED_APPLICATION = 2,
|
||||
}
|
||||
|
||||
declare enum MessageButtonStyles {
|
||||
PRIMARY = 1,
|
||||
SECONDARY = 2,
|
||||
SUCCESS = 3,
|
||||
DANGER = 4,
|
||||
LINK = 5,
|
||||
}
|
||||
|
||||
declare enum MessageComponentTypes {
|
||||
ACTION_ROW = 1,
|
||||
BUTTON = 2,
|
||||
}
|
||||
|
||||
declare enum NSFWLevels {
|
||||
DEFAULT = 0,
|
||||
EXPLICIT = 1,
|
||||
@@ -61,15 +77,16 @@ declare module 'discord.js' {
|
||||
import BaseCollection from '@discordjs/collection';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import {
|
||||
ApplicationCommandOptionType as ApplicationCommandOptionTypes,
|
||||
ApplicationCommandPermissionType as ApplicationCommandPermissionTypes,
|
||||
APIInteractionDataResolvedChannel as RawInteractionDataResolvedChannel,
|
||||
APIInteractionDataResolvedGuildMember as RawInteractionDataResolvedGuildMember,
|
||||
APIInteractionGuildMember as RawInteractionGuildMember,
|
||||
APIMessage as RawMessage,
|
||||
APIOverwrite as RawOverwrite,
|
||||
APIPartialEmoji as RawEmoji,
|
||||
APIRole as RawRole,
|
||||
Snowflake as APISnowflake,
|
||||
ApplicationCommandOptionType as ApplicationCommandOptionTypes,
|
||||
ApplicationCommandPermissionType as ApplicationCommandPermissionTypes,
|
||||
} from 'discord-api-types/v8';
|
||||
import { EventEmitter } from 'events';
|
||||
import { PathLike } from 'fs';
|
||||
@@ -269,6 +286,13 @@ declare module 'discord.js' {
|
||||
public setRTCRegion(region: string | null): Promise<this>;
|
||||
}
|
||||
|
||||
export class BaseMessageComponent {
|
||||
constructor(data?: BaseMessageComponent | BaseMessageComponentOptions);
|
||||
public type: MessageComponentType | null;
|
||||
private static create(data: MessageComponentOptions): MessageComponent;
|
||||
private static resolveType(type: MessageComponentTypeResolvable): MessageComponentType;
|
||||
}
|
||||
|
||||
class BroadcastDispatcher extends VolumeMixin(StreamDispatcher) {
|
||||
public broadcast: VoiceBroadcast;
|
||||
}
|
||||
@@ -689,6 +713,8 @@ declare module 'discord.js' {
|
||||
ApplicationCommandPermissionTypes: typeof ApplicationCommandPermissionTypes;
|
||||
InteractionTypes: typeof InteractionTypes;
|
||||
InteractionResponseTypes: typeof InteractionResponseTypes;
|
||||
MessageComponentTypes: typeof MessageComponentTypes;
|
||||
MessageButtonStyles: typeof MessageButtonStyles;
|
||||
NSFWLevels: typeof NSFWLevels;
|
||||
};
|
||||
|
||||
@@ -1110,6 +1136,7 @@ declare module 'discord.js' {
|
||||
public user: User;
|
||||
public version: number;
|
||||
public isCommand(): this is CommandInteraction;
|
||||
public isMessageComponent(): this is MessageComponentInteraction;
|
||||
}
|
||||
|
||||
export class Invite extends Base {
|
||||
@@ -1149,6 +1176,7 @@ declare module 'discord.js' {
|
||||
public author: User;
|
||||
public channel: TextChannel | DMChannel | NewsChannel;
|
||||
public readonly cleanContent: string;
|
||||
public components: MessageActionRow[];
|
||||
public content: string;
|
||||
public readonly createdAt: Date;
|
||||
public createdTimestamp: number;
|
||||
@@ -1177,6 +1205,10 @@ declare module 'discord.js' {
|
||||
public webhookID: Snowflake | null;
|
||||
public flags: Readonly<MessageFlags>;
|
||||
public reference: MessageReference | null;
|
||||
public awaitMessageComponentInteractions(
|
||||
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||
options?: AwaitMessageComponentInteractionsOptions,
|
||||
): Promise<Collection<Snowflake, MessageComponentInteraction>>;
|
||||
public awaitReactions(
|
||||
filter: CollectorFilter<[MessageReaction, User]>,
|
||||
options?: AwaitReactionsOptions,
|
||||
@@ -1185,6 +1217,10 @@ declare module 'discord.js' {
|
||||
filter: CollectorFilter<[MessageReaction, User]>,
|
||||
options?: ReactionCollectorOptions,
|
||||
): ReactionCollector;
|
||||
public createMessageComponentInteractionCollector(
|
||||
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||
options?: AwaitMessageComponentInteractionsOptions,
|
||||
): MessageComponentInteractionCollector;
|
||||
public delete(): Promise<Message>;
|
||||
public edit(
|
||||
content: string | null | MessageEditOptions | MessageEmbed | APIMessage | MessageAttachment | MessageAttachment[],
|
||||
@@ -1221,6 +1257,21 @@ declare module 'discord.js' {
|
||||
public unpin(): Promise<Message>;
|
||||
}
|
||||
|
||||
export class MessageActionRow extends BaseMessageComponent {
|
||||
constructor(data?: MessageActionRow | MessageActionRowOptions);
|
||||
public type: 'ACTION_ROW';
|
||||
public components: MessageActionRowComponent[];
|
||||
public addComponents(
|
||||
...components: MessageActionRowComponentResolvable[] | MessageActionRowComponentResolvable[][]
|
||||
): this;
|
||||
public spliceComponents(
|
||||
index: number,
|
||||
deleteCount: number,
|
||||
...components: MessageActionRowComponentResolvable[] | MessageActionRowComponentResolvable[][]
|
||||
): this;
|
||||
public toJSON(): unknown;
|
||||
}
|
||||
|
||||
export class MessageAttachment {
|
||||
constructor(attachment: BufferResolvable | Stream, name?: string, data?: unknown);
|
||||
|
||||
@@ -1239,6 +1290,25 @@ declare module 'discord.js' {
|
||||
public toJSON(): unknown;
|
||||
}
|
||||
|
||||
export class MessageButton extends BaseMessageComponent {
|
||||
constructor(data?: MessageButton | MessageButtonOptions);
|
||||
public customID: string | null;
|
||||
public disabled: boolean;
|
||||
public emoji: string | RawEmoji | null;
|
||||
public label: string | null;
|
||||
public style: MessageButtonStyle | 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(): unknown;
|
||||
private static resolveStyle(style: MessageButtonStyleResolvable): MessageButtonStyle;
|
||||
}
|
||||
|
||||
export class MessageCollector extends Collector<Snowflake, Message> {
|
||||
constructor(
|
||||
channel: TextChannel | DMChannel,
|
||||
@@ -1257,6 +1327,68 @@ declare module 'discord.js' {
|
||||
public dispose(message: Message): Snowflake;
|
||||
}
|
||||
|
||||
export class MessageComponentInteraction extends Interaction {
|
||||
public customID: string;
|
||||
public deferred: boolean;
|
||||
public message: Message | RawMessage;
|
||||
public replied: boolean;
|
||||
public webhook: WebhookClient;
|
||||
public defer(ephemeral?: boolean): Promise<void>;
|
||||
public deferUpdate(): Promise<void>;
|
||||
public deleteReply(): Promise<void>;
|
||||
public editReply(
|
||||
content: string | APIMessage | WebhookEditMessageOptions | MessageEmbed | MessageEmbed[],
|
||||
): Promise<Message | RawMessage>;
|
||||
public editReply(content: string, options?: WebhookEditMessageOptions): Promise<Message | RawMessage>;
|
||||
public fetchReply(): Promise<Message | RawMessage>;
|
||||
public followUp(
|
||||
content: string | APIMessage | InteractionReplyOptions | MessageAdditions,
|
||||
): Promise<Message | RawMessage>;
|
||||
public followUp(content: string, options?: InteractionReplyOptions): Promise<Message | RawMessage>;
|
||||
public reply(content: string | APIMessage | InteractionReplyOptions | MessageAdditions): Promise<void>;
|
||||
public reply(content: string, options?: InteractionReplyOptions): Promise<void>;
|
||||
public update(
|
||||
content: string | APIMessage | WebhookEditMessageOptions | MessageEmbed | MessageEmbed[],
|
||||
): Promise<Message | RawMessage>;
|
||||
public update(content: string, options?: WebhookEditMessageOptions): Promise<Message | RawMessage>;
|
||||
public static resolveType(type: MessageComponentTypeResolvable): MessageComponentType;
|
||||
}
|
||||
|
||||
export class MessageComponentInteractionCollector extends Collector<Snowflake, Interaction> {
|
||||
constructor(
|
||||
source: Message | TextChannel | NewsChannel | DMChannel,
|
||||
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||
options?: MessageComponentInteractionCollectorOptions,
|
||||
);
|
||||
private _handleMessageDeletion(message: Message): void;
|
||||
private _handleChannelDeletion(channel: GuildChannel): void;
|
||||
private _handleGuildDeletion(guild: Guild): void;
|
||||
|
||||
public channel: TextChannel | NewsChannel | DMChannel;
|
||||
public empty(): void;
|
||||
public readonly endReason: string | null;
|
||||
public message: Message | null;
|
||||
public options: MessageComponentInteractionCollectorOptions;
|
||||
public total: number;
|
||||
public users: Collection<Snowflake, User>;
|
||||
|
||||
public collect(interaction: Interaction): Snowflake;
|
||||
public dispose(interaction: Interaction): Snowflake;
|
||||
public on(event: 'collect' | 'dispose', listener: (interaction: Interaction) => Awaited<void>): this;
|
||||
public on(
|
||||
event: 'end',
|
||||
listener: (collected: Collection<Snowflake, Interaction>, reason: string) => Awaited<void>,
|
||||
): this;
|
||||
public on(event: string, listener: (...args: any[]) => Awaited<void>): this;
|
||||
|
||||
public once(event: 'collect' | 'dispose', listener: (interaction: Interaction) => Awaited<void>): this;
|
||||
public once(
|
||||
event: 'end',
|
||||
listener: (collected: Collection<Snowflake, Interaction>, reason: string) => Awaited<void>,
|
||||
): this;
|
||||
public once(event: string, listener: (...args: any[]) => Awaited<void>): this;
|
||||
}
|
||||
|
||||
export class MessageEmbed {
|
||||
constructor(data?: MessageEmbed | MessageEmbedOptions);
|
||||
public author: MessageEmbedAuthor | null;
|
||||
@@ -2338,6 +2470,10 @@ declare module 'discord.js' {
|
||||
readonly lastPinAt: Date | null;
|
||||
typing: boolean;
|
||||
typingCount: number;
|
||||
awaitMessageComponentInteractions(
|
||||
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||
options?: AwaitMessageComponentInteractionsOptions,
|
||||
): Promise<Collection<Snowflake, MessageComponentInteraction>>;
|
||||
awaitMessages(
|
||||
filter: CollectorFilter<[Message]>,
|
||||
options?: AwaitMessagesOptions,
|
||||
@@ -2346,6 +2482,10 @@ declare module 'discord.js' {
|
||||
messages: Collection<Snowflake, Message> | readonly MessageResolvable[] | number,
|
||||
filterOld?: boolean,
|
||||
): Promise<Collection<Snowflake, Message>>;
|
||||
createMessageComponentInteractionCollector(
|
||||
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||
options?: MessageComponentInteractionCollectorOptions,
|
||||
): MessageComponentInteractionCollector;
|
||||
createMessageCollector(filter: CollectorFilter<[Message]>, options?: MessageCollectorOptions): MessageCollector;
|
||||
startTyping(count?: number): Promise<void>;
|
||||
stopTyping(force?: boolean): void;
|
||||
@@ -2552,6 +2692,10 @@ declare module 'discord.js' {
|
||||
new?: any;
|
||||
}
|
||||
|
||||
interface AwaitMessageComponentInteractionsOptions extends MessageComponentInteractionCollectorOptions {
|
||||
errors?: string[];
|
||||
}
|
||||
|
||||
interface AwaitMessagesOptions extends MessageCollectorOptions {
|
||||
errors?: string[];
|
||||
}
|
||||
@@ -2569,6 +2713,10 @@ declare module 'discord.js' {
|
||||
|
||||
type Base64String = string;
|
||||
|
||||
interface BaseMessageComponentOptions {
|
||||
type?: MessageComponentType | MessageComponentTypes;
|
||||
}
|
||||
|
||||
type BitFieldResolvable<T extends string, N extends number | bigint> =
|
||||
| RecursiveReadonlyArray<T | N | Readonly<BitField<T, N>>>
|
||||
| T
|
||||
@@ -3201,16 +3349,53 @@ declare module 'discord.js' {
|
||||
|
||||
type MessageAdditions = MessageEmbed | MessageAttachment | (MessageEmbed | MessageAttachment)[];
|
||||
|
||||
type MessageActionRowComponent = MessageButton;
|
||||
|
||||
type MessageActionRowComponentOptions = MessageButtonOptions;
|
||||
|
||||
type MessageActionRowComponentResolvable = MessageActionRowComponent | MessageActionRowComponentOptions;
|
||||
|
||||
interface MessageActionRowOptions extends BaseMessageComponentOptions {
|
||||
components?: MessageActionRowComponentResolvable[];
|
||||
}
|
||||
|
||||
interface MessageActivity {
|
||||
partyID: string;
|
||||
type: number;
|
||||
}
|
||||
|
||||
interface MessageButtonOptions extends BaseMessageComponentOptions {
|
||||
customID?: string;
|
||||
disabled?: boolean;
|
||||
emoji?: RawEmoji;
|
||||
label?: string;
|
||||
style: MessageButtonStyleResolvable;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
type MessageButtonStyle = keyof typeof MessageButtonStyles;
|
||||
|
||||
type MessageButtonStyleResolvable = MessageButtonStyle | MessageButtonStyles;
|
||||
|
||||
interface MessageCollectorOptions extends CollectorOptions {
|
||||
max?: number;
|
||||
maxProcessed?: number;
|
||||
}
|
||||
|
||||
type MessageComponent = BaseMessageComponent | MessageActionRow | MessageButton;
|
||||
|
||||
interface MessageComponentInteractionCollectorOptions extends CollectorOptions {
|
||||
max?: number;
|
||||
maxComponents?: number;
|
||||
maxUsers?: number;
|
||||
}
|
||||
|
||||
type MessageComponentOptions = BaseMessageComponentOptions | MessageActionRowOptions | MessageButtonOptions;
|
||||
|
||||
type MessageComponentType = keyof typeof MessageComponentTypes;
|
||||
|
||||
type MessageComponentTypeResolvable = MessageComponentType | MessageComponentTypes;
|
||||
|
||||
interface MessageEditOptions {
|
||||
attachments?: MessageAttachment[];
|
||||
content?: string | null;
|
||||
@@ -3219,6 +3404,7 @@ declare module 'discord.js' {
|
||||
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
||||
flags?: BitFieldResolvable<MessageFlagsString, number>;
|
||||
allowedMentions?: MessageMentionOptions;
|
||||
components?: MessageActionRow[] | MessageActionRowOptions[];
|
||||
}
|
||||
|
||||
interface MessageEmbedAuthor {
|
||||
@@ -3311,6 +3497,7 @@ declare module 'discord.js' {
|
||||
nonce?: string | number;
|
||||
content?: string;
|
||||
embed?: MessageEmbed | MessageEmbedOptions;
|
||||
components?: MessageActionRow[] | MessageActionRowOptions[];
|
||||
allowedMentions?: MessageMentionOptions;
|
||||
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
||||
code?: string | boolean;
|
||||
@@ -3715,7 +3902,10 @@ declare module 'discord.js' {
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
type WebhookEditMessageOptions = Pick<WebhookMessageOptions, 'content' | 'embeds' | 'files' | 'allowedMentions'>;
|
||||
type WebhookEditMessageOptions = Pick<
|
||||
WebhookMessageOptions,
|
||||
'content' | 'embeds' | 'files' | 'allowedMentions' | 'components'
|
||||
>;
|
||||
|
||||
interface WebhookMessageOptions extends Omit<MessageOptions, 'embed' | 'reply'> {
|
||||
username?: string;
|
||||
|
||||
Reference in New Issue
Block a user