feat: handle file upload component for v14 (#11179)

* feat: handle file upload component

* chore: fix import

* chore: typings

* fix: `Snowflake`

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
Naiyar
2025-10-24 18:19:37 +06:00
committed by GitHub
parent 0ff239a602
commit 104ad754f3
5 changed files with 60 additions and 7 deletions

View File

@@ -85,6 +85,7 @@ class CommandInteraction extends BaseInteraction {
* @property {Collection<Snowflake, GuildMember|APIGuildMember>} [members] The resolved guild members * @property {Collection<Snowflake, GuildMember|APIGuildMember>} [members] The resolved guild members
* @property {Collection<Snowflake, Role|APIRole>} [roles] The resolved roles * @property {Collection<Snowflake, Role|APIRole>} [roles] The resolved roles
* @property {Collection<Snowflake, BaseChannel|APIChannel>} [channels] The resolved channels * @property {Collection<Snowflake, BaseChannel|APIChannel>} [channels] The resolved channels
* @property {Collection<Snowflake, Attachment>} [attachments] The resolved attachments
*/ */
/** /**
@@ -92,7 +93,6 @@ class CommandInteraction extends BaseInteraction {
* *
* @typedef {BaseInteractionResolvedData} CommandInteractionResolvedData * @typedef {BaseInteractionResolvedData} CommandInteractionResolvedData
* @property {Collection<Snowflake, Message|APIMessage>} [messages] The resolved messages * @property {Collection<Snowflake, Message|APIMessage>} [messages] The resolved messages
* @property {Collection<Snowflake, Attachment>} [attachments] The resolved attachments
*/ */
/** /**

View File

@@ -216,6 +216,17 @@ class ModalSubmitFields {
return null; 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; module.exports = ModalSubmitFields;

View File

@@ -9,6 +9,7 @@ const InteractionResponses = require('./interfaces/InteractionResponses');
const { transformResolved } = require('../util/Util'); const { transformResolved } = require('../util/Util');
const getMessage = lazy(() => require('./Message').Message); const getMessage = lazy(() => require('./Message').Message);
const getAttachment = lazy(() => require('./Attachment'));
/** /**
* @typedef {Object} BaseModalData * @typedef {Object} BaseModalData
@@ -16,6 +17,13 @@ const getMessage = lazy(() => require('./Message').Message);
* @property {number} id The id of the field * @property {number} id The id of the field
*/ */
/**
* @typedef {BaseModalData} FileUploadModalData
* @property {string} customId The custom id of the file upload
* @property {string[]} values The values of the file upload
* @property {Collection<string, Attachment>} [attachments] The resolved attachments
*/
/** /**
* @typedef {BaseModalData} TextInputModalData * @typedef {BaseModalData} TextInputModalData
* @property {string} customId The custom id of the field * @property {string} customId The custom id of the field
@@ -37,7 +45,7 @@ const getMessage = lazy(() => require('./Message').Message);
*/ */
/** /**
* @typedef {SelectMenuModalData|TextInputModalData} ModalData * @typedef {SelectMenuModalData|TextInputModalData|FileUploadModalData} ModalData
*/ */
/** /**
@@ -161,7 +169,7 @@ class ModalSubmitInteraction extends BaseInteraction {
/* eslint-disable max-depth */ /* eslint-disable max-depth */
if (resolved) { if (resolved) {
const { members, users, channels, roles } = resolved; const { members, users, channels, roles, attachments } = resolved;
const valueSet = new Set(rawComponent.values); const valueSet = new Set(rawComponent.values);
if (users) { if (users) {
@@ -204,6 +212,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));
}
}
}
} }
/* eslint-enable max-depth */ /* eslint-enable max-depth */

View File

