@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,165 @@
|
|
|
1
|
+
import { ComponentType } from 'discord-api-types/v10';
|
|
2
|
+
import type { APIStringSelectComponent, APISelectMenuOption } from 'discord-api-types/v10';
|
|
3
|
+
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
|
|
4
|
+
import { resolveBuilder } from '../../util/resolveBuilder.js';
|
|
5
|
+
import { validate } from '../../util/validation.js';
|
|
6
|
+
import { selectMenuStringPredicate } from '../Assertions.js';
|
|
7
|
+
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';
|
|
8
|
+
import { StringSelectMenuOptionBuilder } from './StringSelectMenuOption.js';
|
|
9
|
+
|
|
10
|
+
export interface StringSelectMenuData extends Partial<Omit<APIStringSelectComponent, 'options'>> {
|
|
11
|
+
options: StringSelectMenuOptionBuilder[];
|
|
12
|
+
required?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A builder that creates API-compatible JSON data for string select menus.
|
|
17
|
+
*/
|
|
18
|
+
export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSelectComponent> {
|
|
19
|
+
protected override readonly data: StringSelectMenuData;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The options for this select menu.
|
|
23
|
+
*/
|
|
24
|
+
public get options(): readonly StringSelectMenuOptionBuilder[] {
|
|
25
|
+
return this.data.options;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new string select menu.
|
|
30
|
+
*
|
|
31
|
+
* @param data - The API data to create this string select menu with
|
|
32
|
+
* @example
|
|
33
|
+
* Creating a select menu from an API data object:
|
|
34
|
+
* ```ts
|
|
35
|
+
* const selectMenu = new StringSelectMenuBuilder({
|
|
36
|
+
* custom_id: 'a cool select menu',
|
|
37
|
+
* placeholder: 'select an option',
|
|
38
|
+
* max_values: 2,
|
|
39
|
+
* options: [
|
|
40
|
+
* { label: 'option 1', value: '1' },
|
|
41
|
+
* { label: 'option 2', value: '2' },
|
|
42
|
+
* { label: 'option 3', value: '3' },
|
|
43
|
+
* ],
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
* @example
|
|
47
|
+
* Creating a select menu using setters and API data:
|
|
48
|
+
* ```ts
|
|
49
|
+
* const selectMenu = new StringSelectMenuBuilder({
|
|
50
|
+
* custom_id: 'a cool select menu',
|
|
51
|
+
* })
|
|
52
|
+
* .setMinValues(1)
|
|
53
|
+
* .addOptions({
|
|
54
|
+
* label: 'Catchy',
|
|
55
|
+
* value: 'catch',
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
public constructor(data: Partial<APIStringSelectComponent> = {}) {
|
|
60
|
+
super();
|
|
61
|
+
|
|
62
|
+
const { options = [], ...rest } = data;
|
|
63
|
+
|
|
64
|
+
this.data = {
|
|
65
|
+
...structuredClone(rest),
|
|
66
|
+
options: options.map((option) => new StringSelectMenuOptionBuilder(option)),
|
|
67
|
+
type: ComponentType.StringSelect,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Adds options to this select menu.
|
|
73
|
+
*
|
|
74
|
+
* @param options - The options to add
|
|
75
|
+
*/
|
|
76
|
+
public addOptions(
|
|
77
|
+
...options: RestOrArray<
|
|
78
|
+
| APISelectMenuOption
|
|
79
|
+
| StringSelectMenuOptionBuilder
|
|
80
|
+
| ((builder: StringSelectMenuOptionBuilder) => StringSelectMenuOptionBuilder)
|
|
81
|
+
>
|
|
82
|
+
) {
|
|
83
|
+
const normalizedOptions = normalizeArray(options);
|
|
84
|
+
const resolved = normalizedOptions.map((option) => resolveBuilder(option, StringSelectMenuOptionBuilder));
|
|
85
|
+
|
|
86
|
+
this.data.options.push(...resolved);
|
|
87
|
+
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Sets the options for this select menu.
|
|
93
|
+
*
|
|
94
|
+
* @param options - The options to set
|
|
95
|
+
*/
|
|
96
|
+
public setOptions(
|
|
97
|
+
...options: RestOrArray<
|
|
98
|
+
| APISelectMenuOption
|
|
99
|
+
| StringSelectMenuOptionBuilder
|
|
100
|
+
| ((builder: StringSelectMenuOptionBuilder) => StringSelectMenuOptionBuilder)
|
|
101
|
+
>
|
|
102
|
+
) {
|
|
103
|
+
return this.spliceOptions(0, this.options.length, ...normalizeArray(options));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Removes, replaces, or inserts options for this select menu.
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* This method behaves similarly
|
|
111
|
+
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/slice | Array.prototype.splice()}.
|
|
112
|
+
* It's useful for modifying and adjusting the order of existing options.
|
|
113
|
+
* @example
|
|
114
|
+
* Remove the first option:
|
|
115
|
+
* ```ts
|
|
116
|
+
* selectMenu.spliceOptions(0, 1);
|
|
117
|
+
* ```
|
|
118
|
+
* @example
|
|
119
|
+
* Remove the first n option:
|
|
120
|
+
* ```ts
|
|
121
|
+
* const n = 4;
|
|
122
|
+
* selectMenu.spliceOptions(0, n);
|
|
123
|
+
* ```
|
|
124
|
+
* @example
|
|
125
|
+
* Remove the last option:
|
|
126
|
+
* ```ts
|
|
127
|
+
* selectMenu.spliceOptions(-1, 1);
|
|
128
|
+
* ```
|
|
129
|
+
* @param index - The index to start at
|
|
130
|
+
* @param deleteCount - The number of options to remove
|
|
131
|
+
* @param options - The replacing option objects or builders
|
|
132
|
+
*/
|
|
133
|
+
public spliceOptions(
|
|
134
|
+
index: number,
|
|
135
|
+
deleteCount: number,
|
|
136
|
+
...options: (
|
|
137
|
+
| APISelectMenuOption
|
|
138
|
+
| StringSelectMenuOptionBuilder
|
|
139
|
+
| ((builder: StringSelectMenuOptionBuilder) => StringSelectMenuOptionBuilder)
|
|
140
|
+
)[]
|
|
141
|
+
) {
|
|
142
|
+
const resolved = options.map((option) => resolveBuilder(option, StringSelectMenuOptionBuilder));
|
|
143
|
+
|
|
144
|
+
this.data.options ??= [];
|
|
145
|
+
this.data.options.splice(index, deleteCount, ...resolved);
|
|
146
|
+
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* {@inheritDoc ComponentBuilder.toJSON}
|
|
152
|
+
*/
|
|
153
|
+
public override toJSON(validationOverride?: boolean): APIStringSelectComponent {
|
|
154
|
+
const { options, ...rest } = this.data;
|
|
155
|
+
const data = {
|
|
156
|
+
...(structuredClone(rest) as APIStringSelectComponent),
|
|
157
|
+
// selectMenuStringPredicate covers the validation of options
|
|
158
|
+
options: options.map((option) => option.toJSON(false)),
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
validate(selectMenuStringPredicate, data, validationOverride);
|
|
162
|
+
|
|
163
|
+
return data as APIStringSelectComponent;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { JSONEncodable } from '@ovencord/util';
|
|
2
|
+
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
|
|
3
|
+
import { validate } from '../../util/validation.js';
|
|
4
|
+
import { selectMenuStringOptionPredicate } from '../Assertions.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A builder that creates API-compatible JSON data for string select menu options.
|
|
8
|
+
*/
|
|
9
|
+
export class StringSelectMenuOptionBuilder implements JSONEncodable<APISelectMenuOption> {
|
|
10
|
+
private readonly data: Partial<APISelectMenuOption>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new string select menu option.
|
|
14
|
+
*
|
|
15
|
+
* @param data - The API data to create this string select menu option with
|
|
16
|
+
* @example
|
|
17
|
+
* Creating a string select menu option from an API data object:
|
|
18
|
+
* ```ts
|
|
19
|
+
* const selectMenuOption = new SelectMenuOptionBuilder({
|
|
20
|
+
* label: 'catchy label',
|
|
21
|
+
* value: '1',
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
* @example
|
|
25
|
+
* Creating a string select menu option using setters and API data:
|
|
26
|
+
* ```ts
|
|
27
|
+
* const selectMenuOption = new SelectMenuOptionBuilder({
|
|
28
|
+
* default: true,
|
|
29
|
+
* value: '1',
|
|
30
|
+
* })
|
|
31
|
+
* .setLabel('woah');
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
public constructor(data: Partial<APISelectMenuOption> = {}) {
|
|
35
|
+
this.data = structuredClone(data);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sets the label for this option.
|
|
40
|
+
*
|
|
41
|
+
* @param label - The label to use
|
|
42
|
+
*/
|
|
43
|
+
public setLabel(label: string) {
|
|
44
|
+
this.data.label = label;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Sets the value for this option.
|
|
50
|
+
*
|
|
51
|
+
* @param value - The value to use
|
|
52
|
+
*/
|
|
53
|
+
public setValue(value: string) {
|
|
54
|
+
this.data.value = value;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Sets the description for this option.
|
|
60
|
+
*
|
|
61
|
+
* @param description - The description to use
|
|
62
|
+
*/
|
|
63
|
+
public setDescription(description: string) {
|
|
64
|
+
this.data.description = description;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Clears the description for this option.
|
|
70
|
+
*/
|
|
71
|
+
public clearDescription() {
|
|
72
|
+
this.data.description = undefined;
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Sets whether this option is selected by default.
|
|
78
|
+
*
|
|
79
|
+
* @param isDefault - Whether this option is selected by default
|
|
80
|
+
*/
|
|
81
|
+
public setDefault(isDefault = true) {
|
|
82
|
+
this.data.default = isDefault;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Sets the emoji to display for this option.
|
|
88
|
+
*
|
|
89
|
+
* @param emoji - The emoji to use
|
|
90
|
+
*/
|
|
91
|
+
public setEmoji(emoji: APIMessageComponentEmoji) {
|
|
92
|
+
this.data.emoji = emoji;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Clears the emoji for this option.
|
|
98
|
+
*/
|
|
99
|
+
public clearEmoji() {
|
|
100
|
+
this.data.emoji = undefined;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* {@inheritDoc ComponentBuilder.toJSON}
|
|
106
|
+
*/
|
|
107
|
+
public toJSON(validationOverride?: boolean): APISelectMenuOption {
|
|
108
|
+
const clone = structuredClone(this.data);
|
|
109
|
+
validate(selectMenuStringOptionPredicate, clone, validationOverride);
|
|
110
|
+
|
|
111
|
+
return clone as APISelectMenuOption;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type APIUserSelectComponent,
|
|
3
|
+
type Snowflake,
|
|
4
|
+
ComponentType,
|
|
5
|
+
SelectMenuDefaultValueType,
|
|
6
|
+
} from 'discord-api-types/v10';
|
|
7
|
+
import { type RestOrArray, normalizeArray } from '../../util/normalizeArray.js';
|
|
8
|
+
import { validate } from '../../util/validation.js';
|
|
9
|
+
import { selectMenuUserPredicate } from '../Assertions.js';
|
|
10
|
+
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A builder that creates API-compatible JSON data for user select menus.
|
|
14
|
+
*/
|
|
15
|
+
export class UserSelectMenuBuilder extends BaseSelectMenuBuilder<APIUserSelectComponent> {
|
|
16
|
+
protected override readonly data: Partial<APIUserSelectComponent>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new user select menu.
|
|
20
|
+
*
|
|
21
|
+
* @param data - The API data to create this user select menu with
|
|
22
|
+
* @example
|
|
23
|
+
* Creating a select menu from an API data object:
|
|
24
|
+
* ```ts
|
|
25
|
+
* const selectMenu = new UserSelectMenuBuilder({
|
|
26
|
+
* custom_id: 'a cool select menu',
|
|
27
|
+
* placeholder: 'select an option',
|
|
28
|
+
* max_values: 2,
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
* @example
|
|
32
|
+
* Creating a select menu using setters and API data:
|
|
33
|
+
* ```ts
|
|
34
|
+
* const selectMenu = new UserSelectMenuBuilder({
|
|
35
|
+
* custom_id: 'a cool select menu',
|
|
36
|
+
* })
|
|
37
|
+
* .setMinValues(1);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
public constructor(data: Partial<APIUserSelectComponent> = {}) {
|
|
41
|
+
super();
|
|
42
|
+
this.data = { ...structuredClone(data), type: ComponentType.UserSelect };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Adds default users to this auto populated select menu.
|
|
47
|
+
*
|
|
48
|
+
* @param users - The users to add
|
|
49
|
+
*/
|
|
50
|
+
public addDefaultUsers(...users: RestOrArray<Snowflake>) {
|
|
51
|
+
const normalizedValues = normalizeArray(users);
|
|
52
|
+
|
|
53
|
+
this.data.default_values ??= [];
|
|
54
|
+
this.data.default_values.push(
|
|
55
|
+
...normalizedValues.map((id) => ({
|
|
56
|
+
id,
|
|
57
|
+
type: SelectMenuDefaultValueType.User as const,
|
|
58
|
+
})),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sets default users for this auto populated select menu.
|
|
66
|
+
*
|
|
67
|
+
* @param users - The users to set
|
|
68
|
+
*/
|
|
69
|
+
public setDefaultUsers(...users: RestOrArray<Snowflake>) {
|
|
70
|
+
const normalizedValues = normalizeArray(users);
|
|
71
|
+
|
|
72
|
+
this.data.default_values = normalizedValues.map((id) => ({
|
|
73
|
+
id,
|
|
74
|
+
type: SelectMenuDefaultValueType.User as const,
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* {@inheritDoc ComponentBuilder.toJSON}
|
|
82
|
+
*/
|
|
83
|
+
public override toJSON(validationOverride?: boolean): APIUserSelectComponent {
|
|
84
|
+
const clone = structuredClone(this.data);
|
|
85
|
+
validate(selectMenuUserPredicate, clone, validationOverride);
|
|
86
|
+
|
|
87
|
+
return clone as APIUserSelectComponent;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ComponentType, TextInputStyle } from 'discord-api-types/v10';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { customIdPredicate, idPredicate } from '../../Assertions.js';
|
|
4
|
+
|
|
5
|
+
export const textInputPredicate = z.object({
|
|
6
|
+
id: idPredicate,
|
|
7
|
+
type: z.literal(ComponentType.TextInput),
|
|
8
|
+
custom_id: customIdPredicate,
|
|
9
|
+
style: z.nativeEnum(TextInputStyle),
|
|
10
|
+
min_length: z.number().min(0).max(4_000).optional(),
|
|
11
|
+
max_length: z.number().min(1).max(4_000).optional(),
|
|
12
|
+
placeholder: z.string().max(100).optional(),
|
|
13
|
+
value: z.string().min(0).max(4_000).optional(),
|
|
14
|
+
required: z.boolean().optional(),
|
|
15
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
|
|
2
|
+
import { validate } from '../../util/validation.js';
|
|
3
|
+
import { ComponentBuilder } from '../Component.js';
|
|
4
|
+
import { textInputPredicate } from './Assertions.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A builder that creates API-compatible JSON data for text inputs.
|
|
8
|
+
*/
|
|
9
|
+
export class TextInputBuilder extends ComponentBuilder<APITextInputComponent> {
|
|
10
|
+
/**
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
protected readonly data: Partial<APITextInputComponent>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new text input.
|
|
17
|
+
*
|
|
18
|
+
* @param data - The API data to create this text input with
|
|
19
|
+
* @example
|
|
20
|
+
* Creating a text input from an API data object:
|
|
21
|
+
* ```ts
|
|
22
|
+
* const textInput = new TextInputBuilder({
|
|
23
|
+
* custom_id: 'a cool text input',
|
|
24
|
+
* placeholder: 'Type something',
|
|
25
|
+
* style: TextInputStyle.Short,
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
* @example
|
|
29
|
+
* Creating a text input using setters and API data:
|
|
30
|
+
* ```ts
|
|
31
|
+
* const textInput = new TextInputBuilder({
|
|
32
|
+
* placeholder: 'Type something else',
|
|
33
|
+
* })
|
|
34
|
+
* .setCustomId('woah')
|
|
35
|
+
* .setStyle(TextInputStyle.Paragraph);
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
public constructor(data: Partial<APITextInputComponent> = {}) {
|
|
39
|
+
super();
|
|
40
|
+
this.data = { ...structuredClone(data), type: ComponentType.TextInput };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Sets the custom id for this text input.
|
|
45
|
+
*
|
|
46
|
+
* @param customId - The custom id to use
|
|
47
|
+
*/
|
|
48
|
+
public setCustomId(customId: string) {
|
|
49
|
+
this.data.custom_id = customId;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sets the style for this text input.
|
|
55
|
+
*
|
|
56
|
+
* @param style - The style to use
|
|
57
|
+
*/
|
|
58
|
+
public setStyle(style: TextInputStyle) {
|
|
59
|
+
this.data.style = style;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Sets the minimum length of text for this text input.
|
|
65
|
+
*
|
|
66
|
+
* @param minLength - The minimum length of text for this text input
|
|
67
|
+
*/
|
|
68
|
+
public setMinLength(minLength: number) {
|
|
69
|
+
this.data.min_length = minLength;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Clears the minimum length of text for this text input.
|
|
75
|
+
*/
|
|
76
|
+
public clearMinLength() {
|
|
77
|
+
this.data.min_length = undefined;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sets the maximum length of text for this text input.
|
|
83
|
+
*
|
|
84
|
+
* @param maxLength - The maximum length of text for this text input
|
|
85
|
+
*/
|
|
86
|
+
public setMaxLength(maxLength: number) {
|
|
87
|
+
this.data.max_length = maxLength;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Clears the maximum length of text for this text input.
|
|
93
|
+
*/
|
|
94
|
+
public clearMaxLength() {
|
|
95
|
+
this.data.max_length = undefined;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Sets the placeholder for this text input.
|
|
101
|
+
*
|
|
102
|
+
* @param placeholder - The placeholder to use
|
|
103
|
+
*/
|
|
104
|
+
public setPlaceholder(placeholder: string) {
|
|
105
|
+
this.data.placeholder = placeholder;
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Clears the placeholder for this text input.
|
|
111
|
+
*/
|
|
112
|
+
public clearPlaceholder() {
|
|
113
|
+
this.data.placeholder = undefined;
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Sets the value for this text input.
|
|
119
|
+
*
|
|
120
|
+
* @param value - The value to use
|
|
121
|
+
*/
|
|
122
|
+
public setValue(value: string) {
|
|
123
|
+
this.data.value = value;
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Clears the value for this text input.
|
|
129
|
+
*/
|
|
130
|
+
public clearValue() {
|
|
131
|
+
this.data.value = undefined;
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Sets whether this text input is required.
|
|
137
|
+
*
|
|
138
|
+
* @param required - Whether this text input is required
|
|
139
|
+
*/
|
|
140
|
+
public setRequired(required = true) {
|
|
141
|
+
this.data.required = required;
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* {@inheritDoc ComponentBuilder.toJSON}
|
|
147
|
+
*/
|
|
148
|
+
public toJSON(validationOverride?: boolean): APITextInputComponent {
|
|
149
|
+
const clone = structuredClone(this.data);
|
|
150
|
+
validate(textInputPredicate, clone, validationOverride);
|
|
151
|
+
|
|
152
|
+
return clone as APITextInputComponent;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ComponentType, SeparatorSpacingSize } from 'discord-api-types/v10';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { idPredicate } from '../../Assertions.js';
|
|
4
|
+
import { actionRowPredicate } from '../Assertions.js';
|
|
5
|
+
|
|
6
|
+
const unfurledMediaItemPredicate = z.object({
|
|
7
|
+
url: z.string().url().refine((url) => url.startsWith('http:') || url.startsWith('https:') || url.startsWith('attachment:'), { message: 'URL must use http, https, or attachment protocol' }),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const thumbnailPredicate = z.object({
|
|
11
|
+
type: z.literal(ComponentType.Thumbnail),
|
|
12
|
+
id: idPredicate,
|
|
13
|
+
media: unfurledMediaItemPredicate,
|
|
14
|
+
description: z.string().min(1).max(1_024).nullish(),
|
|
15
|
+
spoiler: z.boolean().optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const unfurledMediaItemAttachmentOnlyPredicate = z.object({
|
|
19
|
+
url: z.string().url().refine((url) => url.startsWith('attachment:'), { message: 'URL must use attachment protocol' }),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const filePredicate = z.object({
|
|
23
|
+
type: z.literal(ComponentType.File),
|
|
24
|
+
id: idPredicate,
|
|
25
|
+
file: unfurledMediaItemAttachmentOnlyPredicate,
|
|
26
|
+
spoiler: z.boolean().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const separatorPredicate = z.object({
|
|
30
|
+
type: z.literal(ComponentType.Separator),
|
|
31
|
+
id: idPredicate,
|
|
32
|
+
divider: z.boolean().optional(),
|
|
33
|
+
spacing: z.nativeEnum(SeparatorSpacingSize).optional(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const textDisplayPredicate = z.object({
|
|
37
|
+
type: z.literal(ComponentType.TextDisplay),
|
|
38
|
+
id: idPredicate,
|
|
39
|
+
content: z.string().min(1).max(4_000),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const mediaGalleryItemPredicate = z.object({
|
|
43
|
+
id: idPredicate,
|
|
44
|
+
media: unfurledMediaItemPredicate,
|
|
45
|
+
description: z.string().min(1).max(1_024).nullish(),
|
|
46
|
+
spoiler: z.boolean().optional(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const mediaGalleryPredicate = z.object({
|
|
50
|
+
type: z.literal(ComponentType.MediaGallery),
|
|
51
|
+
id: idPredicate,
|
|
52
|
+
items: z.array(mediaGalleryItemPredicate).min(1).max(10),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const sectionPredicate = z.object({
|
|
56
|
+
type: z.literal(ComponentType.Section),
|
|
57
|
+
id: idPredicate,
|
|
58
|
+
components: z.array(textDisplayPredicate).min(1).max(3),
|
|
59
|
+
accessory: z.union([
|
|
60
|
+
z.object({ type: z.literal(ComponentType.Button) }),
|
|
61
|
+
z.object({ type: z.literal(ComponentType.Thumbnail) }),
|
|
62
|
+
]),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const containerPredicate = z.object({
|
|
66
|
+
type: z.literal(ComponentType.Container),
|
|
67
|
+
id: idPredicate,
|
|
68
|
+
components: z
|
|
69
|
+
.array(
|
|
70
|
+
z.union([
|
|
71
|
+
actionRowPredicate,
|
|
72
|
+
filePredicate,
|
|
73
|
+
mediaGalleryPredicate,
|
|
74
|
+
sectionPredicate,
|
|
75
|
+
separatorPredicate,
|
|
76
|
+
textDisplayPredicate,
|
|
77
|
+
]),
|
|
78
|
+
)
|
|
79
|
+
.min(1),
|
|
80
|
+
spoiler: z.boolean().optional(),
|
|
81
|
+
accent_color: z.number().int().min(0).max(0xffffff).nullish(),
|
|
82
|
+
});
|