@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,87 @@
1
+ import type { JSONEncodable } from '@ovencord/util';
2
+ import type {
3
+ ApplicationIntegrationType,
4
+ InteractionContextType,
5
+ Permissions,
6
+ RESTPostAPIApplicationCommandsJSONBody,
7
+ } from 'discord-api-types/v10';
8
+ import type { RestOrArray } from '../../util/normalizeArray.js';
9
+ import { normalizeArray } from '../../util/normalizeArray.js';
10
+
11
+ export interface CommandData extends Partial<
12
+ Pick<RESTPostAPIApplicationCommandsJSONBody, 'contexts' | 'default_member_permissions' | 'integration_types' | 'nsfw'>
13
+ > {}
14
+
15
+ /**
16
+ * The base class for all command builders.
17
+ */
18
+ export abstract class CommandBuilder<
19
+ Command extends RESTPostAPIApplicationCommandsJSONBody,
20
+ > implements JSONEncodable<Command> {
21
+ /**
22
+ * The API data associated with this command.
23
+ *
24
+ * @internal
25
+ */
26
+ declare protected readonly data: CommandData;
27
+
28
+ /**
29
+ * Sets the contexts of this command.
30
+ *
31
+ * @param contexts - The contexts
32
+ */
33
+ public setContexts(...contexts: RestOrArray<InteractionContextType>) {
34
+ this.data.contexts = normalizeArray(contexts);
35
+ return this;
36
+ }
37
+
38
+ /**
39
+ * Sets the integration types of this command.
40
+ *
41
+ * @param integrationTypes - The integration types
42
+ */
43
+ public setIntegrationTypes(...integrationTypes: RestOrArray<ApplicationIntegrationType>) {
44
+ this.data.integration_types = normalizeArray(integrationTypes);
45
+ return this;
46
+ }
47
+
48
+ /**
49
+ * Sets the default permissions a member should have in order to run the command.
50
+ *
51
+ * @remarks
52
+ * You can set this to `'0'` to disable the command by default.
53
+ * @param permissions - The permissions bit field to set
54
+ * @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions}
55
+ */
56
+ public setDefaultMemberPermissions(permissions: Permissions | bigint | number) {
57
+ this.data.default_member_permissions = typeof permissions === 'string' ? permissions : permissions.toString();
58
+ return this;
59
+ }
60
+
61
+ /**
62
+ * Clears the default permissions a member should have in order to run the command.
63
+ */
64
+ public clearDefaultMemberPermissions() {
65
+ this.data.default_member_permissions = undefined;
66
+ return this;
67
+ }
68
+
69
+ /**
70
+ * Sets whether this command is NSFW.
71
+ *
72
+ * @param nsfw - Whether this command is NSFW
73
+ */
74
+ public setNSFW(nsfw = true) {
75
+ this.data.nsfw = nsfw;
76
+ return this;
77
+ }
78
+
79
+ /**
80
+ * Serializes this builder to API-compatible JSON data.
81
+ *
82
+ * Note that by disabling validation, there is no guarantee that the resulting object will be valid.
83
+ *
84
+ * @param validationOverride - Force validation to run/not run regardless of your global preference
85
+ */
86
+ public abstract toJSON(validationOverride?: boolean): Command;
87
+ }
@@ -0,0 +1,68 @@
1
+ import type { Locale, RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10';
2
+
3
+ export interface SharedNameData extends Partial<
4
+ Pick<RESTPostAPIApplicationCommandsJSONBody, 'name_localizations' | 'name'>
5
+ > {}
6
+
7
+ /**
8
+ * This mixin holds name and description symbols for chat input commands.
9
+ */
10
+ export class SharedName {
11
+ /**
12
+ * @internal
13
+ */
14
+ protected readonly data: SharedNameData = {};
15
+
16
+ /**
17
+ * Sets the name of this command.
18
+ *
19
+ * @param name - The name to use
20
+ */
21
+ public setName(name: string): this {
22
+ this.data.name = name;
23
+ return this;
24
+ }
25
+
26
+ /**
27
+ * Sets a name localization for this command.
28
+ *
29
+ * @param locale - The locale to set
30
+ * @param localizedName - The localized name for the given `locale`
31
+ */
32
+ public setNameLocalization(locale: Locale, localizedName: string) {
33
+ this.data.name_localizations ??= {};
34
+ this.data.name_localizations[locale] = localizedName;
35
+
36
+ return this;
37
+ }
38
+
39
+ /**
40
+ * Clears a name localization for this command.
41
+ *
42
+ * @param locale - The locale to clear
43
+ */
44
+ public clearNameLocalization(locale: Locale) {
45
+ this.data.name_localizations ??= {};
46
+ this.data.name_localizations[locale] = undefined;
47
+
48
+ return this;
49
+ }
50
+
51
+ /**
52
+ * Sets the name localizations for this command.
53
+ *
54
+ * @param localizedNames - The object of localized names to set
55
+ */
56
+ public setNameLocalizations(localizedNames: Partial<Record<Locale, string>>) {
57
+ this.data.name_localizations = structuredClone(localizedNames);
58
+ return this;
59
+ }
60
+
61
+ /**
62
+ * Clears all name localizations for this command.
63
+ */
64
+ public clearNameLocalizations() {
65
+ this.data.name_localizations = undefined;
66
+ return this;
67
+ }
68
+ }
@@ -0,0 +1,69 @@
1
+ import type { APIApplicationCommand, Locale } from 'discord-api-types/v10';
2
+ import type { SharedNameData } from './SharedName.js';
3
+ import { SharedName } from './SharedName.js';
4
+
5
+ export interface SharedNameAndDescriptionData
6
+ extends SharedNameData, Partial<Pick<APIApplicationCommand, 'description_localizations' | 'description'>> {}
7
+
8
+ /**
9
+ * This mixin holds name and description symbols for chat input commands.
10
+ */
11
+ export class SharedNameAndDescription extends SharedName {
12
+ /**
13
+ * @internal
14
+ */
15
+ protected override readonly data: SharedNameAndDescriptionData = {};
16
+
17
+ /**
18
+ * Sets the description of this command.
19
+ *
20
+ * @param description - The description to use
21
+ */
22
+ public setDescription(description: string) {
23
+ this.data.description = description;
24
+ return this;
25
+ }
26
+
27
+ /**
28
+ * Sets a description localization for this command.
29
+ *
30
+ * @param locale - The locale to set
31
+ * @param localizedDescription - The localized description for the given `locale`
32
+ */
33
+ public setDescriptionLocalization(locale: Locale, localizedDescription: string) {
34
+ this.data.description_localizations ??= {};
35
+ this.data.description_localizations[locale] = localizedDescription;
36
+
37
+ return this;
38
+ }
39
+
40
+ /**
41
+ * Clears a description localization for this command.
42
+ *
43
+ * @param locale - The locale to clear
44
+ */
45
+ public clearDescriptionLocalization(locale: Locale) {
46
+ this.data.description_localizations ??= {};
47
+ this.data.description_localizations[locale] = undefined;
48
+
49
+ return this;
50
+ }
51
+
52
+ /**
53
+ * Sets the description localizations for this command.
54
+ *
55
+ * @param localizedDescriptions - The object of localized descriptions to set
56
+ */
57
+ public setDescriptionLocalizations(localizedDescriptions: Partial<Record<Locale, string>>) {
58
+ this.data.description_localizations = structuredClone(localizedDescriptions);
59
+ return this;
60
+ }
61
+
62
+ /**
63
+ * Clears all description localizations for this command.
64
+ */
65
+ public clearDescriptionLocalizations() {
66
+ this.data.description_localizations = undefined;
67
+ return this;
68
+ }
69
+ }
@@ -0,0 +1,180 @@
1
+ import {
2
+ ApplicationIntegrationType,
3
+ InteractionContextType,
4
+ ApplicationCommandOptionType,
5
+ ApplicationCommandType,
6
+ } from 'discord-api-types/v10';
7
+ import { z } from 'zod';
8
+ import { localeMapPredicate, memberPermissionsPredicate } from '../../../Assertions.js';
9
+ import { ApplicationCommandOptionAllowedChannelTypes } from './mixins/ApplicationCommandOptionChannelTypesMixin.js';
10
+
11
+ const namePredicate = z
12
+ .string()
13
+ .min(1)
14
+ .max(32)
15
+ .regex(/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u);
16
+
17
+ const descriptionPredicate = z.string().min(1).max(100);
18
+
19
+ const sharedNameAndDescriptionPredicate = z.object({
20
+ name: namePredicate,
21
+ name_localizations: localeMapPredicate.optional(),
22
+ description: descriptionPredicate,
23
+ description_localizations: localeMapPredicate.optional(),
24
+ });
25
+
26
+ const numericMixinNumberOptionPredicate = z.object({
27
+ max_value: z.number().optional(),
28
+ min_value: z.number().optional(),
29
+ });
30
+
31
+ const numericMixinIntegerOptionPredicate = z.object({
32
+ max_value: z.number().int().optional(),
33
+ min_value: z.number().int().optional(),
34
+ });
35
+
36
+ const channelMixinOptionPredicate = z.object({
37
+ channel_types: z.union(ApplicationCommandOptionAllowedChannelTypes.map(type => z.literal(type)) as any).array().optional(),
38
+ });
39
+
40
+ const autocompleteMixinOptionPredicate = z.object({
41
+ autocomplete: z.literal(true),
42
+ choices: z.union([z.never(), z.never().array(), z.undefined()]).optional(),
43
+ });
44
+
45
+ const choiceValueStringPredicate = z.string().min(1).max(100);
46
+ const choiceValueNumberPredicate = z.number();
47
+ const choiceBasePredicate = z.object({
48
+ name: choiceValueStringPredicate,
49
+ name_localizations: localeMapPredicate.optional(),
50
+ });
51
+ const choiceStringPredicate = z.object({
52
+ ...choiceBasePredicate.shape,
53
+ value: choiceValueStringPredicate,
54
+ });
55
+ const choiceNumberPredicate = z.object({
56
+ ...choiceBasePredicate.shape,
57
+ value: choiceValueNumberPredicate,
58
+ });
59
+
60
+ const choiceBaseMixinPredicate = z.object({
61
+ autocomplete: z.literal(false).optional(),
62
+ });
63
+ const choiceStringMixinPredicate = z.object({
64
+ ...choiceBaseMixinPredicate.shape,
65
+ choices: choiceStringPredicate.array().max(25).optional(),
66
+ });
67
+ const choiceNumberMixinPredicate = z.object({
68
+ ...choiceBaseMixinPredicate.shape,
69
+ choices: choiceNumberPredicate.array().max(25).optional(),
70
+ });
71
+
72
+ export const baseBasicOptionPredicate = z.object({
73
+ ...sharedNameAndDescriptionPredicate.shape,
74
+ required: z.boolean().optional(),
75
+ });
76
+
77
+ export const attachmentOptionPredicate = z.object({
78
+ ...baseBasicOptionPredicate.shape,
79
+ type: z.literal(ApplicationCommandOptionType.Attachment),
80
+ });
81
+
82
+ export const booleanOptionPredicate = z.object({
83
+ ...baseBasicOptionPredicate.shape,
84
+ type: z.literal(ApplicationCommandOptionType.Boolean),
85
+ });
86
+
87
+ export const mentionableOptionPredicate = z.object({
88
+ ...baseBasicOptionPredicate.shape,
89
+ type: z.literal(ApplicationCommandOptionType.Mentionable),
90
+ });
91
+
92
+ export const roleOptionPredicate = z.object({
93
+ ...baseBasicOptionPredicate.shape,
94
+ type: z.literal(ApplicationCommandOptionType.Role),
95
+ });
96
+
97
+ export const userOptionPredicate = z.object({
98
+ ...baseBasicOptionPredicate.shape,
99
+ type: z.literal(ApplicationCommandOptionType.User),
100
+ });
101
+
102
+ const autocompleteOrStringChoicesMixinOptionPredicate = z.discriminatedUnion('autocomplete', [
103
+ autocompleteMixinOptionPredicate,
104
+ choiceStringMixinPredicate,
105
+ ]);
106
+
107
+ const autocompleteOrNumberChoicesMixinOptionPredicate = z.discriminatedUnion('autocomplete', [
108
+ autocompleteMixinOptionPredicate,
109
+ choiceNumberMixinPredicate,
110
+ ]);
111
+
112
+ export const channelOptionPredicate = z.object({
113
+ ...baseBasicOptionPredicate.shape,
114
+ ...channelMixinOptionPredicate.shape,
115
+ type: z.literal(ApplicationCommandOptionType.Channel),
116
+ });
117
+
118
+ export const integerOptionPredicate = z
119
+ .object({
120
+ ...baseBasicOptionPredicate.shape,
121
+ ...numericMixinIntegerOptionPredicate.shape,
122
+ type: z.literal(ApplicationCommandOptionType.Integer),
123
+ })
124
+ .and(autocompleteOrNumberChoicesMixinOptionPredicate);
125
+
126
+ export const numberOptionPredicate = z
127
+ .object({
128
+ ...baseBasicOptionPredicate.shape,
129
+ ...numericMixinNumberOptionPredicate.shape,
130
+ type: z.literal(ApplicationCommandOptionType.Number),
131
+ })
132
+ .and(autocompleteOrNumberChoicesMixinOptionPredicate);
133
+
134
+ export const stringOptionPredicate = z
135
+ .object({
136
+ ...baseBasicOptionPredicate.shape,
137
+ max_length: z.number().min(1).max(6_000).optional(),
138
+ min_length: z.number().min(0).max(6_000).optional(),
139
+ type: z.literal(ApplicationCommandOptionType.String),
140
+ })
141
+ .and(autocompleteOrStringChoicesMixinOptionPredicate);
142
+
143
+ const basicOptionPredicates = [
144
+ attachmentOptionPredicate,
145
+ booleanOptionPredicate,
146
+ channelOptionPredicate,
147
+ integerOptionPredicate,
148
+ mentionableOptionPredicate,
149
+ numberOptionPredicate,
150
+ roleOptionPredicate,
151
+ stringOptionPredicate,
152
+ userOptionPredicate,
153
+ ];
154
+
155
+ export const chatInputCommandSubcommandPredicate = z.object({
156
+ ...sharedNameAndDescriptionPredicate.shape,
157
+ type: z.literal(ApplicationCommandOptionType.Subcommand),
158
+ options: z.array(z.union(basicOptionPredicates as any)).max(25).optional(),
159
+ });
160
+
161
+ export const chatInputCommandSubcommandGroupPredicate = z.object({
162
+ ...sharedNameAndDescriptionPredicate.shape,
163
+ type: z.literal(ApplicationCommandOptionType.SubcommandGroup),
164
+ options: z.array(chatInputCommandSubcommandPredicate).min(1).max(25),
165
+ });
166
+
167
+ export const chatInputCommandPredicate = z.object({
168
+ ...sharedNameAndDescriptionPredicate.shape,
169
+ contexts: z.array(z.nativeEnum(InteractionContextType)).optional(),
170
+ default_member_permissions: memberPermissionsPredicate.optional(),
171
+ integration_types: z.array(z.nativeEnum(ApplicationIntegrationType)).optional(),
172
+ nsfw: z.boolean().optional(),
173
+ options: z
174
+ .union([
175
+ z.array(z.union(basicOptionPredicates as any)).max(25),
176
+ z.array(z.union([chatInputCommandSubcommandPredicate, chatInputCommandSubcommandGroupPredicate])).max(25),
177
+ ])
178
+ .optional(),
179
+ type: z.literal(ApplicationCommandType.ChatInput).optional(),
180
+ });
@@ -0,0 +1,40 @@
1
+ import { ApplicationCommandType, type RESTPostAPIChatInputApplicationCommandsJSONBody } from 'discord-api-types/v10';
2
+ import { Mixin } from 'ts-mixer';
3
+ import { validate } from '../../../util/validation.js';
4
+ import { CommandBuilder } from '../Command.js';
5
+ import { SharedNameAndDescription } from '../SharedNameAndDescription.js';
6
+ import { chatInputCommandPredicate } from './Assertions.js';
7
+ import { SharedChatInputCommandOptions } from './mixins/SharedChatInputCommandOptions.js';
8
+ import { SharedChatInputCommandSubcommands } from './mixins/SharedSubcommands.js';
9
+
10
+ /**
11
+ * A builder that creates API-compatible JSON data for chat input commands.
12
+ *
13
+ * @mixes {@link CommandBuilder}\<{@link discord-api-types/v10#(RESTPostAPIChatInputApplicationCommandsJSONBody:interface)}\>
14
+ * @mixes {@link SharedChatInputCommandOptions}
15
+ * @mixes {@link SharedNameAndDescription}
16
+ * @mixes {@link SharedChatInputCommandSubcommands}
17
+ */
18
+ export class ChatInputCommandBuilder extends Mixin(
19
+ CommandBuilder<RESTPostAPIChatInputApplicationCommandsJSONBody>,
20
+ SharedChatInputCommandOptions,
21
+ SharedNameAndDescription,
22
+ SharedChatInputCommandSubcommands,
23
+ ) {
24
+ /**
25
+ * {@inheritDoc CommandBuilder.toJSON}
26
+ */
27
+ public toJSON(validationOverride?: boolean): RESTPostAPIChatInputApplicationCommandsJSONBody {
28
+ const { options, ...rest } = this.data;
29
+
30
+ const data: RESTPostAPIChatInputApplicationCommandsJSONBody = {
31
+ ...structuredClone(rest as Omit<RESTPostAPIChatInputApplicationCommandsJSONBody, 'options'>),
32
+ type: ApplicationCommandType.ChatInput,
33
+ options: options?.map((option) => option.toJSON(false)),
34
+ };
35
+
36
+ validate(chatInputCommandPredicate, data, validationOverride);
37
+
38
+ return data;
39
+ }
40
+ }
@@ -0,0 +1,117 @@
1
+ import type { JSONEncodable } from '@ovencord/util';
2
+ import type {
3
+ APIApplicationCommandSubcommandOption,
4
+ APIApplicationCommandSubcommandGroupOption,
5
+ } from 'discord-api-types/v10';
6
+ import { ApplicationCommandOptionType } from 'discord-api-types/v10';
7
+ import { Mixin } from 'ts-mixer';
8
+ import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray.js';
9
+ import { resolveBuilder } from '../../../util/resolveBuilder.js';
10
+ import { validate } from '../../../util/validation.js';
11
+ import type { SharedNameAndDescriptionData } from '../SharedNameAndDescription.js';
12
+ import { SharedNameAndDescription } from '../SharedNameAndDescription.js';
13
+ import { chatInputCommandSubcommandGroupPredicate, chatInputCommandSubcommandPredicate } from './Assertions.js';
14
+ import { SharedChatInputCommandOptions } from './mixins/SharedChatInputCommandOptions.js';
15
+
16
+ export interface ChatInputCommandSubcommandGroupData {
17
+ options?: ChatInputCommandSubcommandBuilder[];
18
+ }
19
+
20
+ /**
21
+ * Represents a folder for subcommands.
22
+ *
23
+ * @see {@link https://discord.com/developers/docs/interactions/application-commands#subcommands-and-subcommand-groups}
24
+ */
25
+ export class ChatInputCommandSubcommandGroupBuilder
26
+ extends SharedNameAndDescription
27
+ implements JSONEncodable<APIApplicationCommandSubcommandGroupOption>
28
+ {
29
+ /**
30
+ * The API data associated with this subcommand group.
31
+ *
32
+ * @internal
33
+ */
34
+ declare protected readonly data: ChatInputCommandSubcommandGroupData & SharedNameAndDescriptionData;
35
+
36
+ /**
37
+ * The options within this subcommand group.
38
+ */
39
+ public get options(): readonly ChatInputCommandSubcommandBuilder[] {
40
+ return (this.data.options ??= []);
41
+ }
42
+
43
+ /**
44
+ * Adds a new subcommand to this group.
45
+ *
46
+ * @param input - A function that returns a subcommand builder or an already built builder
47
+ */
48
+ public addSubcommands(
49
+ ...input: RestOrArray<
50
+ | ChatInputCommandSubcommandBuilder
51
+ | ((subcommandGroup: ChatInputCommandSubcommandBuilder) => ChatInputCommandSubcommandBuilder)
52
+ >
53
+ ) {
54
+ const normalized = normalizeArray(input);
55
+
56
+ const result = normalized.map((builder) => resolveBuilder(builder, ChatInputCommandSubcommandBuilder));
57
+
58
+ this.data.options ??= [];
59
+ this.data.options.push(...result);
60
+
61
+ return this;
62
+ }
63
+
64
+ /**
65
+ * Serializes this builder to API-compatible JSON data.
66
+ *
67
+ * Note that by disabling validation, there is no guarantee that the resulting object will be valid.
68
+ *
69
+ * @param validationOverride - Force validation to run/not run regardless of your global preference
70
+ */
71
+ public toJSON(validationOverride?: boolean): APIApplicationCommandSubcommandGroupOption {
72
+ const { options, ...rest } = this.data;
73
+
74
+ const data = {
75
+ ...(structuredClone(rest) as Omit<APIApplicationCommandSubcommandGroupOption, 'type'>),
76
+ type: ApplicationCommandOptionType.SubcommandGroup as const,
77
+ options: options?.map((option) => option.toJSON(validationOverride)) ?? [],
78
+ };
79
+
80
+ validate(chatInputCommandSubcommandGroupPredicate, data, validationOverride);
81
+
82
+ return data;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * A builder that creates API-compatible JSON data for chat input command subcommands.
88
+ *
89
+ * @mixes {@link SharedNameAndDescription}
90
+ * @mixes {@link SharedChatInputCommandOptions}
91
+ * @see {@link https://discord.com/developers/docs/interactions/application-commands#subcommands-and-subcommand-groups}
92
+ */
93
+ export class ChatInputCommandSubcommandBuilder
94
+ extends Mixin(SharedNameAndDescription, SharedChatInputCommandOptions)
95
+ implements JSONEncodable<APIApplicationCommandSubcommandOption>
96
+ {
97
+ /**
98
+ * Serializes this builder to API-compatible JSON data.
99
+ *
100
+ * Note that by disabling validation, there is no guarantee that the resulting object will be valid.
101
+ *
102
+ * @param validationOverride - Force validation to run/not run regardless of your global preference
103
+ */
104
+ public toJSON(validationOverride?: boolean): APIApplicationCommandSubcommandOption {
105
+ const { options, ...rest } = this.data;
106
+
107
+ const data = {
108
+ ...(structuredClone(rest) as Omit<APIApplicationCommandSubcommandOption, 'type'>),
109
+ type: ApplicationCommandOptionType.Subcommand as const,
110
+ options: options?.map((option) => option.toJSON(validationOverride)) ?? [],
111
+ };
112
+
113
+ validate(chatInputCommandSubcommandPredicate, data, validationOverride);
114
+
115
+ return data;
116
+ }
117
+ }
@@ -0,0 +1,52 @@
1
+ import type { APIApplicationCommandIntegerOption } from 'discord-api-types/v10';
2
+
3
+ export interface ApplicationCommandNumericOptionMinMaxValueData extends Pick<
4
+ APIApplicationCommandIntegerOption,
5
+ 'max_value' | 'min_value'
6
+ > {}
7
+
8
+ /**
9
+ * This mixin holds minimum and maximum symbols used for options.
10
+ */
11
+ export abstract class ApplicationCommandNumericOptionMinMaxValueMixin {
12
+ /**
13
+ * @internal
14
+ */
15
+ declare protected readonly data: ApplicationCommandNumericOptionMinMaxValueData;
16
+
17
+ /**
18
+ * Sets the maximum number value of this option.
19
+ *
20
+ * @param max - The maximum value this option can be
21
+ */
22
+ public setMaxValue(max: number): this {
23
+ this.data.max_value = max;
24
+ return this;
25
+ }
26
+
27
+ /**
28
+ * Removes the maximum number value of this option.
29
+ */
30
+ public clearMaxValue(): this {
31
+ this.data.max_value = undefined;
32
+ return this;
33
+ }
34
+
35
+ /**
36
+ * Sets the minimum number value of this option.
37
+ *
38
+ * @param min - The minimum value this option can be
39
+ */
40
+ public setMinValue(min: number): this {
41
+ this.data.min_value = min;
42
+ return this;
43
+ }
44
+
45
+ /**
46
+ * Removes the minimum number value of this option.
47
+ */
48
+ public clearMinValue(): this {
49
+ this.data.min_value = undefined;
50
+ return this;
51
+ }
52
+ }
@@ -0,0 +1,57 @@
1
+ import { ChannelType, type APIApplicationCommandChannelOption } from 'discord-api-types/v10';
2
+ import { normalizeArray, type RestOrArray } from '../../../../util/normalizeArray';
3
+
4
+ export const ApplicationCommandOptionAllowedChannelTypes = [
5
+ ChannelType.GuildText,
6
+ ChannelType.GuildVoice,
7
+ ChannelType.GuildCategory,
8
+ ChannelType.GuildAnnouncement,
9
+ ChannelType.AnnouncementThread,
10
+ ChannelType.PublicThread,
11
+ ChannelType.PrivateThread,
12
+ ChannelType.GuildStageVoice,
13
+ ChannelType.GuildForum,
14
+ ChannelType.GuildMedia,
15
+ ] as const;
16
+
17
+ /**
18
+ * Allowed channel types used for a channel option.
19
+ */
20
+ export type ApplicationCommandOptionAllowedChannelType = (typeof ApplicationCommandOptionAllowedChannelTypes)[number];
21
+
22
+ export interface ApplicationCommandOptionChannelTypesData extends Pick<
23
+ APIApplicationCommandChannelOption,
24
+ 'channel_types'
25
+ > {}
26
+
27
+ /**
28
+ * This mixin holds channel type symbols used for options.
29
+ */
30
+ export class ApplicationCommandOptionChannelTypesMixin {
31
+ /**
32
+ * @internal
33
+ */
34
+ declare protected readonly data: ApplicationCommandOptionChannelTypesData;
35
+
36
+ /**
37
+ * Adds channel types to this option.
38
+ *
39
+ * @param channelTypes - The channel types
40
+ */
41
+ public addChannelTypes(...channelTypes: RestOrArray<ApplicationCommandOptionAllowedChannelType>) {
42
+ this.data.channel_types ??= [];
43
+ this.data.channel_types.push(...normalizeArray(channelTypes));
44
+
45
+ return this;
46
+ }
47
+
48
+ /**
49
+ * Sets the channel types for this option.
50
+ *
51
+ * @param channelTypes - The channel types
52
+ */
53
+ public setChannelTypes(...channelTypes: RestOrArray<ApplicationCommandOptionAllowedChannelType>) {
54
+ this.data.channel_types = normalizeArray(channelTypes);
55
+ return this;
56
+ }
57
+ }