@@ -23,7 +23,8 @@ const { ComponentType } = require('discord-api-types/v10');
/** /**
* @typedef {StringSelectMenuComponentData|TextInputComponentData|UserSelectMenuComponentData| * @typedef {StringSelectMenuComponentData|TextInputComponentData|UserSelectMenuComponentData|
* RoleSelectMenuComponentData|MentionableSelectMenuComponentData|ChannelSelectMenuComponentData} ComponentInLabelData * RoleSelectMenuComponentData|MentionableSelectMenuComponentData|ChannelSelectMenuComponentData|
* FileUploadComponentData} ComponentInLabelData
*/ */
/** /**
@@ -43,6 +44,14 @@ const { ComponentType } = require('discord-api-types/v10');
* @property {string} [url] The URL of the button * @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 * @typedef {BaseComponentData} BaseSelectMenuComponentData
* @property {string} customId The custom id of the select menu * @property {string} customId The custom id of the select menu

View File

@@ -367,7 +367,8 @@ export type ComponentInLabelData =
| UserSelectMenuComponentData | UserSelectMenuComponentData
| ChannelSelectMenuComponentData | ChannelSelectMenuComponentData
| RoleSelectMenuComponentData | RoleSelectMenuComponentData
| MentionableSelectMenuComponentData; | MentionableSelectMenuComponentData
| FileUploadComponentData;
export interface LabelComponentData extends BaseComponentData { export interface LabelComponentData extends BaseComponentData {
type: ComponentType.Label; type: ComponentType.Label;
@@ -2823,7 +2824,12 @@ export interface SelectMenuModalData<Cached extends CacheType = CacheType>
values: readonly string[]; values: readonly string[];
} }
export type ModalData = SelectMenuModalData | TextInputModalData; export interface FileUploadModalData extends BaseModalData<ComponentType.FileUpload> {
customId: string;
files: readonly Attachment[];
}
export type ModalData = FileUploadModalData | SelectMenuModalData | TextInputModalData;
export interface LabelModalData extends BaseModalData<ComponentType.Label> { export interface LabelModalData extends BaseModalData<ComponentType.Label> {
component: readonly ModalData[]; component: readonly ModalData[];
@@ -2900,6 +2906,8 @@ export class ModalSubmitFields<Cached extends CacheType = CacheType> {
public getSelectedMentionables(customId: string, required: true): ModalSelectedMentionables<Cached>; public getSelectedMentionables(customId: string, required: true): ModalSelectedMentionables<Cached>;
public getSelectedMentionables(customId: string, required?: boolean): ModalSelectedMentionables<Cached> | null; public getSelectedMentionables(customId: string, required?: boolean): ModalSelectedMentionables<Cached> | null;
public getUploadedFiles(customId: string, required: true): ReadonlyCollection<Snowflake, Attachment>;
public getUploadedFiles(customId: string, required?: boolean): ReadonlyCollection<Snowflake, Attachment> | null;
} }
export interface ModalMessageModalSubmitInteraction<Cached extends CacheType = CacheType> export interface ModalMessageModalSubmitInteraction<Cached extends CacheType = CacheType>
@@ -6184,6 +6192,7 @@ export interface CommandInteractionOption<Cached extends CacheType = CacheType>
} }
export interface BaseInteractionResolvedData<Cached extends CacheType = CacheType> { export interface BaseInteractionResolvedData<Cached extends CacheType = CacheType> {
attachments?: ReadonlyCollection<Snowflake, Attachment>;
channels?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Channel, APIInteractionDataResolvedChannel>>; channels?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Channel, APIInteractionDataResolvedChannel>>;
members?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, GuildMember, APIInteractionDataResolvedGuildMember>>; members?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, GuildMember, APIInteractionDataResolvedGuildMember>>;
roles?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Role, APIRole>>; roles?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Role, APIRole>>;
@@ -6192,7 +6201,6 @@ export interface BaseInteractionResolvedData<Cached extends CacheType = CacheTyp
export interface CommandInteractionResolvedData<Cached extends CacheType = CacheType> export interface CommandInteractionResolvedData<Cached extends CacheType = CacheType>
extends BaseInteractionResolvedData<Cached> { extends BaseInteractionResolvedData<Cached> {
attachments?: ReadonlyCollection<Snowflake, Attachment>;
messages?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Message, APIMessage>>; messages?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Message, APIMessage>>;
} }
@@ -7430,6 +7438,14 @@ export interface TextInputComponentData extends BaseComponentData {
placeholder?: string; placeholder?: string;
} }
export interface FileUploadComponentData extends BaseComponentData {
customId: string;
maxValues?: number;
minValues?: number;
required?: number;
type: ComponentType.FileUpload;
}
export type MessageTarget = export type MessageTarget =
| Interaction | Interaction
| InteractionWebhook | InteractionWebhook