@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,346 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
APITextInputComponent,
|
|
3
|
+
APIActionRowComponent,
|
|
4
|
+
APIComponentInActionRow,
|
|
5
|
+
APIChannelSelectComponent,
|
|
6
|
+
APIMentionableSelectComponent,
|
|
7
|
+
APIRoleSelectComponent,
|
|
8
|
+
APIStringSelectComponent,
|
|
9
|
+
APIUserSelectComponent,
|
|
10
|
+
APIButtonComponentWithCustomId,
|
|
11
|
+
APIButtonComponentWithSKUId,
|
|
12
|
+
APIButtonComponentWithURL,
|
|
13
|
+
} from 'discord-api-types/v10';
|
|
14
|
+
import { ComponentType } from 'discord-api-types/v10';
|
|
15
|
+
import { normalizeArray, type RestOrArray } from '../util/normalizeArray.js';
|
|
16
|
+
import { resolveBuilder } from '../util/resolveBuilder.js';
|
|
17
|
+
import { validate } from '../util/validation.js';
|
|
18
|
+
import { actionRowPredicate } from './Assertions.js';
|
|
19
|
+
import { ComponentBuilder } from './Component.js';
|
|
20
|
+
import type { AnyActionRowComponentBuilder } from './Components.js';
|
|
21
|
+
import { createComponentBuilder } from './Components.js';
|
|
22
|
+
import {
|
|
23
|
+
DangerButtonBuilder,
|
|
24
|
+
PrimaryButtonBuilder,
|
|
25
|
+
SecondaryButtonBuilder,
|
|
26
|
+
SuccessButtonBuilder,
|
|
27
|
+
} from './button/CustomIdButton.js';
|
|
28
|
+
import { LinkButtonBuilder } from './button/LinkButton.js';
|
|
29
|
+
import { PremiumButtonBuilder } from './button/PremiumButton.js';
|
|
30
|
+
import { ChannelSelectMenuBuilder } from './selectMenu/ChannelSelectMenu.js';
|
|
31
|
+
import { MentionableSelectMenuBuilder } from './selectMenu/MentionableSelectMenu.js';
|
|
32
|
+
import { RoleSelectMenuBuilder } from './selectMenu/RoleSelectMenu.js';
|
|
33
|
+
import { StringSelectMenuBuilder } from './selectMenu/StringSelectMenu.js';
|
|
34
|
+
import { UserSelectMenuBuilder } from './selectMenu/UserSelectMenu.js';
|
|
35
|
+
import { TextInputBuilder } from './textInput/TextInput.js';
|
|
36
|
+
|
|
37
|
+
export interface ActionRowBuilderData extends Partial<
|
|
38
|
+
Omit<APIActionRowComponent<APIComponentInActionRow>, 'components'>
|
|
39
|
+
> {
|
|
40
|
+
components: AnyActionRowComponentBuilder[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A builder that creates API-compatible JSON data for action rows.
|
|
45
|
+
*/
|
|
46
|
+
export class ActionRowBuilder extends ComponentBuilder<APIActionRowComponent<APIComponentInActionRow>> {
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
protected readonly data: ActionRowBuilderData;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The components within this action row.
|
|
54
|
+
*/
|
|
55
|
+
public get components(): readonly AnyActionRowComponentBuilder[] {
|
|
56
|
+
return this.data.components;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates a new action row.
|
|
61
|
+
*
|
|
62
|
+
* @param data - The API data to create this action row with
|
|
63
|
+
* @example
|
|
64
|
+
* Creating an action row from an API data object:
|
|
65
|
+
* ```ts
|
|
66
|
+
* const actionRow = new ActionRowBuilder({
|
|
67
|
+
* components: [
|
|
68
|
+
* {
|
|
69
|
+
* custom_id: "custom id",
|
|
70
|
+
* label: "Type something",
|
|
71
|
+
* style: TextInputStyle.Short,
|
|
72
|
+
* type: ComponentType.TextInput,
|
|
73
|
+
* },
|
|
74
|
+
* ],
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
* @example
|
|
78
|
+
* Creating an action row using setters and API data:
|
|
79
|
+
* ```ts
|
|
80
|
+
* const actionRow = new ActionRowBuilder({
|
|
81
|
+
* components: [
|
|
82
|
+
* {
|
|
83
|
+
* custom_id: "custom id",
|
|
84
|
+
* label: "Click me",
|
|
85
|
+
* style: ButtonStyle.Primary,
|
|
86
|
+
* type: ComponentType.Button,
|
|
87
|
+
* },
|
|
88
|
+
* ],
|
|
89
|
+
* })
|
|
90
|
+
* .addComponents(button2, button3);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
public constructor(data: Partial<APIActionRowComponent<APIComponentInActionRow>> = {}) {
|
|
94
|
+
super();
|
|
95
|
+
|
|
96
|
+
const { components = [], ...rest } = data;
|
|
97
|
+
|
|
98
|
+
this.data = {
|
|
99
|
+
...structuredClone(rest),
|
|
100
|
+
components: components.map((component) => createComponentBuilder(component)),
|
|
101
|
+
type: ComponentType.ActionRow,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Adds primary button components to this action row.
|
|
107
|
+
*
|
|
108
|
+
* @param input - The buttons to add
|
|
109
|
+
*/
|
|
110
|
+
public addPrimaryButtonComponents(
|
|
111
|
+
...input: RestOrArray<
|
|
112
|
+
APIButtonComponentWithCustomId | PrimaryButtonBuilder | ((builder: PrimaryButtonBuilder) => PrimaryButtonBuilder)
|
|
113
|
+
>
|
|
114
|
+
): this {
|
|
115
|
+
const normalized = normalizeArray(input);
|
|
116
|
+
const resolved = normalized.map((component) => resolveBuilder(component, PrimaryButtonBuilder));
|
|
117
|
+
|
|
118
|
+
this.data.components.push(...resolved);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Adds secondary button components to this action row.
|
|
124
|
+
*
|
|
125
|
+
* @param input - The buttons to add
|
|
126
|
+
*/
|
|
127
|
+
public addSecondaryButtonComponents(
|
|
128
|
+
...input: RestOrArray<
|
|
129
|
+
| APIButtonComponentWithCustomId
|
|
130
|
+
| SecondaryButtonBuilder
|
|
131
|
+
| ((builder: SecondaryButtonBuilder) => SecondaryButtonBuilder)
|
|
132
|
+
>
|
|
133
|
+
): this {
|
|
134
|
+
const normalized = normalizeArray(input);
|
|
135
|
+
const resolved = normalized.map((component) => resolveBuilder(component, SecondaryButtonBuilder));
|
|
136
|
+
|
|
137
|
+
this.data.components.push(...resolved);
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Adds success button components to this action row.
|
|
143
|
+
*
|
|
144
|
+
* @param input - The buttons to add
|
|
145
|
+
*/
|
|
146
|
+
public addSuccessButtonComponents(
|
|
147
|
+
...input: RestOrArray<
|
|
148
|
+
APIButtonComponentWithCustomId | SuccessButtonBuilder | ((builder: SuccessButtonBuilder) => SuccessButtonBuilder)
|
|
149
|
+
>
|
|
150
|
+
): this {
|
|
151
|
+
const normalized = normalizeArray(input);
|
|
152
|
+
const resolved = normalized.map((component) => resolveBuilder(component, SuccessButtonBuilder));
|
|
153
|
+
|
|
154
|
+
this.data.components.push(...resolved);
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Adds danger button components to this action row.
|
|
160
|
+
*/
|
|
161
|
+
public addDangerButtonComponents(
|
|
162
|
+
...input: RestOrArray<
|
|
163
|
+
APIButtonComponentWithCustomId | DangerButtonBuilder | ((builder: DangerButtonBuilder) => DangerButtonBuilder)
|
|
164
|
+
>
|
|
165
|
+
): this {
|
|
166
|
+
const normalized = normalizeArray(input);
|
|
167
|
+
const resolved = normalized.map((component) => resolveBuilder(component, DangerButtonBuilder));
|
|
168
|
+
|
|
169
|
+
this.data.components.push(...resolved);
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generically add any type of component to this action row, only takes in an instance of a component builder.
|
|
175
|
+
*/
|
|
176
|
+
public addComponents(...input: RestOrArray<AnyActionRowComponentBuilder>): this {
|
|
177
|
+
const normalized = normalizeArray(input);
|
|
178
|
+
this.data.components.push(...normalized);
|
|
179
|
+
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Adds SKU id button components to this action row.
|
|
185
|
+
*
|
|
186
|
+
* @param input - The buttons to add
|
|
187
|
+
*/
|
|
188
|
+
public addPremiumButtonComponents(
|
|
189
|
+
...input: RestOrArray<
|
|
190
|
+
APIButtonComponentWithSKUId | PremiumButtonBuilder | ((builder: PremiumButtonBuilder) => PremiumButtonBuilder)
|
|
191
|
+
>
|
|
192
|
+
): this {
|
|
193
|
+
const normalized = normalizeArray(input);
|
|
194
|
+
const resolved = normalized.map((component) => resolveBuilder(component, PremiumButtonBuilder));
|
|
195
|
+
|
|
196
|
+
this.data.components.push(...resolved);
|
|
197
|
+
return this;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Adds URL button components to this action row.
|
|
202
|
+
*
|
|
203
|
+
* @param input - The buttons to add
|
|
204
|
+
*/
|
|
205
|
+
public addLinkButtonComponents(
|
|
206
|
+
...input: RestOrArray<
|
|
207
|
+
APIButtonComponentWithURL | LinkButtonBuilder | ((builder: LinkButtonBuilder) => LinkButtonBuilder)
|
|
208
|
+
>
|
|
209
|
+
): this {
|
|
210
|
+
const normalized = normalizeArray(input);
|
|
211
|
+
const resolved = normalized.map((component) => resolveBuilder(component, LinkButtonBuilder));
|
|
212
|
+
|
|
213
|
+
this.data.components.push(...resolved);
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Adds a channel select menu component to this action row.
|
|
219
|
+
*
|
|
220
|
+
* @param input - A function that returns a component builder or an already built builder
|
|
221
|
+
*/
|
|
222
|
+
public addChannelSelectMenuComponent(
|
|
223
|
+
input:
|
|
224
|
+
| APIChannelSelectComponent
|
|
225
|
+
| ChannelSelectMenuBuilder
|
|
226
|
+
| ((builder: ChannelSelectMenuBuilder) => ChannelSelectMenuBuilder),
|
|
227
|
+
): this {
|
|
228
|
+
this.data.components.push(resolveBuilder(input, ChannelSelectMenuBuilder));
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Adds a mentionable select menu component to this action row.
|
|
234
|
+
*
|
|
235
|
+
* @param input - A function that returns a component builder or an already built builder
|
|
236
|
+
*/
|
|
237
|
+
public addMentionableSelectMenuComponent(
|
|
238
|
+
input:
|
|
239
|
+
| APIMentionableSelectComponent
|
|
240
|
+
| MentionableSelectMenuBuilder
|
|
241
|
+
| ((builder: MentionableSelectMenuBuilder) => MentionableSelectMenuBuilder),
|
|
242
|
+
): this {
|
|
243
|
+
this.data.components.push(resolveBuilder(input, MentionableSelectMenuBuilder));
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Adds a role select menu component to this action row.
|
|
249
|
+
*
|
|
250
|
+
* @param input - A function that returns a component builder or an already built builder
|
|
251
|
+
*/
|
|
252
|
+
public addRoleSelectMenuComponent(
|
|
253
|
+
input: APIRoleSelectComponent | RoleSelectMenuBuilder | ((builder: RoleSelectMenuBuilder) => RoleSelectMenuBuilder),
|
|
254
|
+
): this {
|
|
255
|
+
this.data.components.push(resolveBuilder(input, RoleSelectMenuBuilder));
|
|
256
|
+
return this;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Adds a string select menu component to this action row.
|
|
261
|
+
*
|
|
262
|
+
* @param input - A function that returns a component builder or an already built builder
|
|
263
|
+
*/
|
|
264
|
+
public addStringSelectMenuComponent(
|
|
265
|
+
input:
|
|
266
|
+
| APIStringSelectComponent
|
|
267
|
+
| StringSelectMenuBuilder
|
|
268
|
+
| ((builder: StringSelectMenuBuilder) => StringSelectMenuBuilder),
|
|
269
|
+
): this {
|
|
270
|
+
this.data.components.push(resolveBuilder(input, StringSelectMenuBuilder));
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Adds a user select menu component to this action row.
|
|
276
|
+
*
|
|
277
|
+
* @param input - A function that returns a component builder or an already built builder
|
|
278
|
+
*/
|
|
279
|
+
public addUserSelectMenuComponent(
|
|
280
|
+
input: APIUserSelectComponent | UserSelectMenuBuilder | ((builder: UserSelectMenuBuilder) => UserSelectMenuBuilder),
|
|
281
|
+
): this {
|
|
282
|
+
this.data.components.push(resolveBuilder(input, UserSelectMenuBuilder));
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Adds a text input component to this action row.
|
|
288
|
+
*
|
|
289
|
+
* @param input - A function that returns a component builder or an already built builder
|
|
290
|
+
*/
|
|
291
|
+
public addTextInputComponent(
|
|
292
|
+
input: APITextInputComponent | TextInputBuilder | ((builder: TextInputBuilder) => TextInputBuilder),
|
|
293
|
+
): this {
|
|
294
|
+
this.data.components.push(resolveBuilder(input, TextInputBuilder));
|
|
295
|
+
return this;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Removes, replaces, or inserts components for this action row.
|
|
300
|
+
*
|
|
301
|
+
* @remarks
|
|
302
|
+
* This method behaves similarly
|
|
303
|
+
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
|
|
304
|
+
*
|
|
305
|
+
* It's useful for modifying and adjusting order of the already-existing components of an action row.
|
|
306
|
+
* @example
|
|
307
|
+
* Remove the first component:
|
|
308
|
+
* ```ts
|
|
309
|
+
* actionRow.spliceComponents(0, 1);
|
|
310
|
+
* ```
|
|
311
|
+
* @example
|
|
312
|
+
* Remove the first n components:
|
|
313
|
+
* ```ts
|
|
314
|
+
* const n = 4;
|
|
315
|
+
* actionRow.spliceComponents(0, n);
|
|
316
|
+
* ```
|
|
317
|
+
* @example
|
|
318
|
+
* Remove the last component:
|
|
319
|
+
* ```ts
|
|
320
|
+
* actionRow.spliceComponents(-1, 1);
|
|
321
|
+
* ```
|
|
322
|
+
* @param index - The index to start at
|
|
323
|
+
* @param deleteCount - The number of components to remove
|
|
324
|
+
* @param components - The replacing component objects
|
|
325
|
+
*/
|
|
326
|
+
public spliceComponents(index: number, deleteCount: number, ...components: AnyActionRowComponentBuilder[]): this {
|
|
327
|
+
this.data.components.splice(index, deleteCount, ...components);
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* {@inheritDoc ComponentBuilder.toJSON}
|
|
333
|
+
*/
|
|
334
|
+
public override toJSON(validationOverride?: boolean): APIActionRowComponent<APIComponentInActionRow> {
|
|
335
|
+
const { components, ...rest } = this.data;
|
|
336
|
+
|
|
337
|
+
const data = {
|
|
338
|
+
...structuredClone(rest),
|
|
339
|
+
components: components.map((component) => (component as any).toJSON(validationOverride)),
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
validate(actionRowPredicate, data, validationOverride);
|
|
343
|
+
|
|
344
|
+
return data as APIActionRowComponent<APIComponentInActionRow>;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { ButtonStyle, ChannelType, ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { idPredicate, customIdPredicate, snowflakePredicate } from '../Assertions.js';
|
|
4
|
+
|
|
5
|
+
export const emojiPredicate = z
|
|
6
|
+
.strictObject({
|
|
7
|
+
id: snowflakePredicate.optional(),
|
|
8
|
+
name: z.string().min(1).max(32).optional(),
|
|
9
|
+
animated: z.boolean().optional(),
|
|
10
|
+
})
|
|
11
|
+
.refine((data) => data.id !== undefined || data.name !== undefined, {
|
|
12
|
+
message: "Either 'id' or 'name' must be provided",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const buttonPredicateBase = z.strictObject({
|
|
16
|
+
type: z.literal(ComponentType.Button),
|
|
17
|
+
disabled: z.boolean().optional(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const buttonLabelPredicate = z.string().min(1).max(80);
|
|
21
|
+
|
|
22
|
+
const buttonCustomIdPredicateBase = buttonPredicateBase
|
|
23
|
+
.extend({
|
|
24
|
+
custom_id: customIdPredicate,
|
|
25
|
+
emoji: emojiPredicate.optional(),
|
|
26
|
+
label: buttonLabelPredicate.optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const buttonPrimaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Primary) });
|
|
32
|
+
const buttonSecondaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Secondary) });
|
|
33
|
+
const buttonSuccessPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Success) });
|
|
34
|
+
const buttonDangerPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Danger) });
|
|
35
|
+
|
|
36
|
+
const buttonPrimaryRefinedPredicate = buttonPrimaryPredicate.refine((data) => data.emoji !== undefined || data.label !== undefined, {
|
|
37
|
+
message: 'Buttons with a custom id must have either an emoji or a label.',
|
|
38
|
+
});
|
|
39
|
+
const buttonSecondaryRefinedPredicate = buttonSecondaryPredicate.refine((data) => data.emoji !== undefined || data.label !== undefined, {
|
|
40
|
+
message: 'Buttons with a custom id must have either an emoji or a label.',
|
|
41
|
+
});
|
|
42
|
+
const buttonSuccessRefinedPredicate = buttonSuccessPredicate.refine((data) => data.emoji !== undefined || data.label !== undefined, {
|
|
43
|
+
message: 'Buttons with a custom id must have either an emoji or a label.',
|
|
44
|
+
});
|
|
45
|
+
const buttonDangerRefinedPredicate = buttonDangerPredicate.refine((data) => data.emoji !== undefined || data.label !== undefined, {
|
|
46
|
+
message: 'Buttons with a custom id must have either an emoji or a label.',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const buttonLinkPredicate = buttonPredicateBase
|
|
50
|
+
.extend({
|
|
51
|
+
style: z.literal(ButtonStyle.Link),
|
|
52
|
+
url: z.string().url().max(512).refine((url) => url.startsWith('http:') || url.startsWith('https:') || url.startsWith('discord:'), { message: 'URL must use http, https, or discord protocol' }),
|
|
53
|
+
emoji: emojiPredicate.optional(),
|
|
54
|
+
label: buttonLabelPredicate.optional(),
|
|
55
|
+
})
|
|
56
|
+
.refine((data) => data.emoji !== undefined || data.label !== undefined, {
|
|
57
|
+
message: 'Link buttons must have either an emoji or a label.',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const buttonPremiumPredicate = buttonPredicateBase.extend({
|
|
61
|
+
style: z.literal(ButtonStyle.Premium),
|
|
62
|
+
sku_id: snowflakePredicate,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const buttonPredicate = z.union([
|
|
66
|
+
buttonLinkPredicate,
|
|
67
|
+
buttonPrimaryRefinedPredicate,
|
|
68
|
+
buttonSecondaryRefinedPredicate,
|
|
69
|
+
buttonSuccessRefinedPredicate,
|
|
70
|
+
buttonDangerRefinedPredicate,
|
|
71
|
+
buttonPremiumPredicate,
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
const selectMenuBasePredicate = z.object({
|
|
75
|
+
id: idPredicate,
|
|
76
|
+
placeholder: z.string().max(150).optional(),
|
|
77
|
+
min_values: z.number().min(0).max(25).optional(),
|
|
78
|
+
max_values: z.number().min(0).max(25).optional(),
|
|
79
|
+
custom_id: customIdPredicate,
|
|
80
|
+
disabled: z.boolean().optional(),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export const selectMenuChannelPredicate = selectMenuBasePredicate.extend({
|
|
84
|
+
type: z.literal(ComponentType.ChannelSelect),
|
|
85
|
+
channel_types: z.nativeEnum(ChannelType).array().optional(),
|
|
86
|
+
default_values: z
|
|
87
|
+
.object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.Channel) })
|
|
88
|
+
.array()
|
|
89
|
+
.max(25)
|
|
90
|
+
.optional(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export const selectMenuMentionablePredicate = selectMenuBasePredicate.extend({
|
|
94
|
+
type: z.literal(ComponentType.MentionableSelect),
|
|
95
|
+
default_values: z
|
|
96
|
+
.object({
|
|
97
|
+
id: snowflakePredicate,
|
|
98
|
+
type: z.union([z.literal(SelectMenuDefaultValueType.Role), z.literal(SelectMenuDefaultValueType.User)]),
|
|
99
|
+
})
|
|
100
|
+
.array()
|
|
101
|
+
.max(25)
|
|
102
|
+
.optional(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const selectMenuRolePredicate = selectMenuBasePredicate.extend({
|
|
106
|
+
type: z.literal(ComponentType.RoleSelect),
|
|
107
|
+
default_values: z
|
|
108
|
+
.object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.Role) })
|
|
109
|
+
.array()
|
|
110
|
+
.max(25)
|
|
111
|
+
.optional(),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
export const selectMenuStringOptionPredicate = z.object({
|
|
115
|
+
label: z.string().min(1).max(100),
|
|
116
|
+
value: z.string().min(1).max(100),
|
|
117
|
+
description: z.string().min(1).max(100).optional(),
|
|
118
|
+
emoji: emojiPredicate.optional(),
|
|
119
|
+
default: z.boolean().optional(),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
export const selectMenuStringPredicate = selectMenuBasePredicate
|
|
123
|
+
.extend({
|
|
124
|
+
type: z.literal(ComponentType.StringSelect),
|
|
125
|
+
options: selectMenuStringOptionPredicate.array().min(1).max(25),
|
|
126
|
+
})
|
|
127
|
+
.superRefine((value, ctx) => {
|
|
128
|
+
const addIssue = (name: string, minimum: number) =>
|
|
129
|
+
ctx.addIssue({
|
|
130
|
+
code: z.ZodIssueCode.too_small,
|
|
131
|
+
message: `The number of options must be greater than or equal to ${name}`,
|
|
132
|
+
inclusive: true,
|
|
133
|
+
minimum,
|
|
134
|
+
type: 'array',
|
|
135
|
+
path: ['options'],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (value.min_values !== undefined && value.options.length < value.min_values) {
|
|
139
|
+
addIssue('min_values', value.min_values);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (
|
|
143
|
+
value.min_values !== undefined &&
|
|
144
|
+
value.max_values !== undefined &&
|
|
145
|
+
value.min_values > value.max_values
|
|
146
|
+
) {
|
|
147
|
+
ctx.addIssue({
|
|
148
|
+
code: z.ZodIssueCode.too_big,
|
|
149
|
+
message: `The maximum amount of options must be greater than or equal to the minimum amount of options`,
|
|
150
|
+
inclusive: true,
|
|
151
|
+
maximum: value.max_values,
|
|
152
|
+
type: 'number',
|
|
153
|
+
path: ['min_values'],
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
export const selectMenuUserPredicate = selectMenuBasePredicate.extend({
|
|
159
|
+
type: z.literal(ComponentType.UserSelect),
|
|
160
|
+
default_values: z
|
|
161
|
+
.object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.User) })
|
|
162
|
+
.array()
|
|
163
|
+
.max(25)
|
|
164
|
+
.optional(),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
export const actionRowPredicate = z.object({
|
|
168
|
+
id: idPredicate,
|
|
169
|
+
type: z.literal(ComponentType.ActionRow),
|
|
170
|
+
components: z.union([
|
|
171
|
+
z
|
|
172
|
+
.object({ type: z.literal(ComponentType.Button) })
|
|
173
|
+
.array()
|
|
174
|
+
.min(1)
|
|
175
|
+
.max(5),
|
|
176
|
+
z
|
|
177
|
+
.object({
|
|
178
|
+
type: z.union([
|
|
179
|
+
z.literal(ComponentType.ChannelSelect),
|
|
180
|
+
z.literal(ComponentType.MentionableSelect),
|
|
181
|
+
z.literal(ComponentType.StringSelect),
|
|
182
|
+
z.literal(ComponentType.RoleSelect),
|
|
183
|
+
z.literal(ComponentType.TextInput),
|
|
184
|
+
z.literal(ComponentType.UserSelect),
|
|
185
|
+
]),
|
|
186
|
+
})
|
|
187
|
+
.array()
|
|
188
|
+
.length(1),
|
|
189
|
+
]),
|
|
190
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { JSONEncodable } from '@ovencord/util';
|
|
2
|
+
import type { APIBaseComponent, ComponentType } from 'discord-api-types/v10';
|
|
3
|
+
|
|
4
|
+
export interface ComponentBuilderBaseData {
|
|
5
|
+
id?: number | undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The base component builder that contains common symbols for all sorts of components.
|
|
10
|
+
*
|
|
11
|
+
* @typeParam Component - The type of API data that is stored within the builder
|
|
12
|
+
*/
|
|
13
|
+
export abstract class ComponentBuilder<
|
|
14
|
+
Component extends APIBaseComponent<ComponentType>,
|
|
15
|
+
> implements JSONEncodable<Component> {
|
|
16
|
+
/**
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
protected abstract readonly data: ComponentBuilderBaseData;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sets the id of this component.
|
|
23
|
+
*
|
|
24
|
+
* @param id - The id to use
|
|
25
|
+
*/
|
|
26
|
+
public setId(id: number) {
|
|
27
|
+
this.data.id = id;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Clears the id of this component, defaulting to a default incremented id.
|
|
33
|
+
*/
|
|
34
|
+
public clearId() {
|
|
35
|
+
this.data.id = undefined;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Serializes this builder to API-compatible JSON data.
|
|
41
|
+
*
|
|
42
|
+
* Note that by disabling validation, there is no guarantee that the resulting object will be valid.
|
|
43
|
+
*
|
|
44
|
+
* @param validationOverride - Force validation to run/not run regardless of your global preference
|
|
45
|
+
*/
|
|
46
|
+
public abstract toJSON(validationOverride?: boolean): Component;
|
|
47
|
+
}
|