@ovencord/builders 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +92 -0
  3. package/package.json +70 -0
  4. package/src/Assertions.ts +15 -0
  5. package/src/components/ActionRow.ts +346 -0
  6. package/src/components/Assertions.ts +190 -0
  7. package/src/components/Component.ts +47 -0
  8. package/src/components/Components.ts +275 -0
  9. package/src/components/button/Button.ts +34 -0
  10. package/src/components/button/CustomIdButton.ts +74 -0
  11. package/src/components/button/LinkButton.ts +39 -0
  12. package/src/components/button/PremiumButton.ts +26 -0
  13. package/src/components/button/mixins/EmojiOrLabelButtonMixin.ts +52 -0
  14. package/src/components/fileUpload/Assertions.ts +12 -0
  15. package/src/components/fileUpload/FileUpload.ts +109 -0
  16. package/src/components/label/Assertions.ts +28 -0
  17. package/src/components/label/Label.ts +215 -0
  18. package/src/components/selectMenu/BaseSelectMenu.ts +89 -0
  19. package/src/components/selectMenu/ChannelSelectMenu.ts +115 -0
  20. package/src/components/selectMenu/MentionableSelectMenu.ts +126 -0
  21. package/src/components/selectMenu/RoleSelectMenu.ts +89 -0
  22. package/src/components/selectMenu/StringSelectMenu.ts +165 -0
  23. package/src/components/selectMenu/StringSelectMenuOption.ts +113 -0
  24. package/src/components/selectMenu/UserSelectMenu.ts +89 -0
  25. package/src/components/textInput/Assertions.ts +15 -0
  26. package/src/components/textInput/TextInput.ts +154 -0
  27. package/src/components/v2/Assertions.ts +82 -0
  28. package/src/components/v2/Container.ts +254 -0
  29. package/src/components/v2/File.ts +81 -0
  30. package/src/components/v2/MediaGallery.ts +128 -0
  31. package/src/components/v2/MediaGalleryItem.ts +85 -0
  32. package/src/components/v2/Section.ts +266 -0
  33. package/src/components/v2/Separator.ts +82 -0
  34. package/src/components/v2/TextDisplay.ts +63 -0
  35. package/src/components/v2/Thumbnail.ts +100 -0
  36. package/src/index.ts +109 -0
  37. package/src/interactions/commands/Command.ts +87 -0
  38. package/src/interactions/commands/SharedName.ts +68 -0
  39. package/src/interactions/commands/SharedNameAndDescription.ts +69 -0
  40. package/src/interactions/commands/chatInput/Assertions.ts +180 -0
  41. package/src/interactions/commands/chatInput/ChatInputCommand.ts +40 -0
  42. package/src/interactions/commands/chatInput/ChatInputCommandSubcommands.ts +117 -0
  43. package/src/interactions/commands/chatInput/mixins/ApplicationCommandNumericOptionMinMaxValueMixin.ts +52 -0
  44. package/src/interactions/commands/chatInput/mixins/ApplicationCommandOptionChannelTypesMixin.ts +57 -0
  45. package/src/interactions/commands/chatInput/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts +32 -0
  46. package/src/interactions/commands/chatInput/mixins/ApplicationCommandOptionWithChoicesMixin.ts +43 -0
  47. package/src/interactions/commands/chatInput/mixins/SharedChatInputCommandOptions.ts +204 -0
  48. package/src/interactions/commands/chatInput/mixins/SharedSubcommands.ts +61 -0
  49. package/src/interactions/commands/chatInput/options/ApplicationCommandOptionBase.ts +66 -0
  50. package/src/interactions/commands/chatInput/options/attachment.ts +20 -0
  51. package/src/interactions/commands/chatInput/options/boolean.ts +20 -0
  52. package/src/interactions/commands/chatInput/options/channel.ts +28 -0
  53. package/src/interactions/commands/chatInput/options/integer.ts +34 -0
  54. package/src/interactions/commands/chatInput/options/mentionable.ts +20 -0
  55. package/src/interactions/commands/chatInput/options/number.ts +34 -0
  56. package/src/interactions/commands/chatInput/options/role.ts +20 -0
  57. package/src/interactions/commands/chatInput/options/string.ts +71 -0
  58. package/src/interactions/commands/chatInput/options/user.ts +20 -0
  59. package/src/interactions/commands/contextMenu/Assertions.ts +31 -0
  60. package/src/interactions/commands/contextMenu/ContextMenuCommand.ts +42 -0
  61. package/src/interactions/commands/contextMenu/MessageCommand.ts +19 -0
  62. package/src/interactions/commands/contextMenu/UserCommand.ts +19 -0
  63. package/src/interactions/modals/Assertions.ts +27 -0
  64. package/src/interactions/modals/Modal.ts +158 -0
  65. package/src/messages/AllowedMentions.ts +193 -0
  66. package/src/messages/Assertions.ts +148 -0
  67. package/src/messages/Attachment.ts +209 -0
  68. package/src/messages/Message.ts +692 -0
  69. package/src/messages/MessageReference.ts +111 -0
  70. package/src/messages/embed/Assertions.ts +53 -0
  71. package/src/messages/embed/Embed.ts +352 -0
  72. package/src/messages/embed/EmbedAuthor.ts +83 -0
  73. package/src/messages/embed/EmbedField.ts +67 -0
  74. package/src/messages/embed/EmbedFooter.ts +65 -0
  75. package/src/messages/poll/Assertions.ts +20 -0
  76. package/src/messages/poll/Poll.ts +243 -0
  77. package/src/messages/poll/PollAnswer.ts +77 -0
  78. package/src/messages/poll/PollAnswerMedia.ts +38 -0
  79. package/src/messages/poll/PollMedia.ts +41 -0
  80. package/src/messages/poll/PollQuestion.ts +20 -0
  81. package/src/util/ValidationError.ts +21 -0
  82. package/src/util/normalizeArray.ts +19 -0
  83. package/src/util/resolveBuilder.ts +40 -0
  84. package/src/util/validation.ts +58 -0
