@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.
- package/LICENSE +191 -0
- package/README.md +92 -0
- package/package.json +70 -0
- package/src/Assertions.ts +15 -0
- package/src/components/ActionRow.ts +346 -0
- package/src/components/Assertions.ts +190 -0
- package/src/components/Component.ts +47 -0
- package/src/components/Components.ts +275 -0
- package/src/components/button/Button.ts +34 -0
- package/src/components/button/CustomIdButton.ts +74 -0
- package/src/components/button/LinkButton.ts +39 -0
- package/src/components/button/PremiumButton.ts +26 -0
- package/src/components/button/mixins/EmojiOrLabelButtonMixin.ts +52 -0
- package/src/components/fileUpload/Assertions.ts +12 -0
- package/src/components/fileUpload/FileUpload.ts +109 -0
- package/src/components/label/Assertions.ts +28 -0
- package/src/components/label/Label.ts +215 -0
- package/src/components/selectMenu/BaseSelectMenu.ts +89 -0
- package/src/components/selectMenu/ChannelSelectMenu.ts +115 -0
- package/src/components/selectMenu/MentionableSelectMenu.ts +126 -0
- package/src/components/selectMenu/RoleSelectMenu.ts +89 -0
- package/src/components/selectMenu/StringSelectMenu.ts +165 -0
- package/src/components/selectMenu/StringSelectMenuOption.ts +113 -0
- package/src/components/selectMenu/UserSelectMenu.ts +89 -0
- package/src/components/textInput/Assertions.ts +15 -0
- package/src/components/textInput/TextInput.ts +154 -0
- package/src/components/v2/Assertions.ts +82 -0
- package/src/components/v2/Container.ts +254 -0
- package/src/components/v2/File.ts +81 -0
- package/src/components/v2/MediaGallery.ts +128 -0
- package/src/components/v2/MediaGalleryItem.ts +85 -0
- package/src/components/v2/Section.ts +266 -0
- package/src/components/v2/Separator.ts +82 -0
- package/src/components/v2/TextDisplay.ts +63 -0
- package/src/components/v2/Thumbnail.ts +100 -0
- package/src/index.ts +109 -0
- package/src/interactions/commands/Command.ts +87 -0
- package/src/interactions/commands/SharedName.ts +68 -0
- package/src/interactions/commands/SharedNameAndDescription.ts +69 -0
- package/src/interactions/commands/chatInput/Assertions.ts +180 -0
- package/src/interactions/commands/chatInput/ChatInputCommand.ts +40 -0
- package/src/interactions/commands/chatInput/ChatInputCommandSubcommands.ts +117 -0
- package/src/interactions/commands/chatInput/mixins/ApplicationCommandNumericOptionMinMaxValueMixin.ts +52 -0
- package/src/interactions/commands/chatInput/mixins/ApplicationCommandOptionChannelTypesMixin.ts +57 -0
- package/src/interactions/commands/chatInput/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts +32 -0
- package/src/interactions/commands/chatInput/mixins/ApplicationCommandOptionWithChoicesMixin.ts +43 -0
- package/src/interactions/commands/chatInput/mixins/SharedChatInputCommandOptions.ts +204 -0
- package/src/interactions/commands/chatInput/mixins/SharedSubcommands.ts +61 -0
- package/src/interactions/commands/chatInput/options/ApplicationCommandOptionBase.ts +66 -0
- package/src/interactions/commands/chatInput/options/attachment.ts +20 -0
- package/src/interactions/commands/chatInput/options/boolean.ts +20 -0
- package/src/interactions/commands/chatInput/options/channel.ts +28 -0
- package/src/interactions/commands/chatInput/options/integer.ts +34 -0
- package/src/interactions/commands/chatInput/options/mentionable.ts +20 -0
- package/src/interactions/commands/chatInput/options/number.ts +34 -0
- package/src/interactions/commands/chatInput/options/role.ts +20 -0
- package/src/interactions/commands/chatInput/options/string.ts +71 -0
- package/src/interactions/commands/chatInput/options/user.ts +20 -0
- package/src/interactions/commands/contextMenu/Assertions.ts +31 -0
- package/src/interactions/commands/contextMenu/ContextMenuCommand.ts +42 -0
- package/src/interactions/commands/contextMenu/MessageCommand.ts +19 -0
- package/src/interactions/commands/contextMenu/UserCommand.ts +19 -0
- package/src/interactions/modals/Assertions.ts +27 -0
- package/src/interactions/modals/Modal.ts +158 -0
- package/src/messages/AllowedMentions.ts +193 -0
- package/src/messages/Assertions.ts +148 -0
- package/src/messages/Attachment.ts +209 -0
- package/src/messages/Message.ts +692 -0
- package/src/messages/MessageReference.ts +111 -0
- package/src/messages/embed/Assertions.ts +53 -0
- package/src/messages/embed/Embed.ts +352 -0
- package/src/messages/embed/EmbedAuthor.ts +83 -0
- package/src/messages/embed/EmbedField.ts +67 -0
- package/src/messages/embed/EmbedFooter.ts +65 -0
- package/src/messages/poll/Assertions.ts +20 -0
- package/src/messages/poll/Poll.ts +243 -0
- package/src/messages/poll/PollAnswer.ts +77 -0
- package/src/messages/poll/PollAnswerMedia.ts +38 -0
- package/src/messages/poll/PollMedia.ts +41 -0
- package/src/messages/poll/PollQuestion.ts +20 -0
- package/src/util/ValidationError.ts +21 -0
- package/src/util/normalizeArray.ts +19 -0
- package/src/util/resolveBuilder.ts +40 -0
- 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
|
+
});
|