From e4450c8417624b71d779cb4f41692538f9165e10 Mon Sep 17 00:00:00 2001 From: sowgro Date: Sat, 2 Sep 2023 19:12:47 -0400 Subject: first commit --- .../structures/interfaces/InteractionResponses.js | 320 +++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 node_modules/discord.js/src/structures/interfaces/InteractionResponses.js (limited to 'node_modules/discord.js/src/structures/interfaces/InteractionResponses.js') diff --git a/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js b/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js new file mode 100644 index 0000000..15256e3 --- /dev/null +++ b/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js @@ -0,0 +1,320 @@ +'use strict'; + +const { isJSONEncodable } = require('@discordjs/util'); +const { InteractionResponseType, MessageFlags, Routes, InteractionType } = require('discord-api-types/v10'); +const { DiscordjsError, ErrorCodes } = require('../../errors'); +const InteractionCollector = require('../InteractionCollector'); +const InteractionResponse = require('../InteractionResponse'); +const MessagePayload = require('../MessagePayload'); + +/** + * @typedef {Object} ModalComponentData + * @property {string} title The title of the modal + * @property {string} customId The custom id of the modal + * @property {ActionRow[]} components The components within this modal + */ + +/** + * Interface for classes that support shared interaction response types. + * @interface + */ +class InteractionResponses { + /** + * Options for deferring the reply to an {@link BaseInteraction}. + * @typedef {Object} InteractionDeferReplyOptions + * @property {boolean} [ephemeral] Whether the reply should be ephemeral + * @property {boolean} [fetchReply] Whether to fetch the reply + */ + + /** + * Options for deferring and updating the reply to a {@link MessageComponentInteraction}. + * @typedef {Object} InteractionDeferUpdateOptions + * @property {boolean} [fetchReply] Whether to fetch the reply + */ + + /** + * Options for a reply to a {@link BaseInteraction}. + * @typedef {BaseMessageOptions} InteractionReplyOptions + * @property {boolean} [tts=false] Whether the message should be spoken aloud + * @property {boolean} [ephemeral] Whether the reply should be ephemeral + * @property {boolean} [fetchReply] Whether to fetch the reply + * @property {MessageFlags} [flags] Which flags to set for the message. + * Only `MessageFlags.SuppressEmbeds` and `MessageFlags.Ephemeral` can be set. + */ + + /** + * Options for updating the message received from a {@link MessageComponentInteraction}. + * @typedef {MessageEditOptions} InteractionUpdateOptions + * @property {boolean} [fetchReply] Whether to fetch the reply + */ + + /** + * Defers the reply to this interaction. + * @param {InteractionDeferReplyOptions} [options] Options for deferring the reply to this interaction + * @returns {Promise} + * @example + * // Defer the reply to this interaction + * interaction.deferReply() + * .then(console.log) + * .catch(console.error) + * @example + * // Defer to send an ephemeral reply later + * interaction.deferReply({ ephemeral: true }) + * .then(console.log) + * .catch(console.error); + */ + async deferReply(options = {}) { + if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); + this.ephemeral = options.ephemeral ?? false; + await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { + body: { + type: InteractionResponseType.DeferredChannelMessageWithSource, + data: { + flags: options.ephemeral ? MessageFlags.Ephemeral : undefined, + }, + }, + auth: false, + }); + this.deferred = true; + + return options.fetchReply ? this.fetchReply() : new InteractionResponse(this); + } + + /** + * Creates a reply to this interaction. + * Use the `fetchReply` option to get the bot's reply message. + * @param {string|MessagePayload|InteractionReplyOptions} options The options for the reply + * @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}`)) + * .catch(console.error); + * @example + * // Create an ephemeral reply with an embed + * const embed = new EmbedBuilder().setDescription('Pong!'); + * + * interaction.reply({ embeds: [embed], ephemeral: true }) + * .then(() => console.log('Reply sent.')) + * .catch(console.error); + */ + async reply(options) { + if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); + this.ephemeral = options.ephemeral ?? false; + + 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), { + body: { + type: InteractionResponseType.ChannelMessageWithSource, + data, + }, + files, + auth: false, + }); + this.replied = true; + + return options.fetchReply ? this.fetchReply() : new InteractionResponse(this); + } + + /** + * Fetches a reply to this interaction. + * @see Webhook#fetchMessage + * @param {Snowflake|'@original'} [message='@original'] The response to fetch + * @returns {Promise} + * @example + * // Fetch the initial reply to this interaction + * interaction.fetchReply() + * .then(reply => console.log(`Replied with ${reply.content}`)) + * .catch(console.error); + */ + fetchReply(message = '@original') { + return this.webhook.fetchMessage(message); + } + + /** + * Options that can be passed into {@link InteractionResponses#editReply}. + * @typedef {WebhookMessageEditOptions} InteractionEditReplyOptions + * @property {MessageResolvable|'@original'} [message='@original'] The response to edit + */ + + /** + * Edits a reply to this interaction. + * @see Webhook#editMessage + * @param {string|MessagePayload|InteractionEditReplyOptions} options The new options for the message + * @returns {Promise} + * @example + * // Edit the initial reply to this interaction + * interaction.editReply('New content') + * .then(console.log) + * .catch(console.error); + */ + async editReply(options) { + if (!this.deferred && !this.replied) throw new DiscordjsError(ErrorCodes.InteractionNotReplied); + const msg = await this.webhook.editMessage(options.message ?? '@original', options); + this.replied = true; + return msg; + } + + /** + * Deletes a reply to this interaction. + * @see Webhook#deleteMessage + * @param {MessageResolvable|'@original'} [message='@original'] The response to delete + * @returns {Promise} + * @example + * // Delete the initial reply to this interaction + * interaction.deleteReply() + * .then(console.log) + * .catch(console.error); + */ + async deleteReply(message = '@original') { + await this.webhook.deleteMessage(message); + } + + /** + * Send a follow-up message to this interaction. + * @param {string|MessagePayload|InteractionReplyOptions} options The options for the reply + * @returns {Promise} + */ + followUp(options) { + if (!this.deferred && !this.replied) return Promise.reject(new DiscordjsError(ErrorCodes.InteractionNotReplied)); + return this.webhook.send(options); + } + + /** + * 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} + * @example + * // Defer updating and reset the component's loading state + * interaction.deferUpdate() + * .then(console.log) + * .catch(console.error); + */ + async deferUpdate(options = {}) { + if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); + await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { + body: { + type: InteractionResponseType.DeferredMessageUpdate, + }, + auth: false, + }); + this.deferred = true; + + return 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} + * @example + * // Remove the components from the message + * interaction.update({ + * content: "A component interaction was received", + * components: [] + * }) + * .then(console.log) + * .catch(console.error); + */ + async update(options) { + if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); + + 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), { + body: { + type: InteractionResponseType.UpdateMessage, + data, + }, + files, + auth: false, + }); + this.replied = true; + + return 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} + */ + async showModal(modal) { + if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); + 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, + }); + this.replied = true; + } + + /** + * An object containing the same properties as {@link CollectorOptions}, but a few less: + * @typedef {Object} AwaitModalSubmitOptions + * @property {CollectorFilter} [filter] The filter applied to this collector + * @property {number} time Time in milliseconds to wait for an interaction before rejecting + */ + + /** + * Collects a single modal submit interaction that passes the filter. + * The Promise will reject if the time expires. + * @param {AwaitModalSubmitOptions} options Options to pass to the internal collector + * @returns {Promise} + * @example + * // Collect a modal submit interaction + * const filter = (interaction) => interaction.customId === 'modal'; + * interaction.awaitModalSubmit({ filter, time: 15_000 }) + * .then(interaction => console.log(`${interaction.customId} was submitted!`)) + * .catch(console.error); + */ + awaitModalSubmit(options) { + if (typeof options.time !== 'number') throw new DiscordjsError(ErrorCodes.InvalidType, 'time', 'number'); + const _options = { ...options, max: 1, interactionType: InteractionType.ModalSubmit }; + return new Promise((resolve, reject) => { + const collector = new InteractionCollector(this.client, _options); + collector.once('end', (interactions, reason) => { + const interaction = interactions.first(); + if (interaction) resolve(interaction); + else reject(new DiscordjsError(ErrorCodes.InteractionCollectorError, reason)); + }); + }); + } + + static applyToClass(structure, ignore = []) { + const props = [ + 'deferReply', + 'reply', + 'fetchReply', + 'editReply', + 'deleteReply', + 'followUp', + 'deferUpdate', + 'update', + 'showModal', + 'awaitModalSubmit', + ]; + + for (const prop of props) { + if (ignore.includes(prop)) continue; + Object.defineProperty( + structure.prototype, + prop, + Object.getOwnPropertyDescriptor(InteractionResponses.prototype, prop), + ); + } + } +} + +module.exports = InteractionResponses; -- cgit v1.2.3