Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions packages/discord.js/src/structures/MessagePayload.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const { Buffer } = require('node:buffer');
const { isJSONEncodable, lazy } = require('@discordjs/util');
const { isJSONEncodable, isRawFileEncodable, lazy } = require('@discordjs/util');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const { DiscordjsError, DiscordjsRangeError, ErrorCodes } = require('../errors/index.js');
const { resolveFile } = require('../util/DataResolver.js');
Expand Down Expand Up @@ -190,13 +190,20 @@ class MessagePayload {
}
}

const attachments = this.options.files?.map((file, index) => ({
id: index.toString(),
description: file.description,
title: file.title,
waveform: file.waveform,
duration_secs: file.duration,
}));
const attachments = this.options.files?.map((file, index) =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const attachments = this.options.files?.map((file, index) =>
let attachments = this.options.files?.map((file, index) =>

isRawFileEncodable(file)
? {
id: index.toString(),
...file.toJSON(),
}
: {
id: index.toString(),
description: file.description,
title: file.title,
waveform: file.waveform,
duration_secs: file.duration,
},
);

// Only passable during edits
if (Array.isArray(this.options.attachments)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (Array.isArray(this.options.attachments)) {
if (Array.isArray(this.options.attachments)) {
attachments ??= [];

Expand Down Expand Up @@ -276,6 +283,8 @@ class MessagePayload {
if (ownAttachment) {
attachment = fileLike;
name = findName(attachment);
} else if (isRawFileEncodable(fileLike)) {
return fileLike.getRawFile();
} else {
attachment = fileLike.attachment;
name = fileLike.name ?? findName(attachment);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class TextBasedChannel {
* @property {Array<(EmbedBuilder|Embed|APIEmbed)>} [embeds] The embeds for the message
* @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
* (see {@link https://discord.com/developers/docs/resources/message#allowed-mentions-object here} for more details)
* @property {Array<(Attachment|AttachmentPayload|BufferResolvable|FileBodyEncodable<APIAttachment>|Stream)>} [files]
* @property {Array<(Attachment|AttachmentPayload|BufferResolvable|RawFileEncodable|Stream)>} [files]
* The files to send with the message.
* @property {Array<(ActionRowBuilder|MessageTopLevelComponent|APIMessageTopLevelComponent)>} [components]
* Action rows containing interactive components for the message (buttons, select menus) and other
Expand Down
4 changes: 2 additions & 2 deletions packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MessagePort, Worker } from 'node:worker_threads';
import { ApplicationCommandOptionAllowedChannelType, MessageActionRowComponentBuilder } from '@discordjs/builders';
import { Collection, ReadonlyCollection } from '@discordjs/collection';
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions, EmojiURLOptions } from '@discordjs/rest';
import { Awaitable, FileBodyEncodable, JSONEncodable } from '@discordjs/util';
import { Awaitable, FileBodyEncodable, JSONEncodable, RawFileEncodable } from '@discordjs/util';
import { WebSocketManager, WebSocketManagerOptions } from '@discordjs/ws';
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
import {
Expand Down Expand Up @@ -6721,7 +6721,7 @@ export interface BaseMessageOptions {
)[];
content?: string;
embeds?: readonly (APIEmbed | JSONEncodable<APIEmbed>)[];
files?: readonly (Attachment | AttachmentPayload | BufferResolvable | FileBodyEncodable<APIAttachment> | Stream)[];
files?: readonly (Attachment | AttachmentPayload | BufferResolvable | RawFileEncodable | Stream)[];
}

export interface MessageOptionsPoll {
Expand Down
22 changes: 22 additions & 0 deletions packages/util/src/encodables.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { RESTAPIAttachment } from 'discord-api-types/v10';
import type { RawFile } from './RawFile.js';

/**
Expand Down Expand Up @@ -59,3 +60,24 @@ export interface FileBodyEncodable<BodyValue> {
export function isFileBodyEncodable(maybeEncodable: unknown): maybeEncodable is FileBodyEncodable<unknown> {
return maybeEncodable !== null && typeof maybeEncodable === 'object' && 'toFileBody' in maybeEncodable;
}

/**
* Represents an object capable of representing itself as a raw file attachment.
* Objects implementing this interface can return binary file data to be sent as part of
* multipart/form-data requests.
*/
export interface RawFileEncodable extends JSONEncodable<RESTAPIAttachment> {
/**
* Returns the raw file of an attachment.
*/
getRawFile(): Partial<RawFile> | undefined;
}

/**
* Indicates if an object is raw file encodable or not.
*
* @param maybeEncodable - The object to check against
*/
export function isRawFileEncodable(maybeEncodable: unknown): maybeEncodable is RawFileEncodable {
return isJSONEncodable(maybeEncodable) && 'getRawFile' in maybeEncodable;
}