UNPKG

discord.js

Version:

A powerful library for interacting with the Discord API

233 lines (208 loc) • 8.07 kB
'use strict'; const { Collection } = require('@discordjs/collection'); const { ComponentType } = require('discord-api-types/v10'); const { DiscordjsTypeError, ErrorCodes } = require('../errors'); /** * @typedef {Object} ModalSelectedMentionables * @property {Collection<Snowflake, User>} users The selected users * @property {Collection<Snowflake, GuildMember | APIGuildMember>} members The selected members * @property {Collection<Snowflake, Role | APIRole>} roles The selected roles */ /** * Represents the serialized fields from a modal submit interaction */ class ModalSubmitFields { constructor(components, resolved) { /** * The components within the modal * * @type {Array<ActionRowModalData|LabelModalData|TextDisplayModalData>} */ this.components = components; /** * The interaction resolved data * * @name ModalSubmitFields#resolved * @type {?Readonly<BaseInteractionResolvedData>} */ Object.defineProperty(this, 'resolved', { value: resolved ? Object.freeze(resolved) : null }); /** * The extracted fields from the modal * @type {Collection<string, ModalData>} */ this.fields = components.reduce((accumulator, next) => { // For legacy support of action rows if ('components' in next) { for (const component of next.components) accumulator.set(component.customId, component); } // For label components if ('component' in next) { accumulator.set(next.component.customId, next.component); } return accumulator; }, new Collection()); } /** * Gets a field given a custom id from a component * @param {string} customId The custom id of the component * @param {ComponentType} [type] The type of the component * @returns {ModalData} */ getField(customId, type) { const field = this.fields.get(customId); if (!field) throw new DiscordjsTypeError(ErrorCodes.ModalSubmitInteractionFieldNotFound, customId); if (type !== undefined && type !== field.type) { throw new DiscordjsTypeError(ErrorCodes.ModalSubmitInteractionFieldType, customId, field.type, type); } return field; } /** * Gets a component by custom id and property and checks its type. * * @param {string} customId The custom id of the component. * @param {ComponentType[]} allowedTypes The allowed types of the component. * @param {string[]} properties The properties to check for for `required`. * @param {boolean} required Whether to throw an error if the component value(s) are not found. * @returns {ModalData} The option, if found. * @private */ _getTypedComponent(customId, allowedTypes, properties, required) { const component = this.getField(customId); if (!allowedTypes.includes(component.type)) { throw new DiscordjsTypeError( ErrorCodes.ModalSubmitInteractionFieldNotFound, customId, component.type, allowedTypes.join(', '), ); } else if (required && properties.every(prop => component[prop] === null || component[prop] === undefined)) { throw new DiscordjsTypeError(ErrorCodes.ModalSubmitInteractionFieldEmpty, customId, component.type); } return component; } /** * Gets the value of a text input component given a custom id * @param {string} customId The custom id of the text input component * @returns {string} */ getTextInputValue(customId) { return this._getTypedComponent(customId, [ComponentType.TextInput]).value; } /** * Gets the values of a string select component given a custom id * * @param {string} customId The custom id of the string select component * @returns {string[]} */ getStringSelectValues(customId) { return this._getTypedComponent(customId, [ComponentType.StringSelect]).values; } /** * Gets users component * * @param {string} customId The custom id of the component * @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty * @returns {?Collection<Snowflake, User>} The selected users, or null if none were selected and not required */ getSelectedUsers(customId, required = false) { const component = this._getTypedComponent( customId, [ComponentType.UserSelect, ComponentType.MentionableSelect], ['users'], required, ); return component.users ?? null; } /** * Gets roles component * * @param {string} customId The custom id of the component * @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty * @returns {?Collection<Snowflake, Role|APIRole>} The selected roles, or null if none were selected and not required */ getSelectedRoles(customId, required = false) { const component = this._getTypedComponent( customId, [ComponentType.RoleSelect, ComponentType.MentionableSelect], ['roles'], required, ); return component.roles ?? null; } /** * Gets channels component * * @param {string} customId The custom id of the component * @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty * @param {ChannelType[]} [channelTypes=[]] The allowed types of channels. If empty, all channel types are allowed. * @returns {?Collection<Snowflake, GuildChannel|ThreadChannel|APIChannel>} The selected channels, * or null if none were selected and not required */ getSelectedChannels(customId, required = false, channelTypes = []) { const component = this._getTypedComponent(customId, [ComponentType.ChannelSelect], ['channels'], required); const channels = component.channels; if (channels && channelTypes.length > 0) { for (const channel of channels.values()) { if (!channelTypes.includes(channel.type)) { throw new DiscordjsTypeError( ErrorCodes.ModalSubmitInteractionComponentInvalidChannelType, customId, channel.type, channelTypes.join(', '), ); } } } return channels ?? null; } /** * Gets members component * * @param {string} customId The custom id of the component * @returns {?Collection<Snowflake, GuildMember|APIGuildMember>} The selected members, * or null if none were selected or the users were not present in the guild */ getSelectedMembers(customId) { const component = this._getTypedComponent( customId, [ComponentType.UserSelect, ComponentType.MentionableSelect], ['members'], false, ); return component.members ?? null; } /** * Gets mentionables component * * @param {string} customId The custom id of the component * @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty * @returns {?ModalSelectedMentionables} The selected mentionables, or null if none were selected and not required */ getSelectedMentionables(customId, required = false) { const component = this._getTypedComponent( customId, [ComponentType.MentionableSelect], ['users', 'members', 'roles'], required, ); if (component.users || component.members || component.roles) { return { users: component.users ?? new Collection(), members: component.members ?? new Collection(), roles: component.roles ?? new Collection(), }; } return null; } /** * Gets file upload component * * @param {string} customId The custom id of the component * @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty * @returns {?Collection<Snowflake, Attachment>} The uploaded files, or null if none were uploaded and not required */ getUploadedFiles(customId, required = false) { return this._getTypedComponent(customId, [ComponentType.FileUpload], ['attachments'], required).attachments ?? null; } } module.exports = ModalSubmitFields;