From cc86c9b4f1001c0de270f5ddfc3f7242cbb73dd2 Mon Sep 17 00:00:00 2001 From: March 7th <71698422+aiko-chan-ai@users.noreply.github.com> Date: Mon, 12 Dec 2022 19:28:13 +0700 Subject: [PATCH] fix: Modal reply response --- src/structures/InteractionResponse.js | 7 ++- src/structures/Modal.js | 82 +++++++++++++++++++-------- typings/index.d.ts | 4 +- 3 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/structures/InteractionResponse.js b/src/structures/InteractionResponse.js index 2a8ba11..e86f786 100644 --- a/src/structures/InteractionResponse.js +++ b/src/structures/InteractionResponse.js @@ -86,10 +86,11 @@ class InteractionResponse extends Base { /** * 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} */ - awaitModal(time = 120_000) { + awaitModal(time) { + if (!time || typeof time !== 'number' || time < 0) throw new Error('INVALID_TIME'); return new Promise((resolve, reject) => { const handler = modal => { timeout.refresh(); @@ -103,7 +104,7 @@ class InteractionResponse extends Base { this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler); this.client.decrementMaxListeners(); reject(new Error('MODAL_TIMEOUT')); - }, time || 120_000).unref(); + }, time).unref(); this.client.incrementMaxListeners(); this.client.on(Events.INTERACTION_MODAL_CREATE, handler); }); diff --git a/src/structures/Modal.js b/src/structures/Modal.js index f27e727..2b804e9 100644 --- a/src/structures/Modal.js +++ b/src/structures/Modal.js @@ -1,5 +1,6 @@ 'use strict'; +const { setTimeout } = require('node:timers'); const BaseMessageComponent = require('./BaseMessageComponent'); const User = require('./User'); const SnowflakeUtil = require('../util/SnowflakeUtil'); @@ -153,7 +154,7 @@ class Modal { * @typedef {Object} ModalReplyData * @property {?GuildResolvable} [guild] Guild 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} * @example * client.on('interactionModalCreate', modal => { + * // 1. * modal.reply({ * data: [ * { @@ -175,35 +177,39 @@ class Modal { * channel: '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) { - 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'); + async reply(data = {}) { if (!this.application) throw new Error('Modal cannot reply (Missing Application)'); const data_cache = this.sendFromInteraction; - const guild = this.client.guilds.resolveId(data.guild) || data_cache.guildId || null; - const channel = this.client.channels.resolveId(data.channel) || data_cache.channelId; + const guild = this.client.guilds.resolveId(data?.guild) || data_cache.guildId || null; + const channel = this.client.channels.resolveId(data?.channel) || data_cache.channelId; if (!channel) throw new Error('Modal cannot reply (Missing data)'); // 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.data.find(d => d.customId == this.components[i].components[0].customId); - if (this.components[i].components[0].required == true && !value) { - throw new Error( - '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') { + if (Array.isArray(data?.data) && data?.data?.length > 0) { + for (let i = 0; i < this.components.length; i++) { + const value = data.data.find(d => d.customId == this.components[i].components[0].customId); + if (this.components[i].components[0].required == true && !value) { throw new Error( - 'MODAL_REPLY_DATA_INVALID\n' + - `value must be a single line, got multiple lines [Custom ID: ${value.customId}]`, + 'MODAL_REQUIRED_FIELD_MISSING\n' + + `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 @@ -233,10 +239,40 @@ class Modal { await this.client.api.interactions.post({ data: postData, }); - return { - nonce, - id: this.id, - }; + this.client._interactionCache.set(nonce, { + channelId: channel, + 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); + }); } } diff --git a/typings/index.d.ts b/typings/index.d.ts index 1052b93..b8c205a 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2328,7 +2328,7 @@ export class Modal { export interface ModalReplyData { guild?: GuildResolvable; channel?: TextChannelResolvable; - data: TextInputComponentReplyData[]; + data?: TextInputComponentReplyData[]; } export interface TextInputComponentReplyData { @@ -3994,7 +3994,7 @@ export class InteractionResponse extends Base { public id: Snowflake; public nonce: Snowflake; public sendData: object; - public awaitModal(time?: number): Modal; + public awaitModal(time: number): Modal; } export interface MessageSearchOptions {