UNPKG

slash-create-modify

Version:

Create and sync Discord slash commands!

330 lines (329 loc) 16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MessageInteractionContext = void 0; const constants_1 = require("../../constants"); const util_1 = require("../../util"); const member_1 = require("../member"); const user_1 = require("../user"); const message_1 = require("../message"); const permissions_1 = require("../permissions"); /** Represents a interaction context that handles messages. */ class MessageInteractionContext { /** * @param creator The instantiating creator. * @param data The interaction data. * @param respond The response function for the interaction. */ constructor(creator, data, respond) { /** The time when the interaction was created. */ this.invokedAt = Date.now(); /** Whether the initial response was sent. */ this.initiallyResponded = false; /** Whether there is a deferred message available. */ this.deferred = false; this.creator = creator; this._respond = respond; this.interactionToken = data.token; this.interactionID = data.id; this.channelID = data.channel_id; this.guildID = 'guild_id' in data ? data.guild_id : undefined; this.locale = 'locale' in data ? data.locale : undefined; this.guildLocale = 'guild_locale' in data ? data.guild_locale : undefined; this.member = 'guild_id' in data ? new member_1.Member(data.member, this.creator, data.guild_id) : undefined; this.user = new user_1.User('guild_id' in data ? data.member.user : data.user, this.creator); this.appPermissions = data.app_permissions ? new permissions_1.Permissions(BigInt(data.app_permissions)) : undefined; } /** Whether the interaction has expired. Interactions last 15 minutes. */ get expired() { return this.invokedAt + 1000 * 60 * 15 < Date.now(); } /** * Fetches a message. * @param messageID The ID of the message, defaults to the original message */ async fetch(messageID = '@original') { const data = await this.creator.requestHandler.request('GET', constants_1.Endpoints.MESSAGE(this.creator.options.applicationID, this.interactionToken, messageID)); if (messageID === '@original') this.messageID = data.id; return new message_1.Message(data, this.creator, this); } /** * Sends a message, if it already made an initial response, this will create a follow-up message. * IF the context has created a deferred message, it will edit that deferred message, * and future calls to this function create follow ups. * This will return a boolean if it's an initial response, otherwise a {@link Message} will be returned. * Note that when making a follow-up message, the `ephemeral` option is ignored. * @param content The content of the message * @param options The message options */ async send(content, options) { if (this.expired) throw new Error('This interaction has expired'); if (typeof content !== 'string') options = content; else if (typeof options !== 'object') options = {}; if (typeof options !== 'object') throw new Error('Message options is not an object.'); options = { ...options }; if (!options.content && typeof content === 'string') options.content = content; if (!options.content && !options.embeds && !options.file) throw new Error('Message content, embeds and files are not given.'); if (options.ephemeral && !options.flags) options.flags = constants_1.InteractionResponseFlags.EPHEMERAL; const allowedMentions = options.allowedMentions ? util_1.formatAllowedMentions(options.allowedMentions, this.creator.allowedMentions) : this.creator.allowedMentions; if (!this.initiallyResponded) { this.initiallyResponded = true; clearTimeout(this._timeout); await this._respond({ status: 200, body: { type: constants_1.InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { tts: options.tts, content: options.content, embeds: options.embeds, flags: options.flags, allowed_mentions: allowedMentions, components: options.components, attachments: options.attachments } }, files: options.file ? (Array.isArray(options.file) ? options.file : [options.file]) : undefined }); return true; } else if (this.initiallyResponded && this.deferred) return this.editOriginal(content, options); else return this.sendFollowUp(content, options); } /** * Sends a follow-up message. * @param content The content of the message * @param options The message options */ async sendFollowUp(content, options) { if (this.expired) throw new Error('This interaction has expired'); if (typeof content !== 'string') options = content; else if (typeof options !== 'object') options = {}; if (typeof options !== 'object') throw new Error('Message options is not an object.'); options = { ...options }; if (!options.content && typeof content === 'string') options.content = content; if (!options.content && !options.embeds && !options.file) throw new Error('Message content, embeds or files need to be given.'); if (options.ephemeral && !options.flags) options.flags = constants_1.InteractionResponseFlags.EPHEMERAL; const allowedMentions = options.allowedMentions ? util_1.formatAllowedMentions(options.allowedMentions, this.creator.allowedMentions) : this.creator.allowedMentions; const data = await this.creator.requestHandler.request('POST', constants_1.Endpoints.FOLLOWUP_MESSAGE(this.creator.options.applicationID, this.interactionToken), true, { tts: options.tts, content: options.content, embeds: options.embeds, allowed_mentions: allowedMentions, components: options.components, flags: options.flags, attachments: options.attachments }, options.file); return new message_1.Message(data, this.creator, this); } /** * Edits a message. * @param messageID The message's ID * @param content The content of the message * @param options The message options */ async edit(messageID, content, options) { if (this.expired) throw new Error('This interaction has expired'); if (typeof content !== 'string') options = content; else if (typeof options !== 'object') options = {}; if (typeof options !== 'object') throw new Error('Message options is not an object.'); options = { ...options }; if (!options.content && typeof content === 'string') options.content = content; if (!options.content && !options.embeds && !options.components && !options.file && !options.attachments) throw new Error('No valid options were given.'); const allowedMentions = options.allowedMentions ? util_1.formatAllowedMentions(options.allowedMentions, this.creator.allowedMentions) : this.creator.allowedMentions; let data; try { data = await this.creator.requestHandler.request('PATCH', constants_1.Endpoints.MESSAGE(this.creator.options.applicationID, this.interactionToken, messageID), true, { content: options.content, embeds: options.embeds, allowed_mentions: allowedMentions, components: options.components, attachments: options.attachments }, options.file); } catch (e) { data = await this.creator.requestHandler.request('PATCH', `/channels/${this.channelID}/messages/${this.message.id}`, true, { content: options.content, embeds: options.embeds, allowed_mentions: allowedMentions, components: options.components, attachments: options.attachments }, options.file); } return new message_1.Message(data, this.creator, this); } /** * Edits the original message. * Note: This will error with ephemeral messages or deferred ephemeral messages. * @param content The content of the message * @param options The message options */ async editOriginal(content, options) { this.deferred = false; const message = await this.edit('@original', content, options); this.messageID = message.id; return message; } /** * Deletes a message. If the message ID was not defined, the original message is used. * @param messageID The message's ID */ async delete(messageID) { if (this.expired) throw new Error('This interaction has expired'); const res = await this.creator.requestHandler.request('DELETE', constants_1.Endpoints.MESSAGE(this.creator.options.applicationID, this.interactionToken, messageID)); if (!messageID || messageID === '@original') this.messageID = undefined; return res; } /** * Creates a deferred message. To users, this will show as * "Bot is thinking..." until the deferred message is edited. * @param ephemeral Whether to make the deferred message ephemeral. * @returns Whether the deferred message passed */ async defer(ephemeral = false) { if (!this.initiallyResponded && !this.deferred) { this.initiallyResponded = true; this.deferred = true; clearTimeout(this._timeout); await this._respond({ status: 200, body: { type: constants_1.InteractionResponseType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, data: { flags: ephemeral ? constants_1.InteractionResponseFlags.EPHEMERAL : 0 } } }); return true; } return false; } /** * Registers a component callback from the initial message. * This unregisters automatically when the context expires. * @param custom_id The custom ID of the component to register * @param callback The callback to use on interaction * @param expiration The expiration time of the callback in milliseconds. Use null for no expiration (Although, in this case, global components might be more consistent). * @param onExpired A function to be called when the component expires. */ registerComponent(custom_id, callback, expiration = 1000 * 60 * 15, onExpired) { if (!this.initiallyResponded || this.deferred) throw new Error('You must send a message before registering components'); if (!this.messageID) throw new Error('Fetch your original message or use deferred messages before registering components'); this.creator._componentCallbacks.set(`${this.messageID}-${custom_id}`, { callback, expires: expiration != null ? Date.now() + expiration : undefined, onExpired }); if (expiration != null && this.creator.options.componentTimeouts) setTimeout(() => { if (this.creator._componentCallbacks.has(`${this.messageID}-${custom_id}`)) { if (onExpired) onExpired(); this.creator._componentCallbacks.delete(`${this.messageID}-${custom_id}`); } }, expiration); } /** * Registers a component callback from a message. * This unregisters automatically when the context expires. * @param message_id The message ID of the component to register * @param custom_id The custom ID of the component to register * @param callback The callback to use on interaction * @param expiration The expiration time of the callback in milliseconds. Use null for no expiration (Although, in this case, global components might be more consistent). * @param onExpired A function to be called when the component expires. */ registerComponentFrom(message_id, custom_id, callback, expiration = 1000 * 60 * 15, onExpired) { this.creator._componentCallbacks.set(`${message_id}-${custom_id}`, { callback, expires: expiration != null ? Date.now() + expiration : undefined, onExpired }); if (expiration != null && this.creator.options.componentTimeouts) setTimeout(() => { if (this.creator._componentCallbacks.has(`${message_id}-${custom_id}`)) { if (onExpired) onExpired(); this.creator._componentCallbacks.delete(`${message_id}-${custom_id}`); } }, expiration); } /** * Unregisters a component callback. * @param custom_id The custom ID of the component to unregister * @param message_id The message ID of the component to unregister, defaults to initial message ID if any */ unregisterComponent(custom_id, message_id) { if (!message_id) { if (!this.messageID) throw new Error('The initial message ID was not provided by the context!'); else message_id = this.messageID; } return this.creator._componentCallbacks.delete(`${message_id}-${custom_id}`); } /** * Registers a wildcard component callback on a message. * This unregisters automatically when the context expires. * @param message_id The message ID of the component to register * @param callback The callback to use on interaction * @param expiration The expiration time of the callback in milliseconds. Use null for no expiration (Although, in this case, global components might be more consistent). * @param onExpired A function to be called when the component expires. */ registerWildcardComponent(message_id, callback, expiration = 1000 * 60 * 15, onExpired) { if (this.expired) throw new Error('This interaction has expired'); this.creator._componentCallbacks.set(`${message_id}-*`, { callback, expires: expiration != null ? this.invokedAt + expiration : undefined, onExpired }); if (expiration != null && this.creator.options.componentTimeouts) setTimeout(() => { if (this.creator._componentCallbacks.has(`${message_id}-*`)) { if (onExpired) onExpired(); this.creator._componentCallbacks.delete(`${message_id}-*`); } }, expiration); } /** * Unregisters a component callback. * @param message_id The message ID of the component to unregister, defaults to the invoking message ID. */ unregisterWildcardComponent(message_id) { return this.creator._componentCallbacks.delete(`${message_id}-*`); } } exports.MessageInteractionContext = MessageInteractionContext;