discord.js-selfbot-v13/src/structures/Modal.js

224 lines
7.1 KiB
JavaScript
Raw Normal View History

2022-05-14 08:06:15 +00:00
'use strict';
const BaseMessageComponent = require('./BaseMessageComponent');
2022-06-13 16:53:43 +00:00
const User = require('./User');
const SnowflakeUtil = require('../util/SnowflakeUtil');
2022-05-14 08:06:15 +00:00
const Util = require('../util/Util');
/**
* Represents a modal (form) to be shown in response to an interaction
*/
class Modal {
/**
* @typedef {Object} ModalOptions
* @property {string} [customId] A unique string to be sent in the interaction when clicked
* @property {string} [title] The title to be displayed on this modal
* @property {MessageActionRow[]|MessageActionRowOptions[]} [components]
* Action rows containing interactive components for the modal (text input components)
*/
/**
* @param {Modal|ModalOptions} data Modal to clone or raw data
* @param {Client} client The client constructing this Modal, if provided
*/
constructor(data = {}, client = null) {
/**
* A list of MessageActionRows in the modal
* @type {MessageActionRow[]}
*/
this.components = data.components?.map(c => BaseMessageComponent.create(c, client)) ?? [];
/**
* A unique string to be sent in the interaction when submitted
* @type {?string}
*/
this.customId = data.custom_id ?? data.customId ?? null;
/**
* The title to be displayed on this modal
* @type {?string}
*/
this.title = data.title ?? null;
2022-06-13 16:53:43 +00:00
/**
* Timestamp (Discord epoch) of when this modal was created
* @type {?Snowflake}
*/
this.nonce = data.nonce ?? null;
/**
2022-09-28 15:03:20 +00:00
* ID slash / button / menu when modal is displayed
2022-06-13 16:53:43 +00:00
* @type {?Snowflake}
*/
this.id = data.id ?? null;
/**
* Application sending the modal
* @type {?Object}
*/
this.application = data.application
? {
...data.application,
bot: data.application.bot ? new User(client, data.application.bot) : null,
}
: null;
this.client = client;
2022-05-14 08:06:15 +00:00
}
/**
* Adds components to the modal.
* @param {...MessageActionRowResolvable[]} components The components to add
* @returns {Modal}
*/
addComponents(...components) {
this.components.push(...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
return this;
}
/**
* Sets the components of the modal.
* @param {...MessageActionRowResolvable[]} components The components to set
* @returns {Modal}
*/
setComponents(...components) {
this.spliceComponents(0, this.components.length, components);
return this;
}
/**
* Sets the custom id for this modal
* @param {string} customId A unique string to be sent in the interaction when submitted
* @returns {Modal}
*/
setCustomId(customId) {
this.customId = Util.verifyString(customId, RangeError, 'MODAL_CUSTOM_ID');
return this;
}
/**
* Removes, replaces, and inserts components in the modal.
* @param {number} index The index to start at
* @param {number} deleteCount The number of components to remove
* @param {...MessageActionRowResolvable[]} [components] The replacing components
* @returns {Modal}
*/
spliceComponents(index, deleteCount, ...components) {
this.components.splice(index, deleteCount, ...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
return this;
}
/**
* Sets the title of this modal
* @param {string} title The title to be displayed on this modal
* @returns {Modal}
*/
setTitle(title) {
this.title = Util.verifyString(title, RangeError, 'MODAL_TITLE');
return this;
}
toJSON() {
return {
components: this.components.map(c => c.toJSON()),
custom_id: this.customId,
title: this.title,
2022-06-13 16:53:43 +00:00
id: this.id,
};
}
/**
* @typedef {Object} ModalReplyData
* @property {string} [customId] TextInputComponent custom id
* @property {string} [value] TextInputComponent value
*/
/**
* Reply to this modal with data. (Event only)
2022-09-28 15:03:20 +00:00
* @param {?Snowflake} guildId GuildID of the guild to send the modal to
2022-06-13 16:53:43 +00:00
* @param {Snowflake} channelId ChannelID of the channel to send the modal to
* @param {...ModalReplyData} data Data to send with the modal
2022-06-15 16:07:24 +00:00
* @returns {Promise<Snowflake>} Nonce (Discord Timestamp) when command was sent
2022-06-13 17:03:51 +00:00
* @example
* // With Event
* client.on('interactionModalCreate', modal => {
2022-06-13 17:07:36 +00:00
* modal.reply('guildId', 'channelId', {
* customId: 'code',
* value: '1+1'
* }, {
2022-06-13 17:06:11 +00:00
* customId: 'message',
2022-06-13 17:07:36 +00:00
* value: 'hello'
2022-06-13 17:03:51 +00:00
* })
* })
2022-06-13 16:53:43 +00:00
*/
async reply(guildId, channelId, ...data) {
if (!this.application) throw new Error('Modal cannot reply (Missing Application)');
2022-09-28 15:03:20 +00:00
let guild, channel;
if (guildId) {
guild = this.client.guilds.cache.get(guildId);
if (!guild) throw new Error('GUILD_NOT_FOUND', `Guild ${guildId} not found`);
}
channel = guild ? guild.channels.cache.get(channelId) : this.client.channels.cache.get(channelId);
if (!channel) {
throw new Error('CHANNEL_NOT_FOUND', `Channel ${channelId} ${guildId ? `in guild ${guildId}` : ''} not found`);
}
2022-06-13 16:53:43 +00:00
// Add data to components
// this.components = [ MessageActionRow.components = [ TextInputComponent ] ]
// 5 MessageActionRow / Modal, 1 TextInputComponent / 1 MessageActionRow
for (let i = 0; i < this.components.length; i++) {
const value = data.find(d => d.customId == this.components[i].components[0].customId);
if (this.components[i].components[0].required == true) {
if (!value) {
throw new Error(
'MODAL_REQUIRED_FIELD_MISSING\n' +
`Required fieldId ${this.components[i].components[0].customId} missing value`,
);
}
if (!(typeof value?.value == 'string')) {
throw new Error(
'MODAL_REPLY_DATA_INVALID\n' +
`Data (Required) must be strings, got ${typeof value.value} [Custom ID: ${value.customId}]`,
);
}
}
if (value) {
if (!(typeof value?.value == 'string')) {
console.warn(
'Warning: MODAL_REPLY_DATA_INVALID',
`Data (Not required) must be strings, got ${typeof value.value} [Custom ID: ${value.customId}]`,
);
continue;
}
this.components[i].components[0].value = value.value;
}
delete this.components[i].components[0].maxLength;
delete this.components[i].components[0].minLength;
delete this.components[i].components[0].required;
delete this.components[i].components[0].placeholder;
delete this.components[i].components[0].label;
delete this.components[i].components[0].style;
}
// Filter
this.components = this.components.filter(c => c.components[0].value && c.components[0].value !== '');
// Get Object
const dataFinal = this.toJSON();
delete dataFinal.title;
2022-06-15 16:07:24 +00:00
const nonce = SnowflakeUtil.generate();
2022-06-13 16:53:43 +00:00
const postData = {
2022-06-15 11:52:06 +00:00
type: 5, // Modal
2022-06-13 16:53:43 +00:00
application_id: this.application.id,
2022-09-28 15:03:20 +00:00
guild_id: guildId || null,
2022-06-13 16:53:43 +00:00
channel_id: channelId,
data: dataFinal,
2022-06-15 16:07:24 +00:00
nonce,
2022-06-13 16:53:43 +00:00
session_id: this.client.session_id,
2022-05-14 08:06:15 +00:00
};
2022-06-13 16:53:43 +00:00
await this.client.api.interactions.post({
data: postData,
});
2022-06-15 16:07:24 +00:00
return nonce;
2022-05-14 08:06:15 +00:00
}
}
module.exports = Modal;