@@ -0,0 +1,692 @@
1
+ import type { FileBodyEncodable, FileBodyEncodableResult, JSONEncodable, RawFile } from '@ovencord/util';
2
+ import type {
3
+ APIActionRowComponent,
4
+ APIAllowedMentions,
5
+ APIAttachment,
6
+ APIEmbed,
7
+ APIComponentInMessageActionRow,
8
+ APIMessageReference,
9
+ APIPoll,
10
+ RESTPostAPIChannelMessageJSONBody,
11
+ Snowflake,
12
+ MessageFlags,
13
+ APIContainerComponent,
14
+ APIFileComponent,
15
+ APIMediaGalleryComponent,
16
+ APISectionComponent,
17
+ APISeparatorComponent,
18
+ APITextDisplayComponent,
19
+ APIMessageTopLevelComponent,
20
+ } from 'discord-api-types/v10';
21
+ import { ActionRowBuilder } from '../components/ActionRow.js';
22
+ import { ComponentBuilder } from '../components/Component.js';
23
+ import type { MessageTopLevelComponentBuilder } from '../components/Components.js';
24
+ import { createComponentBuilder } from '../components/Components.js';
25
+ import { ContainerBuilder } from '../components/v2/Container.js';
26
+ import { FileBuilder } from '../components/v2/File.js';
27
+ import { MediaGalleryBuilder } from '../components/v2/MediaGallery.js';
28
+ import { SectionBuilder } from '../components/v2/Section.js';
29
+ import { SeparatorBuilder } from '../components/v2/Separator.js';
30
+ import { TextDisplayBuilder } from '../components/v2/TextDisplay.js';
31
+ import { normalizeArray, type RestOrArray } from '../util/normalizeArray.js';
32
+ import { resolveBuilder } from '../util/resolveBuilder.js';
33
+ import { validate } from '../util/validation.js';
34
+ import { AllowedMentionsBuilder } from './AllowedMentions.js';
35
+ import { fileBodyMessagePredicate, messagePredicate } from './Assertions.js';
36
+ import { AttachmentBuilder } from './Attachment.js';
37
+ import { MessageReferenceBuilder } from './MessageReference.js';
38
+ import { EmbedBuilder } from './embed/Embed.js';
39
+ import { PollBuilder } from './poll/Poll.js';
40
+
41
+ export interface MessageBuilderData extends Partial<
42
+ Omit<
43
+ RESTPostAPIChannelMessageJSONBody,
44
+ 'allowed_mentions' | 'attachments' | 'components' | 'embeds' | 'message_reference' | 'poll'
45
+ >
46
+ > {
47
+ allowed_mentions?: AllowedMentionsBuilder;
48
+ attachments: AttachmentBuilder[];
49
+ components: MessageTopLevelComponentBuilder[];
50
+ embeds: EmbedBuilder[];
51
+ message_reference?: MessageReferenceBuilder;
52
+ poll?: PollBuilder;
53
+ }
54
+
55
+ /**
56
+ * A builder that creates API-compatible JSON data for messages.
57
+ */
58
+ export class MessageBuilder
59
+ implements JSONEncodable<RESTPostAPIChannelMessageJSONBody>, FileBodyEncodable<RESTPostAPIChannelMessageJSONBody>
60
+ {
61
+ /**
62
+ * The API data associated with this message.
63
+ */
64
+ private readonly data: MessageBuilderData;
65
+
66
+ /**
67
+ * Gets the attachments of this message.
68
+ */
69
+ public get attachments(): readonly AttachmentBuilder[] {
70
+ return this.data.attachments;
71
+ }
72
+
73
+ /**
74
+ * Gets the components of this message.
75
+ */
76
+ public get components(): readonly MessageTopLevelComponentBuilder[] {
77
+ return this.data.components;
78
+ }
79
+
80
+ /**
81
+ * Gets the embeds of this message.
82
+ */
83
+ public get embeds(): readonly EmbedBuilder[] {
84
+ return this.data.embeds;
85
+ }
86
+
87
+ /**
88
+ * Creates a new message builder.
89
+ *
90
+ * @param data - The API data to create this message with
91
+ */
92
+ public constructor(data: Partial<RESTPostAPIChannelMessageJSONBody> = {}) {
93
+ const { attachments = [], embeds = [], components = [], message_reference, poll, allowed_mentions, ...rest } = data;
94
+
95
+ this.data = {
96
+ ...structuredClone(rest),
97
+ allowed_mentions: allowed_mentions && new AllowedMentionsBuilder(allowed_mentions),
98
+ attachments: attachments.map((attachment) => new AttachmentBuilder(attachment)),
99
+ embeds: embeds.map((embed) => new EmbedBuilder(embed)),
100
+ poll: poll && new PollBuilder(poll),
101
+ components: components.map((component) => createComponentBuilder(component)),
102
+ message_reference: message_reference && new MessageReferenceBuilder(message_reference),
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Sets the content of the message.
108
+ *
109
+ * @param content - The content to set
110
+ */
111
+ public setContent(content: string): this {
112
+ this.data.content = content;
113
+ return this;
114
+ }
115
+
116
+ /**
117
+ * Clears the content of the message.
118
+ */
119
+ public clearContent(): this {
120
+ this.data.content = undefined;
121
+ return this;
122
+ }
123
+
124
+ /**
125
+ * Sets the nonce of the message.
126
+ *
127
+ * @param nonce - The nonce to set
128
+ */
129
+ public setNonce(nonce: number | string): this {
130
+ this.data.nonce = nonce;
131
+ return this;
132
+ }
133
+
134
+ /**
135
+ * Clears the nonce of the message.
136
+ */
137
+ public clearNonce(): this {
138
+ this.data.nonce = undefined;
139
+ return this;
140
+ }
141
+
142
+ /**
143
+ * Sets whether the message is TTS.
144
+ *
145
+ * @param tts - Whether the message is TTS
146
+ */
147
+ public setTTS(tts = true): this {
148
+ this.data.tts = tts;
149
+ return this;
150
+ }
151
+
152
+ /**
153
+ * Appends embeds to this message.
154
+ *
155
+ * @remarks
156
+ * The maximum amount of embeds that can be added is 10.
157
+ * @example
158
+ * Using an array:
159
+ * ```ts
160
+ * const embeds: APIEmbed[] = ...;
161
+ * const message = new MessageBuilder()
162
+ * .addEmbeds(embeds);
163
+ * ```
164
+ * @example
165
+ * Using rest parameters (variadic):
166
+ * ```ts
167
+ * const message = new MessageBuilder()
168
+ * .addEmbeds(
169
+ * { title: 'Embed 1' },
170
+ * { title: 'Embed 2' },
171
+ * );
172
+ * ```
173
+ * @param embeds - The embeds to add
174
+ */
175
+ public addEmbeds(...embeds: RestOrArray<APIEmbed | EmbedBuilder | ((builder: EmbedBuilder) => EmbedBuilder)>): this {
176
+ this.data.embeds ??= [];
177
+
178
+ const resolved = normalizeArray(embeds).map((embed) => resolveBuilder(embed, EmbedBuilder));
179
+ this.data.embeds.push(...resolved);
180
+
181
+ return this;
182
+ }
183
+
184
+ /**
185
+ * Removes, replaces, or inserts embeds for this message.
186
+ *
187
+ * @remarks
188
+ * This method behaves similarly
189
+ * to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
190
+ *
191
+ * It's useful for modifying and adjusting order of the already-existing embeds of a message.
192
+ * @example
193
+ * Remove the first embed:
194
+ * ```ts
195
+ * message.spliceEmbeds(0, 1);
196
+ * ```
197
+ * @example
198
+ * Remove the first n embeds:
199
+ * ```ts
200
+ * const n = 4;
201
+ * message.spliceEmbeds(0, n);
202
+ * ```
203
+ * @example
204
+ * Remove the last embed:
205
+ * ```ts
206
+ * message.spliceEmbeds(-1, 1);
207
+ * ```
208
+ * @param start - The index to start at
209
+ * @param deleteCount - The amount of embeds to remove
210
+ * @param embeds - The embeds to insert
211
+ */
212
+ public spliceEmbeds(
213
+ start: number,
214
+ deleteCount: number,
215
+ ...embeds: RestOrArray<APIEmbed | EmbedBuilder | ((builder: EmbedBuilder) => EmbedBuilder)>
216
+ ): this {
217
+ this.data.embeds ??= [];
218
+ const resolved = normalizeArray(embeds).map((embed) => resolveBuilder(embed, EmbedBuilder));
219
+
220
+ this.data.embeds.splice(start, deleteCount, ...resolved);
221
+ return this;
222
+ }
223
+
224
+ /**
225
+ * Sets the embeds for this message.
226
+ *
227
+ * @param embeds - The embeds to set
228
+ */
229
+ public setEmbeds(...embeds: RestOrArray<APIEmbed | EmbedBuilder | ((builder: EmbedBuilder) => EmbedBuilder)>): this {
230
+ return this.spliceEmbeds(0, this.embeds.length, ...normalizeArray(embeds));
231
+ }
232
+
233
+ /**
234
+ * Sets the allowed mentions for this message.
235
+ *
236
+ * @param allowedMentions - The allowed mentions to set
237
+ */
238
+ public setAllowedMentions(
239
+ allowedMentions:
240
+ | AllowedMentionsBuilder
241
+ | APIAllowedMentions
242
+ | ((builder: AllowedMentionsBuilder) => AllowedMentionsBuilder) = new AllowedMentionsBuilder(),
243
+ ): this {
244
+ this.data.allowed_mentions = resolveBuilder(allowedMentions, AllowedMentionsBuilder);
245
+ return this;
246
+ }
247
+
248
+ /**
249
+ * Updates the allowed mentions for this message (and creates it if it doesn't exist)
250
+ *
251
+ * @param updater - The function to update the allowed mentions with
252
+ */
253
+ public updateAllowedMentions(updater: (builder: AllowedMentionsBuilder) => void): this {
254
+ updater((this.data.allowed_mentions ??= new AllowedMentionsBuilder()));
255
+ return this;
256
+ }
257
+
258
+ /**
259
+ * Clears the allowed mentions for this message.
260
+ */
261
+ public clearAllowedMentions(): this {
262
+ this.data.allowed_mentions = undefined;
263
+ return this;
264
+ }
265
+
266
+ /**
267
+ * Sets the message reference for this message.
268
+ *
269
+ * @param reference - The reference to set
270
+ */
271
+ public setMessageReference(
272
+ reference:
273
+ | APIMessageReference
274
+ | MessageReferenceBuilder
275
+ | ((builder: MessageReferenceBuilder) => MessageReferenceBuilder),
276
+ ): this {
277
+ this.data.message_reference = resolveBuilder(reference, MessageReferenceBuilder);
278
+ return this;
279
+ }
280
+
281
+ /**
282
+ * Updates the message reference for this message (and creates it if it doesn't exist)
283
+ *
284
+ * @param updater - The function to update the message reference with
285
+ */
286
+ public updateMessageReference(updater: (builder: MessageReferenceBuilder) => void): this {
287
+ updater((this.data.message_reference ??= new MessageReferenceBuilder()));
288
+ return this;
289
+ }
290
+
291
+ /**
292
+ * Clears the message reference for this message.
293
+ */
294
+ public clearMessageReference(): this {
295
+ this.data.message_reference = undefined;
296
+ return this;
297
+ }
298
+
299
+ /**
300
+ * Adds action row components to this message.
301
+ *
302
+ * @param components - The action row components to add
303
+ */
304
+ public addActionRowComponents(
305
+ ...components: RestOrArray<
306
+ | ActionRowBuilder
307
+ | APIActionRowComponent<APIComponentInMessageActionRow>
308
+ | ((builder: ActionRowBuilder) => ActionRowBuilder)
309
+ >
310
+ ): this {
311
+ this.data.components ??= [];
312
+
313
+ const resolved = normalizeArray(components).map((component) => resolveBuilder(component, ActionRowBuilder));
314
+ this.data.components.push(...resolved);
315
+
316
+ return this;
317
+ }
318
+
319
+ /**
320
+ * Adds container components to this message.
321
+ *
322
+ * @param components - The container components to add
323
+ */
324
+ public addContainerComponents(
325
+ ...components: RestOrArray<
326
+ APIContainerComponent | ContainerBuilder | ((builder: ContainerBuilder) => ContainerBuilder)
327
+ >
328
+ ): this {
329
+ this.data.components ??= [];
330
+
331
+ const resolved = normalizeArray(components).map((component) => resolveBuilder(component, ContainerBuilder));
332
+ this.data.components.push(...resolved);
333
+
334
+ return this;
335
+ }
336
+
337
+ /**
338
+ * Adds file components to this message.
339
+ *
340
+ * @param components - The file components to add
341
+ */
342
+ public addFileComponents(
343
+ ...components: RestOrArray<APIFileComponent | FileBuilder | ((builder: FileBuilder) => FileBuilder)>
344
+ ): this {
345
+ this.data.components ??= [];
346
+
347
+ const resolved = normalizeArray(components).map((component) => resolveBuilder(component, FileBuilder));
348
+ this.data.components.push(...resolved);
349
+
350
+ return this;
351
+ }
352
+
353
+ /**
354
+ * Adds media gallery components to this message.
355
+ *
356
+ * @param components - The media gallery components to add
357
+ */
358
+ public addMediaGalleryComponents(
359
+ ...components: RestOrArray<
360
+ APIMediaGalleryComponent | MediaGalleryBuilder | ((builder: MediaGalleryBuilder) => MediaGalleryBuilder)
361
+ >
362
+ ): this {
363
+ this.data.components ??= [];
364
+
365
+ const resolved = normalizeArray(components).map((component) => resolveBuilder(component, MediaGalleryBuilder));
366
+ this.data.components.push(...resolved);
367
+
368
+ return this;
369
+ }
370
+
371
+ /**
372
+ * Adds section components to this message.
373
+ *
374
+ * @param components - The section components to add
375
+ */
376
+ public addSectionComponents(
377
+ ...components: RestOrArray<APISectionComponent | SectionBuilder | ((builder: SectionBuilder) => SectionBuilder)>
378
+ ): this {
379
+ this.data.components ??= [];
380
+
381
+ const resolved = normalizeArray(components).map((component) => resolveBuilder(component, SectionBuilder));
382
+ this.data.components.push(...resolved);
383
+
384
+ return this;
385
+ }
386
+
387
+ /**
388
+ * Adds separator components to this message.
389
+ *
390
+ * @param components - The separator components to add
391
+ */
392
+ public addSeparatorComponents(
393
+ ...components: RestOrArray<
394
+ APISeparatorComponent | SeparatorBuilder | ((builder: SeparatorBuilder) => SeparatorBuilder)
395
+ >
396
+ ): this {
397
+ this.data.components ??= [];
398
+
399
+ const resolved = normalizeArray(components).map((component) => resolveBuilder(component, SeparatorBuilder));
400
+ this.data.components.push(...resolved);
401
+
402
+ return this;
403
+ }
404
+
405
+ /**
406
+ * Adds text display components to this message.
407
+ *
408
+ * @param components - The text display components to add
409
+ */
410
+ public addTextDisplayComponents(
411
+ ...components: RestOrArray<
412
+ APITextDisplayComponent | TextDisplayBuilder | ((builder: TextDisplayBuilder) => TextDisplayBuilder)
413
+ >
414
+ ): this {
415
+ this.data.components ??= [];
416
+
417
+ const resolved = normalizeArray(components).map((component) => resolveBuilder(component, TextDisplayBuilder));
418
+ this.data.components.push(...resolved);
419
+
420
+ return this;
421
+ }
422
+
423
+ /**
424
+ * Removes, replaces, or inserts components for this message.
425
+ *
426
+ * @remarks
427
+ * This method behaves similarly
428
+ * to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
429
+ *
430
+ * It's useful for modifying and adjusting order of the already-existing components of a message.
431
+ * @example
432
+ * Remove the first component:
433
+ * ```ts
434
+ * message.spliceComponents(0, 1);
435
+ * ```
436
+ * @example
437
+ * Remove the first n components:
438
+ * ```ts
439
+ * const n = 4;
440
+ * message.spliceComponents(0, n);
441
+ * ```
442
+ * @example
443
+ * Remove the last component:
444
+ * ```ts
445
+ * message.spliceComponents(-1, 1);
446
+ * ```
447
+ * @param start - The index to start at
448
+ * @param deleteCount - The amount of components to remove
449
+ * @param components - The components to insert
450
+ */
451
+ public spliceComponents(
452
+ start: number,
453
+ deleteCount: number,
454
+ ...components: RestOrArray<APIMessageTopLevelComponent | MessageTopLevelComponentBuilder>
455
+ ): this {
456
+ this.data.components ??= [];
457
+ const resolved = normalizeArray(components).map((component) =>
458
+ component instanceof ComponentBuilder ? component : createComponentBuilder(component),
459
+ );
460
+
461
+ this.data.components.splice(start, deleteCount, ...resolved);
462
+ return this;
463
+ }
464
+
465
+ /**
466
+ * Sets the sticker ids of this message.
467
+ *
468
+ * @param stickerIds - The ids of the stickers to set
469
+ */
470
+ public setStickerIds(...stickerIds: RestOrArray<Snowflake>): this {
471
+ return this.spliceStickerIds(0, this.data.sticker_ids?.length ?? 0, ...normalizeArray(stickerIds));
472
+ }
473
+
474
+ /**
475
+ * Adds sticker ids to this message.
476
+ *
477
+ * @param stickerIds - The ids of the stickers to add
478
+ */
479
+ public addStickerIds(...stickerIds: RestOrArray<Snowflake>): this {
480
+ this.data.sticker_ids ??= [] as unknown as MessageBuilderData['sticker_ids'];
481
+ this.data.sticker_ids!.push(...normalizeArray(stickerIds));
482
+ return this;
483
+ }
484
+
485
+ /**
486
+ * Removes, replaces, or inserts sticker ids for this message.
487
+ *
488
+ * @remarks
489
+ * This method behaves similarly
490
+ * to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
491
+ *
492
+ * It's useful for modifying and adjusting order of the already-existing sticker ids of a message.
493
+ * @example
494
+ * Remove the first sticker id:
495
+ * ```ts
496
+ * message.spliceStickerIds(0, 1);
497
+ * ```
498
+ * @example
499
+ * Remove the first n sticker ids:
500
+ * ```ts
501
+ * const n = 4;
502
+ * message.spliceStickerIds(0, n);
503
+ * ```
504
+ * @example
505
+ * Remove the last sticker id:
506
+ * ```ts
507
+ * message.spliceStickerIds(-1, 1);
508
+ * ```
509
+ * @param index - The index to start at
510
+ * @param deleteCount - The amount of sticker ids to remove
511
+ * @param stickerIds - The sticker ids to insert
512
+ */
513
+ public spliceStickerIds(index: number, deleteCount: number, ...stickerIds: RestOrArray<Snowflake>): this {
514
+ this.data.sticker_ids ??= [] as unknown as MessageBuilderData['sticker_ids'];
515
+ this.data.sticker_ids!.splice(index, deleteCount, ...normalizeArray(stickerIds));
516
+ return this;
517
+ }
518
+
519
+ /**
520
+ * Sets attachments for this message.
521
+ *
522
+ * @param attachments - The attachments to set
523
+ */
524
+ public setAttachments(
525
+ ...attachments: RestOrArray<APIAttachment | AttachmentBuilder | ((builder: AttachmentBuilder) => AttachmentBuilder)>
526
+ ): this {
527
+ return this.spliceAttachments(0, this.data.attachments.length, ...normalizeArray(attachments));
528
+ }
529
+
530
+ /**
531
+ * Adds attachments to this message.
532
+ *
533
+ * @param attachments - The attachments to add
534
+ */
535
+ public addAttachments(
536
+ ...attachments: RestOrArray<APIAttachment | AttachmentBuilder | ((builder: AttachmentBuilder) => AttachmentBuilder)>
537
+ ): this {
538
+ const resolved = normalizeArray(attachments).map((attachment) => resolveBuilder(attachment, AttachmentBuilder));
539
+ this.data.attachments.push(...resolved);
540
+
541
+ return this;
542
+ }
543
+
544
+ /**
545
+ * Removes, replaces, or inserts attachments for this message.
546
+ *
547
+ * @remarks
548
+ * This method behaves similarly
549
+ * to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
550
+ *
551
+ * It's useful for modifying and adjusting order of the already-existing attachments of a message.
552
+ * @example
553
+ * Remove the first attachment:
554
+ * ```ts
555
+ * message.spliceAttachments(0, 1);
556
+ * ```
557
+ * @example
558
+ * Remove the first n attachments:
559
+ * ```ts
560
+ * const n = 4;
561
+ * message.spliceAttachments(0, n);
562
+ * ```
563
+ * @example
564
+ * Remove the last attachment:
565
+ * ```ts
566
+ * message.spliceAttachments(-1, 1);
567
+ * ```
568
+ * @param start - The index to start at
569
+ * @param deleteCount - The amount of attachments to remove
570
+ * @param attachments - The attachments to insert
571
+ */
572
+ public spliceAttachments(
573
+ start: number,
574
+ deleteCount: number,
575
+ ...attachments: RestOrArray<APIAttachment | AttachmentBuilder | ((builder: AttachmentBuilder) => AttachmentBuilder)>
576
+ ): this {
577
+ const resolved = normalizeArray(attachments).map((attachment) => resolveBuilder(attachment, AttachmentBuilder));
578
+ this.data.attachments.splice(start, deleteCount, ...resolved);
579
+
580
+ return this;
581
+ }
582
+
583
+ /**
584
+ * Sets the flags for this message.
585
+ *
586
+ * @param flags - The flags to set
587
+ */
588
+ public setFlags(flags: MessageFlags): this {
589
+ this.data.flags = flags;
590
+ return this;
591
+ }
592
+
593
+ /**
594
+ * Clears the flags for this message.
595
+ */
596
+ public clearFlags(): this {
597
+ this.data.flags = undefined;
598
+ return this;
599
+ }
600
+
601
+ /**
602
+ * Sets whether to enforce recent uniqueness of the nonce of this message.
603
+ *
604
+ * @param enforceNonce - Whether to enforce recent uniqueness of the nonce of this message
605
+ */
606
+ public setEnforceNonce(enforceNonce = true): this {
607
+ this.data.enforce_nonce = enforceNonce;
608
+ return this;
609
+ }
610
+
611
+ /**
612
+ * Sets the poll for this message.
613
+ *
614
+ * @param poll - The poll to set
615
+ */
616
+ public setPoll(poll: APIPoll | PollBuilder | ((builder: PollBuilder) => PollBuilder)): this {
617
+ this.data.poll = resolveBuilder(poll, PollBuilder);
618
+ return this;
619
+ }
620
+
621
+ /**
622
+ * Updates the poll for this message (and creates it if it doesn't exist)
623
+ *
624
+ * @param updater - The function to update the poll with
625
+ */
626
+ public updatePoll(updater: (builder: PollBuilder) => void): this {
627
+ updater((this.data.poll ??= new PollBuilder()));
628
+ return this;
629
+ }
630
+
631
+ /**
632
+ * Clears the poll for this message.
633
+ */
634
+ public clearPoll(): this {
635
+ this.data.poll = undefined;
636
+ return this;
637
+ }
638
+
639
+ /**
640
+ * Serializes this builder to API-compatible JSON data.
641
+ *
642
+ * Note that by disabling validation, there is no guarantee that the resulting object will be valid.
643
+ *
644
+ * @param validationOverride - Force validation to run/not run regardless of your global preference
645
+ */
646
+ public toJSON(validationOverride?: boolean): RESTPostAPIChannelMessageJSONBody {
647
+ const { poll, allowed_mentions, attachments, embeds, components, message_reference, ...rest } = this.data;
648
+
649
+ const data = {
650
+ ...structuredClone(rest),
651
+ // Wherever we pass false, it's covered by the messagePredicate already
652
+ poll: poll?.toJSON(false),
653
+ allowed_mentions: allowed_mentions?.toJSON(false),
654
+ attachments: attachments.map((attachment) => attachment.toJSON(false)),
655
+ embeds: embeds.map((embed) => embed.toJSON(false)),
656
+ // Here, the messagePredicate does specific constraints rather than using the componentPredicate
657
+ components: components.map((component) => component.toJSON(validationOverride)),
658
+ message_reference: message_reference?.toJSON(false),
659
+ };
660
+
661
+ validate(messagePredicate, data, validationOverride);
662
+
663
+ return data as RESTPostAPIChannelMessageJSONBody;
664
+ }
665
+
666
+ /**
667
+ * Serializes this builder to both JSON body and file data for multipart/form-data requests.
668
+ *
669
+ * @param validationOverride - Force validation to run/not run regardless of your global preference
670
+ * @remarks
671
+ * This method extracts file data from attachments that have files set via {@link AttachmentBuilder.setFileData}.
672
+ * The returned body includes attachment metadata, while files contains the binary data for upload.
673
+ */
674
+ public toFileBody(validationOverride?: boolean): FileBodyEncodableResult<RESTPostAPIChannelMessageJSONBody> {
675
+ const body = this.toJSON(false);
676
+
677
+ const files: RawFile[] = [];
678
+ for (const attachment of this.data.attachments) {
679
+ const rawFile = attachment.getRawFile();
680
+ // Only if data or content type are set, since that implies the intent is to send a new file.
681
+ // In case it's contentType but not data, a validation error will be thrown right after.
682
+ if (rawFile?.data || rawFile?.contentType) {
683
+ files.push(rawFile as RawFile);
684
+ }
685
+ }
686
+
687
+ const combined = { body, files };
688
+ validate(fileBodyMessagePredicate, combined, validationOverride);
689
+
690
+ return combined as FileBodyEncodableResult<RESTPostAPIChannelMessageJSONBody>;
691
+ }
692
+ }