fix: Modal reply response

This commit is contained in:
March 7th 2022-12-12 19:28:13 +07:00
parent 798650929b
commit cc86c9b4f1
3 changed files with 65 additions and 28 deletions

View File

@ -86,10 +86,11 @@ class InteractionResponse extends Base {
/** /**
* Get Modal send from interaction * Get Modal send from interaction
* @param {?number} time Time to wait for modal (Default: 120000) * @param {number} time Time to wait for modal
* @returns {Modal} * @returns {Modal}
*/ */
awaitModal(time = 120_000) { awaitModal(time) {
if (!time || typeof time !== 'number' || time < 0) throw new Error('INVALID_TIME');
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const handler = modal => { const handler = modal => {
timeout.refresh(); timeout.refresh();
@ -103,7 +104,7 @@ class InteractionResponse extends Base {
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler); this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners(); this.client.decrementMaxListeners();
reject(new Error('MODAL_TIMEOUT')); reject(new Error('MODAL_TIMEOUT'));
}, time || 120_000).unref(); }, time).unref();
this.client.incrementMaxListeners(); this.client.incrementMaxListeners();
this.client.on(Events.INTERACTION_MODAL_CREATE, handler); this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
}); });

View File

@ -1,5 +1,6 @@
'use strict'; 'use strict';
const { setTimeout } = require('node:timers');
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
const User = require('./User'); const User = require('./User');
const SnowflakeUtil = require('../util/SnowflakeUtil'); const SnowflakeUtil = require('../util/SnowflakeUtil');
@ -153,7 +154,7 @@ class Modal {
* @typedef {Object} ModalReplyData * @typedef {Object} ModalReplyData
* @property {?GuildResolvable} [guild] Guild to send the modal to * @property {?GuildResolvable} [guild] Guild to send the modal to
* @property {?TextChannelResolvable} [channel] User to send the modal to * @property {?TextChannelResolvable} [channel] User to send the modal to
* @property {TextInputComponentReplyData[]} [data] Reply data * @property {?TextInputComponentReplyData[]} [data] Reply data
*/ */
/** /**
@ -162,6 +163,7 @@ class Modal {
* @returns {Promise<InteractionResponse>} * @returns {Promise<InteractionResponse>}
* @example * @example
* client.on('interactionModalCreate', modal => { * client.on('interactionModalCreate', modal => {
* // 1.
* modal.reply({ * modal.reply({
* data: [ * data: [
* { * {
@ -175,35 +177,39 @@ class Modal {
* channel: 'id', // optional * channel: 'id', // optional
* guild: 'id', // optional * guild: 'id', // optional
* }) * })
* // or 2.
* modal.components[0].components[0].setValue('1+1');
* modal.components[1].components[0].setValue('hello');
* modal.reply();
* }) * })
*/ */
async reply(data) { async reply(data = {}) {
if (typeof data !== 'object') throw new TypeError('ModalReplyData must be an object');
if (!Array.isArray(data.data)) throw new TypeError('ModalReplyData.data must be an array');
if (!this.application) throw new Error('Modal cannot reply (Missing Application)'); if (!this.application) throw new Error('Modal cannot reply (Missing Application)');
const data_cache = this.sendFromInteraction; const data_cache = this.sendFromInteraction;
const guild = this.client.guilds.resolveId(data.guild) || data_cache.guildId || null; const guild = this.client.guilds.resolveId(data?.guild) || data_cache.guildId || null;
const channel = this.client.channels.resolveId(data.channel) || data_cache.channelId; const channel = this.client.channels.resolveId(data?.channel) || data_cache.channelId;
if (!channel) throw new Error('Modal cannot reply (Missing data)'); if (!channel) throw new Error('Modal cannot reply (Missing data)');
// Add data to components // Add data to components
// this.components = [ MessageActionRow.components = [ TextInputComponent ] ] // this.components = [ MessageActionRow.components = [ TextInputComponent ] ]
// 5 MessageActionRow / Modal, 1 TextInputComponent / 1 MessageActionRow // 5 MessageActionRow / Modal, 1 TextInputComponent / 1 MessageActionRow
for (let i = 0; i < this.components.length; i++) { if (Array.isArray(data?.data) && data?.data?.length > 0) {
const value = data.data.find(d => d.customId == this.components[i].components[0].customId); for (let i = 0; i < this.components.length; i++) {
if (this.components[i].components[0].required == true && !value) { const value = data.data.find(d => d.customId == this.components[i].components[0].customId);
throw new Error( if (this.components[i].components[0].required == true && !value) {
'MODAL_REQUIRED_FIELD_MISSING\n' +
`Required fieldId ${this.components[i].components[0].customId} missing value`,
);
}
if (value) {
if (value?.value?.includes('\n') && this.components[i].components[0].style == 'SHORT') {
throw new Error( throw new Error(
'MODAL_REPLY_DATA_INVALID\n' + 'MODAL_REQUIRED_FIELD_MISSING\n' +
`value must be a single line, got multiple lines [Custom ID: ${value.customId}]`, `Required fieldId ${this.components[i].components[0].customId} missing value`,
); );
} }
this.components[i].components[0].setValue(value.value); if (value) {
if (value?.value?.includes('\n') && this.components[i].components[0].style == 'SHORT') {
throw new Error(
'MODAL_REPLY_DATA_INVALID\n' +
`value must be a single line, got multiple lines [Custom ID: ${value.customId}]`,
);
}
this.components[i].components[0].setValue(value.value);
}
} }
} }
// Get Object // Get Object
@ -233,10 +239,40 @@ class Modal {
await this.client.api.interactions.post({ await this.client.api.interactions.post({
data: postData, data: postData,
}); });
return { this.client._interactionCache.set(nonce, {
nonce, channelId: channel,
id: this.id, guildId: guild,
}; metadata: postData,
});
return new Promise((resolve, reject) => {
const handler = data => {
timeout.refresh();
if (data.metadata.nonce !== nonce) return;
clearTimeout(timeout);
this.client.removeListener('interactionResponse', handler);
this.client.decrementMaxListeners();
if (data.status) {
resolve(data.metadata);
} else {
reject(
new Error('INTERACTION_ERROR', {
cause: data,
}),
);
}
};
const timeout = setTimeout(() => {
this.client.removeListener('interactionResponse', handler);
this.client.decrementMaxListeners();
reject(
new Error('INTERACTION_TIMEOUT', {
cause: data,
}),
);
}, this.client.options.interactionTimeout).unref();
this.client.incrementMaxListeners();
this.client.on('interactionResponse', handler);
});
} }
} }

4
typings/index.d.ts vendored
View File

@ -2328,7 +2328,7 @@ export class Modal {
export interface ModalReplyData { export interface ModalReplyData {
guild?: GuildResolvable; guild?: GuildResolvable;
channel?: TextChannelResolvable; channel?: TextChannelResolvable;
data: TextInputComponentReplyData[]; data?: TextInputComponentReplyData[];
} }
export interface TextInputComponentReplyData { export interface TextInputComponentReplyData {
@ -3994,7 +3994,7 @@ export class InteractionResponse extends Base {
public id: Snowflake; public id: Snowflake;
public nonce: Snowflake; public nonce: Snowflake;
public sendData: object; public sendData: object;
public awaitModal(time?: number): Modal; public awaitModal(time: number): Modal;
} }
export interface MessageSearchOptions { export interface MessageSearchOptions {