2022-05-14 08:06:15 +00:00
|
|
|
'use strict';
|
|
|
|
|
2022-12-12 12:28:13 +00:00
|
|
|
const { setTimeout } = require('node:timers');
|
2022-05-14 08:06:15 +00:00
|
|
|
const BaseMessageComponent = require('./BaseMessageComponent');
|
2024-01-08 13:41:56 +00:00
|
|
|
const { InteractionTypes, Events } = require('../util/Constants');
|
2022-06-13 16:53:43 +00:00
|
|
|
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
2022-05-14 08:06:15 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a modal (form) to be shown in response to an interaction
|
|
|
|
*/
|
|
|
|
class Modal {
|
|
|
|
/**
|
2024-01-08 13:41:56 +00:00
|
|
|
* @param {Object} data Modal to clone or raw data
|
2022-05-14 08:06:15 +00:00
|
|
|
* @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}
|
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
this.customId = data.custom_id;
|
2022-05-14 08:06:15 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The title to be displayed on this modal
|
|
|
|
* @type {?string}
|
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
this.title = data.title;
|
2022-06-13 16:53:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp (Discord epoch) of when this modal was created
|
2024-01-08 13:41:56 +00:00
|
|
|
* @type {Snowflake}
|
2022-06-13 16:53:43 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
this.nonce = data.nonce;
|
2022-06-13 16:53:43 +00:00
|
|
|
|
|
|
|
/**
|
2022-09-28 15:03:20 +00:00
|
|
|
* ID slash / button / menu when modal is displayed
|
2024-01-08 13:41:56 +00:00
|
|
|
* @type {Snowflake}
|
2022-06-13 16:53:43 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
this.id = data.id;
|
2022-06-13 16:53:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Application sending the modal
|
2024-01-08 13:41:56 +00:00
|
|
|
* @type {Snowflake}
|
2022-06-13 16:53:43 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
this.applicationId = data.application.id;
|
2022-05-14 08:06:15 +00:00
|
|
|
|
2024-01-08 13:41:56 +00:00
|
|
|
/**
|
|
|
|
* The id of the channel the message was sent in
|
|
|
|
* @type {Snowflake}
|
|
|
|
*/
|
|
|
|
this.channelId = data.channel_id;
|
2022-05-14 08:06:15 +00:00
|
|
|
|
2024-01-08 13:41:56 +00:00
|
|
|
Object.defineProperty(this, 'client', {
|
|
|
|
value: client,
|
|
|
|
writable: false,
|
|
|
|
});
|
2022-05-14 08:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-01-08 13:41:56 +00:00
|
|
|
* The id of the guild the message was sent in, if any
|
|
|
|
* @type {?Snowflake}
|
|
|
|
* @readonly
|
2022-05-14 08:06:15 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
get guildId() {
|
|
|
|
return this.client.channels.cache.get(this.channelId)?.guildId || null;
|
2022-05-14 08:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-01-08 13:41:56 +00:00
|
|
|
* The channel that the message was sent in
|
|
|
|
* @type {TextBasedChannels}
|
|
|
|
* @readonly
|
2022-05-14 08:06:15 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
get channel() {
|
|
|
|
return this.client.channels.resolve(this.channelId);
|
2022-05-14 08:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-01-08 13:41:56 +00:00
|
|
|
* The guild the message was sent in (if in a guild channel)
|
|
|
|
* @type {?Guild}
|
|
|
|
* @readonly
|
2022-05-14 08:06:15 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
get guild() {
|
|
|
|
return this.client.guilds.resolve(this.guildId) ?? this.channel?.guild ?? null;
|
2022-05-14 08:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reply to this modal with data. (Event only)
|
2024-01-08 13:41:56 +00:00
|
|
|
* @returns {Promise<Message|Modal>}
|
2022-06-13 17:03:51 +00:00
|
|
|
* @example
|
|
|
|
* client.on('interactionModalCreate', modal => {
|
2024-01-08 13:41:56 +00:00
|
|
|
* // Modal > ActionRow > TextInput
|
|
|
|
* modal.components[0].components[0].setValue('1+1');
|
|
|
|
* modal.components[1].components[0].setValue('hello');
|
|
|
|
* modal.reply();
|
2022-06-13 17:03:51 +00:00
|
|
|
* })
|
2022-06-13 16:53:43 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
reply() {
|
|
|
|
if (!this.applicationId || !this.client || !this.channelId) throw new Error('Modal cannot reply');
|
2022-06-13 16:53:43 +00:00
|
|
|
// Get Object
|
|
|
|
const dataFinal = this.toJSON();
|
2022-12-08 08:14:17 +00:00
|
|
|
dataFinal.components = dataFinal.components
|
|
|
|
.map(c => {
|
2024-01-08 13:41:56 +00:00
|
|
|
c.components[0] = {
|
|
|
|
type: c.components[0].type,
|
|
|
|
value: c.components[0].value,
|
|
|
|
custom_id: c.components[0].custom_id,
|
|
|
|
};
|
2022-12-08 08:14:17 +00:00
|
|
|
return c;
|
|
|
|
})
|
|
|
|
.filter(c => c.components[0].value && c.components[0].value !== '');
|
2022-06-13 16:53:43 +00:00
|
|
|
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 = {
|
2024-01-08 13:41:56 +00:00
|
|
|
type: InteractionTypes.MODAL_SUBMIT, // Modal
|
|
|
|
application_id: this.applicationId,
|
|
|
|
guild_id: this.guildId,
|
|
|
|
channel_id: this.channelId,
|
2022-06-13 16:53:43 +00:00
|
|
|
data: dataFinal,
|
2022-06-15 16:07:24 +00:00
|
|
|
nonce,
|
2024-01-08 13:41:56 +00:00
|
|
|
session_id: this.client.ws.shards.first()?.sessionId,
|
2022-05-14 08:06:15 +00:00
|
|
|
};
|
2024-01-08 13:41:56 +00:00
|
|
|
this.client.api.interactions.post({
|
2022-06-13 16:53:43 +00:00
|
|
|
data: postData,
|
|
|
|
});
|
2022-12-12 12:28:13 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
2024-01-09 13:15:49 +00:00
|
|
|
const timeoutMs = 5_000;
|
2024-01-08 13:41:56 +00:00
|
|
|
// Waiting for MsgCreate / ModalCreate
|
2022-12-12 12:28:13 +00:00
|
|
|
const handler = data => {
|
2024-01-08 13:41:56 +00:00
|
|
|
if (data.nonce !== nonce) return;
|
2022-12-12 12:28:13 +00:00
|
|
|
clearTimeout(timeout);
|
2024-01-08 13:41:56 +00:00
|
|
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
|
|
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
2022-12-12 12:28:13 +00:00
|
|
|
this.client.decrementMaxListeners();
|
2024-01-08 13:41:56 +00:00
|
|
|
resolve(data);
|
2022-12-12 12:28:13 +00:00
|
|
|
};
|
|
|
|
const timeout = setTimeout(() => {
|
2024-01-08 13:41:56 +00:00
|
|
|
this.client.removeListener(Events.MESSAGE_CREATE, handler);
|
|
|
|
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
|
2022-12-12 12:28:13 +00:00
|
|
|
this.client.decrementMaxListeners();
|
2024-01-08 13:41:56 +00:00
|
|
|
reject(new Error('INTERACTION_FAILED'));
|
|
|
|
}, timeoutMs).unref();
|
2022-12-12 12:28:13 +00:00
|
|
|
this.client.incrementMaxListeners();
|
2024-01-08 13:41:56 +00:00
|
|
|
this.client.on(Events.MESSAGE_CREATE, handler);
|
|
|
|
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
|
2022-12-12 12:28:13 +00:00
|
|
|
});
|
2022-05-14 08:06:15 +00:00
|
|
|
}
|
2024-01-08 13:41:56 +00:00
|
|
|
|
|
|
|
// TypeScript
|
|
|
|
/**
|
|
|
|
* Check data
|
|
|
|
* @type {boolean}
|
|
|
|
* @readonly
|
|
|
|
*/
|
|
|
|
get isMessage() {
|
|
|
|
return false;
|
|
|
|
}
|
2022-05-14 08:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Modal;
|