UNPKG

darkcord

Version:

A NodeJS Package to interact with Discord API

1,040 lines (1,039 loc) 32.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AutocompleteInteraction = exports.CommandInteraction = exports.AutoCompleteInteractionData = exports.UserApplicationCommandInteractionData = exports.MessageApplicationCommandInteractionData = exports.ChatInputApplicationCommandInteractionData = exports.AutoCompleteInteractionDataOptions = exports.CommandInteractionOptions = exports.ModalSubmitInteraction = exports.ComponentInteraction = exports.SelectMenuInteractionData = exports.SelectMenuInteractionValues = exports.ReplyableInteraction = exports.Interaction = void 0; const v10_1 = require("discord-api-types/v10"); const Resolvable_1 = require("../utils/Resolvable"); const index_1 = require("../utils/index"); const Base_1 = require("./Base"); const Member_1 = require("./Member"); const Message_1 = require("./Message"); class Interaction extends Base_1.Base { /** * Id of the application this interaction is for */ applicationId; /** * Type of interaction */ type; /** * Continuation token for responding to the interaction */ token; /** * Read-only property, always 1 */ version; /** * Member of the invoked command */ member = null; /** * User of the invoked command */ user = null; /** * The guild id it was sent from */ guildId; /** * The guild object it was sent from */ guild; /** * The channel it was sent from */ channel; /** * The channel id it was sent from * @deprecated use `channel.id` instead */ channelId; constructor(data) { super(data, data.client); this.applicationId = data.application_id; this.type = data.type; this.token = data.token; this.version = data.version; this.guildId = data.guild_id; this.guild = data.client.guilds.cache.get(data.guild_id); this.channel = data.client.channels.cache.get(data.channel?.id) ?? data.channel; this.channelId = data.channel_id; this.member = null; if ("member" in data && data.member && this.guild) { this.member = this.guild.members.add(new Member_1.Member(data.member, this.guild)); this.user = this.member.user; } else if ("member" in data && data.member) { this.member = data.member; this.user = data.member.user ? data.client.users.add(data.member.user) : null; } else { this.user = data.user ? data.client.users.add(data.user) : null; } } static from(data, res) { switch (data.type) { case v10_1.InteractionType.ApplicationCommand: { return new CommandInteraction(data, res); } case v10_1.InteractionType.MessageComponent: { return new ComponentInteraction(data, res); } case v10_1.InteractionType.ApplicationCommandAutocomplete: { return new AutocompleteInteraction(data, res); } case v10_1.InteractionType.ModalSubmit: { return new ModalSubmitInteraction(data, res); } default: { return new Interaction(data); } } } isCommand() { return this instanceof CommandInteraction; } isComponent() { return this instanceof ComponentInteraction; } isAutoComplete() { return this instanceof AutocompleteInteraction; } isModalSubmit() { return this instanceof ModalSubmitInteraction; } toJSON() { return Base_1.Base.toJSON(this, [ "applicationId", "createdAt", "id", "rawData", "token", "type", "version", ]); } } exports.Interaction = Interaction; class ReplyableInteraction extends Interaction { // For http response _http; /** * This interaction is received for webserver */ isHTTP; /** * The interaction is acknowledged */ acknowledged; constructor(data, httpResponse) { super(data); this._http = httpResponse; this.isHTTP = Boolean(httpResponse); this.acknowledged = false; } /** * Respond to this interaction with defer */ async defer(flags) { if (this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionAlreadyAcknowledged", message: "You have already acknowledged this interaction.", }); } if (this.isHTTP) { await this._http?.send({ flags: flags || 0, }, v10_1.InteractionResponseType.DeferredChannelMessageWithSource); this.acknowledged = true; } else { await this._client.rest.respondInteraction(this.id, this.token, { flags: flags || 0, }, v10_1.InteractionResponseType.DeferredChannelMessageWithSource); this.acknowledged = true; } } /** * Delete a interaction reply * @param messageId id of the reply to be deleted * @returns */ deleteReply(messageId) { if (!this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionNoAcknowledged", message: "Acknowledge the interaction first", }); } return this._client.rest.deleteWebhookMessage(this.applicationId, this.token, messageId); } /** * Delete the original reply of this interaction * @returns */ deleteOriginalReply() { return this.deleteReply("@original"); } /** * Respond to this interaction * @param content The content of response */ async reply(content) { if (this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionAlreadyAcknowledged", message: "You have already acknowledged this interaction.", }); } content = (0, index_1.transformMessagePostData)(content); if (this.isHTTP) { await this._http?.send(content, v10_1.InteractionResponseType.ChannelMessageWithSource); } else { await this._client.rest.respondInteraction(this.id, this.token, content, v10_1.InteractionResponseType.ChannelMessageWithSource); } this.acknowledged = true; } /** * Edit a interaction reply * @param messageId * @param content */ async editReply(messageId, content) { await this._client.rest.editWebhookMessage(this.applicationId, this.token, messageId, (0, index_1.transformMessagePostData)(content)); } /** * Edit the original reply of this interaction * @param content * @returns */ editOriginalReply(content) { return this.editReply("@original", content); } /** * Create a followup message for the original reply of this interaction * @param content * @returns */ createFollowUp(content) { if (!this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionNoAcknowledged", message: "Acknowledge the interaction first", }); } return this._client.rest.executeWebhook(this.applicationId, this.token, (0, index_1.transformMessagePostData)(content)); } /** * Delete a followup message * @param messageId The id of the followup to be deleted * @returns */ deleteFollowUp(messageId) { return this._client.rest.deleteWebhookMessage(this.applicationId, this.token, messageId); } /** * Edit a followup message * @param messageId The id of the followup to be edited * @param content The new content of followup * @returns */ async editFollowUp(messageId, content) { const data = (await this._client.rest.editWebhookMessage(this.applicationId, this.token, messageId, content)); const channel = this._client.channels.cache.get(data.channel_id); const message = channel?.messages?.get(data.id); if (message) { return message._update(data); } else { const guildId = channel.isGuildChannel() ? channel.guildId : undefined; const message_1 = new Message_1.Message({ client: this._client, ...data }, guildId ? Resolvable_1.Resolvable.resolveGuild(guildId, this._client) : undefined); return Resolvable_1.Resolvable.resolveMessage(message_1, this._client); } } /** * Get a followup message * @param messageId The id of the followup * @returns */ async getFollowUp(messageId) { const data = (await this._client.rest.getWebhookMessage(this.applicationId, this.token, messageId)); const channel = this._client.channels.cache.get(data.channel_id); const guildId = channel.isGuildChannel() ? channel.guildId : undefined; const message = new Message_1.Message({ client: this._client, ...data }, guildId ? Resolvable_1.Resolvable.resolveGuild(guildId, this._client) : undefined); return Resolvable_1.Resolvable.resolveMessage(message, this._client); } /** * Get the original reply of this interaction * @returns */ async getOriginalReply() { if (!this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionNoAcknowledged", message: "Acknowledge the interaction first", }); } const rawMessage = (await this._client.rest.getWebhookMessage(this.applicationId, this.token, "@original")); const channel = this._client.channels.cache.get(rawMessage.channel_id); const guildId = channel.isGuildChannel() ? channel.guildId : undefined; const message = new Message_1.Message({ ...rawMessage, client: this._client }, guildId ? Resolvable_1.Resolvable.resolveGuild(guildId, this._client) : undefined); return Resolvable_1.Resolvable.resolveMessage(message, this._client); } toJSON() { return Base_1.Base.toJSON(this, [ "applicationId", "createdAt", "id", "rawData", "token", "type", "version", "acknowledged", ]); } } exports.ReplyableInteraction = ReplyableInteraction; class SelectMenuInteractionValues { _client; guild; #resolved; _rawValues; constructor(resolved, values, _client, guild) { this._client = _client; this.guild = guild; this.#resolved = resolved; this._rawValues = values; } _get(fn) { if (!this.#resolved) { throw new Error("Cannot get channels, mentions, roles or members in string select menu"); } return this._rawValues.map(fn).filter((r) => !!r); } /** * Get the selected channels * @returns */ channels() { return this._get((id) => { const channel = this.#resolved?.channels?.[id]; return channel && this._client.channels.cache.get(channel.id); }); } /** * Get the selected users * @returns */ users() { return this._get((id) => { const user = this.#resolved?.users?.[id]; return user && this._client.users.get(user.id); }); } /** * Get the selected members * @returns */ members() { return this._get((id) => { const member = this.#resolved?.members?.[id]; return member && this.guild?.members.cache.get(id); }); } /** * Get the selected roles * @returns */ roles() { return this._get((id) => { const role = this.#resolved?.roles?.[id]; return role && this.guild?.roles.cache.get(role.id); }); } /** * Get the selected mentionables (roles, channels, users, members) * @returns */ mentionables() { return this._get((id) => { const user = this.#resolved?.users?.[id]; const channel = this.#resolved?.channels?.[id]; const role = this.#resolved?.roles?.[id]; const member = this.#resolved?.members?.[id]; if (member) { return this.guild?.members.cache.get(id) || this._client.users.get(id); } if (user) { return this._client.users.get(user.id); } if (channel) { return this._client.channels.cache.get(channel.id); } if (role) { return this._client.roles.cache.get(role.id); } }); } /** * Get the selected strings * @returns */ strings() { return this._rawValues; } } exports.SelectMenuInteractionValues = SelectMenuInteractionValues; class SelectMenuInteractionData { /** * The type of the component */ componentType; /** * The custom_id of the component */ customId; /** * Resolved data of the select menu */ resolved; /** * Values of the select menu */ values; constructor(data, guild) { this.componentType = data.component_type; this.customId = data.custom_id; this.resolved = null; if ("resolved" in data) { this.resolved = data.resolved; } this.values = new SelectMenuInteractionValues(this.resolved, data.values, data.client, guild); } } exports.SelectMenuInteractionData = SelectMenuInteractionData; class ComponentInteraction extends ReplyableInteraction { /** * The type of the component */ componentType; /** * The custom_id of the component */ customId; /** * The selected language of the invoking user */ locale; /** * The guild's preferred locale, if invoked in a guild */ guildLocale; /** * For components, the message they were attached to */ message; /** * The component data payload */ data; constructor(data, httpResponse) { super(data, httpResponse); this.componentType = data.data.component_type; this.customId = data.data.custom_id; this.locale = data.locale; this.guildLocale = data.guild_locale ?? null; const rawMessage = data.message; this.message = Resolvable_1.Resolvable.resolveMessage(new Message_1.Message({ ...rawMessage, client: data.client, }), data.client); this.data = null; if ([ v10_1.ComponentType.ChannelSelect, v10_1.ComponentType.MentionableSelect, v10_1.ComponentType.RoleSelect, v10_1.ComponentType.StringSelect, v10_1.ComponentType.UserSelect, ].some((type) => this.componentType === type)) { this.data = new SelectMenuInteractionData({ client: data.client, ...data.data, }, this.guild); } } async deferUpdate() { if (this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionAlreadyAcknowledged", message: "You have already acknowledged this interaction.", }); } if (this.isHTTP) { await this._http?.send({}, v10_1.InteractionResponseType.DeferredMessageUpdate); } else { await this._client.rest.respondInteraction(this.id, this.token, {}, v10_1.InteractionResponseType.DeferredMessageUpdate); } } async editParent(content) { content = (0, index_1.transformMessagePostData)(content); if (this.acknowledged) { return this.editOriginalReply(content); } if (this.isHTTP) { await this._http?.send(content, v10_1.InteractionResponseType.UpdateMessage); } else { await this._client.rest.respondInteraction(this.id, this.token, content, v10_1.InteractionResponseType.UpdateMessage); } } toJSON() { return Base_1.Base.toJSON(this, [ "applicationId", "createdAt", "id", "rawData", "token", "type", "version", "channel", "channelId", "locale", "guildLocale", "message", "componentType", "acknowledged", "customId", ]); } } exports.ComponentInteraction = ComponentInteraction; class ModalSubmitInteraction extends ReplyableInteraction { /** * The selected language of the invoking user */ locale; /** * The guild's preferred locale, if invoked in a guild */ guildLocale; /** * For components, the message they were attached to */ message; /** * A developer-defined identifier for the component, max 100 characters */ customId; /** * A list of child components */ components; constructor(data, httpResponse) { super(data, httpResponse); this.locale = data.locale; this.guildLocale = data.guild_locale ?? null; this.message = data.message ? Resolvable_1.Resolvable.resolveMessage(new Message_1.Message({ ...data.message, client: data.client }), data.client) : null; this.customId = data.data.custom_id; this.components = data.data.components; } async deferUpdate() { if (this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionAlreadyAcknowledged", message: "You have already acknowledged this interaction.", }); } if (this.isHTTP) { await this._http?.send({}, v10_1.InteractionResponseType.DeferredMessageUpdate); } else { await this._client.rest.respondInteraction(this.id, this.token, {}, v10_1.InteractionResponseType.DeferredMessageUpdate); } } async editParent(data) { if (this.acknowledged) { return this.editOriginalReply(data); } if (this.isHTTP) { await this._http?.send(data, v10_1.InteractionResponseType.UpdateMessage); } else { await this._client.rest.respondInteraction(this.id, this.token, data, v10_1.InteractionResponseType.UpdateMessage); } } toJSON() { return Base_1.Base.toJSON(this, [ "applicationId", "createdAt", "id", "rawData", "token", "type", "version", "acknowledged", "channel", "channelId", "locale", "guildLocale", "message", ]); } } exports.ModalSubmitInteraction = ModalSubmitInteraction; class CommandInteractionOptions { _client; guild; #options; subCommand; subCommandGroup; #resolved; constructor(options, resolved, _client, guild) { this._client = _client; this.guild = guild; this.#resolved = resolved; if (options[0]?.type === v10_1.ApplicationCommandOptionType.Subcommand) { this.subCommand = options[0].name; options = options[0].options ?? []; } if (options[0]?.type === v10_1.ApplicationCommandOptionType.SubcommandGroup) { this.subCommandGroup = options[0].name; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition options = options[0].options ?? []; } this.#options = options; } /** * Get a option * @param name The name of option * @returns */ get(name) { const option = this.#options.find((o) => o.name === name); switch (option?.type) { case v10_1.ApplicationCommandOptionType.Attachment: { return { value: this.#resolved.attachments?.[option.value] ?? null, name: option.name, type: option.type, }; } case v10_1.ApplicationCommandOptionType.User: { const user = this.#resolved.users?.[option.value]; return { value: user ? this._client.users.add(user) : null, name: option.name, type: option.type, }; } case v10_1.ApplicationCommandOptionType.Channel: { const channel = this.#resolved.channels?.[option.value]; return { value: channel ? this._client.channels.cache.get(channel.id) || channel : null, name: option.name, type: option.type, }; } case v10_1.ApplicationCommandOptionType.Role: { const role = this.#resolved.roles?.[option.value]; return { value: role && this.guild ? this.guild.roles.add(role) : null, name: option.name, type: option.type, }; } case v10_1.ApplicationCommandOptionType.Mentionable: { const user = this.#resolved.users?.[option.value]; const role = this.#resolved.roles?.[option.value]; const member = this.#resolved.members?.[option.value]; return { value: (member && this.guild?.members.cache.get(option.value)) || (user && this._client.users.add(user)) || (role && this.guild?.roles.add(role)) || null, name: option.name, type: option.type, }; } default: { return option; } } } /** * Get a string option value * @param name Option name * @returns */ string(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.String ? r.value : null; } /** * Get a number option value * @param name Option name * @returns */ number(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.Number ? r.value : null; } /** * Get a integer option value * @param name Option name * @returns */ integer(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.Integer ? r.value : null; } /** * Get a boolean option value * @param name Option name * @returns */ boolean(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.Boolean ? r.value : null; } /** * Get a attachment option value * @param name Option name * @returns */ attachment(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.Attachment ? r.value : null; } /** * Get a user option value * @param name Option name * @returns */ user(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.User ? r.value : null; } /** * Get a role option value * @param name Option name * @returns */ role(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.Role ? r.value : null; } /** * Get a channel option value * @param name Option name * @returns */ channel(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.Channel ? r.value : null; } /** * Get a mentionable (user, member, role, channel) option value * @param name Option name * @returns */ mentionable(name) { const r = this.get(name); return r?.type === v10_1.ApplicationCommandOptionType.Mentionable ? r.value : null; } toArray() { return this.#options; } } exports.CommandInteractionOptions = CommandInteractionOptions; class AutoCompleteInteractionDataOptions extends CommandInteractionOptions { /** * Gets the focused option * @returns */ focusedOption() { return super.toArray()[0]; } } exports.AutoCompleteInteractionDataOptions = AutoCompleteInteractionDataOptions; class ChatInputApplicationCommandInteractionData extends Base_1.Base { /** * The type of the invoked command */ type; /** * The name of the invoked command */ name; /** * The guild ID of the invoked command */ guildId; /** * The options of the invoked command */ options; constructor(data, guild) { super(data, guild?._client); this.name = data.name; this.type = data.type; this.options = data.options ? new CommandInteractionOptions(data.options, data.resolved, data.client, guild) : null; } } exports.ChatInputApplicationCommandInteractionData = ChatInputApplicationCommandInteractionData; class MessageApplicationCommandInteractionData extends Base_1.Base { guild; /** * The message that the interaction was executed */ message; /** * Id of the message targeted */ targetId; /** * The type of the invoked command */ type; /** * The name of the invoked command */ name; constructor(data, guild) { super(data, data.client); this.guild = guild; this.name = data.name; this.type = data.type; this.targetId = data.target_id; const rawMessage = data.resolved.messages[0]; const channel = data.client.channels.cache.get(rawMessage.channel_id); const message = (this.message = new Message_1.Message({ ...rawMessage, client: data.client, })); if (channel && channel.isText()) { this.message = channel.messages.cache.get(rawMessage.id) ?? channel.messages.add(message); } } } exports.MessageApplicationCommandInteractionData = MessageApplicationCommandInteractionData; class UserApplicationCommandInteractionData extends Base_1.Base { /** * User target */ target; /** * The name of the invoked command */ name; /** * Id of the user targeted */ targetId; /** * Guild member target */ targetMember; /** * The type of the invoked command */ type; constructor(data, guild) { super(data, guild?._client); this.type = data.type; this.target = this._client.cache.users.get(data.target_id) ?? data.resolved.users[data.target_id]; this.targetId = data.target_id; this.name = data.name; this.targetMember = guild?.members.cache.get(data.target_id) ?? data.resolved.members?.[data.target_id]; } } exports.UserApplicationCommandInteractionData = UserApplicationCommandInteractionData; class AutoCompleteInteractionData extends Base_1.Base { /** * The name of the invoked command */ name; /** * The type of the invoked command */ type; /** * The options of the invoked command */ options; constructor(data, guild) { super(data); this.name = data.name; this.type = data.type; this.options = data.options ? new AutoCompleteInteractionDataOptions(data.options, data.resolved, data.client, guild) : null; } } exports.AutoCompleteInteractionData = AutoCompleteInteractionData; class CommandInteraction extends ReplyableInteraction { /** * The guild's preferred locale, if invoked in a guild */ guildLocale; /** * The command data payload */ data; /** * The name of the invoked command */ commandName; /** * The selected language of the invoking user */ locale; /** * For components, the message they were attached to */ message; constructor(data, httpResponse) { super(data, httpResponse); this.guildLocale = data.guild_locale; this.commandName = data.data.name; this.locale = data.locale; this.message = data.message ? Resolvable_1.Resolvable.resolveMessage(new Message_1.Message({ ...data.message, client: data.client }), data.client) : null; if (data.data.type === v10_1.ApplicationCommandType.ChatInput) { this.data = new ChatInputApplicationCommandInteractionData({ ...data.data, client: this._client, }, this.guild); } else if (data.data.type === v10_1.ApplicationCommandType.Message) { this.data = new MessageApplicationCommandInteractionData({ ...data.data, client: this._client, }, this.guild); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (data.data.type === v10_1.ApplicationCommandType.User) { this.data = new UserApplicationCommandInteractionData({ ...data.data, client: this._client, }, this.guild); } } /** * Create a modal */ async createModal(data) { if (this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionAlreadyAcknowledged", message: "You have already acknowledged this interaction.", }); } if (this.isHTTP) { await this._http?.send(data, v10_1.InteractionResponseType.Modal); this.acknowledged = true; } else { await this._client.rest.respondInteraction(this.id, this.token, data, v10_1.InteractionResponseType.Modal); this.acknowledged = true; } } isMessageCommand() { return this.data.type === v10_1.ApplicationCommandType.Message; } isUserCommand() { return this.data.type === v10_1.ApplicationCommandType.User; } isChatInputCommand() { return this.data.type === v10_1.ApplicationCommandType.ChatInput; } toJSON() { return Base_1.Base.toJSON(this, [ "applicationId", "createdAt", "id", "rawData", "token", "type", "version", "acknowledged", "channel", "channelId", "commandName", "data", "guild", "guildId", "guildLocale", "message", "member", "user", "locale", ]); } } exports.CommandInteraction = CommandInteraction; class AutocompleteInteraction extends Interaction { // For http response _http; /** * This interaction is received for webserver */ isHTTP; /** * The interaction is acknowledged */ acknowledged; /** * The command data payload */ data; constructor(data, httpResponse) { super(data); this._http = httpResponse; this.isHTTP = Boolean(httpResponse); this.acknowledged = false; this.data = new AutoCompleteInteractionData({ ...data.data, client: data.client, }, data.client.guilds.cache.get(data.guild_id)); } async result(choices) { if (this.acknowledged) { throw (0, index_1.MakeError)({ name: "InteractionAlreadyAcknowledged", message: "You have already acknowledged this interaction.", }); } if (this.isHTTP) { await this._http?.send({ choices }, v10_1.InteractionResponseType.ApplicationCommandAutocompleteResult); } else { await this._client.rest.respondInteraction(this.id, this.token, { choices }, v10_1.InteractionResponseType.ApplicationCommandAutocompleteResult); } this.acknowledged = true; } } exports.AutocompleteInteraction = AutocompleteInteraction;