mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-19 04:53:30 +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;
|
let Structures;
|
||||||
|
|
||||||
module.exports = (client, { d: data }) => {
|
module.exports = (client, { d: data }) => {
|
||||||
if (data.type === InteractionTypes.APPLICATION_COMMAND) {
|
let interaction;
|
||||||
if (!Structures) Structures = require('../../../util/Structures');
|
switch (data.type) {
|
||||||
const CommandInteraction = Structures.get('CommandInteraction');
|
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');
|
||||||
|
|
||||||
/**
|
interaction = new MessageComponentInteraction(client, data);
|
||||||
* Emitted when an interaction is created.
|
break;
|
||||||
* @event Client#interaction
|
}
|
||||||
* @param {Interaction} interaction The interaction which was created
|
default:
|
||||||
*/
|
client.emit(Events.DEBUG, `[INTERACTION] Received interaction with unknown type: ${data.type}`);
|
||||||
client.emit(Events.INTERACTION_CREATE, interaction);
|
return;
|
||||||
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_DESCRIPTION: 'MessageEmbed description must be a string.',
|
||||||
EMBED_AUTHOR_NAME: 'MessageEmbed author name 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}`,
|
FILE_NOT_FOUND: file => `File could not be found: ${file}`,
|
||||||
|
|
||||||
USER_NO_DMCHANNEL: 'No DM Channel exists!',
|
USER_NO_DMCHANNEL: 'No DM Channel exists!',
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ module.exports = {
|
|||||||
BaseGuild: require('./structures/BaseGuild'),
|
BaseGuild: require('./structures/BaseGuild'),
|
||||||
BaseGuildEmoji: require('./structures/BaseGuildEmoji'),
|
BaseGuildEmoji: require('./structures/BaseGuildEmoji'),
|
||||||
BaseGuildVoiceChannel: require('./structures/BaseGuildVoiceChannel'),
|
BaseGuildVoiceChannel: require('./structures/BaseGuildVoiceChannel'),
|
||||||
|
BaseMessageComponent: require('./structures/BaseMessageComponent'),
|
||||||
CategoryChannel: require('./structures/CategoryChannel'),
|
CategoryChannel: require('./structures/CategoryChannel'),
|
||||||
Channel: require('./structures/Channel'),
|
Channel: require('./structures/Channel'),
|
||||||
ClientApplication: require('./structures/ClientApplication'),
|
ClientApplication: require('./structures/ClientApplication'),
|
||||||
@@ -92,8 +93,12 @@ module.exports = {
|
|||||||
Interaction: require('./structures/Interaction'),
|
Interaction: require('./structures/Interaction'),
|
||||||
Invite: require('./structures/Invite'),
|
Invite: require('./structures/Invite'),
|
||||||
Message: require('./structures/Message'),
|
Message: require('./structures/Message'),
|
||||||
|
MessageActionRow: require('./structures/MessageActionRow'),
|
||||||
MessageAttachment: require('./structures/MessageAttachment'),
|
MessageAttachment: require('./structures/MessageAttachment'),
|
||||||
|
MessageButton: require('./structures/MessageButton'),
|
||||||
MessageCollector: require('./structures/MessageCollector'),
|
MessageCollector: require('./structures/MessageCollector'),
|
||||||
|
MessageComponentInteraction: require('./structures/MessageComponentInteraction'),
|
||||||
|
MessageComponentInteractionCollector: require('./structures/MessageComponentInteractionCollector'),
|
||||||
MessageEmbed: require('./structures/MessageEmbed'),
|
MessageEmbed: require('./structures/MessageEmbed'),
|
||||||
MessageMentions: require('./structures/MessageMentions'),
|
MessageMentions: require('./structures/MessageMentions'),
|
||||||
MessageReaction: require('./structures/MessageReaction'),
|
MessageReaction: require('./structures/MessageReaction'),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||||
const MessageAttachment = require('./MessageAttachment');
|
const MessageAttachment = require('./MessageAttachment');
|
||||||
const MessageEmbed = require('./MessageEmbed');
|
const MessageEmbed = require('./MessageEmbed');
|
||||||
const { RangeError } = require('../errors');
|
const { RangeError } = require('../errors');
|
||||||
@@ -151,6 +152,8 @@ class APIMessage {
|
|||||||
}
|
}
|
||||||
const embeds = embedLikes.map(e => new MessageEmbed(e).toJSON());
|
const embeds = embedLikes.map(e => new MessageEmbed(e).toJSON());
|
||||||
|
|
||||||
|
const components = this.options.components?.map(c => BaseMessageComponent.create(c).toJSON());
|
||||||
|
|
||||||
let username;
|
let username;
|
||||||
let avatarURL;
|
let avatarURL;
|
||||||
if (isWebhook) {
|
if (isWebhook) {
|
||||||
@@ -196,6 +199,7 @@ class APIMessage {
|
|||||||
nonce,
|
nonce,
|
||||||
embed: !isWebhookLike ? (this.options.embed === null ? null : embeds[0]) : undefined,
|
embed: !isWebhookLike ? (this.options.embed === null ? null : embeds[0]) : undefined,
|
||||||
embeds: isWebhookLike ? embeds : undefined,
|
embeds: isWebhookLike ? embeds : undefined,
|
||||||
|
components,
|
||||||
username,
|
username,
|
||||||
avatar_url: avatarURL,
|
avatar_url: avatarURL,
|
||||||
allowed_mentions:
|
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';
|
'use strict';
|
||||||
|
|
||||||
const APIMessage = require('./APIMessage');
|
|
||||||
const Interaction = require('./Interaction');
|
const Interaction = require('./Interaction');
|
||||||
|
const InteractionResponses = require('./interfaces/InteractionResponses');
|
||||||
const WebhookClient = require('../client/WebhookClient');
|
const WebhookClient = require('../client/WebhookClient');
|
||||||
const { Error } = require('../errors');
|
|
||||||
const Collection = require('../util/Collection');
|
const Collection = require('../util/Collection');
|
||||||
const { ApplicationCommandOptionTypes, InteractionResponseTypes } = require('../util/Constants');
|
const { ApplicationCommandOptionTypes } = require('../util/Constants');
|
||||||
const MessageFlags = require('../util/MessageFlags');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a command interaction.
|
* Represents a command interaction.
|
||||||
* @extends {Interaction}
|
* @extends {Interaction}
|
||||||
|
* @implements {InteractionResponses}
|
||||||
*/
|
*/
|
||||||
class CommandInteraction extends Interaction {
|
class CommandInteraction extends Interaction {
|
||||||
constructor(client, data) {
|
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;
|
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.
|
* Represents an option of a received command interaction.
|
||||||
* @typedef {Object} CommandInteractionOption
|
* @typedef {Object} CommandInteractionOption
|
||||||
@@ -203,24 +82,6 @@ class CommandInteraction extends Interaction {
|
|||||||
* @property {Role|Object} [role] The resolved role
|
* @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.
|
* Transforms an option received from the API.
|
||||||
* @param {Object} option The received option
|
* @param {Object} option The received option
|
||||||
@@ -267,6 +128,17 @@ class CommandInteraction extends Interaction {
|
|||||||
}
|
}
|
||||||
return optionsCollection;
|
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;
|
module.exports = CommandInteraction;
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ class DMChannel extends Channel {
|
|||||||
get typingCount() {}
|
get typingCount() {}
|
||||||
createMessageCollector() {}
|
createMessageCollector() {}
|
||||||
awaitMessages() {}
|
awaitMessages() {}
|
||||||
|
createMessageComponentInteractionCollector() {}
|
||||||
|
awaitMessageComponentInteractions() {}
|
||||||
// Doesn't work on DM channels; bulkDelete() {}
|
// Doesn't work on DM channels; bulkDelete() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,14 @@ class Interaction extends Base {
|
|||||||
isCommand() {
|
isCommand() {
|
||||||
return InteractionTypes[this.type] === InteractionTypes.APPLICATION_COMMAND;
|
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;
|
module.exports = Interaction;
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
const APIMessage = require('./APIMessage');
|
const APIMessage = require('./APIMessage');
|
||||||
const Base = require('./Base');
|
const Base = require('./Base');
|
||||||
|
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||||
const ClientApplication = require('./ClientApplication');
|
const ClientApplication = require('./ClientApplication');
|
||||||
const MessageAttachment = require('./MessageAttachment');
|
const MessageAttachment = require('./MessageAttachment');
|
||||||
|
const MessageComponentInteractionCollector = require('./MessageComponentInteractionCollector');
|
||||||
const Embed = require('./MessageEmbed');
|
const Embed = require('./MessageEmbed');
|
||||||
const Mentions = require('./MessageMentions');
|
const Mentions = require('./MessageMentions');
|
||||||
const ReactionCollector = require('./ReactionCollector');
|
const ReactionCollector = require('./ReactionCollector');
|
||||||
@@ -123,6 +125,12 @@ class Message extends Base {
|
|||||||
*/
|
*/
|
||||||
this.embeds = (data.embeds || []).map(e => new Embed(e, true));
|
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
|
* A collection of attachments in the message - e.g. Pictures - mapped by their ID
|
||||||
* @type {Collection<Snowflake, MessageAttachment>}
|
* @type {Collection<Snowflake, MessageAttachment>}
|
||||||
@@ -282,6 +290,8 @@ class Message extends Base {
|
|||||||
if ('tts' in data) this.tts = data.tts;
|
if ('tts' in data) this.tts = data.tts;
|
||||||
if ('embeds' in data) this.embeds = data.embeds.map(e => new Embed(e, true));
|
if ('embeds' in data) this.embeds = data.embeds.map(e => new Embed(e, true));
|
||||||
else this.embeds = this.embeds.slice();
|
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) {
|
if ('attachments' in data) {
|
||||||
this.attachments = new Collection();
|
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
|
* Whether the message is editable by the client user
|
||||||
* @type {boolean}
|
* @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() {}
|
get typingCount() {}
|
||||||
createMessageCollector() {}
|
createMessageCollector() {}
|
||||||
awaitMessages() {}
|
awaitMessages() {}
|
||||||
|
createMessageComponentInteractionCollector() {}
|
||||||
|
awaitMessageComponentInteractions() {}
|
||||||
bulkDelete() {}
|
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 SnowflakeUtil = require('../../util/SnowflakeUtil');
|
||||||
const Collection = require('../../util/Collection');
|
const Collection = require('../../util/Collection');
|
||||||
const { RangeError, TypeError } = require('../../errors');
|
const { RangeError, TypeError } = require('../../errors');
|
||||||
|
const MessageComponentInteractionCollector = require('../MessageComponentInteractionCollector');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for classes that have text-channel-like features.
|
* 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.
|
* Bulk deletes given messages that are newer than two weeks.
|
||||||
* @param {Collection<Snowflake, Message>|MessageResolvable[]|number} messages
|
* @param {Collection<Snowflake, Message>|MessageResolvable[]|number} messages
|
||||||
@@ -379,6 +419,8 @@ class TextBasedChannel {
|
|||||||
'typingCount',
|
'typingCount',
|
||||||
'createMessageCollector',
|
'createMessageCollector',
|
||||||
'awaitMessages',
|
'awaitMessages',
|
||||||
|
'createMessageComponentInteractionCollector',
|
||||||
|
'awaitMessageComponentInteractions',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const prop of props) {
|
for (const prop of props) {
|
||||||
|
|||||||
@@ -799,15 +799,18 @@ exports.ApplicationCommandPermissionTypes = createEnum([null, 'ROLE', 'USER']);
|
|||||||
* The type of an {@link Interaction} object:
|
* The type of an {@link Interaction} object:
|
||||||
* * PING
|
* * PING
|
||||||
* * APPLICATION_COMMAND
|
* * APPLICATION_COMMAND
|
||||||
|
* * MESSAGE_COMPONENT
|
||||||
* @typedef {string} InteractionType
|
* @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:
|
* The type of an interaction response:
|
||||||
* * PONG
|
* * PONG
|
||||||
* * CHANNEL_MESSAGE_WITH_SOURCE
|
* * CHANNEL_MESSAGE_WITH_SOURCE
|
||||||
* * DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
|
* * DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
|
||||||
|
* * DEFERRED_MESSAGE_UPDATE
|
||||||
|
* * UPDATE_MESSAGE
|
||||||
* @typedef {string} InteractionResponseType
|
* @typedef {string} InteractionResponseType
|
||||||
*/
|
*/
|
||||||
exports.InteractionResponseTypes = createEnum([
|
exports.InteractionResponseTypes = createEnum([
|
||||||
@@ -817,8 +820,29 @@ exports.InteractionResponseTypes = createEnum([
|
|||||||
null,
|
null,
|
||||||
'CHANNEL_MESSAGE_WITH_SOURCE',
|
'CHANNEL_MESSAGE_WITH_SOURCE',
|
||||||
'DEFERRED_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
|
* NSFW level of a Guild
|
||||||
* * DEFAULT
|
* * DEFAULT
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
* * **`Role`**
|
* * **`Role`**
|
||||||
* * **`User`**
|
* * **`User`**
|
||||||
* * **`CommandInteraction`**
|
* * **`CommandInteraction`**
|
||||||
|
* * **`MessageComponentInteraction`**
|
||||||
* @typedef {string} ExtendableStructure
|
* @typedef {string} ExtendableStructure
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -111,6 +112,7 @@ const structures = {
|
|||||||
Role: require('../structures/Role'),
|
Role: require('../structures/Role'),
|
||||||
User: require('../structures/User'),
|
User: require('../structures/User'),
|
||||||
CommandInteraction: require('../structures/CommandInteraction'),
|
CommandInteraction: require('../structures/CommandInteraction'),
|
||||||
|
MessageComponentInteraction: require('../structures/MessageComponentInteraction'),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Structures;
|
module.exports = Structures;
|
||||||
|
|||||||
196
typings/index.d.ts
vendored
196
typings/index.d.ts
vendored
@@ -25,11 +25,14 @@ declare enum InteractionResponseTypes {
|
|||||||
PONG = 1,
|
PONG = 1,
|
||||||
CHANNEL_MESSAGE_WITH_SOURCE = 4,
|
CHANNEL_MESSAGE_WITH_SOURCE = 4,
|
||||||
DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5,
|
DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5,
|
||||||
|
DEFERRED_MESSAGE_UPDATE = 6,
|
||||||
|
UPDATE_MESSAGE = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
declare enum InteractionTypes {
|
declare enum InteractionTypes {
|
||||||
PING = 1,
|
PING = 1,
|
||||||
APPLICATION_COMMAND = 2,
|
APPLICATION_COMMAND = 2,
|
||||||
|
MESSAGE_COMPONENT = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
declare enum InviteTargetType {
|
declare enum InviteTargetType {
|
||||||
@@ -37,6 +40,19 @@ declare enum InviteTargetType {
|
|||||||
EMBEDDED_APPLICATION = 2,
|
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 {
|
declare enum NSFWLevels {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
EXPLICIT = 1,
|
EXPLICIT = 1,
|
||||||
@@ -61,15 +77,16 @@ declare module 'discord.js' {
|
|||||||
import BaseCollection from '@discordjs/collection';
|
import BaseCollection from '@discordjs/collection';
|
||||||
import { ChildProcess } from 'child_process';
|
import { ChildProcess } from 'child_process';
|
||||||
import {
|
import {
|
||||||
ApplicationCommandOptionType as ApplicationCommandOptionTypes,
|
|
||||||
ApplicationCommandPermissionType as ApplicationCommandPermissionTypes,
|
|
||||||
APIInteractionDataResolvedChannel as RawInteractionDataResolvedChannel,
|
APIInteractionDataResolvedChannel as RawInteractionDataResolvedChannel,
|
||||||
APIInteractionDataResolvedGuildMember as RawInteractionDataResolvedGuildMember,
|
APIInteractionDataResolvedGuildMember as RawInteractionDataResolvedGuildMember,
|
||||||
APIInteractionGuildMember as RawInteractionGuildMember,
|
APIInteractionGuildMember as RawInteractionGuildMember,
|
||||||
APIMessage as RawMessage,
|
APIMessage as RawMessage,
|
||||||
APIOverwrite as RawOverwrite,
|
APIOverwrite as RawOverwrite,
|
||||||
|
APIPartialEmoji as RawEmoji,
|
||||||
APIRole as RawRole,
|
APIRole as RawRole,
|
||||||
Snowflake as APISnowflake,
|
Snowflake as APISnowflake,
|
||||||
|
ApplicationCommandOptionType as ApplicationCommandOptionTypes,
|
||||||
|
ApplicationCommandPermissionType as ApplicationCommandPermissionTypes,
|
||||||
} from 'discord-api-types/v8';
|
} from 'discord-api-types/v8';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { PathLike } from 'fs';
|
import { PathLike } from 'fs';
|
||||||
@@ -269,6 +286,13 @@ declare module 'discord.js' {
|
|||||||
public setRTCRegion(region: string | null): Promise<this>;
|
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) {
|
class BroadcastDispatcher extends VolumeMixin(StreamDispatcher) {
|
||||||
public broadcast: VoiceBroadcast;
|
public broadcast: VoiceBroadcast;
|
||||||
}
|
}
|
||||||
@@ -689,6 +713,8 @@ declare module 'discord.js' {
|
|||||||
ApplicationCommandPermissionTypes: typeof ApplicationCommandPermissionTypes;
|
ApplicationCommandPermissionTypes: typeof ApplicationCommandPermissionTypes;
|
||||||
InteractionTypes: typeof InteractionTypes;
|
InteractionTypes: typeof InteractionTypes;
|
||||||
InteractionResponseTypes: typeof InteractionResponseTypes;
|
InteractionResponseTypes: typeof InteractionResponseTypes;
|
||||||
|
MessageComponentTypes: typeof MessageComponentTypes;
|
||||||
|
MessageButtonStyles: typeof MessageButtonStyles;
|
||||||
NSFWLevels: typeof NSFWLevels;
|
NSFWLevels: typeof NSFWLevels;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1110,6 +1136,7 @@ declare module 'discord.js' {
|
|||||||
public user: User;
|
public user: User;
|
||||||
public version: number;
|
public version: number;
|
||||||
public isCommand(): this is CommandInteraction;
|
public isCommand(): this is CommandInteraction;
|
||||||
|
public isMessageComponent(): this is MessageComponentInteraction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Invite extends Base {
|
export class Invite extends Base {
|
||||||
@@ -1149,6 +1176,7 @@ declare module 'discord.js' {
|
|||||||
public author: User;
|
public author: User;
|
||||||
public channel: TextChannel | DMChannel | NewsChannel;
|
public channel: TextChannel | DMChannel | NewsChannel;
|
||||||
public readonly cleanContent: string;
|
public readonly cleanContent: string;
|
||||||
|
public components: MessageActionRow[];
|
||||||
public content: string;
|
public content: string;
|
||||||
public readonly createdAt: Date;
|
public readonly createdAt: Date;
|
||||||
public createdTimestamp: number;
|
public createdTimestamp: number;
|
||||||
@@ -1177,6 +1205,10 @@ declare module 'discord.js' {
|
|||||||
public webhookID: Snowflake | null;
|
public webhookID: Snowflake | null;
|
||||||
public flags: Readonly<MessageFlags>;
|
public flags: Readonly<MessageFlags>;
|
||||||
public reference: MessageReference | null;
|
public reference: MessageReference | null;
|
||||||
|
public awaitMessageComponentInteractions(
|
||||||
|
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||||
|
options?: AwaitMessageComponentInteractionsOptions,
|
||||||
|
): Promise<Collection<Snowflake, MessageComponentInteraction>>;
|
||||||
public awaitReactions(
|
public awaitReactions(
|
||||||
filter: CollectorFilter<[MessageReaction, User]>,
|
filter: CollectorFilter<[MessageReaction, User]>,
|
||||||
options?: AwaitReactionsOptions,
|
options?: AwaitReactionsOptions,
|
||||||
@@ -1185,6 +1217,10 @@ declare module 'discord.js' {
|
|||||||
filter: CollectorFilter<[MessageReaction, User]>,
|
filter: CollectorFilter<[MessageReaction, User]>,
|
||||||
options?: ReactionCollectorOptions,
|
options?: ReactionCollectorOptions,
|
||||||
): ReactionCollector;
|
): ReactionCollector;
|
||||||
|
public createMessageComponentInteractionCollector(
|
||||||
|
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||||
|
options?: AwaitMessageComponentInteractionsOptions,
|
||||||
|
): MessageComponentInteractionCollector;
|
||||||
public delete(): Promise<Message>;
|
public delete(): Promise<Message>;
|
||||||
public edit(
|
public edit(
|
||||||
content: string | null | MessageEditOptions | MessageEmbed | APIMessage | MessageAttachment | MessageAttachment[],
|
content: string | null | MessageEditOptions | MessageEmbed | APIMessage | MessageAttachment | MessageAttachment[],
|
||||||
@@ -1221,6 +1257,21 @@ declare module 'discord.js' {
|
|||||||
public unpin(): Promise<Message>;
|
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 {
|
export class MessageAttachment {
|
||||||
constructor(attachment: BufferResolvable | Stream, name?: string, data?: unknown);
|
constructor(attachment: BufferResolvable | Stream, name?: string, data?: unknown);
|
||||||
|
|
||||||
@@ -1239,6 +1290,25 @@ declare module 'discord.js' {
|
|||||||
public toJSON(): unknown;
|
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> {
|
export class MessageCollector extends Collector<Snowflake, Message> {
|
||||||
constructor(
|
constructor(
|
||||||
channel: TextChannel | DMChannel,
|
channel: TextChannel | DMChannel,
|
||||||
@@ -1257,6 +1327,68 @@ declare module 'discord.js' {
|
|||||||
public dispose(message: Message): Snowflake;
|
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 {
|
export class MessageEmbed {
|
||||||
constructor(data?: MessageEmbed | MessageEmbedOptions);
|
constructor(data?: MessageEmbed | MessageEmbedOptions);
|
||||||
public author: MessageEmbedAuthor | null;
|
public author: MessageEmbedAuthor | null;
|
||||||
@@ -2338,6 +2470,10 @@ declare module 'discord.js' {
|
|||||||
readonly lastPinAt: Date | null;
|
readonly lastPinAt: Date | null;
|
||||||
typing: boolean;
|
typing: boolean;
|
||||||
typingCount: number;
|
typingCount: number;
|
||||||
|
awaitMessageComponentInteractions(
|
||||||
|
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||||
|
options?: AwaitMessageComponentInteractionsOptions,
|
||||||
|
): Promise<Collection<Snowflake, MessageComponentInteraction>>;
|
||||||
awaitMessages(
|
awaitMessages(
|
||||||
filter: CollectorFilter<[Message]>,
|
filter: CollectorFilter<[Message]>,
|
||||||
options?: AwaitMessagesOptions,
|
options?: AwaitMessagesOptions,
|
||||||
@@ -2346,6 +2482,10 @@ declare module 'discord.js' {
|
|||||||
messages: Collection<Snowflake, Message> | readonly MessageResolvable[] | number,
|
messages: Collection<Snowflake, Message> | readonly MessageResolvable[] | number,
|
||||||
filterOld?: boolean,
|
filterOld?: boolean,
|
||||||
): Promise<Collection<Snowflake, Message>>;
|
): Promise<Collection<Snowflake, Message>>;
|
||||||
|
createMessageComponentInteractionCollector(
|
||||||
|
filter: CollectorFilter<[MessageComponentInteraction]>,
|
||||||
|
options?: MessageComponentInteractionCollectorOptions,
|
||||||
|
): MessageComponentInteractionCollector;
|
||||||
createMessageCollector(filter: CollectorFilter<[Message]>, options?: MessageCollectorOptions): MessageCollector;
|
createMessageCollector(filter: CollectorFilter<[Message]>, options?: MessageCollectorOptions): MessageCollector;
|
||||||
startTyping(count?: number): Promise<void>;
|
startTyping(count?: number): Promise<void>;
|
||||||
stopTyping(force?: boolean): void;
|
stopTyping(force?: boolean): void;
|
||||||
@@ -2552,6 +2692,10 @@ declare module 'discord.js' {
|
|||||||
new?: any;
|
new?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AwaitMessageComponentInteractionsOptions extends MessageComponentInteractionCollectorOptions {
|
||||||
|
errors?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
interface AwaitMessagesOptions extends MessageCollectorOptions {
|
interface AwaitMessagesOptions extends MessageCollectorOptions {
|
||||||
errors?: string[];
|
errors?: string[];
|
||||||
}
|
}
|
||||||
@@ -2569,6 +2713,10 @@ declare module 'discord.js' {
|
|||||||
|
|
||||||
type Base64String = string;
|
type Base64String = string;
|
||||||
|
|
||||||
|
interface BaseMessageComponentOptions {
|
||||||
|
type?: MessageComponentType | MessageComponentTypes;
|
||||||
|
}
|
||||||
|
|
||||||
type BitFieldResolvable<T extends string, N extends number | bigint> =
|
type BitFieldResolvable<T extends string, N extends number | bigint> =
|
||||||
| RecursiveReadonlyArray<T | N | Readonly<BitField<T, N>>>
|
| RecursiveReadonlyArray<T | N | Readonly<BitField<T, N>>>
|
||||||
| T
|
| T
|
||||||
@@ -3201,16 +3349,53 @@ declare module 'discord.js' {
|
|||||||
|
|
||||||
type MessageAdditions = MessageEmbed | MessageAttachment | (MessageEmbed | MessageAttachment)[];
|
type MessageAdditions = MessageEmbed | MessageAttachment | (MessageEmbed | MessageAttachment)[];
|
||||||
|
|
||||||
|
type MessageActionRowComponent = MessageButton;
|
||||||
|
|
||||||
|
type MessageActionRowComponentOptions = MessageButtonOptions;
|
||||||
|
|
||||||
|
type MessageActionRowComponentResolvable = MessageActionRowComponent | MessageActionRowComponentOptions;
|
||||||
|
|
||||||
|
interface MessageActionRowOptions extends BaseMessageComponentOptions {
|
||||||
|
components?: MessageActionRowComponentResolvable[];
|
||||||
|
}
|
||||||
|
|
||||||
interface MessageActivity {
|
interface MessageActivity {
|
||||||
partyID: string;
|
partyID: string;
|
||||||
type: number;
|
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 {
|
interface MessageCollectorOptions extends CollectorOptions {
|
||||||
max?: number;
|
max?: number;
|
||||||
maxProcessed?: 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 {
|
interface MessageEditOptions {
|
||||||
attachments?: MessageAttachment[];
|
attachments?: MessageAttachment[];
|
||||||
content?: string | null;
|
content?: string | null;
|
||||||
@@ -3219,6 +3404,7 @@ declare module 'discord.js' {
|
|||||||
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
||||||
flags?: BitFieldResolvable<MessageFlagsString, number>;
|
flags?: BitFieldResolvable<MessageFlagsString, number>;
|
||||||
allowedMentions?: MessageMentionOptions;
|
allowedMentions?: MessageMentionOptions;
|
||||||
|
components?: MessageActionRow[] | MessageActionRowOptions[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MessageEmbedAuthor {
|
interface MessageEmbedAuthor {
|
||||||
@@ -3311,6 +3497,7 @@ declare module 'discord.js' {
|
|||||||
nonce?: string | number;
|
nonce?: string | number;
|
||||||
content?: string;
|
content?: string;
|
||||||
embed?: MessageEmbed | MessageEmbedOptions;
|
embed?: MessageEmbed | MessageEmbedOptions;
|
||||||
|
components?: MessageActionRow[] | MessageActionRowOptions[];
|
||||||
allowedMentions?: MessageMentionOptions;
|
allowedMentions?: MessageMentionOptions;
|
||||||
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
||||||
code?: string | boolean;
|
code?: string | boolean;
|
||||||
@@ -3715,7 +3902,10 @@ declare module 'discord.js' {
|
|||||||
reason?: string;
|
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'> {
|
interface WebhookMessageOptions extends Omit<MessageOptions, 'embed' | 'reply'> {
|
||||||
username?: string;
|
username?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user