diff --git a/packages/discord.js/src/structures/CommandInteraction.js b/packages/discord.js/src/structures/CommandInteraction.js index 11aa38579583..079c9293912f 100644 --- a/packages/discord.js/src/structures/CommandInteraction.js +++ b/packages/discord.js/src/structures/CommandInteraction.js @@ -96,6 +96,7 @@ class CommandInteraction extends BaseInteraction { * @property {Collection} [members] The resolved guild members * @property {Collection} [roles] The resolved roles * @property {Collection} [channels] The resolved channels + * @property {Collection} [attachments] The resolved attachments */ /** @@ -103,7 +104,6 @@ class CommandInteraction extends BaseInteraction { * * @typedef {BaseInteractionResolvedData} CommandInteractionResolvedData * @property {Collection} [messages] The resolved messages - * @property {Collection} [attachments] The resolved attachments */ /** diff --git a/packages/discord.js/src/structures/ModalComponentResolver.js b/packages/discord.js/src/structures/ModalComponentResolver.js index 3991316cda3e..48c33f50ee6e 100644 --- a/packages/discord.js/src/structures/ModalComponentResolver.js +++ b/packages/discord.js/src/structures/ModalComponentResolver.js @@ -222,6 +222,17 @@ class ModalComponentResolver { 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} 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; + } } exports.ModalComponentResolver = ModalComponentResolver; diff --git a/packages/discord.js/src/structures/ModalSubmitInteraction.js b/packages/discord.js/src/structures/ModalSubmitInteraction.js index efd574da8925..167932f284b1 100644 --- a/packages/discord.js/src/structures/ModalSubmitInteraction.js +++ b/packages/discord.js/src/structures/ModalSubmitInteraction.js @@ -9,6 +9,7 @@ const { ModalComponentResolver } = require('./ModalComponentResolver.js'); const { InteractionResponses } = require('./interfaces/InteractionResponses.js'); const getMessage = lazy(() => require('./Message.js').Message); +const getAttachment = lazy(() => require('./Attachment.js').Attachment); /** * @typedef {Object} BaseModalData @@ -26,6 +27,13 @@ const getMessage = lazy(() => require('./Message.js').Message); * @property {Collection} [channels] The resolved channels */ +/** + * @typedef {BaseModalData} FileUploadModalData + * @property {string} customId The custom id of the file upload + * @property {string[]} values The values of the file upload + * @property {Collection} [attachments] The resolved attachments + */ + /** * @typedef {BaseModalData} TextInputModalData * @property {string} customId The custom id of the component @@ -37,7 +45,7 @@ const getMessage = lazy(() => require('./Message.js').Message); */ /** - * @typedef {SelectMenuModalData|TextInputModalData} ModalData + * @typedef {SelectMenuModalData|TextInputModalData|FileUploadModalData} ModalData */ /** @@ -155,7 +163,7 @@ class ModalSubmitInteraction extends BaseInteraction { if (rawComponent.values) { data.values = rawComponent.values; if (resolved) { - const { members, users, channels, roles } = resolved; + const { members, users, channels, roles, attachments } = resolved; const valueSet = new Set(rawComponent.values); if (users) { @@ -198,6 +206,15 @@ class ModalSubmitInteraction extends BaseInteraction { } } } + + if (attachments) { + data.attachments = new Collection(); + for (const [id, attachment] of Object.entries(attachments)) { + if (valueSet.has(id)) { + data.attachments.set(id, new (getAttachment())(attachment)); + } + } + } } } diff --git a/packages/discord.js/src/util/Components.js b/packages/discord.js/src/util/Components.js index 7a8488e496d0..3f507cfd0973 100644 --- a/packages/discord.js/src/util/Components.js +++ b/packages/discord.js/src/util/Components.js @@ -24,7 +24,7 @@ const { ComponentType } = require('discord-api-types/v10'); /** * @typedef {StringSelectMenuComponentData|TextInputComponentData|UserSelectMenuComponentData| - * RoleSelectMenuComponentData|MentionableSelectMenuComponentData|ChannelSelectMenuComponentData} ComponentInLabelData + * RoleSelectMenuComponentData|MentionableSelectMenuComponentData|ChannelSelectMenuComponentData|FileUploadComponentData} ComponentInLabelData */ /** @@ -44,6 +44,14 @@ const { ComponentType } = require('discord-api-types/v10'); * @property {string} [url] The URL of the button */ +/** + * @typedef {BaseComponentData} FileUploadComponentData + * @property {string} customId The custom id of the file upload + * @property {number} [minValues] The minimum number of files that can be uploaded (0-10) + * @property {number} [maxValues] The maximum number of files that can be uploaded (1-10) + * @property {boolean} [required] Whether this component is required in modals + */ + /** * @typedef {BaseComponentData} BaseSelectMenuComponentData * @property {string} customId The custom id of the select menu diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index d687f05ee662..9325fbb69e80 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -2580,7 +2580,12 @@ export interface SelectMenuModalData values: readonly string[]; } -export type ModalData = SelectMenuModalData | TextInputModalData; +export interface FileUploadModalData extends BaseModalData { + customId: string; + files: readonly Attachment[]; +} + +export type ModalData = FileUploadModalData | SelectMenuModalData | TextInputModalData; export interface LabelModalData extends BaseModalData { component: readonly ModalData[]; @@ -2653,6 +2658,8 @@ export class ModalComponentResolver { public getSelectedMentionables(customId: string, required: true): ModalSelectedMentionables; public getSelectedMentionables(customId: string, required?: boolean): ModalSelectedMentionables | null; + public getUploadedFiles(customId: string, required: true): ReadonlyCollection; + public getUploadedFiles(customId: string, required?: boolean): ReadonlyCollection | null; } export interface ModalMessageModalSubmitInteraction @@ -5621,6 +5628,7 @@ export interface CommandInteractionOption } export interface BaseInteractionResolvedData { + attachments?: ReadonlyCollection; channels?: ReadonlyCollection>; members?: ReadonlyCollection>; roles?: ReadonlyCollection>; @@ -5629,7 +5637,6 @@ export interface BaseInteractionResolvedData extends BaseInteractionResolvedData { - attachments?: ReadonlyCollection; messages?: ReadonlyCollection>; } @@ -6838,6 +6845,14 @@ export interface TextInputComponentData extends BaseComponentData { value?: string; } +export interface FileUploadComponentData extends BaseComponentData { + customId: string; + maxValues?: number; + minValues?: number; + required?: number; + type: ComponentType.FileUpload; +} + export type MessageTarget = | ChannelManager | Interaction