fix: Modal reply event failed

#1201 #1200 #1064
This commit is contained in:
Elysia
2024-07-04 17:26:04 +07:00
parent f7bb7a4bdd
commit f63b1528d4
5 changed files with 59 additions and 118 deletions

View File

@@ -1,7 +1,6 @@
'use strict'; 'use strict';
const process = require('node:process'); const process = require('node:process');
const { setTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const Base = require('./Base'); const Base = require('./Base');
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
@@ -15,13 +14,7 @@ const { Sticker } = require('./Sticker');
const Application = require('./interfaces/Application'); const Application = require('./interfaces/Application');
const { Error } = require('../errors'); const { Error } = require('../errors');
const ReactionManager = require('../managers/ReactionManager'); const ReactionManager = require('../managers/ReactionManager');
const { const { InteractionTypes, MessageTypes, SystemMessageTypes, MessageComponentTypes } = require('../util/Constants');
InteractionTypes,
MessageTypes,
SystemMessageTypes,
MessageComponentTypes,
Events,
} = require('../util/Constants');
const MessageFlags = require('../util/MessageFlags'); const MessageFlags = require('../util/MessageFlags');
const Permissions = require('../util/Permissions'); const Permissions = require('../util/Permissions');
const SnowflakeUtil = require('../util/SnowflakeUtil'); const SnowflakeUtil = require('../util/SnowflakeUtil');
@@ -1095,38 +1088,7 @@ class Message extends Base {
this.client.api.interactions.post({ this.client.api.interactions.post({
data, data,
}); });
return new Promise((resolve, reject) => { return Util.createPromiseInteraction(this.client, nonce, 5_000, true, this);
const timeoutMs = 5_000;
// Waiting for MsgCreate / ModalCreate
const handler = data => {
// UnhandledPacket
if (data.d?.nonce == nonce && data.t == 'INTERACTION_SUCCESS') {
// Interaction#deferUpdate
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
resolve(this);
}
if (data.nonce !== nonce) return;
clearTimeout(timeout);
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
resolve(data);
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
reject(new Error('INTERACTION_FAILED'));
}, timeoutMs).unref();
this.client.incrementMaxListeners();
this.client.on(Events.MESSAGE_CREATE, handler);
this.client.on(Events.UNHANDLED_PACKET, handler);
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
});
} }
/** /**
@@ -1197,38 +1159,7 @@ class Message extends Base {
this.client.api.interactions.post({ this.client.api.interactions.post({
data, data,
}); });
return new Promise((resolve, reject) => { return Util.createPromiseInteraction(this.client, nonce, 5_000, true, this);
const timeoutMs = 5_000;
// Waiting for MsgCreate / ModalCreate
const handler = data => {
// UnhandledPacket
if (data.d?.nonce == nonce && data.t == 'INTERACTION_SUCCESS') {
// Interaction#deferUpdate
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
resolve(this);
}
if (data.nonce !== nonce) return;
clearTimeout(timeout);
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
resolve(data);
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.UNHANDLED_PACKET, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
reject(new Error('INTERACTION_FAILED'));
}, timeoutMs).unref();
this.client.incrementMaxListeners();
this.client.on(Events.MESSAGE_CREATE, handler);
this.client.on(Events.UNHANDLED_PACKET, handler);
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
});
} }
/** /**

View File

@@ -1,9 +1,9 @@
'use strict'; 'use strict';
const { setTimeout } = require('node:timers');
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
const { InteractionTypes, Events } = require('../util/Constants'); const { InteractionTypes } = require('../util/Constants');
const SnowflakeUtil = require('../util/SnowflakeUtil'); const SnowflakeUtil = require('../util/SnowflakeUtil');
const Util = require('../util/Util');
/** /**
* Represents a modal (form) to be shown in response to an interaction * Represents a modal (form) to be shown in response to an interaction
@@ -56,6 +56,12 @@ class Modal {
*/ */
this.channelId = data.channel_id; this.channelId = data.channel_id;
/**
* Whether this interaction has already been replied to
* @type {boolean}
*/
this.replied = false;
Object.defineProperty(this, 'client', { Object.defineProperty(this, 'client', {
value: client, value: client,
writable: false, writable: false,
@@ -110,7 +116,7 @@ class Modal {
* }) * })
*/ */
reply() { reply() {
if (!this.applicationId || !this.client || !this.channelId) throw new Error('Modal cannot reply'); if (!this.applicationId || !this.client || !this.channelId || this.replied) throw new Error('Modal cannot reply');
// Get Object // Get Object
const dataFinal = this.toJSON(); const dataFinal = this.toJSON();
dataFinal.components = dataFinal.components dataFinal.components = dataFinal.components
@@ -137,27 +143,8 @@ class Modal {
this.client.api.interactions.post({ this.client.api.interactions.post({
data: postData, data: postData,
}); });
return new Promise((resolve, reject) => { this.replied = true;
const timeoutMs = 5_000; return Util.createPromiseInteraction(this.client, nonce, 5_000, true, this);
// Waiting for MsgCreate / ModalCreate
const handler = data => {
if (data.nonce !== nonce) return;
clearTimeout(timeout);
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
resolve(data);
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
reject(new Error('INTERACTION_FAILED'));
}, timeoutMs).unref();
this.client.incrementMaxListeners();
this.client.on(Events.MESSAGE_CREATE, handler);
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
});
} }
// TypeScript // TypeScript

View File

@@ -342,27 +342,7 @@ class TextBasedChannel {
data: body, data: body,
usePayloadJSON: true, usePayloadJSON: true,
}); });
return new Promise((resolve, reject) => { return Util.createPromiseInteraction(this.client, nonce, 5000);
const timeoutMs = 5_000;
// Waiting for MsgCreate / ModalCreate
const handler = data => {
if (data.nonce !== nonce) return;
clearTimeout(timeout);
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
resolve(data);
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.MESSAGE_CREATE, handler);
this.client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
this.client.decrementMaxListeners();
reject(new Error('INTERACTION_FAILED'));
}, timeoutMs).unref();
this.client.incrementMaxListeners();
this.client.on(Events.MESSAGE_CREATE, handler);
this.client.on(Events.INTERACTION_MODAL_CREATE, handler);
});
} }
/** /**

View File

@@ -3,9 +3,10 @@
const { Agent } = require('node:http'); const { Agent } = require('node:http');
const { parse } = require('node:path'); const { parse } = require('node:path');
const process = require('node:process'); const process = require('node:process');
const { setTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const { Colors } = require('./Constants'); const { Colors, Events } = require('./Constants');
const { Error: DiscordError, RangeError, TypeError } = require('../errors'); const { Error: DiscordError, RangeError, TypeError } = require('../errors');
const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k); const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
const isObject = d => typeof d === 'object' && d !== null; const isObject = d => typeof d === 'object' && d !== null;
@@ -818,6 +819,47 @@ class Util extends null {
static verifyProxyAgent(object) { static verifyProxyAgent(object) {
return typeof object == 'object' && object.httpAgent instanceof Agent && object.httpsAgent instanceof Agent; return typeof object == 'object' && object.httpAgent instanceof Agent && object.httpsAgent instanceof Agent;
} }
static createPromiseInteraction(client, nonce, timeoutMs = 5_000, isHandlerDeferUpdate = false, parent) {
return new Promise((resolve, reject) => {
// Waiting for MsgCreate / ModalCreate
let dataFromInteractionSuccess;
let dataFromNormal;
const handler = data => {
// UnhandledPacket
if (isHandlerDeferUpdate && data.d?.nonce == nonce && data.t == 'INTERACTION_SUCCESS') {
// Interaction#deferUpdate
client.removeListener(Events.MESSAGE_CREATE, handler);
client.removeListener(Events.UNHANDLED_PACKET, handler);
client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
dataFromInteractionSuccess = parent;
}
if (data.nonce !== nonce) return;
clearTimeout(timeout);
client.removeListener(Events.MESSAGE_CREATE, handler);
client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
if (isHandlerDeferUpdate) client.removeListener(Events.UNHANDLED_PACKET, handler);
client.decrementMaxListeners();
dataFromNormal = data;
resolve(data);
};
const timeout = setTimeout(() => {
if (dataFromInteractionSuccess || dataFromNormal) {
resolve(dataFromNormal || dataFromInteractionSuccess);
return;
}
client.removeListener(Events.MESSAGE_CREATE, handler);
client.removeListener(Events.INTERACTION_MODAL_CREATE, handler);
if (isHandlerDeferUpdate) client.removeListener(Events.UNHANDLED_PACKET, handler);
client.decrementMaxListeners();
reject(new Error('INTERACTION_FAILED'));
}, timeoutMs).unref();
client.incrementMaxListeners();
client.on(Events.MESSAGE_CREATE, handler);
client.on(Events.INTERACTION_MODAL_CREATE, handler);
if (isHandlerDeferUpdate) client.on(Events.UNHANDLED_PACKET, handler);
});
}
} }
module.exports = Util; module.exports = Util;

1
typings/index.d.ts vendored
View File

@@ -2278,6 +2278,7 @@ export class Modal {
public channelId: Snowflake; public channelId: Snowflake;
public readonly channel: TextBasedChannel; public readonly channel: TextBasedChannel;
public readonly guild: Guild | null; public readonly guild: Guild | null;
public readonly replied: boolean;
public reply(): Promise<Message | Modal>; public reply(): Promise<Message | Modal>;
public toJSON(): RawModalSubmitInteractionData; public toJSON(): RawModalSubmitInteractionData;
} }