feat(Interactions): option to auto-fetch replies (#5831)

This commit is contained in:
monbrey
2021-06-30 07:32:33 +10:00
committed by GitHub
parent 5dfd7dd1bf
commit 5e28ff83cb
4 changed files with 50 additions and 14 deletions

View File

@@ -126,6 +126,7 @@ const Messages = {
INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.', INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.',
INTERACTION_NOT_REPLIED: 'This interaction has not been deferred or replied to.', INTERACTION_NOT_REPLIED: 'This interaction has not been deferred or replied to.',
INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be fetched or deleted.', INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be fetched or deleted.',
INTERACTION_FETCH_EPHEMERAL: 'Ephemeral responses cannot be fetched.',
}; };
for (const [name, message] of Object.entries(Messages)) register(name, message); for (const [name, message] of Object.entries(Messages)) register(name, message);

View File

@@ -16,9 +16,9 @@ class MessageComponentInteraction extends Interaction {
/** /**
* The message to which the component was attached * The message to which the component was attached
* @type {?(Message|APIMessage)} * @type {Message|APIMessage}
*/ */
this.message = data.message ? this.channel?.messages.add(data.message) ?? data.message : null; this.message = this.channel?.messages.add(data.message) ?? data.message;
/** /**
* The custom ID of the component which was interacted with * The custom ID of the component which was interacted with

View File

@@ -25,7 +25,7 @@ class InteractionResponses {
/** /**
* Defers the reply to this interaction. * Defers the reply to this interaction.
* @param {InteractionDeferOptions} [options] Options for deferring the reply to this interaction * @param {InteractionDeferOptions} [options] Options for deferring the reply to this interaction
* @returns {Promise<void>} * @returns {Promise<Message|void>}
* @example * @example
* // Defer the reply to this interaction * // Defer the reply to this interaction
* interaction.defer() * interaction.defer()
@@ -37,18 +37,21 @@ class InteractionResponses {
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async defer({ ephemeral } = {}) { async defer(options = {}) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
this.ephemeral = ephemeral ?? false; if (options.fetchReply && options.ephemeral) throw new Error('INTERACTION_FETCH_EPHEMERAL');
this.ephemeral = options.ephemeral ?? false;
await this.client.api.interactions(this.id, this.token).callback.post({ await this.client.api.interactions(this.id, this.token).callback.post({
data: { data: {
type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, type: InteractionResponseTypes.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE,
data: { data: {
flags: ephemeral ? MessageFlags.FLAGS.EPHEMERAL : undefined, flags: options.ephemeral ? MessageFlags.FLAGS.EPHEMERAL : undefined,
}, },
}, },
}); });
this.deferred = true; this.deferred = true;
return options.fetchReply ? this.fetchReply() : undefined;
} }
/** /**
@@ -70,6 +73,7 @@ class InteractionResponses {
*/ */
async reply(options) { async reply(options) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
if (options.fetchReply && options.ephemeral) throw new Error('INTERACTION_FETCH_EPHEMERAL');
this.ephemeral = options.ephemeral ?? false; this.ephemeral = options.ephemeral ?? false;
let messagePayload; let messagePayload;
@@ -86,6 +90,8 @@ class InteractionResponses {
files, files,
}); });
this.replied = true; this.replied = true;
return options.fetchReply ? this.fetchReply() : undefined;
} }
/** /**
@@ -146,28 +152,34 @@ class InteractionResponses {
} }
/** /**
* Defers an update to the message to which the component was attached * Defers an update to the message to which the component was attached.
* @returns {Promise<void>} * @param {InteractionDeferUpdateOptions} [options] Options for deferring the update to this interaction
* @returns {Promise<Message|void>}
* @example * @example
* // Defer updating and reset the component's loading state * // Defer updating and reset the component's loading state
* interaction.deferUpdate() * interaction.deferUpdate()
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async deferUpdate() { async deferUpdate(options = {}) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
if (options.fetchReply && new MessageFlags(this.message.flags).has(MessageFlags.FLAGS.EPHEMERAL)) {
throw new Error('INTERACTION_FETCH_EPHEMERAL');
}
await this.client.api.interactions(this.id, this.token).callback.post({ await this.client.api.interactions(this.id, this.token).callback.post({
data: { data: {
type: InteractionResponseTypes.DEFERRED_MESSAGE_UPDATE, type: InteractionResponseTypes.DEFERRED_MESSAGE_UPDATE,
}, },
}); });
this.deferred = true; this.deferred = true;
return options.fetchReply ? this.fetchReply() : undefined;
} }
/** /**
* Updates the original message whose button was pressed * Updates the original message whose button was pressed.
* @param {string|MessagePayload|WebhookEditMessageOptions} options The options for the reply * @param {string|MessagePayload|WebhookEditMessageOptions} options The options for the reply
* @returns {Promise<void>} * @returns {Promise<Message|void>}
* @example * @example
* // Remove the components from the message * // Remove the components from the message
* interaction.update({ * interaction.update({
@@ -179,6 +191,9 @@ class InteractionResponses {
*/ */
async update(options) { async update(options) {
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED'); if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
if (options.fetchReply && new MessageFlags(this.message.flags).has(MessageFlags.FLAGS.EPHEMERAL)) {
throw new Error('INTERACTION_FETCH_EPHEMERAL');
}
let messagePayload; let messagePayload;
if (options instanceof MessagePayload) messagePayload = options; if (options instanceof MessagePayload) messagePayload = options;
@@ -194,6 +209,8 @@ class InteractionResponses {
files, files,
}); });
this.replied = true; this.replied = true;
return options.fetchReply ? this.fetchReply() : undefined;
} }
static applyToClass(structure, ignore = []) { static applyToClass(structure, ignore = []) {

24
typings/index.d.ts vendored
View File

@@ -558,11 +558,14 @@ declare module 'discord.js' {
public options: Collection<string, CommandInteractionOption>; public options: Collection<string, CommandInteractionOption>;
public replied: boolean; public replied: boolean;
public webhook: InteractionWebhook; public webhook: InteractionWebhook;
public defer(options?: InteractionDeferOptions & { fetchReply: true }): Promise<Message | RawMessage>;
public defer(options?: InteractionDeferOptions): Promise<void>; public defer(options?: InteractionDeferOptions): Promise<void>;
public deleteReply(): Promise<void>; public deleteReply(): Promise<void>;
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<Message | APIMessage>; public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<Message | APIMessage>;
public fetchReply(): Promise<Message | APIMessage>; public fetchReply(): Promise<Message | APIMessage>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message | APIMessage>; public reply(
options: string | MessagePayload | (InteractionReplyOptions & { fetchReply: true }),
): Promise<Message | APIMessage>;
public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>; public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
private transformOption(option: unknown, resolved: unknown): CommandInteractionOption; private transformOption(option: unknown, resolved: unknown): CommandInteractionOption;
private _createOptionsCollection(options: unknown, resolved: unknown): Collection<string, CommandInteractionOption>; private _createOptionsCollection(options: unknown, resolved: unknown): Collection<string, CommandInteractionOption>;
@@ -1417,14 +1420,23 @@ declare module 'discord.js' {
public message: Message | APIMessage; public message: Message | APIMessage;
public replied: boolean; public replied: boolean;
public webhook: InteractionWebhook; public webhook: InteractionWebhook;
public defer(options?: InteractionDeferOptions & { fetchReply: true }): Promise<Message | RawMessage>;
public defer(options?: InteractionDeferOptions): Promise<void>; public defer(options?: InteractionDeferOptions): Promise<void>;
public deferUpdate(): Promise<void>; public deferUpdate(options?: InteractionDeferUpdateOptions & { fetchReply: true }): Promise<Message | RawMessage>;
public deferUpdate(options?: InteractionDeferUpdateOptions): Promise<void>;
public deleteReply(): Promise<void>; public deleteReply(): Promise<void>;
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<Message | APIMessage>; public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<Message | APIMessage>;
public fetchReply(): Promise<Message | APIMessage>; public fetchReply(): Promise<Message | APIMessage>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message | APIMessage>; public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message | APIMessage>;
public reply(
options: string | MessagePayload | (InteractionReplyOptions & { fetchReply: true }),
): Promise<Message | APIMessage>;
public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>; public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
public update(content: string | MessagePayload | WebhookEditMessageOptions): Promise<void>; public update(
content: string | MessagePayload | (InteractionUpdateOptions & { fetchReply: true }),
): Promise<Message | APIMessage>;
public update(content: string | MessagePayload | InteractionUpdateOptions): Promise<void>;
public static resolveType(type: MessageComponentTypeResolvable): MessageComponentType; public static resolveType(type: MessageComponentTypeResolvable): MessageComponentType;
} }
@@ -3609,16 +3621,22 @@ declare module 'discord.js' {
interface InteractionDeferOptions { interface InteractionDeferOptions {
ephemeral?: boolean; ephemeral?: boolean;
fetchReply?: boolean;
} }
interface InteractionDeferUpdateOptions extends Omit<InteractionDeferOptions, 'ephemeral'> {}
interface InteractionReplyOptions extends Omit<WebhookMessageOptions, 'username' | 'avatarURL'> { interface InteractionReplyOptions extends Omit<WebhookMessageOptions, 'username' | 'avatarURL'> {
ephemeral?: boolean; ephemeral?: boolean;
fetchReply?: boolean;
} }
type InteractionResponseType = keyof typeof InteractionResponseTypes; type InteractionResponseType = keyof typeof InteractionResponseTypes;
type InteractionType = keyof typeof InteractionTypes; type InteractionType = keyof typeof InteractionTypes;
interface InteractionUpdateOptions extends Omit<InteractionReplyOptions, 'ephemeral'> {}
type IntentsString = type IntentsString =
| 'GUILDS' | 'GUILDS'
| 'GUILD_MEMBERS' | 'GUILD_MEMBERS'