@lunibee/builders 0.1.7 → 0.2.0
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.
- package/README.md +62 -11
- package/package.json +10 -10
- package/src/attachment.ts +70 -68
- package/src/commands.ts +629 -621
- package/src/components.ts +759 -643
- package/src/embed.ts +193 -195
- package/src/index.ts +45 -39
package/src/components.ts
CHANGED
|
@@ -1,643 +1,759 @@
|
|
|
1
|
-
// Re-export the canonical ButtonStyle from @lunibee/types so the builders package
|
|
2
|
-
// never ships a drifting duplicate (the old local copy was missing `Premium: 6`).
|
|
3
|
-
import { ButtonStyle } from "@lunibee/types";
|
|
4
|
-
export { ButtonStyle };
|
|
5
|
-
|
|
6
|
-
/** Component type constants exposed by Lunibee. */
|
|
7
|
-
export const ComponentType = {
|
|
8
|
-
ActionRow: 1,
|
|
9
|
-
Button: 2,
|
|
10
|
-
StringSelect: 3,
|
|
11
|
-
TextInput: 4,
|
|
12
|
-
UserSelect: 5,
|
|
13
|
-
RoleSelect: 6,
|
|
14
|
-
MentionableSelect: 7,
|
|
15
|
-
ChannelSelect: 8,
|
|
16
|
-
Section: 9,
|
|
17
|
-
TextDisplay: 10,
|
|
18
|
-
Thumbnail: 11,
|
|
19
|
-
MediaGallery: 12,
|
|
20
|
-
File: 13,
|
|
21
|
-
Separator: 14,
|
|
22
|
-
ContentInventoryEntry: 16,
|
|
23
|
-
Container: 17,
|
|
24
|
-
} as const;
|
|
25
|
-
export const TextInputStyle = { Short: 1, Paragraph: 2 } as const;
|
|
26
|
-
export interface APIComponentEmoji {
|
|
27
|
-
id?: string | null;
|
|
28
|
-
name?: string | null;
|
|
29
|
-
animated?: boolean;
|
|
30
|
-
}
|
|
31
|
-
export interface APIButtonComponent {
|
|
32
|
-
type: typeof ComponentType.Button;
|
|
33
|
-
style: 1 | 2 | 3 | 4 | 5 | 6;
|
|
34
|
-
custom_id?: string;
|
|
35
|
-
label?: string;
|
|
36
|
-
emoji?: APIComponentEmoji;
|
|
37
|
-
url?: string;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
| typeof ComponentType.
|
|
62
|
-
| typeof ComponentType.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
|
|
|
156
|
-
|
|
|
157
|
-
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (
|
|
167
|
-
throw new
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
this
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
this.#data =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
this.#data.
|
|
405
|
-
return this;
|
|
406
|
-
}
|
|
407
|
-
public
|
|
408
|
-
|
|
409
|
-
this.#data
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
this.#
|
|
419
|
-
return this;
|
|
420
|
-
}
|
|
421
|
-
public
|
|
422
|
-
validateText(value,
|
|
423
|
-
this.#
|
|
424
|
-
return this;
|
|
425
|
-
}
|
|
426
|
-
public
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
return
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
public
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
this
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
this.#
|
|
478
|
-
return this;
|
|
479
|
-
}
|
|
480
|
-
public
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
this.#
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
this
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
this.#
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
1
|
+
// Re-export the canonical ButtonStyle from @lunibee/types so the builders package
|
|
2
|
+
// never ships a drifting duplicate (the old local copy was missing `Premium: 6`).
|
|
3
|
+
import { ButtonStyle } from "@lunibee/types";
|
|
4
|
+
export { ButtonStyle };
|
|
5
|
+
|
|
6
|
+
/** Component type constants exposed by Lunibee. */
|
|
7
|
+
export const ComponentType = {
|
|
8
|
+
ActionRow: 1,
|
|
9
|
+
Button: 2,
|
|
10
|
+
StringSelect: 3,
|
|
11
|
+
TextInput: 4,
|
|
12
|
+
UserSelect: 5,
|
|
13
|
+
RoleSelect: 6,
|
|
14
|
+
MentionableSelect: 7,
|
|
15
|
+
ChannelSelect: 8,
|
|
16
|
+
Section: 9,
|
|
17
|
+
TextDisplay: 10,
|
|
18
|
+
Thumbnail: 11,
|
|
19
|
+
MediaGallery: 12,
|
|
20
|
+
File: 13,
|
|
21
|
+
Separator: 14,
|
|
22
|
+
ContentInventoryEntry: 16,
|
|
23
|
+
Container: 17,
|
|
24
|
+
} as const;
|
|
25
|
+
export const TextInputStyle = { Short: 1, Paragraph: 2 } as const;
|
|
26
|
+
export interface APIComponentEmoji {
|
|
27
|
+
id?: string | null;
|
|
28
|
+
name?: string | null;
|
|
29
|
+
animated?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface APIButtonComponent {
|
|
32
|
+
type: typeof ComponentType.Button;
|
|
33
|
+
style: 1 | 2 | 3 | 4 | 5 | 6;
|
|
34
|
+
custom_id?: string;
|
|
35
|
+
label?: string;
|
|
36
|
+
emoji?: APIComponentEmoji;
|
|
37
|
+
url?: string;
|
|
38
|
+
/** SKU for premium (style 6) buttons. */
|
|
39
|
+
sku_id?: string;
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface APISelectOption {
|
|
43
|
+
label: string;
|
|
44
|
+
value: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
emoji?: APIComponentEmoji;
|
|
47
|
+
default?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface APIStringSelectComponent {
|
|
50
|
+
type: typeof ComponentType.StringSelect;
|
|
51
|
+
custom_id?: string;
|
|
52
|
+
placeholder?: string;
|
|
53
|
+
min_values?: number;
|
|
54
|
+
max_values?: number;
|
|
55
|
+
required?: boolean;
|
|
56
|
+
disabled?: boolean;
|
|
57
|
+
options?: APISelectOption[];
|
|
58
|
+
}
|
|
59
|
+
export interface APIEntitySelectComponent {
|
|
60
|
+
type:
|
|
61
|
+
| typeof ComponentType.UserSelect
|
|
62
|
+
| typeof ComponentType.RoleSelect
|
|
63
|
+
| typeof ComponentType.MentionableSelect
|
|
64
|
+
| typeof ComponentType.ChannelSelect;
|
|
65
|
+
custom_id?: string;
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
min_values?: number;
|
|
68
|
+
max_values?: number;
|
|
69
|
+
required?: boolean;
|
|
70
|
+
disabled?: boolean;
|
|
71
|
+
default_values?: APISelectDefaultValue[];
|
|
72
|
+
}
|
|
73
|
+
export interface APITextInputComponent {
|
|
74
|
+
type: typeof ComponentType.TextInput;
|
|
75
|
+
style: typeof TextInputStyle.Short | typeof TextInputStyle.Paragraph;
|
|
76
|
+
custom_id?: string;
|
|
77
|
+
label?: string;
|
|
78
|
+
placeholder?: string;
|
|
79
|
+
min_length?: number;
|
|
80
|
+
max_length?: number;
|
|
81
|
+
required?: boolean;
|
|
82
|
+
value?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface APIModalComponent {
|
|
85
|
+
type: 9;
|
|
86
|
+
custom_id?: string;
|
|
87
|
+
title?: string;
|
|
88
|
+
components: APIActionRowComponent[];
|
|
89
|
+
}
|
|
90
|
+
export type APIActionRowChild =
|
|
91
|
+
| APIButtonComponent
|
|
92
|
+
| APIStringSelectComponent
|
|
93
|
+
| APIEntitySelectComponent
|
|
94
|
+
| APITextInputComponent;
|
|
95
|
+
export interface APIActionRowComponent {
|
|
96
|
+
type: typeof ComponentType.ActionRow;
|
|
97
|
+
components: APIActionRowChild[];
|
|
98
|
+
}
|
|
99
|
+
export interface APISectionComponent {
|
|
100
|
+
type: typeof ComponentType.Section;
|
|
101
|
+
components?: APIComponent[]; // text displays
|
|
102
|
+
accessory?: APIComponent; // buttons, thumbnails, etc.
|
|
103
|
+
}
|
|
104
|
+
export interface APITextDisplayComponent {
|
|
105
|
+
type: typeof ComponentType.TextDisplay;
|
|
106
|
+
content: string;
|
|
107
|
+
}
|
|
108
|
+
export interface APIMediaGalleryComponent {
|
|
109
|
+
type: typeof ComponentType.MediaGallery;
|
|
110
|
+
items: { media: { url: string; description?: string } }[];
|
|
111
|
+
}
|
|
112
|
+
/** Default values accepted by an auto-populated entity select. */
|
|
113
|
+
export interface APISelectDefaultValue {
|
|
114
|
+
id: string;
|
|
115
|
+
type: "user" | "role" | "channel";
|
|
116
|
+
}
|
|
117
|
+
export interface APIFileComponent {
|
|
118
|
+
type: typeof ComponentType.File;
|
|
119
|
+
file: { url: string };
|
|
120
|
+
spoiler?: boolean;
|
|
121
|
+
}
|
|
122
|
+
export interface APISeparatorComponent {
|
|
123
|
+
type: typeof ComponentType.Separator;
|
|
124
|
+
spacing?: 1 | 2;
|
|
125
|
+
divider?: boolean;
|
|
126
|
+
}
|
|
127
|
+
export interface APIThumbnailComponent {
|
|
128
|
+
type: typeof ComponentType.Thumbnail;
|
|
129
|
+
url: string;
|
|
130
|
+
proxy_url?: string;
|
|
131
|
+
width?: number;
|
|
132
|
+
height?: number;
|
|
133
|
+
description?: string;
|
|
134
|
+
spoiler?: boolean;
|
|
135
|
+
}
|
|
136
|
+
export interface APIContentInventoryEntryComponent {
|
|
137
|
+
type: typeof ComponentType.ContentInventoryEntry;
|
|
138
|
+
id: string;
|
|
139
|
+
}
|
|
140
|
+
export interface APIContainerComponent {
|
|
141
|
+
type: typeof ComponentType.Container;
|
|
142
|
+
components: APIComponent[];
|
|
143
|
+
accent_color?: number;
|
|
144
|
+
spoiler?: boolean;
|
|
145
|
+
}
|
|
146
|
+
export type APIComponent =
|
|
147
|
+
| APIActionRowComponent
|
|
148
|
+
| APIButtonComponent
|
|
149
|
+
| APIStringSelectComponent
|
|
150
|
+
| APIEntitySelectComponent
|
|
151
|
+
| APITextInputComponent
|
|
152
|
+
| APISectionComponent
|
|
153
|
+
| APITextDisplayComponent
|
|
154
|
+
| APIThumbnailComponent
|
|
155
|
+
| APIMediaGalleryComponent
|
|
156
|
+
| APIFileComponent
|
|
157
|
+
| APISeparatorComponent
|
|
158
|
+
| APIContentInventoryEntryComponent
|
|
159
|
+
| APIContainerComponent;
|
|
160
|
+
|
|
161
|
+
export class ActionRowBuilder<
|
|
162
|
+
T extends { toJSON(): APIActionRowChild } = { toJSON(): APIActionRowChild },
|
|
163
|
+
> {
|
|
164
|
+
readonly #components: T[] = [];
|
|
165
|
+
public addComponents(...components: T[]): this {
|
|
166
|
+
if (!components.length)
|
|
167
|
+
throw new TypeError("At least one component is required.");
|
|
168
|
+
if (this.#components.length + components.length > 5)
|
|
169
|
+
throw new RangeError(
|
|
170
|
+
"An action row cannot contain more than 5 components.",
|
|
171
|
+
);
|
|
172
|
+
this.#components.push(...components);
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
public clearComponents(): this {
|
|
176
|
+
this.#components.length = 0;
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
public toJSON(): APIActionRowComponent {
|
|
180
|
+
const components = this.#components.map((component) =>
|
|
181
|
+
component.toJSON(),
|
|
182
|
+
);
|
|
183
|
+
if (components.length === 0)
|
|
184
|
+
throw new RangeError("An action row needs at least one component.");
|
|
185
|
+
const types = components.map((component) => component.type);
|
|
186
|
+
if (
|
|
187
|
+
types.some((type) => type !== ComponentType.Button) &&
|
|
188
|
+
components.length > 1
|
|
189
|
+
)
|
|
190
|
+
throw new RangeError(
|
|
191
|
+
"A select menu or text input must be alone in its action row.",
|
|
192
|
+
);
|
|
193
|
+
return { type: ComponentType.ActionRow, components };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export class StringSelectBuilder {
|
|
197
|
+
readonly #data: APIStringSelectComponent = {
|
|
198
|
+
type: ComponentType.StringSelect,
|
|
199
|
+
};
|
|
200
|
+
public setCustomId(value: string): this {
|
|
201
|
+
validateText(value, 100, "Component custom ID");
|
|
202
|
+
this.#data.custom_id = value;
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
public setPlaceholder(value: string): this {
|
|
206
|
+
validateText(value, 150, "Component placeholder");
|
|
207
|
+
this.#data.placeholder = value;
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
public setMinValues(value: number): this {
|
|
211
|
+
validateCount(value, "min_values", 0);
|
|
212
|
+
this.#data.min_values = value;
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
public setMaxValues(value: number): this {
|
|
216
|
+
validateCount(value, "max_values", 1);
|
|
217
|
+
this.#data.max_values = value;
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
public setRequired(value = true): this {
|
|
221
|
+
this.#data.required = value;
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
public setDisabled(value = true): this {
|
|
225
|
+
this.#data.disabled = value;
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
public addOptions(...options: APISelectOption[]): this {
|
|
229
|
+
if (!options.length)
|
|
230
|
+
throw new TypeError("At least one select option is required.");
|
|
231
|
+
const current = this.#data.options ?? [];
|
|
232
|
+
if (current.length + options.length > 25)
|
|
233
|
+
throw new RangeError(
|
|
234
|
+
"A string select cannot contain more than 25 options.",
|
|
235
|
+
);
|
|
236
|
+
for (const option of options) {
|
|
237
|
+
validateText(option.label, 100, "Select option label");
|
|
238
|
+
validateText(option.value, 100, "Select option value");
|
|
239
|
+
if (option.description)
|
|
240
|
+
validateText(
|
|
241
|
+
option.description,
|
|
242
|
+
100,
|
|
243
|
+
"Select option description",
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
this.#data.options = [
|
|
247
|
+
...current,
|
|
248
|
+
...options.map((option) => ({ ...option })),
|
|
249
|
+
];
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
public toJSON(): APIStringSelectComponent {
|
|
253
|
+
assertSelect(this.#data);
|
|
254
|
+
const count = this.#data.options?.length ?? 0;
|
|
255
|
+
if (count === 0)
|
|
256
|
+
throw new RangeError("A string select needs at least one option.");
|
|
257
|
+
if ((this.#data.max_values ?? 1) > count)
|
|
258
|
+
throw new RangeError(
|
|
259
|
+
"max_values cannot exceed the number of options.",
|
|
260
|
+
);
|
|
261
|
+
return structuredClone(this.#data);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
export class ButtonBuilder {
|
|
265
|
+
readonly #data: APIButtonComponent = {
|
|
266
|
+
type: ComponentType.Button,
|
|
267
|
+
style: ButtonStyle.Secondary,
|
|
268
|
+
};
|
|
269
|
+
/** Sets the style and drops fields that style cannot carry. */
|
|
270
|
+
public setStyle(style: APIButtonComponent["style"]): this {
|
|
271
|
+
const data = this.#data;
|
|
272
|
+
data.style = style;
|
|
273
|
+
if (style === ButtonStyle.Link) {
|
|
274
|
+
delete data.custom_id;
|
|
275
|
+
delete data.sku_id;
|
|
276
|
+
} else if (style === ButtonStyle.Premium) {
|
|
277
|
+
delete data.custom_id;
|
|
278
|
+
delete data.url;
|
|
279
|
+
delete data.label;
|
|
280
|
+
delete data.emoji;
|
|
281
|
+
} else {
|
|
282
|
+
delete data.url;
|
|
283
|
+
delete data.sku_id;
|
|
284
|
+
}
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
public setCustomId(value: string): this {
|
|
288
|
+
validateText(value, 100, "Button custom ID");
|
|
289
|
+
if (this.#data.style === ButtonStyle.Link)
|
|
290
|
+
throw new TypeError("Link buttons cannot use custom IDs.");
|
|
291
|
+
this.#data.custom_id = value;
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
public setLabel(value: string): this {
|
|
295
|
+
validateText(value, 80, "Button label");
|
|
296
|
+
this.#data.label = value;
|
|
297
|
+
return this;
|
|
298
|
+
}
|
|
299
|
+
public setURL(value: string): this {
|
|
300
|
+
validateText(value, 512, "Button URL");
|
|
301
|
+
let url: URL;
|
|
302
|
+
try {
|
|
303
|
+
url = new URL(value);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
throw new TypeError("Button URL must be a valid URL.", {
|
|
306
|
+
cause: error,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
this.setStyle(ButtonStyle.Link);
|
|
310
|
+
this.#data.url = url.toString();
|
|
311
|
+
return this;
|
|
312
|
+
}
|
|
313
|
+
/** Makes this a premium (purchase) button for a SKU. Premium buttons carry no label, emoji, custom ID or URL. */
|
|
314
|
+
public setSKUId(skuId: string): this {
|
|
315
|
+
validateText(skuId, 20, "Button SKU ID");
|
|
316
|
+
this.setStyle(ButtonStyle.Premium);
|
|
317
|
+
this.#data.sku_id = skuId;
|
|
318
|
+
return this;
|
|
319
|
+
}
|
|
320
|
+
public setEmoji(emoji: APIComponentEmoji | string): this {
|
|
321
|
+
this.#data.emoji =
|
|
322
|
+
typeof emoji === "string" ? { name: emoji } : { ...emoji };
|
|
323
|
+
return this;
|
|
324
|
+
}
|
|
325
|
+
public setDisabled(value = true): this {
|
|
326
|
+
this.#data.disabled = value;
|
|
327
|
+
return this;
|
|
328
|
+
}
|
|
329
|
+
public toJSON(): APIButtonComponent {
|
|
330
|
+
const data = this.#data;
|
|
331
|
+
if (data.style === ButtonStyle.Link) {
|
|
332
|
+
if (!data.url) throw new TypeError("Link buttons require a URL.");
|
|
333
|
+
if (data.custom_id || data.sku_id)
|
|
334
|
+
throw new TypeError(
|
|
335
|
+
"Link buttons cannot have a custom ID or SKU ID.",
|
|
336
|
+
);
|
|
337
|
+
} else if (data.style === ButtonStyle.Premium) {
|
|
338
|
+
if (!data.sku_id)
|
|
339
|
+
throw new TypeError("Premium buttons require a SKU ID.");
|
|
340
|
+
if (data.custom_id || data.url || data.label || data.emoji)
|
|
341
|
+
throw new TypeError(
|
|
342
|
+
"Premium buttons cannot have a custom ID, URL, label or emoji.",
|
|
343
|
+
);
|
|
344
|
+
} else {
|
|
345
|
+
if (!data.custom_id)
|
|
346
|
+
throw new TypeError("Non-link buttons require a custom ID.");
|
|
347
|
+
if (data.url || data.sku_id)
|
|
348
|
+
throw new TypeError(
|
|
349
|
+
"Only link buttons have a URL and only premium buttons have a SKU ID.",
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
if (data.style !== ButtonStyle.Premium && !data.label && !data.emoji)
|
|
353
|
+
throw new TypeError("Buttons require a label or an emoji.");
|
|
354
|
+
return structuredClone(data);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
export class EntitySelectBuilder {
|
|
358
|
+
readonly #data: APIEntitySelectComponent;
|
|
359
|
+
public constructor(
|
|
360
|
+
type:
|
|
361
|
+
| typeof ComponentType.UserSelect
|
|
362
|
+
| typeof ComponentType.RoleSelect
|
|
363
|
+
| typeof ComponentType.MentionableSelect
|
|
364
|
+
| typeof ComponentType.ChannelSelect,
|
|
365
|
+
) {
|
|
366
|
+
if (
|
|
367
|
+
type !== ComponentType.UserSelect &&
|
|
368
|
+
type !== ComponentType.RoleSelect &&
|
|
369
|
+
type !== ComponentType.MentionableSelect &&
|
|
370
|
+
type !== ComponentType.ChannelSelect
|
|
371
|
+
)
|
|
372
|
+
throw new TypeError("Invalid select component type.");
|
|
373
|
+
this.#data = { type };
|
|
374
|
+
}
|
|
375
|
+
public setCustomId(value: string): this {
|
|
376
|
+
validateText(value, 100, "Component custom ID");
|
|
377
|
+
this.#data.custom_id = value;
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
public setPlaceholder(value: string): this {
|
|
381
|
+
validateText(value, 150, "Component placeholder");
|
|
382
|
+
this.#data.placeholder = value;
|
|
383
|
+
return this;
|
|
384
|
+
}
|
|
385
|
+
public setMinValues(value: number): this {
|
|
386
|
+
validateCount(value, "min_values", 0);
|
|
387
|
+
this.#data.min_values = value;
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
public setMaxValues(value: number): this {
|
|
391
|
+
validateCount(value, "max_values", 1);
|
|
392
|
+
this.#data.max_values = value;
|
|
393
|
+
return this;
|
|
394
|
+
}
|
|
395
|
+
public setRequired(value = true): this {
|
|
396
|
+
this.#data.required = value;
|
|
397
|
+
return this;
|
|
398
|
+
}
|
|
399
|
+
public setDisabled(value = true): this {
|
|
400
|
+
this.#data.disabled = value;
|
|
401
|
+
return this;
|
|
402
|
+
}
|
|
403
|
+
public setDefaultValues(...values: APISelectDefaultValue[]): this {
|
|
404
|
+
this.#data.default_values = values.map((value) => ({ ...value }));
|
|
405
|
+
return this;
|
|
406
|
+
}
|
|
407
|
+
public toJSON(): APIEntitySelectComponent {
|
|
408
|
+
assertSelect(this.#data);
|
|
409
|
+
return structuredClone(this.#data);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
export class ModalBuilder {
|
|
413
|
+
readonly #components: Array<{ toJSON(): APIActionRowComponent }> = [];
|
|
414
|
+
#custom_id?: string;
|
|
415
|
+
#title?: string;
|
|
416
|
+
public setCustomId(value: string): this {
|
|
417
|
+
validateText(value, 100, "Modal custom ID");
|
|
418
|
+
this.#custom_id = value;
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
public setTitle(value: string): this {
|
|
422
|
+
validateText(value, 45, "Modal title");
|
|
423
|
+
this.#title = value;
|
|
424
|
+
return this;
|
|
425
|
+
}
|
|
426
|
+
public addComponents(
|
|
427
|
+
...components: Array<{ toJSON(): APIActionRowComponent }>
|
|
428
|
+
): this {
|
|
429
|
+
if (!components.length)
|
|
430
|
+
throw new TypeError("At least one modal component is required.");
|
|
431
|
+
if (this.#components.length + components.length > 5)
|
|
432
|
+
throw new RangeError(
|
|
433
|
+
"A modal cannot contain more than 5 action rows.",
|
|
434
|
+
);
|
|
435
|
+
this.#components.push(...components);
|
|
436
|
+
return this;
|
|
437
|
+
}
|
|
438
|
+
public toJSON(): APIModalComponent {
|
|
439
|
+
if (!this.#custom_id || !this.#title)
|
|
440
|
+
throw new TypeError("Modals require a custom ID and a title.");
|
|
441
|
+
if (this.#components.length === 0)
|
|
442
|
+
throw new RangeError("Modals require at least one component.");
|
|
443
|
+
return {
|
|
444
|
+
type: 9,
|
|
445
|
+
...(this.#custom_id ? { custom_id: this.#custom_id } : {}),
|
|
446
|
+
...(this.#title ? { title: this.#title } : {}),
|
|
447
|
+
components: this.#components.map((component) => component.toJSON()),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
export class TextInputBuilder {
|
|
452
|
+
readonly #data: APITextInputComponent = {
|
|
453
|
+
type: ComponentType.TextInput,
|
|
454
|
+
style: TextInputStyle.Short,
|
|
455
|
+
};
|
|
456
|
+
public setStyle(style: APITextInputComponent["style"]): this {
|
|
457
|
+
this.#data.style = style;
|
|
458
|
+
return this;
|
|
459
|
+
}
|
|
460
|
+
public setCustomId(value: string): this {
|
|
461
|
+
validateText(value, 100, "Text input custom ID");
|
|
462
|
+
this.#data.custom_id = value;
|
|
463
|
+
return this;
|
|
464
|
+
}
|
|
465
|
+
public setLabel(value: string): this {
|
|
466
|
+
validateText(value, 45, "Text input label");
|
|
467
|
+
this.#data.label = value;
|
|
468
|
+
return this;
|
|
469
|
+
}
|
|
470
|
+
public setPlaceholder(value: string): this {
|
|
471
|
+
validateText(value, 100, "Text input placeholder");
|
|
472
|
+
this.#data.placeholder = value;
|
|
473
|
+
return this;
|
|
474
|
+
}
|
|
475
|
+
public setMinLength(value: number): this {
|
|
476
|
+
validateLength(value, "min_length", 4000);
|
|
477
|
+
this.#data.min_length = value;
|
|
478
|
+
return this;
|
|
479
|
+
}
|
|
480
|
+
public setMaxLength(value: number): this {
|
|
481
|
+
validateLength(value, "max_length", 4000);
|
|
482
|
+
this.#data.max_length = value;
|
|
483
|
+
return this;
|
|
484
|
+
}
|
|
485
|
+
public setRequired(value = true): this {
|
|
486
|
+
this.#data.required = value;
|
|
487
|
+
return this;
|
|
488
|
+
}
|
|
489
|
+
public setValue(value: string): this {
|
|
490
|
+
validateText(value, 4000, "Text input value");
|
|
491
|
+
this.#data.value = value;
|
|
492
|
+
return this;
|
|
493
|
+
}
|
|
494
|
+
public toJSON(): APITextInputComponent {
|
|
495
|
+
const data = this.#data;
|
|
496
|
+
if (!data.custom_id)
|
|
497
|
+
throw new TypeError("Text inputs require a custom ID.");
|
|
498
|
+
if (
|
|
499
|
+
data.min_length !== undefined &&
|
|
500
|
+
data.max_length !== undefined &&
|
|
501
|
+
data.min_length > data.max_length
|
|
502
|
+
)
|
|
503
|
+
throw new RangeError("min_length cannot exceed max_length.");
|
|
504
|
+
return structuredClone(data);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export class ContainerBuilder {
|
|
509
|
+
readonly #components: { toJSON(): APIComponent }[] = [];
|
|
510
|
+
#accentColor?: number;
|
|
511
|
+
#spoiler?: boolean;
|
|
512
|
+
public addComponents(...components: { toJSON(): APIComponent }[]): this {
|
|
513
|
+
if (!components.length)
|
|
514
|
+
throw new TypeError("At least one component is required.");
|
|
515
|
+
this.#components.push(...components);
|
|
516
|
+
return this;
|
|
517
|
+
}
|
|
518
|
+
public setAccentColor(color: number): this {
|
|
519
|
+
if (!Number.isInteger(color) || color < 0 || color > 0xffffff) {
|
|
520
|
+
throw new RangeError(
|
|
521
|
+
"Accent color must be an integer between 0 and 16777215 (0xFFFFFF).",
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
this.#accentColor = color;
|
|
525
|
+
return this;
|
|
526
|
+
}
|
|
527
|
+
/** Marks the whole container as a spoiler (blurred until clicked). */
|
|
528
|
+
public setSpoiler(spoiler = true): this {
|
|
529
|
+
this.#spoiler = spoiler;
|
|
530
|
+
return this;
|
|
531
|
+
}
|
|
532
|
+
public toJSON(): APIContainerComponent {
|
|
533
|
+
const data: APIContainerComponent = {
|
|
534
|
+
type: ComponentType.Container,
|
|
535
|
+
components: this.#components.map((c) => c.toJSON()),
|
|
536
|
+
};
|
|
537
|
+
if (this.#accentColor !== undefined)
|
|
538
|
+
data.accent_color = this.#accentColor;
|
|
539
|
+
if (this.#spoiler !== undefined) data.spoiler = this.#spoiler;
|
|
540
|
+
return data;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export class SectionBuilder {
|
|
545
|
+
readonly #components: { toJSON(): APIComponent }[] = [];
|
|
546
|
+
#accessory?: { toJSON(): APIComponent };
|
|
547
|
+
public addComponents(...components: { toJSON(): APIComponent }[]): this {
|
|
548
|
+
if (!components.length)
|
|
549
|
+
throw new TypeError("At least one component is required.");
|
|
550
|
+
this.#components.push(...components);
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
public setAccessory(accessory: { toJSON(): APIComponent }): this {
|
|
554
|
+
this.#accessory = accessory;
|
|
555
|
+
return this;
|
|
556
|
+
}
|
|
557
|
+
public toJSON(): APISectionComponent {
|
|
558
|
+
return {
|
|
559
|
+
type: ComponentType.Section,
|
|
560
|
+
components: this.#components.length
|
|
561
|
+
? this.#components.map((c) => c.toJSON())
|
|
562
|
+
: undefined,
|
|
563
|
+
accessory: this.#accessory?.toJSON(),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
export class TextDisplayBuilder {
|
|
569
|
+
#content = "";
|
|
570
|
+
public setContent(content: string): this {
|
|
571
|
+
validateText(content, 4000, "Text display content");
|
|
572
|
+
this.#content = content;
|
|
573
|
+
return this;
|
|
574
|
+
}
|
|
575
|
+
public toJSON(): APITextDisplayComponent {
|
|
576
|
+
return {
|
|
577
|
+
type: ComponentType.TextDisplay,
|
|
578
|
+
content: this.#content,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export class MediaGalleryBuilder {
|
|
584
|
+
readonly #items: { media: { url: string; description?: string } }[] = [];
|
|
585
|
+
public addItems(...items: { url: string; description?: string }[]): this {
|
|
586
|
+
if (!items.length) {
|
|
587
|
+
throw new TypeError("At least one media gallery item is required.");
|
|
588
|
+
}
|
|
589
|
+
if (this.#items.length + items.length > 10) {
|
|
590
|
+
throw new RangeError(
|
|
591
|
+
"MediaGallery can only contain up to 10 items.",
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
this.#items.push(...items.map((item) => ({ media: item })));
|
|
595
|
+
return this;
|
|
596
|
+
}
|
|
597
|
+
public toJSON(): APIMediaGalleryComponent {
|
|
598
|
+
return {
|
|
599
|
+
type: ComponentType.MediaGallery,
|
|
600
|
+
items: structuredClone(this.#items),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export class FileComponentBuilder {
|
|
606
|
+
#url = "";
|
|
607
|
+
#spoiler?: boolean;
|
|
608
|
+
public setUrl(url: string): this {
|
|
609
|
+
validateText(url, 2048, "File component URL");
|
|
610
|
+
this.#url = url;
|
|
611
|
+
return this;
|
|
612
|
+
}
|
|
613
|
+
public setSpoiler(spoiler = true): this {
|
|
614
|
+
this.#spoiler = spoiler;
|
|
615
|
+
return this;
|
|
616
|
+
}
|
|
617
|
+
public toJSON(): APIFileComponent {
|
|
618
|
+
if (!this.#url) throw new Error("File component must have a URL.");
|
|
619
|
+
const data: APIFileComponent = {
|
|
620
|
+
type: ComponentType.File,
|
|
621
|
+
file: { url: this.#url },
|
|
622
|
+
};
|
|
623
|
+
if (this.#spoiler !== undefined) data.spoiler = this.#spoiler;
|
|
624
|
+
return data;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export class SeparatorBuilder {
|
|
629
|
+
#spacing?: 1 | 2;
|
|
630
|
+
#divider?: boolean;
|
|
631
|
+
public setSpacing(spacing: 1 | 2): this {
|
|
632
|
+
this.#spacing = spacing;
|
|
633
|
+
return this;
|
|
634
|
+
}
|
|
635
|
+
public setDivider(divider = true): this {
|
|
636
|
+
this.#divider = divider;
|
|
637
|
+
return this;
|
|
638
|
+
}
|
|
639
|
+
public toJSON(): APISeparatorComponent {
|
|
640
|
+
const data: APISeparatorComponent = {
|
|
641
|
+
type: ComponentType.Separator,
|
|
642
|
+
};
|
|
643
|
+
if (this.#spacing !== undefined) data.spacing = this.#spacing;
|
|
644
|
+
if (this.#divider !== undefined) data.divider = this.#divider;
|
|
645
|
+
return data;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export class ThumbnailBuilder {
|
|
650
|
+
#url = "";
|
|
651
|
+
#description?: string;
|
|
652
|
+
#spoiler?: boolean;
|
|
653
|
+
public setUrl(url: string): this {
|
|
654
|
+
validateText(url, 2048, "Thumbnail URL");
|
|
655
|
+
this.#url = url;
|
|
656
|
+
return this;
|
|
657
|
+
}
|
|
658
|
+
/** Sets the thumbnail's alt-text description. */
|
|
659
|
+
public setDescription(description: string): this {
|
|
660
|
+
validateText(description, 1024, "Thumbnail description");
|
|
661
|
+
this.#description = description;
|
|
662
|
+
return this;
|
|
663
|
+
}
|
|
664
|
+
/** Marks the thumbnail as a spoiler (blurred until clicked). */
|
|
665
|
+
public setSpoiler(spoiler = true): this {
|
|
666
|
+
this.#spoiler = spoiler;
|
|
667
|
+
return this;
|
|
668
|
+
}
|
|
669
|
+
public toJSON(): APIThumbnailComponent {
|
|
670
|
+
const data: APIThumbnailComponent = {
|
|
671
|
+
type: ComponentType.Thumbnail,
|
|
672
|
+
url: this.#url,
|
|
673
|
+
};
|
|
674
|
+
if (this.#description !== undefined)
|
|
675
|
+
data.description = this.#description;
|
|
676
|
+
if (this.#spoiler !== undefined) data.spoiler = this.#spoiler;
|
|
677
|
+
return data;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export class ContentInventoryEntryBuilder {
|
|
682
|
+
#id = "";
|
|
683
|
+
public setId(id: string): this {
|
|
684
|
+
validateText(id, 100, "Content inventory entry ID");
|
|
685
|
+
this.#id = id;
|
|
686
|
+
return this;
|
|
687
|
+
}
|
|
688
|
+
public toJSON(): APIContentInventoryEntryComponent {
|
|
689
|
+
return {
|
|
690
|
+
type: ComponentType.ContentInventoryEntry,
|
|
691
|
+
id: this.#id,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function validateText(value: string, max: number, field: string): void {
|
|
697
|
+
if (typeof value !== "string")
|
|
698
|
+
throw new TypeError(`${field} must be a string.`);
|
|
699
|
+
if (!value.trim())
|
|
700
|
+
throw new RangeError(`${field} must be a non-empty string.`);
|
|
701
|
+
if (value.length > max)
|
|
702
|
+
throw new RangeError(`${field} cannot exceed ${max} characters.`);
|
|
703
|
+
}
|
|
704
|
+
function validateCount(
|
|
705
|
+
value: number,
|
|
706
|
+
field: string,
|
|
707
|
+
min: number,
|
|
708
|
+
max = 25,
|
|
709
|
+
): void {
|
|
710
|
+
if (!Number.isInteger(value) || value < min || value > max)
|
|
711
|
+
throw new RangeError(
|
|
712
|
+
`${field} must be an integer between ${min} and ${max}.`,
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
function validateLength(value: number, field: string, max = 4000): void {
|
|
716
|
+
if (!Number.isInteger(value) || value < 0 || value > max)
|
|
717
|
+
throw new RangeError(
|
|
718
|
+
`${field} must be an integer between 0 and ${max}.`,
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/** Discord.js-familiar alias for {@link StringSelectBuilder}. */
|
|
723
|
+
export { StringSelectBuilder as StringSelectMenuBuilder };
|
|
724
|
+
/** User select menu builder (Discord.js-familiar). */
|
|
725
|
+
export class UserSelectMenuBuilder extends EntitySelectBuilder {
|
|
726
|
+
public constructor() {
|
|
727
|
+
super(ComponentType.UserSelect);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/** Role select menu builder (Discord.js-familiar). */
|
|
731
|
+
export class RoleSelectMenuBuilder extends EntitySelectBuilder {
|
|
732
|
+
public constructor() {
|
|
733
|
+
super(ComponentType.RoleSelect);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
/** Mentionable select menu builder (Discord.js-familiar). */
|
|
737
|
+
export class MentionableSelectMenuBuilder extends EntitySelectBuilder {
|
|
738
|
+
public constructor() {
|
|
739
|
+
super(ComponentType.MentionableSelect);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
/** Channel select menu builder (Discord.js-familiar). */
|
|
743
|
+
export class ChannelSelectMenuBuilder extends EntitySelectBuilder {
|
|
744
|
+
public constructor() {
|
|
745
|
+
super(ComponentType.ChannelSelect);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/** Checks the fields every select menu needs. */
|
|
750
|
+
function assertSelect(data: {
|
|
751
|
+
custom_id?: string;
|
|
752
|
+
min_values?: number;
|
|
753
|
+
max_values?: number;
|
|
754
|
+
}): void {
|
|
755
|
+
if (!data.custom_id)
|
|
756
|
+
throw new TypeError("Select menus require a custom ID.");
|
|
757
|
+
if ((data.min_values ?? 1) > (data.max_values ?? 1))
|
|
758
|
+
throw new RangeError("min_values cannot exceed max_values.");
|
|
759
|
+
}
|