@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,275 @@
1
+ import type {
2
+ APIBaseComponent,
3
+ APIButtonComponent,
4
+ APIMessageComponent,
5
+ APIModalComponent,
6
+ APISectionAccessoryComponent,
7
+ } from 'discord-api-types/v10';
8
+ import { ButtonStyle, ComponentType } from 'discord-api-types/v10';
9
+ import { ActionRowBuilder } from './ActionRow.js';
10
+ import { ComponentBuilder } from './Component.js';
11
+ import {
12
+ DangerButtonBuilder,
13
+ PrimaryButtonBuilder,
14
+ SecondaryButtonBuilder,
15
+ SuccessButtonBuilder,
16
+ } from './button/CustomIdButton.js';
17
+ import { LinkButtonBuilder } from './button/LinkButton.js';
18
+ import { PremiumButtonBuilder } from './button/PremiumButton.js';
19
+ import { FileUploadBuilder } from './fileUpload/FileUpload.js';
20
+ import { LabelBuilder } from './label/Label.js';
21
+ import { ChannelSelectMenuBuilder } from './selectMenu/ChannelSelectMenu.js';
22
+ import { MentionableSelectMenuBuilder } from './selectMenu/MentionableSelectMenu.js';
23
+ import { RoleSelectMenuBuilder } from './selectMenu/RoleSelectMenu.js';
24
+ import { StringSelectMenuBuilder } from './selectMenu/StringSelectMenu.js';
25
+ import { UserSelectMenuBuilder } from './selectMenu/UserSelectMenu.js';
26
+ import { TextInputBuilder } from './textInput/TextInput.js';
27
+ import { ContainerBuilder } from './v2/Container.js';
28
+ import { FileBuilder } from './v2/File.js';
29
+ import { MediaGalleryBuilder } from './v2/MediaGallery.js';
30
+ import { SectionBuilder } from './v2/Section.js';
31
+ import { SeparatorBuilder } from './v2/Separator.js';
32
+ import { TextDisplayBuilder } from './v2/TextDisplay.js';
33
+ import { ThumbnailBuilder } from './v2/Thumbnail.js';
34
+
35
+ /**
36
+ * The builders that may be used as top-level components on messages
37
+ */
38
+ export type MessageTopLevelComponentBuilder =
39
+ | ActionRowBuilder
40
+ | ContainerBuilder
41
+ | FileBuilder
42
+ | MediaGalleryBuilder
43
+ | SectionBuilder
44
+ | SeparatorBuilder
45
+ | TextDisplayBuilder;
46
+
47
+ /**
48
+ * The builders that may be used for messages.
49
+ */
50
+ export type MessageComponentBuilder =
51
+ | MessageActionRowComponentBuilder
52
+ | MessageTopLevelComponentBuilder
53
+ | ThumbnailBuilder;
54
+
55
+ /**
56
+ * The builders that may be used for modals.
57
+ */
58
+ export type ModalComponentBuilder =
59
+ | ActionRowBuilder
60
+ | FileUploadBuilder
61
+ | LabelBuilder
62
+ | ModalActionRowComponentBuilder;
63
+
64
+ /**
65
+ * Any button builder
66
+ */
67
+ export type ButtonBuilder =
68
+ | DangerButtonBuilder
69
+ | LinkButtonBuilder
70
+ | PremiumButtonBuilder
71
+ | PrimaryButtonBuilder
72
+ | SecondaryButtonBuilder
73
+ | SuccessButtonBuilder;
74
+
75
+ /**
76
+ * The builders that may be used within an action row for messages.
77
+ */
78
+ export type MessageActionRowComponentBuilder =
79
+ | ButtonBuilder
80
+ | ChannelSelectMenuBuilder
81
+ | MentionableSelectMenuBuilder
82
+ | RoleSelectMenuBuilder
83
+ | StringSelectMenuBuilder
84
+ | UserSelectMenuBuilder;
85
+
86
+ /**
87
+ * The builders that may be used within an action row for modals.
88
+ */
89
+ export type ModalActionRowComponentBuilder = TextInputBuilder;
90
+
91
+ /**
92
+ * Any action row component builder.
93
+ */
94
+ export type AnyActionRowComponentBuilder = MessageActionRowComponentBuilder | ModalActionRowComponentBuilder;
95
+
96
+ /**
97
+ * Any modal component builder.
98
+ */
99
+ export type AnyModalComponentBuilder = FileUploadBuilder | LabelBuilder | TextDisplayBuilder;
100
+
101
+ /**
102
+ * Components here are mapped to their respective builder.
103
+ */
104
+ export interface MappedComponentTypes {
105
+ /**
106
+ * The action row component type is associated with an {@link ActionRowBuilder}.
107
+ */
108
+ [ComponentType.ActionRow]: ActionRowBuilder;
109
+ /**
110
+ * The button component type is associated with a {@link BaseButtonBuilder}.
111
+ */
112
+ [ComponentType.Button]: ButtonBuilder;
113
+ /**
114
+ * The string select component type is associated with a {@link StringSelectMenuBuilder}.
115
+ */
116
+ [ComponentType.StringSelect]: StringSelectMenuBuilder;
117
+ /**
118
+ * The text input component type is associated with a {@link TextInputBuilder}.
119
+ */
120
+ [ComponentType.TextInput]: TextInputBuilder;
121
+ /**
122
+ * The user select component type is associated with a {@link UserSelectMenuBuilder}.
123
+ */
124
+ [ComponentType.UserSelect]: UserSelectMenuBuilder;
125
+ /**
126
+ * The role select component type is associated with a {@link RoleSelectMenuBuilder}.
127
+ */
128
+ [ComponentType.RoleSelect]: RoleSelectMenuBuilder;
129
+ /**
130
+ * The mentionable select component type is associated with a {@link MentionableSelectMenuBuilder}.
131
+ */
132
+ [ComponentType.MentionableSelect]: MentionableSelectMenuBuilder;
133
+ /**
134
+ * The channel select component type is associated with a {@link ChannelSelectMenuBuilder}.
135
+ */
136
+ [ComponentType.ChannelSelect]: ChannelSelectMenuBuilder;
137
+ /**
138
+ * The thumbnail component type is associated with a {@link ThumbnailBuilder}.
139
+ */
140
+ [ComponentType.Thumbnail]: ThumbnailBuilder;
141
+ /**
142
+ * The file component type is associated with a {@link FileBuilder}.
143
+ */
144
+ [ComponentType.File]: FileBuilder;
145
+ /**
146
+ * The separator component type is associated with a {@link SeparatorBuilder}.
147
+ */
148
+ [ComponentType.Separator]: SeparatorBuilder;
149
+ /**
150
+ * The text display component type is associated with a {@link TextDisplayBuilder}.
151
+ */
152
+ [ComponentType.TextDisplay]: TextDisplayBuilder;
153
+ /**
154
+ * The media gallery component type is associated with a {@link MediaGalleryBuilder}.
155
+ */
156
+ [ComponentType.MediaGallery]: MediaGalleryBuilder;
157
+ /**
158
+ * The section component type is associated with a {@link SectionBuilder}.
159
+ */
160
+ [ComponentType.Section]: SectionBuilder;
161
+ /**
162
+ * The container component type is associated with a {@link ContainerBuilder}.
163
+ */
164
+ [ComponentType.Container]: ContainerBuilder;
165
+ /**
166
+ * The label component type is associated with a {@link LabelBuilder}.
167
+ */
168
+ [ComponentType.Label]: LabelBuilder;
169
+ /**
170
+ * The file upload component type is associated with a {@link FileUploadBuilder}.
171
+ */
172
+ [ComponentType.FileUpload]: FileUploadBuilder;
173
+ }
174
+
175
+ /**
176
+ * Factory for creating components from API data.
177
+ *
178
+ * @typeParam ComponentType - The type of component to use
179
+ * @param data - The API data to transform to a component class
180
+ */
181
+ export function createComponentBuilder<ComponentType extends keyof MappedComponentTypes>(
182
+
183
+ data: (APIModalComponent | APIMessageComponent) & { type: ComponentType },
184
+ ): MappedComponentTypes[ComponentType];
185
+
186
+ /**
187
+ * Factory for creating components from API data.
188
+ *
189
+ * @typeParam Builder - The type of component to use
190
+ * @param data - The API data to transform to a component class
191
+ */
192
+ export function createComponentBuilder<Builder extends MessageComponentBuilder | ModalComponentBuilder>(
193
+ data: Builder,
194
+ ): Builder;
195
+
196
+ export function createComponentBuilder(
197
+ data: APIMessageComponent | APIModalComponent | MessageComponentBuilder,
198
+ ): ComponentBuilder<APIBaseComponent<ComponentType>> {
199
+ if (data instanceof ComponentBuilder || (typeof data === 'object' && data !== null && 'toJSON' in data)) {
200
+ return data as any;
201
+ }
202
+
203
+ const rawData = data as APIMessageComponent | APIModalComponent;
204
+ switch (rawData.type) {
205
+ case ComponentType.ActionRow:
206
+ return new ActionRowBuilder(rawData as any);
207
+ case ComponentType.Button:
208
+ return createButtonBuilder(rawData as any) as any;
209
+ case ComponentType.StringSelect:
210
+ return new StringSelectMenuBuilder(rawData as any);
211
+ case ComponentType.TextInput:
212
+ return new TextInputBuilder(rawData as any);
213
+ case ComponentType.UserSelect:
214
+ return new UserSelectMenuBuilder(rawData as any);
215
+ case ComponentType.RoleSelect:
216
+ return new RoleSelectMenuBuilder(rawData as any);
217
+ case ComponentType.MentionableSelect:
218
+ return new MentionableSelectMenuBuilder(rawData as any);
219
+ case ComponentType.ChannelSelect:
220
+ return new ChannelSelectMenuBuilder(rawData as any);
221
+ case ComponentType.Thumbnail:
222
+ return new ThumbnailBuilder(rawData as any);
223
+ case ComponentType.File:
224
+ return new FileBuilder(rawData as any);
225
+ case ComponentType.Separator:
226
+ return new SeparatorBuilder(rawData as any);
227
+ case ComponentType.TextDisplay:
228
+ return new TextDisplayBuilder(rawData as any);
229
+ case ComponentType.MediaGallery:
230
+ return new MediaGalleryBuilder(rawData as any);
231
+ case ComponentType.Section:
232
+ return new SectionBuilder(rawData as any);
233
+ case ComponentType.Container:
234
+ return new ContainerBuilder(rawData as any);
235
+ case ComponentType.Label:
236
+ return new LabelBuilder(rawData as any);
237
+ case ComponentType.FileUpload:
238
+ return new FileUploadBuilder(rawData as any);
239
+ default:
240
+ // This case can still occur if we get a newer unsupported component type
241
+ throw new Error(`Cannot properly serialize component type: ${rawData.type}`);
242
+ }
243
+ }
244
+
245
+ function createButtonBuilder(data: APIButtonComponent): ButtonBuilder {
246
+ switch (data.style) {
247
+ case ButtonStyle.Primary:
248
+ return new PrimaryButtonBuilder(data);
249
+ case ButtonStyle.Secondary:
250
+ return new SecondaryButtonBuilder(data);
251
+ case ButtonStyle.Success:
252
+ return new SuccessButtonBuilder(data);
253
+ case ButtonStyle.Danger:
254
+ return new DangerButtonBuilder(data);
255
+ case ButtonStyle.Link:
256
+ return new LinkButtonBuilder(data);
257
+ case ButtonStyle.Premium:
258
+ return new PremiumButtonBuilder(data);
259
+ default:
260
+ // @ts-expect-error This case can still occur if we get a newer unsupported button style
261
+ throw new Error(`Cannot properly serialize button with style: ${data.style}`);
262
+ }
263
+ }
264
+
265
+ export function resolveAccessoryComponent(component: APISectionAccessoryComponent) {
266
+ switch (component.type) {
267
+ case ComponentType.Button:
268
+ return createButtonBuilder(component);
269
+ case ComponentType.Thumbnail:
270
+ return new ThumbnailBuilder(component);
271
+ default:
272
+ // @ts-expect-error This case can still occur if we get a newer unsupported component type
273
+ throw new Error(`Cannot properly serialize section accessory component: ${component.type}`);
274
+ }
275
+ }
@@ -0,0 +1,34 @@
1
+ import type { APIButtonComponent } from 'discord-api-types/v10';
2
+ import { validate } from '../../util/validation.js';
3
+ import { buttonPredicate } from '../Assertions.js';
4
+ import { ComponentBuilder } from '../Component.js';
5
+
6
+ /**
7
+ * A builder that creates API-compatible JSON data for buttons.
8
+ */
9
+ export abstract class BaseButtonBuilder<ButtonData extends APIButtonComponent> extends ComponentBuilder<ButtonData> {
10
+ /**
11
+ * @internal
12
+ */
13
+ declare protected readonly data: Partial<ButtonData>;
14
+
15
+ /**
16
+ * Sets whether this button is disabled.
17
+ *
18
+ * @param disabled - Whether to disable this button
19
+ */
20
+ public setDisabled(disabled = true) {
21
+ this.data.disabled = disabled;
22
+ return this;
23
+ }
24
+
25
+ /**
26
+ * {@inheritDoc ComponentBuilder.toJSON}
27
+ */
28
+ public override toJSON(validationOverride?: boolean): ButtonData {
29
+ const clone = structuredClone(this.data);
30
+ validate(buttonPredicate, clone, validationOverride);
31
+
32
+ return clone as ButtonData;
33
+ }
34
+ }
@@ -0,0 +1,74 @@
1
+ import { ButtonStyle, ComponentType, type APIButtonComponentWithCustomId } from 'discord-api-types/v10';
2
+ import { Mixin } from 'ts-mixer';
3
+ import { BaseButtonBuilder } from './Button.js';
4
+ import { EmojiOrLabelButtonMixin } from './mixins/EmojiOrLabelButtonMixin.js';
5
+
6
+ export type CustomIdButtonStyle = APIButtonComponentWithCustomId['style'];
7
+
8
+ /**
9
+ * A builder that creates API-compatible JSON data for buttons with custom IDs.
10
+ *
11
+ * @mixes {@link BaseButtonBuilder}\<{@link discord-api-types/v10#(APIButtonComponentWithCustomId:interface)}\>
12
+ * @mixes {@link EmojiOrLabelButtonMixin}
13
+ */
14
+ export interface CustomIdButtonBuilder extends BaseButtonBuilder<APIButtonComponentWithCustomId>, EmojiOrLabelButtonMixin {}
15
+
16
+ export abstract class CustomIdButtonBuilder extends Mixin(
17
+ BaseButtonBuilder<APIButtonComponentWithCustomId>,
18
+ EmojiOrLabelButtonMixin,
19
+ ) {
20
+ protected override readonly data: Partial<APIButtonComponentWithCustomId>;
21
+
22
+ protected constructor(data: Partial<APIButtonComponentWithCustomId> = {}) {
23
+ super();
24
+ this.data = { ...structuredClone(data), type: ComponentType.Button };
25
+ }
26
+
27
+ /**
28
+ * Sets the custom id for this button.
29
+ *
30
+ * @remarks
31
+ * This method is only applicable to buttons that are not using the `Link` button style.
32
+ * @param customId - The custom id to use
33
+ */
34
+ public setCustomId(customId: string) {
35
+ this.data.custom_id = customId;
36
+ return this;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * A builder that creates API-compatible JSON data for buttons with custom IDs (using the primary style).
42
+ */
43
+ export class PrimaryButtonBuilder extends CustomIdButtonBuilder {
44
+ public constructor(data: Partial<APIButtonComponentWithCustomId> = {}) {
45
+ super({ ...data, style: ButtonStyle.Primary });
46
+ }
47
+ }
48
+
49
+ /**
50
+ * A builder that creates API-compatible JSON data for buttons with custom IDs (using the secondary style).
51
+ */
52
+ export class SecondaryButtonBuilder extends CustomIdButtonBuilder {
53
+ public constructor(data: Partial<APIButtonComponentWithCustomId> = {}) {
54
+ super({ ...data, style: ButtonStyle.Secondary });
55
+ }
56
+ }
57
+
58
+ /**
59
+ * A builder that creates API-compatible JSON data for buttons with custom IDs (using the success style).
60
+ */
61
+ export class SuccessButtonBuilder extends CustomIdButtonBuilder {
62
+ public constructor(data: Partial<APIButtonComponentWithCustomId> = {}) {
63
+ super({ ...data, style: ButtonStyle.Success });
64
+ }
65
+ }
66
+
67
+ /**
68
+ * A builder that creates API-compatible JSON data for buttons with custom IDs (using the danger style).
69
+ */
70
+ export class DangerButtonBuilder extends CustomIdButtonBuilder {
71
+ public constructor(data: Partial<APIButtonComponentWithCustomId> = {}) {
72
+ super({ ...data, style: ButtonStyle.Danger });
73
+ }
74
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ ButtonStyle,
3
+ ComponentType,
4
+ type APIButtonComponent,
5
+ type APIButtonComponentWithURL,
6
+ } from 'discord-api-types/v10';
7
+ import { Mixin } from 'ts-mixer';
8
+ import { BaseButtonBuilder } from './Button.js';
9
+ import { EmojiOrLabelButtonMixin } from './mixins/EmojiOrLabelButtonMixin.js';
10
+
11
+ /**
12
+ * A builder that creates API-compatible JSON data for buttons with links.
13
+ *
14
+ * @mixes {@link BaseButtonBuilder}\<{@link discord-api-types/v10#(APIButtonComponentWithURL:interface)}\>
15
+ * @mixes {@link EmojiOrLabelButtonMixin}
16
+ */
17
+ export interface LinkButtonBuilder extends BaseButtonBuilder<APIButtonComponentWithURL>, EmojiOrLabelButtonMixin {}
18
+
19
+ export class LinkButtonBuilder extends Mixin(BaseButtonBuilder<APIButtonComponentWithURL>, EmojiOrLabelButtonMixin) {
20
+ protected override readonly data: Partial<APIButtonComponentWithURL>;
21
+
22
+ public constructor(data: Partial<APIButtonComponent> = {}) {
23
+ super();
24
+ this.data = { ...structuredClone(data), type: ComponentType.Button, style: ButtonStyle.Link };
25
+ }
26
+
27
+ /**
28
+ * Sets the URL for this button.
29
+ *
30
+ * @remarks
31
+ * This method is only available to buttons using the `Link` button style.
32
+ * Only three types of URL schemes are currently supported: `https://`, `http://`, and `discord://`.
33
+ * @param url - The URL to use
34
+ */
35
+ public setURL(url: string) {
36
+ this.data.url = url;
37
+ return this;
38
+ }
39
+ }
@@ -0,0 +1,26 @@
1
+ import type { APIButtonComponentWithSKUId, Snowflake } from 'discord-api-types/v10';
2
+ import { ButtonStyle, ComponentType } from 'discord-api-types/v10';
3
+ import { BaseButtonBuilder } from './Button.js';
4
+
5
+ /**
6
+ * A builder that creates API-compatible JSON data for premium buttons.
7
+ */
8
+ export class PremiumButtonBuilder extends BaseButtonBuilder<APIButtonComponentWithSKUId> {
9
+ protected override readonly data: Partial<APIButtonComponentWithSKUId>;
10
+
11
+ public constructor(data: Partial<APIButtonComponentWithSKUId> = {}) {
12
+ super();
13
+ this.data = { ...structuredClone(data), type: ComponentType.Button, style: ButtonStyle.Premium };
14
+ }
15
+
16
+ /**
17
+ * Sets the SKU id that represents a purchasable SKU for this button.
18
+ *
19
+ * @remarks Only available when using premium-style buttons.
20
+ * @param skuId - The SKU id to use
21
+ */
22
+ public setSKUId(skuId: Snowflake) {
23
+ this.data.sku_id = skuId;
24
+ return this;
25
+ }
26
+ }
@@ -0,0 +1,52 @@
1
+ import type { APIButtonComponent, APIButtonComponentWithSKUId, APIMessageComponentEmoji } from 'discord-api-types/v10';
2
+
3
+ export interface EmojiOrLabelButtonData extends Pick<
4
+ Exclude<APIButtonComponent, APIButtonComponentWithSKUId>,
5
+ 'emoji' | 'label'
6
+ > {}
7
+
8
+ /**
9
+ * A mixin that adds emoji and label symbols to a button builder.
10
+ */
11
+ export class EmojiOrLabelButtonMixin {
12
+ /**
13
+ * @internal
14
+ */
15
+ declare protected readonly data: EmojiOrLabelButtonData;
16
+
17
+ /**
18
+ * Sets the emoji to display on this button.
19
+ *
20
+ * @param emoji - The emoji to use
21
+ */
22
+ public setEmoji(emoji: APIMessageComponentEmoji) {
23
+ this.data.emoji = emoji;
24
+ return this;
25
+ }
26
+
27
+ /**
28
+ * Clears the emoji on this button.
29
+ */
30
+ public clearEmoji() {
31
+ this.data.emoji = undefined;
32
+ return this;
33
+ }
34
+
35
+ /**
36
+ * Sets the label for this button.
37
+ *
38
+ * @param label - The label to use
39
+ */
40
+ public setLabel(label: string) {
41
+ this.data.label = label;
42
+ return this;
43
+ }
44
+
45
+ /**
46
+ * Clears the label on this button.
47
+ */
48
+ public clearLabel() {
49
+ this.data.label = undefined;
50
+ return this;
51
+ }
52
+ }
@@ -0,0 +1,12 @@
1
+ import { ComponentType } from 'discord-api-types/v10';
2
+ import { z } from 'zod';
3
+ import { customIdPredicate, idPredicate } from '../../Assertions';
4
+
5
+ export const fileUploadPredicate = z.object({
6
+ type: z.literal(ComponentType.FileUpload),
7
+ id: idPredicate,
8
+ custom_id: customIdPredicate,
9
+ min_values: z.number().int().min(0).max(10).optional(),
10
+ max_values: z.number().int().min(1).max(10).optional(),
11
+ required: z.boolean().optional(),
12
+ });
@@ -0,0 +1,109 @@
1
+ import type { APIFileUploadComponent } from 'discord-api-types/v10';
2
+ import { ComponentType } from 'discord-api-types/v10';
3
+ import { validate } from '../../util/validation.js';
4
+ import { ComponentBuilder } from '../Component.js';
5
+ import { fileUploadPredicate } from './Assertions.js';
6
+
7
+ /**
8
+ * A builder that creates API-compatible JSON data for file uploads.
9
+ */
10
+ export class FileUploadBuilder extends ComponentBuilder<APIFileUploadComponent> {
11
+ /**
12
+ * @internal
13
+ */
14
+ protected readonly data: Partial<APIFileUploadComponent>;
15
+
16
+ /**
17
+ * Creates a new file upload.
18
+ *
19
+ * @param data - The API data to create this file upload with
20
+ * @example
21
+ * Creating a file upload from an API data object:
22
+ * ```ts
23
+ * const fileUpload = new FileUploadBuilder({
24
+ * custom_id: "file_upload",
25
+ * min_values: 2,
26
+ * max_values: 5,
27
+ * });
28
+ * ```
29
+ * @example
30
+ * Creating a file upload using setters and API data:
31
+ * ```ts
32
+ * const fileUpload = new FileUploadBuilder({
33
+ * custom_id: "file_upload",
34
+ * min_values: 2,
35
+ * max_values: 5,
36
+ * }).setRequired();
37
+ * ```
38
+ */
39
+ public constructor(data: Partial<APIFileUploadComponent> = {}) {
40
+ super();
41
+ this.data = { ...structuredClone(data), type: ComponentType.FileUpload };
42
+ }
43
+
44
+ /**
45
+ * Sets the custom id for this file upload.
46
+ *
47
+ * @param customId - The custom id to use
48
+ */
49
+ public setCustomId(customId: string) {
50
+ this.data.custom_id = customId;
51
+ return this;
52
+ }
53
+
54
+ /**
55
+ * Sets the minimum number of file uploads required.
56
+ *
57
+ * @param minValues - The minimum values that must be uploaded
58
+ */
59
+ public setMinValues(minValues: number) {
60
+ this.data.min_values = minValues;
61
+ return this;
62
+ }
63
+
64
+ /**
65
+ * Clears the minimum values.
66
+ */
67
+ public clearMinValues() {
68
+ this.data.min_values = undefined;
69
+ return this;
70
+ }
71
+
72
+ /**
73
+ * Sets the maximum number of file uploads required.
74
+ *
75
+ * @param maxValues - The maximum values that can be uploaded
76
+ */
77
+ public setMaxValues(maxValues: number) {
78
+ this.data.max_values = maxValues;
79
+ return this;
80
+ }
81
+
82
+ /**
83
+ * Clears the maximum values.
84
+ */
85
+ public clearMaxValues() {
86
+ this.data.max_values = undefined;
87
+ return this;
88
+ }
89
+
90
+ /**
91
+ * Sets whether this file upload is required.
92
+ *
93
+ * @param required - Whether this file upload is required
94
+ */
95
+ public setRequired(required = true) {
96
+ this.data.required = required;
97
+ return this;
98
+ }
99
+
100
+ /**
101
+ * {@inheritDoc ComponentBuilder.toJSON}
102
+ */
103
+ public toJSON(validationOverride?: boolean): APIFileUploadComponent {
104
+ const clone = structuredClone(this.data);
105
+ validate(fileUploadPredicate, clone, validationOverride);
106
+
107
+ return clone as APIFileUploadComponent;
108
+ }
109
+ }
@@ -0,0 +1,28 @@
1
+ import { ComponentType } from 'discord-api-types/v10';
2
+ import { z } from 'zod';
3
+ import { idPredicate } from '../../Assertions';
4
+ import {
5
+ selectMenuChannelPredicate,
6
+ selectMenuMentionablePredicate,
7
+ selectMenuRolePredicate,
8
+ selectMenuStringPredicate,
9
+ selectMenuUserPredicate,
10
+ } from '../Assertions';
11
+ import { fileUploadPredicate } from '../fileUpload/Assertions';
12
+ import { textInputPredicate } from '../textInput/Assertions';
13
+
14
+ export const labelPredicate = z.object({
15
+ id: idPredicate,
16
+ type: z.literal(ComponentType.Label),
17
+ label: z.string().min(1).max(45),
18
+ description: z.string().min(1).max(100).optional(),
19
+ component: z.union([
20
+ selectMenuStringPredicate,
21
+ textInputPredicate,
22
+ selectMenuUserPredicate,
23
+ selectMenuRolePredicate,
24
+ selectMenuMentionablePredicate,
25
+ selectMenuChannelPredicate,
26
+ fileUploadPredicate,
27
+ ]),
28
+ });