2022-04-16 10:44:43 +00:00
|
|
|
'use strict';
|
|
|
|
|
2022-07-12 12:30:18 +00:00
|
|
|
const { setTimeout } = require('node:timers');
|
2022-04-16 10:44:43 +00:00
|
|
|
const BaseMessageComponent = require('./BaseMessageComponent');
|
|
|
|
const { RangeError } = require('../errors');
|
2022-11-05 13:02:27 +00:00
|
|
|
const { MessageButtonStyles, MessageComponentTypes, InteractionTypes } = require('../util/Constants');
|
2022-06-08 05:35:19 +00:00
|
|
|
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
2022-04-16 10:44:43 +00:00
|
|
|
const Util = require('../util/Util');
|
2022-09-28 12:40:46 +00:00
|
|
|
const { lazy } = require('../util/Util');
|
|
|
|
const Message = lazy(() => require('../structures/Message').Message);
|
2022-04-16 10:44:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a button message component.
|
|
|
|
* @extends {BaseMessageComponent}
|
|
|
|
*/
|
|
|
|
class MessageButton extends BaseMessageComponent {
|
|
|
|
/**
|
|
|
|
* @typedef {BaseMessageComponentOptions} MessageButtonOptions
|
|
|
|
* @property {string} [label] The text to be displayed on this button
|
|
|
|
* @property {string} [customId] A unique string to be sent in the interaction when clicked
|
|
|
|
* @property {MessageButtonStyleResolvable} [style] The style of this button
|
|
|
|
* @property {EmojiIdentifierResolvable} [emoji] The emoji to be displayed to the left of the text
|
|
|
|
* @property {string} [url] Optional URL for link-style buttons
|
|
|
|
* @property {boolean} [disabled=false] Disables the button to prevent interactions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {MessageButton|MessageButtonOptions} [data={}] MessageButton to clone or raw data
|
|
|
|
*/
|
|
|
|
constructor(data = {}) {
|
|
|
|
super({ type: 'BUTTON' });
|
|
|
|
|
|
|
|
this.setup(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
setup(data) {
|
|
|
|
/**
|
|
|
|
* The text to be displayed on this button
|
|
|
|
* @type {?string}
|
|
|
|
*/
|
|
|
|
this.label = data.label ?? null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A unique string to be sent in the interaction when clicked
|
|
|
|
* @type {?string}
|
|
|
|
*/
|
|
|
|
this.customId = data.custom_id ?? data.customId ?? null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The style of this button
|
|
|
|
* @type {?MessageButtonStyle}
|
|
|
|
*/
|
|
|
|
this.style = data.style ? MessageButton.resolveStyle(data.style) : null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Emoji for this button
|
|
|
|
* @type {?RawEmoji}
|
|
|
|
*/
|
|
|
|
this.emoji = data.emoji ? Util.resolvePartialEmoji(data.emoji) : null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The URL this button links to, if it is a Link style button
|
|
|
|
* @type {?string}
|
|
|
|
*/
|
|
|
|
this.url = data.url ?? null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether this button is currently disabled
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
this.disabled = data.disabled ?? false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the custom id for this button
|
|
|
|
* @param {string} customId A unique string to be sent in the interaction when clicked
|
|
|
|
* @returns {MessageButton}
|
|
|
|
*/
|
|
|
|
setCustomId(customId) {
|
|
|
|
this.customId = Util.verifyString(customId, RangeError, 'BUTTON_CUSTOM_ID');
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the interactive status of the button
|
|
|
|
* @param {boolean} [disabled=true] Whether this button should be disabled
|
|
|
|
* @returns {MessageButton}
|
|
|
|
*/
|
|
|
|
setDisabled(disabled = true) {
|
|
|
|
this.disabled = disabled;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the emoji of this button
|
|
|
|
* @param {EmojiIdentifierResolvable} emoji The emoji to be displayed on this button
|
|
|
|
* @returns {MessageButton}
|
|
|
|
*/
|
|
|
|
setEmoji(emoji) {
|
|
|
|
this.emoji = Util.resolvePartialEmoji(emoji);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the label of this button
|
|
|
|
* @param {string} label The text to be displayed on this button
|
|
|
|
* @returns {MessageButton}
|
|
|
|
*/
|
|
|
|
setLabel(label) {
|
|
|
|
this.label = Util.verifyString(label, RangeError, 'BUTTON_LABEL');
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the style of this button
|
|
|
|
* @param {MessageButtonStyleResolvable} style The style of this button
|
|
|
|
* @returns {MessageButton}
|
|
|
|
*/
|
|
|
|
setStyle(style) {
|
|
|
|
this.style = MessageButton.resolveStyle(style);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the URL of this button.
|
|
|
|
* <info>MessageButton#style must be LINK when setting a URL</info>
|
|
|
|
* @param {string} url The URL of this button
|
|
|
|
* @returns {MessageButton}
|
|
|
|
*/
|
|
|
|
setURL(url) {
|
|
|
|
this.url = Util.verifyString(url, RangeError, 'BUTTON_URL');
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transforms the button to a plain object.
|
|
|
|
* @returns {APIMessageButton} The raw data of this button
|
|
|
|
*/
|
|
|
|
toJSON() {
|
|
|
|
return {
|
|
|
|
custom_id: this.customId,
|
|
|
|
disabled: this.disabled,
|
|
|
|
emoji: this.emoji,
|
|
|
|
label: this.label,
|
|
|
|
style: MessageButtonStyles[this.style],
|
|
|
|
type: MessageComponentTypes[this.type],
|
|
|
|
url: this.url,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data that can be resolved to a MessageButtonStyle. This can be
|
|
|
|
* * MessageButtonStyle
|
|
|
|
* * number
|
|
|
|
* @typedef {number|MessageButtonStyle} MessageButtonStyleResolvable
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolves the style of a button
|
|
|
|
* @param {MessageButtonStyleResolvable} style The style to resolve
|
|
|
|
* @returns {MessageButtonStyle}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
static resolveStyle(style) {
|
|
|
|
return typeof style === 'string' ? style : MessageButtonStyles[style];
|
|
|
|
}
|
|
|
|
// Patch Click
|
|
|
|
/**
|
|
|
|
* Click the button
|
|
|
|
* @param {Message} message Discord Message
|
2022-10-09 12:37:04 +00:00
|
|
|
* @returns {Promise<InteractionResponse>}
|
2022-04-16 10:44:43 +00:00
|
|
|
*/
|
|
|
|
async click(message) {
|
2022-07-07 09:10:51 +00:00
|
|
|
const nonce = SnowflakeUtil.generate();
|
2022-09-28 12:40:46 +00:00
|
|
|
if (!(message instanceof Message())) throw new Error('[UNKNOWN_MESSAGE] Please pass a valid Message');
|
2022-04-16 10:44:43 +00:00
|
|
|
if (!this.customId || this.style == 5 || this.disabled) return false; // Button URL, Disabled
|
2022-10-09 12:37:04 +00:00
|
|
|
const data = {
|
2022-11-05 13:02:27 +00:00
|
|
|
type: InteractionTypes.MESSAGE_COMPONENT,
|
2022-10-09 12:37:04 +00:00
|
|
|
nonce,
|
2022-11-05 13:02:27 +00:00
|
|
|
guild_id: message.guild?.id ?? null,
|
2022-10-09 12:37:04 +00:00
|
|
|
channel_id: message.channel.id,
|
|
|
|
message_id: message.id,
|
|
|
|
application_id: message.applicationId ?? message.author.id,
|
|
|
|
session_id: message.client.session_id,
|
|
|
|
message_flags: message.flags.bitfield,
|
2022-04-16 10:44:43 +00:00
|
|
|
data: {
|
2022-11-05 13:02:27 +00:00
|
|
|
component_type: MessageComponentTypes.BUTTON,
|
2022-10-09 12:37:04 +00:00
|
|
|
custom_id: this.customId,
|
2022-04-16 10:44:43 +00:00
|
|
|
},
|
2022-10-09 12:37:04 +00:00
|
|
|
};
|
|
|
|
await message.client.api.interactions.post({
|
|
|
|
data,
|
|
|
|
});
|
|
|
|
message.client._interactionCache.set(nonce, {
|
|
|
|
channelId: message.channelId,
|
|
|
|
guildId: message.guildId,
|
|
|
|
metadata: data,
|
2022-04-16 10:44:43 +00:00
|
|
|
});
|
2022-07-12 12:30:18 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const handler = data => {
|
|
|
|
timeout.refresh();
|
|
|
|
if (data.metadata.nonce !== nonce) return;
|
|
|
|
clearTimeout(timeout);
|
2022-07-12 14:29:59 +00:00
|
|
|
message.client.removeListener('interactionResponse', handler);
|
|
|
|
message.client.decrementMaxListeners();
|
2022-10-30 12:25:32 +00:00
|
|
|
if (data.status) {
|
|
|
|
resolve(data.metadata);
|
|
|
|
} else {
|
|
|
|
reject(
|
|
|
|
new Error('INTERACTION_ERROR', {
|
|
|
|
cause: data,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
2022-07-12 12:30:18 +00:00
|
|
|
};
|
|
|
|
const timeout = setTimeout(() => {
|
2022-07-12 14:29:59 +00:00
|
|
|
message.client.removeListener('interactionResponse', handler);
|
|
|
|
message.client.decrementMaxListeners();
|
2022-07-12 12:30:18 +00:00
|
|
|
reject(new Error('INTERACTION_TIMEOUT'));
|
2022-11-19 10:59:25 +00:00
|
|
|
}, message.client.options.interactionTimeout).unref();
|
2022-07-12 14:29:59 +00:00
|
|
|
message.client.incrementMaxListeners();
|
|
|
|
message.client.on('interactionResponse', handler);
|
2022-07-12 12:30:18 +00:00
|
|
|
});
|
2022-04-16 10:44:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = MessageButton;
|