From 622acbcbf02c3b8e0eae4296964c3e745e19378d Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:58:22 +0000 Subject: [PATCH] feat(InteractionResponses): support `with_response` query parameter (#10636) feat(InteractionResponses): support with_response query parameter Co-authored-by: Ryan Munro --- packages/discord.js/src/index.js | 3 + .../src/structures/InteractionCallback.js | 74 +++++++++++ .../structures/InteractionCallbackResource.js | 52 ++++++++ .../structures/InteractionCallbackResponse.js | 33 +++++ .../interfaces/InteractionResponses.js | 122 +++++++++++++++--- packages/discord.js/typings/index.d.ts | 110 +++++++++++++++- packages/discord.js/typings/index.test-d.ts | 38 ++++++ 7 files changed, 412 insertions(+), 20 deletions(-) create mode 100644 packages/discord.js/src/structures/InteractionCallback.js create mode 100644 packages/discord.js/src/structures/InteractionCallbackResource.js create mode 100644 packages/discord.js/src/structures/InteractionCallbackResponse.js diff --git a/packages/discord.js/src/index.js b/packages/discord.js/src/index.js index d8b00eb435ec..20f56a80be85 100644 --- a/packages/discord.js/src/index.js +++ b/packages/discord.js/src/index.js @@ -147,6 +147,9 @@ exports.GuildScheduledEvent = require('./structures/GuildScheduledEvent').GuildS exports.GuildTemplate = require('./structures/GuildTemplate'); exports.Integration = require('./structures/Integration'); exports.IntegrationApplication = require('./structures/IntegrationApplication'); +exports.InteractionCallback = require('./structures/InteractionCallback'); +exports.InteractionCallbackResource = require('./structures/InteractionCallbackResource'); +exports.InteractionCallbackResponse = require('./structures/InteractionCallbackResponse'); exports.BaseInteraction = require('./structures/BaseInteraction'); exports.InteractionCollector = require('./structures/InteractionCollector'); exports.InteractionResponse = require('./structures/InteractionResponse'); diff --git a/packages/discord.js/src/structures/InteractionCallback.js b/packages/discord.js/src/structures/InteractionCallback.js new file mode 100644 index 000000000000..4abc831470e9 --- /dev/null +++ b/packages/discord.js/src/structures/InteractionCallback.js @@ -0,0 +1,74 @@ +'use strict'; + +const { DiscordSnowflake } = require('@sapphire/snowflake'); + +/** + * Represents an interaction callback response from Discord + */ +class InteractionCallback { + constructor(client, data) { + /** + * The client that instantiated this. + * @name InteractionCallback#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The id of the original interaction response + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The type of the original interaction + * @type {InteractionType} + */ + this.type = data.type; + + /** + * The instance id of the Activity if one was launched or joined + * @type {?string} + */ + this.activityInstanceId = data.activity_instance_id ?? null; + + /** + * The id of the message that was created by the interaction + * @type {?Snowflake} + */ + this.responseMessageId = data.response_message_id ?? null; + + /** + * Whether the message is in a loading state + * @type {?boolean} + */ + this.responseMessageLoading = data.response_message_loading ?? null; + + /** + * Whether the response message was ephemeral + * @type {?boolean} + */ + this.responseMessageEphemeral = data.response_message_ephemeral ?? null; + } + + /** + * The timestamp the original interaction was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return DiscordSnowflake.timestampFrom(this.id); + } + + /** + * The time the original interaction was created at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } +} + +module.exports = InteractionCallback; diff --git a/packages/discord.js/src/structures/InteractionCallbackResource.js b/packages/discord.js/src/structures/InteractionCallbackResource.js new file mode 100644 index 000000000000..ffb088d8100e --- /dev/null +++ b/packages/discord.js/src/structures/InteractionCallbackResource.js @@ -0,0 +1,52 @@ +'use strict'; + +const { lazy } = require('@discordjs/util'); + +const getMessage = lazy(() => require('./Message').Message); + +/** + * Represents the resource that was created by the interaction response. + */ +class InteractionCallbackResource { + constructor(client, data) { + /** + * The client that instantiated this + * @name InteractionCallbackResource#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The interaction callback type + * @type {InteractionResponseType} + */ + this.type = data.type; + + /** + * The Activity launched by an interaction + * @typedef {Object} ActivityInstance + * @property {string} id The instance id of the Activity + */ + + /** + * Represents the Activity launched by this interaction + * @type {?ActivityInstance} + */ + this.activityInstance = data.activity_instance ?? null; + + if ('message' in data) { + /** + * The message created by the interaction + * @type {?Message} + */ + this.message = + this.client.channels.cache.get(data.message.channel_id)?.messages._add(data.message) ?? + new (getMessage())(client, data.message); + } else { + this.message = null; + } + } +} + +module.exports = InteractionCallbackResource; diff --git a/packages/discord.js/src/structures/InteractionCallbackResponse.js b/packages/discord.js/src/structures/InteractionCallbackResponse.js new file mode 100644 index 000000000000..c114648398ae --- /dev/null +++ b/packages/discord.js/src/structures/InteractionCallbackResponse.js @@ -0,0 +1,33 @@ +'use strict'; + +const InteractionCallback = require('./InteractionCallback'); +const InteractionCallbackResource = require('./InteractionCallbackResource'); + +/** + * Represents an interaction's response + */ +class InteractionCallbackResponse { + constructor(client, data) { + /** + * The client that instantiated this + * @name InteractionCallbackResponse#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The interaction object associated with the interaction callback response + * @type {InteractionCallback} + */ + this.interaction = new InteractionCallback(client, data.interaction); + + /** + * The resource that was created by the interaction response + * @type {?InteractionCallbackResource} + */ + this.resource = data.resource ? new InteractionCallbackResource(client, data.resource) : null; + } +} + +module.exports = InteractionCallbackResponse; diff --git a/packages/discord.js/src/structures/interfaces/InteractionResponses.js b/packages/discord.js/src/structures/interfaces/InteractionResponses.js index c0af53ec8d2b..a5f5b824e782 100644 --- a/packages/discord.js/src/structures/interfaces/InteractionResponses.js +++ b/packages/discord.js/src/structures/interfaces/InteractionResponses.js @@ -2,15 +2,18 @@ const process = require('node:process'); const { deprecate } = require('node:util'); +const { makeURLSearchParams } = require('@discordjs/rest'); const { isJSONEncodable } = require('@discordjs/util'); const { InteractionResponseType, MessageFlags, Routes, InteractionType } = require('discord-api-types/v10'); const { DiscordjsError, ErrorCodes } = require('../../errors'); const MessageFlagsBitField = require('../../util/MessageFlagsBitField'); +const InteractionCallbackResponse = require('../InteractionCallbackResponse'); const InteractionCollector = require('../InteractionCollector'); const InteractionResponse = require('../InteractionResponse'); const MessagePayload = require('../MessagePayload'); let deprecationEmittedForEphemeralOption = false; +let deprecationEmittedForFetchReplyOption = false; /** * @typedef {Object} ModalComponentData @@ -31,13 +34,17 @@ class InteractionResponses { * This option is deprecated. Use `flags` instead. * @property {MessageFlagsResolvable} [flags] Flags for the reply. * Only `MessageFlags.Ephemeral` can be set. + * @property {boolean} [withResponse] Whether to return an {@link InteractionCallbackResponse} as the response * @property {boolean} [fetchReply] Whether to fetch the reply + * This option is deprecated. Use `withResponse` or fetch the response instead. */ /** * Options for deferring and updating the reply to a {@link MessageComponentInteraction}. * @typedef {Object} InteractionDeferUpdateOptions + * @property {boolean} [withResponse] Whether to return an {@link InteractionCallbackResponse} as the response * @property {boolean} [fetchReply] Whether to fetch the reply + * This option is deprecated. Use `withResponse` or fetch the response instead. */ /** @@ -46,7 +53,9 @@ class InteractionResponses { * @property {boolean} [ephemeral] Whether the reply should be ephemeral. * This option is deprecated. Use `flags` instead. * @property {boolean} [tts=false] Whether the message should be spoken aloud + * @property {boolean} [withResponse] Whether to return an {@link InteractionCallbackResponse} as the response * @property {boolean} [fetchReply] Whether to fetch the reply + * This option is deprecated. Use `withResponse` or fetch the response instead. * @property {MessageFlagsResolvable} [flags] Which flags to set for the message. * Only `MessageFlags.Ephemeral`, `MessageFlags.SuppressEmbeds`, and `MessageFlags.SuppressNotifications` * can be set. @@ -55,13 +64,21 @@ class InteractionResponses { /** * Options for updating the message received from a {@link MessageComponentInteraction}. * @typedef {MessageEditOptions} InteractionUpdateOptions + * @property {boolean} [withResponse] Whether to return an {@link InteractionCallbackResponse} as the response * @property {boolean} [fetchReply] Whether to fetch the reply + * This option is deprecated. Use `withResponse` or fetch the response instead. + */ + + /** + * Options for showing a modal in response to a {@link BaseInteraction} + * @typedef {Object} ShowModalOptions + * @property {boolean} [withResponse] Whether to return an {@link InteractionCallbackResponse} as the response */ /** * Defers the reply to this interaction. * @param {InteractionDeferReplyOptions} [options] Options for deferring the reply to this interaction - * @returns {Promise} + * @returns {Promise} * @example * // Defer the reply to this interaction * interaction.deferReply() @@ -86,13 +103,24 @@ class InteractionResponses { } } + if ('fetchReply' in options) { + if (!deprecationEmittedForFetchReplyOption) { + process.emitWarning( + // eslint-disable-next-line max-len + `Supplying "fetchReply" for interaction response options is deprecated. Utilize "withResponse" instead or fetch the response after using the method.`, + ); + + deprecationEmittedForFetchReplyOption = true; + } + } + const flags = new MessageFlagsBitField(options.flags); if (options.ephemeral) { flags.add(MessageFlags.Ephemeral); } - await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { + const response = await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.DeferredChannelMessageWithSource, data: { @@ -100,23 +128,28 @@ class InteractionResponses { }, }, auth: false, + query: makeURLSearchParams({ with_response: options.withResponse ?? false }), }); this.deferred = true; this.ephemeral = flags.has(MessageFlags.Ephemeral); - return options.fetchReply ? this.fetchReply() : new InteractionResponse(this); + return options.withResponse + ? new InteractionCallbackResponse(this.client, response) + : options.fetchReply + ? this.fetchReply() + : new InteractionResponse(this); } /** * Creates a reply to this interaction. - * Use the `fetchReply` option to get the bot's reply message. + * Use the `withResponse` option to get the interaction callback response. * @param {string|MessagePayload|InteractionReplyOptions} options The options for the reply - * @returns {Promise} + * @returns {Promise} * @example * // Reply to the interaction and fetch the response - * interaction.reply({ content: 'Pong!', fetchReply: true }) - * .then((message) => console.log(`Reply sent with content ${message.content}`)) + * interaction.reply({ content: 'Pong!', withResponse: true }) + * .then((response) => console.log(`Reply sent with content ${response.resource.message.content}`)) * .catch(console.error); * @example * // Create an ephemeral reply with an embed @@ -139,25 +172,41 @@ class InteractionResponses { } } + if ('fetchReply' in options) { + if (!deprecationEmittedForFetchReplyOption) { + process.emitWarning( + // eslint-disable-next-line max-len + `Supplying "fetchReply" for interaction response options is deprecated. Utilize "withResponse" instead or fetch the response after using the method.`, + ); + + deprecationEmittedForFetchReplyOption = true; + } + } + let messagePayload; if (options instanceof MessagePayload) messagePayload = options; else messagePayload = MessagePayload.create(this, options); const { body: data, files } = await messagePayload.resolveBody().resolveFiles(); - await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { + const response = await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.ChannelMessageWithSource, data, }, files, auth: false, + query: makeURLSearchParams({ with_response: options.withResponse ?? false }), }); this.ephemeral = Boolean(data.flags & MessageFlags.Ephemeral); this.replied = true; - return options.fetchReply ? this.fetchReply() : new InteractionResponse(this); + return options.withResponse + ? new InteractionCallbackResponse(this.client, response) + : options.fetchReply + ? this.fetchReply() + : new InteractionResponse(this); } /** @@ -229,7 +278,7 @@ class InteractionResponses { /** * Defers an update to the message to which the component was attached. * @param {InteractionDeferUpdateOptions} [options] Options for deferring the update to this interaction - * @returns {Promise} + * @returns {Promise} * @example * // Defer updating and reset the component's loading state * interaction.deferUpdate() @@ -238,21 +287,38 @@ class InteractionResponses { */ async deferUpdate(options = {}) { if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); - await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { + + if ('fetchReply' in options) { + if (!deprecationEmittedForFetchReplyOption) { + process.emitWarning( + // eslint-disable-next-line max-len + `Supplying "fetchReply" for interaction response options is deprecated. Utilize "withResponse" instead or fetch the response after using the method.`, + ); + + deprecationEmittedForFetchReplyOption = true; + } + } + + const response = await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.DeferredMessageUpdate, }, auth: false, + query: makeURLSearchParams({ with_response: options.withResponse ?? false }), }); this.deferred = true; - return options.fetchReply ? this.fetchReply() : new InteractionResponse(this, this.message?.interaction?.id); + return options.withResponse + ? new InteractionCallbackResponse(this.client, response) + : options.fetchReply + ? this.fetchReply() + : new InteractionResponse(this, this.message?.interaction?.id); } /** * Updates the original message of the component on which the interaction was received on. * @param {string|MessagePayload|InteractionUpdateOptions} options The options for the updated message - * @returns {Promise} + * @returns {Promise} * @example * // Remove the components from the message * interaction.update({ @@ -265,40 +331,60 @@ class InteractionResponses { async update(options) { if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); + if ('fetchReply' in options) { + if (!deprecationEmittedForFetchReplyOption) { + process.emitWarning( + // eslint-disable-next-line max-len + `Supplying "fetchReply" for interaction response options is deprecated. Utilize "withResponse" instead or fetch the response after using the method.`, + ); + + deprecationEmittedForFetchReplyOption = true; + } + } + let messagePayload; if (options instanceof MessagePayload) messagePayload = options; else messagePayload = MessagePayload.create(this, options); const { body: data, files } = await messagePayload.resolveBody().resolveFiles(); - await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { + const response = await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.UpdateMessage, data, }, files, auth: false, + query: makeURLSearchParams({ with_response: options.withResponse ?? false }), }); this.replied = true; - return options.fetchReply ? this.fetchReply() : new InteractionResponse(this, this.message.interaction?.id); + return options.withResponse + ? new InteractionCallbackResponse(this.client, response) + : options.fetchReply + ? this.fetchReply() + : new InteractionResponse(this, this.message.interaction?.id); } /** * Shows a modal component * @param {ModalBuilder|ModalComponentData|APIModalInteractionResponseCallbackData} modal The modal to show - * @returns {Promise} + * @param {ShowModalOptions} [options={}] The options for sending this interaction response + * @returns {Promise} */ - async showModal(modal) { + async showModal(modal, options = {}) { if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); - await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { + const response = await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.Modal, data: isJSONEncodable(modal) ? modal.toJSON() : this.client.options.jsonTransformer(modal), }, auth: false, + query: makeURLSearchParams({ with_response: options.withResponse ?? false }), }); this.replied = true; + + return options.withResponse ? new InteractionCallbackResponse(this.client, response) : undefined; } /** diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index f25f5422136a..7a84845b71e8 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -192,6 +192,13 @@ import { SubscriptionStatus, ApplicationWebhookEventStatus, ApplicationWebhookEventType, + GatewaySendPayload, + GatewayDispatchPayload, + RESTPostAPIInteractionCallbackWithResponseResult, + RESTAPIInteractionCallbackObject, + RESTAPIInteractionCallbackResourceObject, + InteractionResponseType, + RESTAPIInteractionCallbackActivityInstanceResource, VoiceChannelEffectSendAnimationType, GatewayVoiceChannelEffectSendDispatchData, } from 'discord-api-types/v10'; @@ -587,6 +594,10 @@ export abstract class CommandInteraction e public inGuild(): this is CommandInteraction<'raw' | 'cached'>; public inCachedGuild(): this is CommandInteraction<'cached'>; public inRawGuild(): this is CommandInteraction<'raw'>; + public deferReply( + options: InteractionDeferReplyOptions & { withResponse: true }, + ): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public deferReply( options: InteractionDeferReplyOptions & { fetchReply: true }, ): Promise>>; @@ -597,6 +608,8 @@ export abstract class CommandInteraction e ): Promise>>; public fetchReply(message?: Snowflake | '@original'): Promise>>; public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise>>; + public reply(options: InteractionReplyOptions & { withResponse: true }): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise>>; public reply( options: string | MessagePayload | InteractionReplyOptions, @@ -606,7 +619,22 @@ export abstract class CommandInteraction e | JSONEncodable | ModalComponentData | APIModalInteractionResponseCallbackData, - ): Promise; + options: ShowModalOptions & { withResponse: true }, + ): Promise; + public showModal( + modal: + | JSONEncodable + | ModalComponentData + | APIModalInteractionResponseCallbackData, + options?: ShowModalOptions & { withResponse: true }, + ): Promise; + public showModal( + modal: + | JSONEncodable + | ModalComponentData + | APIModalInteractionResponseCallbackData, + options?: ShowModalOptions, + ): Promise; /** @deprecated Sending a premium-style button is the new Discord behaviour. */ public sendPremiumRequired(): Promise; public awaitModalSubmit( @@ -2008,6 +2036,33 @@ export class BaseInteraction extends Base public isRepliable(): this is RepliableInteraction; } +export class InteractionCallback { + private constructor(client: Client, data: RESTAPIInteractionCallbackObject); + public activityInstanceId: string | null; + public readonly client: Client; + public get createdAt(): Date; + public get createdTimestamp(): number; + public id: Snowflake; + public responseMessageEphemeral: boolean | null; + public responseMessageId: Snowflake | null; + public responseMessageLoading: boolean | null; + public type: InteractionType; +} + +export class InteractionCallbackResponse { + private constructor(client: Client, data: RESTPostAPIInteractionCallbackWithResponseResult); + public readonly client: Client; + public interaction: InteractionCallback; + public resource: InteractionCallbackResource | null; +} + +export class InteractionCallbackResource { + private constructor(client: Client, data: RESTAPIInteractionCallbackResourceObject); + public activityInstance: RESTAPIInteractionCallbackActivityInstanceResource | null; + public message: Message | null; + public type: InteractionResponseType; +} + export class InteractionCollector extends Collector< Snowflake, Interaction, @@ -2337,10 +2392,18 @@ export class MessageComponentInteraction e public inGuild(): this is MessageComponentInteraction<'raw' | 'cached'>; public inCachedGuild(): this is MessageComponentInteraction<'cached'>; public inRawGuild(): this is MessageComponentInteraction<'raw'>; + public deferReply( + options: InteractionDeferReplyOptions & { withResponse: true }, + ): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public deferReply( options: InteractionDeferReplyOptions & { fetchReply: true }, ): Promise>>; public deferReply(options?: InteractionDeferReplyOptions): Promise>>; + public deferUpdate( + options: InteractionDeferUpdateOptions & { withResponse: true }, + ): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public deferUpdate( options: InteractionDeferUpdateOptions & { fetchReply: true }, ): Promise>>; @@ -2351,10 +2414,14 @@ export class MessageComponentInteraction e ): Promise>>; public fetchReply(message?: Snowflake | '@original'): Promise>>; public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise>>; + public reply(options: InteractionReplyOptions & { withResponse: true }): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise>>; public reply( options: string | MessagePayload | InteractionReplyOptions, ): Promise>>; + public update(options: InteractionUpdateOptions & { withResponse: true }): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise>>; public update( options: string | MessagePayload | InteractionUpdateOptions, @@ -2364,7 +2431,22 @@ export class MessageComponentInteraction e | JSONEncodable | ModalComponentData | APIModalInteractionResponseCallbackData, - ): Promise; + options: ShowModalOptions & { withResponse: true }, + ): Promise; + public showModal( + modal: + | JSONEncodable + | ModalComponentData + | APIModalInteractionResponseCallbackData, + options?: ShowModalOptions, + ): Promise; + public showModal( + modal: + | JSONEncodable + | ModalComponentData + | APIModalInteractionResponseCallbackData, + options?: ShowModalOptions, + ): Promise; /** @deprecated Sending a premium-style button is the new Discord behaviour. */ public sendPremiumRequired(): Promise; public awaitModalSubmit( @@ -2538,6 +2620,8 @@ export interface ModalMessageModalSubmitInteraction { message: Message>; channelId: Snowflake; + update(options: InteractionUpdateOptions & { withResponse: true }): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ update(options: InteractionUpdateOptions & { fetchReply: true }): Promise; update( options: string | MessagePayload | InteractionUpdateOptions, @@ -2558,6 +2642,8 @@ export class ModalSubmitInteraction extend public message: Message> | null; public replied: boolean; public readonly webhook: InteractionWebhook; + public reply(options: InteractionReplyOptions & { withResponse: true }): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise>>; public reply( options: string | MessagePayload | InteractionReplyOptions, @@ -2566,12 +2652,20 @@ export class ModalSubmitInteraction extend public editReply( options: string | MessagePayload | InteractionEditReplyOptions, ): Promise>>; + public deferReply( + options: InteractionDeferReplyOptions & { withResponse: true }, + ): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public deferReply( options: InteractionDeferReplyOptions & { fetchReply: true }, ): Promise>>; public deferReply(options?: InteractionDeferReplyOptions): Promise>>; public fetchReply(message?: Snowflake | '@original'): Promise>>; public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise>>; + public deferUpdate( + options: InteractionDeferUpdateOptions & { withResponse: true }, + ): Promise; + /** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */ public deferUpdate( options: InteractionDeferUpdateOptions & { fetchReply: true }, ): Promise>>; @@ -6442,10 +6536,14 @@ export interface InteractionDeferReplyOptions { Extract, MessageFlags.Ephemeral | MessageFlags.SuppressEmbeds | MessageFlags.SuppressNotifications >; + withResponse?: boolean; + /** @deprecated Use {@link InteractionDeferReplyOptions.withResponse} instead. */ fetchReply?: boolean; } export interface InteractionDeferUpdateOptions { + withResponse?: boolean; + /** @deprecated Use {@link InteractionDeferUpdateOptions.withResponse} instead. */ fetchReply?: boolean; } @@ -6453,6 +6551,8 @@ export interface InteractionReplyOptions extends BaseMessageOptionsWithPoll { /** @deprecated Use {@link InteractionReplyOptions.flags} instead. */ ephemeral?: boolean; tts?: boolean; + withResponse?: boolean; + /** @deprecated Use {@link InteractionReplyOptions.withResponse} instead. */ fetchReply?: boolean; flags?: BitFieldResolvable< Extract, @@ -6461,6 +6561,8 @@ export interface InteractionReplyOptions extends BaseMessageOptionsWithPoll { } export interface InteractionUpdateOptions extends MessageEditOptions { + withResponse?: boolean; + /** @deprecated Use {@link InteractionUpdateOptions.withResponse} instead. */ fetchReply?: boolean; } @@ -6972,6 +7074,10 @@ export interface ShardingManagerOptions { execArgv?: readonly string[]; } +export interface ShowModalOptions { + withResponse?: boolean; +} + export { Snowflake }; export type StageInstanceResolvable = StageInstance | Snowflake; diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index b5fe6884e018..c21271cf664d 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -213,6 +213,7 @@ import { SendableChannels, PollData, UserManager, + InteractionCallbackResponse, } from '.'; import { expectAssignable, @@ -445,6 +446,7 @@ client.on('messageCreate', async message => { const component = await message.awaitMessageComponent({ componentType: ComponentType.Button }); expectType>(component); expectType>(await component.reply({ fetchReply: true })); + expectType(await component.reply({ withResponse: true })); const buttonCollector = message.createMessageComponentCollector({ componentType: ComponentType.Button }); expectType>>(buttonCollector); @@ -1812,10 +1814,14 @@ client.on('interactionCreate', async interaction => { expectType(interaction.guild); expectType>>(interaction.reply({ content: 'a', fetchReply: true })); expectType>>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>>(interaction.editReply({ content: 'a' })); expectType>>(interaction.fetchReply()); expectType>>(interaction.update({ content: 'a', fetchReply: true })); expectType>>(interaction.deferUpdate({ fetchReply: true })); + expectType>(interaction.update({ content: 'a', withResponse: true })); + expectType>(interaction.deferUpdate({ withResponse: true })); expectType>>(interaction.followUp({ content: 'a' })); } else if (interaction.inRawGuild()) { expectAssignable(interaction); @@ -1824,10 +1830,14 @@ client.on('interactionCreate', async interaction => { expectType(interaction.guild); expectType>>(interaction.reply({ content: 'a', fetchReply: true })); expectType>>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>>(interaction.editReply({ content: 'a' })); expectType>>(interaction.fetchReply()); expectType>>(interaction.update({ content: 'a', fetchReply: true })); expectType>>(interaction.deferUpdate({ fetchReply: true })); + expectType>(interaction.update({ content: 'a', withResponse: true })); + expectType>(interaction.deferUpdate({ withResponse: true })); expectType>>(interaction.followUp({ content: 'a' })); } else if (interaction.inGuild()) { expectAssignable(interaction); @@ -1836,10 +1846,14 @@ client.on('interactionCreate', async interaction => { expectType(interaction.guild); expectType>(interaction.reply({ content: 'a', fetchReply: true })); expectType>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>(interaction.editReply({ content: 'a' })); expectType>(interaction.fetchReply()); expectType>(interaction.update({ content: 'a', fetchReply: true })); expectType>(interaction.deferUpdate({ fetchReply: true })); + expectType>(interaction.update({ content: 'a', withResponse: true })); + expectType>(interaction.deferUpdate({ withResponse: true })); expectType>(interaction.followUp({ content: 'a' })); } } @@ -1875,6 +1889,8 @@ client.on('interactionCreate', async interaction => { expectAssignable>(interaction); expectType>>(interaction.reply({ content: 'a', fetchReply: true })); expectType>>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>>(interaction.editReply({ content: 'a' })); expectType>>(interaction.fetchReply()); expectType>>(interaction.followUp({ content: 'a' })); @@ -1883,6 +1899,8 @@ client.on('interactionCreate', async interaction => { expectType(interaction.guild); expectType>>(interaction.reply({ content: 'a', fetchReply: true })); expectType>>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>>(interaction.editReply({ content: 'a' })); expectType>>(interaction.fetchReply()); expectType>>(interaction.followUp({ content: 'a' })); @@ -1891,6 +1909,8 @@ client.on('interactionCreate', async interaction => { expectType(interaction.guild); expectType>(interaction.reply({ content: 'a', fetchReply: true })); expectType>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>(interaction.editReply({ content: 'a' })); expectType>(interaction.fetchReply()); expectType>(interaction.followUp({ content: 'a' })); @@ -1945,18 +1965,21 @@ client.on('interactionCreate', async interaction => { expectType>(interaction.message); expectType(interaction.guild); expectType>>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); } else if (interaction.inRawGuild()) { expectAssignable(interaction); expectType(interaction.component); expectType>(interaction.message); expectType(interaction.guild); expectType>>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); } else if (interaction.inGuild()) { expectAssignable(interaction); expectType(interaction.component); expectType(interaction.message); expectAssignable(interaction.guild); expectType>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); } } @@ -1973,18 +1996,21 @@ client.on('interactionCreate', async interaction => { expectType>(interaction.message); expectType(interaction.guild); expectType>>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); } else if (interaction.inRawGuild()) { expectAssignable(interaction); expectType(interaction.component); expectType>(interaction.message); expectType(interaction.guild); expectType>>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); } else if (interaction.inGuild()) { expectAssignable(interaction); expectType(interaction.component); expectType(interaction.message); expectType(interaction.guild); expectType>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); } } @@ -1997,6 +2023,7 @@ client.on('interactionCreate', async interaction => { expectNotAssignable>(interaction); expectAssignable(interaction); expectType>>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); expectType(interaction.options.getMember('test')); expectType(interaction.options.getChannel('test', true)); @@ -2011,6 +2038,7 @@ client.on('interactionCreate', async interaction => { expectType(interaction.options.getMember('test')); expectAssignable(interaction); expectType>>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); expectType(interaction.options.getChannel('test', true)); expectType(interaction.options.getRole('test', true)); @@ -2035,6 +2063,7 @@ client.on('interactionCreate', async interaction => { } else { expectType(interaction); expectType>(interaction.reply({ fetchReply: true })); + expectType>(interaction.reply({ withResponse: true })); expectType(interaction.options.getMember('test')); expectType(interaction.options.getChannel('test', true)); @@ -2090,27 +2119,36 @@ client.on('interactionCreate', async interaction => { expectType(interaction.guild); expectType>>(interaction.reply({ content: 'a', fetchReply: true })); expectType>>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>>(interaction.editReply({ content: 'a' })); expectType>>(interaction.fetchReply()); expectType>>(interaction.deferUpdate({ fetchReply: true })); + expectType>(interaction.deferUpdate({ withResponse: true })); expectType>>(interaction.followUp({ content: 'a' })); } else if (interaction.inRawGuild()) { expectAssignable(interaction); expectType(interaction.guild); expectType>>(interaction.reply({ content: 'a', fetchReply: true })); expectType>>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>>(interaction.editReply({ content: 'a' })); expectType>>(interaction.fetchReply()); expectType>>(interaction.deferUpdate({ fetchReply: true })); + expectType>(interaction.deferUpdate({ withResponse: true })); expectType>>(interaction.followUp({ content: 'a' })); } else if (interaction.inGuild()) { expectAssignable(interaction); expectType(interaction.guild); expectType>(interaction.reply({ content: 'a', fetchReply: true })); expectType>(interaction.deferReply({ fetchReply: true })); + expectType>(interaction.reply({ content: 'a', withResponse: true })); + expectType>(interaction.deferReply({ withResponse: true })); expectType>(interaction.editReply({ content: 'a' })); expectType>(interaction.fetchReply()); expectType>(interaction.deferUpdate({ fetchReply: true })); + expectType>(interaction.deferUpdate({ withResponse: true })); expectType>(interaction.followUp({ content: 'a' })); } }