fix: message builders (#10802)

* fix: message builders

- Added `clearParse`, `clearRoles`, and `clearUsers` methods to the `AllowedMentionsBuilder`, since passing an empty array and omitting the these fields behave differently
- Strictened assertions
- Removed `AttachmentBuilder#clearId`, as it is a required field
- Added missing `MessageBuilder#setEmbeds`
- Added missing `MessageReferenceBuilder#setFailIfNotExists`
- Improve/fix documentation
- Consistency™️

* fix: updater functions return type

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Almeida
2025-04-25 21:52:00 +01:00
committed by GitHub
parent d81b4be2cd
commit 8f375275ca
6 changed files with 165 additions and 70 deletions

View File

@@ -1,3 +1,5 @@
/* eslint-disable jsdoc/check-param-names */
import type { JSONEncodable } from '@discordjs/util';
import type {
APIActionRowComponent,
@@ -53,6 +55,9 @@ export interface MessageBuilderData
poll?: PollBuilder;
}
/**
* A builder that creates API-compatible JSON data for messages.
*/
export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJSONBody> {
/**
* The API data associated with this message.
@@ -81,19 +86,27 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
}
/**
* Creates new attachment builder from API data.
* Creates a new message builder from API data.
*
* @param data - The API data to create this attachment with
* @param data - The API data to create this message builder with
*/
public constructor(data: Partial<RESTPostAPIChannelMessageJSONBody> = {}) {
public constructor({
attachments = [],
embeds = [],
components = [],
message_reference,
poll,
allowed_mentions,
...data
}: Partial<RESTPostAPIChannelMessageJSONBody> = {}) {
this.data = {
...structuredClone(data),
allowed_mentions: data.allowed_mentions ? new AllowedMentionsBuilder(data.allowed_mentions) : undefined,
attachments: data.attachments?.map((attachment) => new AttachmentBuilder(attachment)) ?? [],
embeds: data.embeds?.map((embed) => new EmbedBuilder(embed)) ?? [],
poll: data.poll ? new PollBuilder(data.poll) : undefined,
components: data.components?.map((component) => createComponentBuilder(component)) ?? [],
message_reference: data.message_reference ? new MessageReferenceBuilder(data.message_reference) : undefined,
allowed_mentions: allowed_mentions && new AllowedMentionsBuilder(allowed_mentions),
attachments: attachments.map((attachment) => new AttachmentBuilder(attachment)),
embeds: embeds.map((embed) => new EmbedBuilder(embed)),
poll: poll && new PollBuilder(poll),
components: components.map((component) => createComponentBuilder(component)),
message_reference: message_reference && new MessageReferenceBuilder(message_reference),
};
}
@@ -135,6 +148,8 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
/**
* Sets whether the message is TTS.
*
* @param tts - Whether the message is TTS
*/
public setTTS(tts = true): this {
this.data.tts = tts;
@@ -213,6 +228,15 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
return this;
}
/**
* Sets the embeds for this message.
*
* @param embeds - The embeds to set
*/
public setEmbeds(...embeds: RestOrArray<APIEmbed | EmbedBuilder | ((builder: EmbedBuilder) => EmbedBuilder)>): this {
return this.spliceEmbeds(0, this.embeds.length, ...normalizeArray(embeds));
}
/**
* Sets the allowed mentions for this message.
*
@@ -233,8 +257,8 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
*
* @param updater - The function to update the allowed mentions with
*/
public updateAllowedMentions(updater: (builder: AllowedMentionsBuilder) => AllowedMentionsBuilder): this {
this.data.allowed_mentions = updater(this.data.allowed_mentions ?? new AllowedMentionsBuilder());
public updateAllowedMentions(updater: (builder: AllowedMentionsBuilder) => void): this {
updater((this.data.allowed_mentions ??= new AllowedMentionsBuilder()));
return this;
}
@@ -266,8 +290,8 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
*
* @param updater - The function to update the message reference with
*/
public updateMessageReference(updater: (builder: MessageReferenceBuilder) => MessageReferenceBuilder): this {
this.data.message_reference = updater(this.data.message_reference ?? new MessageReferenceBuilder());
public updateMessageReference(updater: (builder: MessageReferenceBuilder) => void): this {
updater((this.data.message_reference ??= new MessageReferenceBuilder()));
return this;
}
@@ -451,8 +475,7 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
* @param stickerIds - The ids of the stickers to set
*/
public setStickerIds(...stickerIds: RestOrArray<Snowflake>): this {
this.data.sticker_ids = normalizeArray(stickerIds) as MessageBuilderData['sticker_ids'];
return this;
return this.spliceStickerIds(0, this.data.sticker_ids?.length ?? 0, ...normalizeArray(stickerIds));
}
/**
@@ -508,10 +531,7 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
public setAttachments(
...attachments: RestOrArray<APIAttachment | AttachmentBuilder | ((builder: AttachmentBuilder) => AttachmentBuilder)>
): this {
const resolved = normalizeArray(attachments).map((attachment) => resolveBuilder(attachment, AttachmentBuilder));
this.data.attachments = resolved;
return this;
return this.spliceAttachments(0, this.data.attachments.length, ...normalizeArray(attachments));
}
/**
@@ -569,6 +589,8 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
/**
* Sets the flags for this message.
*
* @param flags - The flags to set
*/
public setFlags(flags: MessageFlags): this {
this.data.flags = flags;
@@ -584,7 +606,9 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
}
/**
* Sets `enforce_nonce` for this message.
* Sets whether to enforce recent uniqueness of the nonce of this message.
*
* @param enforceNonce - Whether to enforce recent uniqueness of the nonce of this message
*/
public setEnforceNonce(enforceNonce = true): this {
this.data.enforce_nonce = enforceNonce;
@@ -606,8 +630,8 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
*
* @param updater - The function to update the poll with
*/
public updatePoll(updater: (builder: PollBuilder) => PollBuilder): this {
this.data.poll = updater(this.data.poll ?? new PollBuilder());
public updatePoll(updater: (builder: PollBuilder) => void): this {
updater((this.data.poll ??= new PollBuilder()));
return this;
}
@@ -632,12 +656,12 @@ export class MessageBuilder implements JSONEncodable<RESTPostAPIChannelMessageJS
const data = {
...structuredClone(rest),
// Wherever we pass false, it's covered by the messagePredicate already
poll: this.data.poll?.toJSON(false),
poll: poll?.toJSON(false),
allowed_mentions: allowed_mentions?.toJSON(false),
attachments: attachments.map((attachment) => attachment.toJSON(false)),
embeds: this.data.embeds.map((embed) => embed.toJSON(false)),
embeds: embeds.map((embed) => embed.toJSON(false)),
// Here, the messagePredicate does specific constraints rather than using the componentPredicate
components: this.data.components?.map((component) => component.toJSON(validationOverride)),
components: components.map((component) => component.toJSON(validationOverride)),
message_reference: message_reference?.toJSON(false),
};