From 07c12101e534fdce836a94bc571b53f75979ea86 Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sun, 5 May 2024 12:03:14 +0200 Subject: [PATCH] fix: slashcommand builder type split (#10253) --- .../SlashCommands/SlashCommands.test.ts | 12 ++ .../src/components/textInput/TextInput.ts | 2 +- packages/builders/src/index.ts | 1 + .../slashCommands/SlashCommandBuilder.ts | 13 +- .../mixins/SharedSlashCommand.ts | 112 ++++++++++++++++++ .../slashCommands/mixins/SharedSubcommands.ts | 107 +---------------- 6 files changed, 135 insertions(+), 112 deletions(-) create mode 100644 packages/builders/src/interactions/slashCommands/mixins/SharedSlashCommand.ts diff --git a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts index 88a33ef94..85e887818 100644 --- a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts +++ b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts @@ -357,6 +357,10 @@ describe('Slash Commands', () => { getBuilder().addStringOption(getStringOption().setChoices({ name: 'owo', value: 'uwu' })), ).not.toThrowError(); }); + + test('GIVEN valid builder with NSFW, THEN does not throw error', () => { + expect(() => getBuilder().setName('foo').setDescription('foo').setNSFW(true)).not.toThrowError(); + }); }); describe('Builder with subcommand (group) options', () => { @@ -519,6 +523,14 @@ describe('Slash Commands', () => { expect(() => getBuilder().setDefaultMemberPermissions(1.1)).toThrowError(); }); + + test('GIVEN valid permission with options THEN does not throw error', () => { + expect(() => + getBuilder().addBooleanOption(getBooleanOption()).setDefaultMemberPermissions('1'), + ).not.toThrowError(); + + expect(() => getBuilder().addChannelOption(getChannelOption()).setDMPermission(false)).not.toThrowError(); + }); }); }); }); diff --git a/packages/builders/src/components/textInput/TextInput.ts b/packages/builders/src/components/textInput/TextInput.ts index 42a458372..dcfa0560c 100644 --- a/packages/builders/src/components/textInput/TextInput.ts +++ b/packages/builders/src/components/textInput/TextInput.ts @@ -140,7 +140,7 @@ export class TextInputBuilder } /** - * {@inheritDoc Equatable.equals} + * Whether this is equal to another structure. */ public equals(other: APITextInputComponent | JSONEncodable): boolean { if (isJSONEncodable(other)) { diff --git a/packages/builders/src/index.ts b/packages/builders/src/index.ts index 43a04d993..539086121 100644 --- a/packages/builders/src/index.ts +++ b/packages/builders/src/index.ts @@ -54,6 +54,7 @@ export * from './interactions/slashCommands/mixins/ApplicationCommandOptionWithC export * from './interactions/slashCommands/mixins/NameAndDescription.js'; export * from './interactions/slashCommands/mixins/SharedSlashCommandOptions.js'; export * from './interactions/slashCommands/mixins/SharedSubcommands.js'; +export * from './interactions/slashCommands/mixins/SharedSlashCommand.js'; export * as ContextMenuCommandAssertions from './interactions/contextMenuCommands/Assertions.js'; export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder.js'; diff --git a/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts b/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts index fe0b0959a..4c23a770d 100644 --- a/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts +++ b/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts @@ -1,13 +1,14 @@ import type { APIApplicationCommandOption, LocalizationMap, Permissions } from 'discord-api-types/v10'; import { mix } from 'ts-mixer'; import { SharedNameAndDescription } from './mixins/NameAndDescription.js'; +import { SharedSlashCommand } from './mixins/SharedSlashCommand.js'; import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions.js'; import { SharedSlashCommandSubcommands } from './mixins/SharedSubcommands.js'; /** * A builder that creates API-compatible JSON data for slash commands. */ -@mix(SharedSlashCommandOptions, SharedNameAndDescription, SharedSlashCommandSubcommands) +@mix(SharedSlashCommandOptions, SharedNameAndDescription, SharedSlashCommandSubcommands, SharedSlashCommand) export class SlashCommandBuilder { /** * The name of this command. @@ -37,7 +38,7 @@ export class SlashCommandBuilder { /** * Whether this command is enabled by default when the application is added to a guild. * - * @deprecated Use {@link SharedSlashCommandSubcommands.setDefaultMemberPermissions} or {@link SharedSlashCommandSubcommands.setDMPermission} instead. + * @deprecated Use {@link SharedSlashCommand.setDefaultMemberPermissions} or {@link SharedSlashCommand.setDMPermission} instead. */ public readonly default_permission: boolean | undefined = undefined; @@ -63,14 +64,16 @@ export class SlashCommandBuilder { export interface SlashCommandBuilder extends SharedNameAndDescription, SharedSlashCommandOptions, - SharedSlashCommandSubcommands {} + SharedSlashCommandSubcommands, + SharedSlashCommand {} /** * An interface specifically for slash command subcommands. */ export interface SlashCommandSubcommandsOnlyBuilder extends SharedNameAndDescription, - SharedSlashCommandSubcommands {} + SharedSlashCommandSubcommands, + SharedSlashCommand {} /** * An interface specifically for slash command options. @@ -78,7 +81,7 @@ export interface SlashCommandSubcommandsOnlyBuilder export interface SlashCommandOptionsOnlyBuilder extends SharedNameAndDescription, SharedSlashCommandOptions, - ToAPIApplicationCommandOptions {} + SharedSlashCommand {} /** * An interface that ensures the `toJSON()` call will return something diff --git a/packages/builders/src/interactions/slashCommands/mixins/SharedSlashCommand.ts b/packages/builders/src/interactions/slashCommands/mixins/SharedSlashCommand.ts new file mode 100644 index 000000000..4d321073c --- /dev/null +++ b/packages/builders/src/interactions/slashCommands/mixins/SharedSlashCommand.ts @@ -0,0 +1,112 @@ +import type { + LocalizationMap, + Permissions, + RESTPostAPIChatInputApplicationCommandsJSONBody, +} from 'discord-api-types/v10'; +import { + validateDMPermission, + validateDefaultMemberPermissions, + validateDefaultPermission, + validateLocalizationMap, + validateNSFW, + validateRequiredParameters, +} from '../Assertions.js'; +import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder.js'; + +/** + * This mixin holds symbols that can be shared in slashcommands independent of options or subcommands. + */ +export class SharedSlashCommand { + public readonly name: string = undefined!; + + public readonly name_localizations?: LocalizationMap; + + public readonly description: string = undefined!; + + public readonly description_localizations?: LocalizationMap; + + public readonly options: ToAPIApplicationCommandOptions[] = []; + + /** + * Sets whether the command is enabled by default when the application is added to a guild. + * + * @remarks + * If set to `false`, you will have to later `PUT` the permissions for this command. + * @param value - Whether or not to enable this command by default + * @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions} + * @deprecated Use {@link SharedSlashCommand.setDefaultMemberPermissions} or {@link SharedSlashCommand.setDMPermission} instead. + */ + public setDefaultPermission(value: boolean) { + // Assert the value matches the conditions + validateDefaultPermission(value); + + Reflect.set(this, 'default_permission', value); + + return this; + } + + /** + * Sets the default permissions a member should have in order to run the command. + * + * @remarks + * You can set this to `'0'` to disable the command by default. + * @param permissions - The permissions bit field to set + * @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions} + */ + public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) { + // Assert the value and parse it + const permissionValue = validateDefaultMemberPermissions(permissions); + + Reflect.set(this, 'default_member_permissions', permissionValue); + + return this; + } + + /** + * Sets if the command is available in direct messages with the application. + * + * @remarks + * By default, commands are visible. This method is only for global commands. + * @param enabled - Whether the command should be enabled in direct messages + * @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions} + */ + public setDMPermission(enabled: boolean | null | undefined) { + // Assert the value matches the conditions + validateDMPermission(enabled); + + Reflect.set(this, 'dm_permission', enabled); + + return this; + } + + /** + * Sets whether this command is NSFW. + * + * @param nsfw - Whether this command is NSFW + */ + public setNSFW(nsfw = true) { + // Assert the value matches the conditions + validateNSFW(nsfw); + Reflect.set(this, 'nsfw', nsfw); + return this; + } + + /** + * Serializes this builder to API-compatible JSON data. + * + * @remarks + * This method runs validations on the data before serializing it. + * As such, it may throw an error if the data is invalid. + */ + public toJSON(): RESTPostAPIChatInputApplicationCommandsJSONBody { + validateRequiredParameters(this.name, this.description, this.options); + + validateLocalizationMap(this.name_localizations); + validateLocalizationMap(this.description_localizations); + + return { + ...this, + options: this.options.map((option) => option.toJSON()), + }; + } +} diff --git a/packages/builders/src/interactions/slashCommands/mixins/SharedSubcommands.ts b/packages/builders/src/interactions/slashCommands/mixins/SharedSubcommands.ts index e5e7b6aa9..ce9274f3b 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/SharedSubcommands.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/SharedSubcommands.ts @@ -1,18 +1,4 @@ -import type { - LocalizationMap, - Permissions, - RESTPostAPIChatInputApplicationCommandsJSONBody, -} from 'discord-api-types/v10'; -import { - assertReturnOfBuilder, - validateDMPermission, - validateDefaultMemberPermissions, - validateDefaultPermission, - validateLocalizationMap, - validateMaxOptionsLength, - validateNSFW, - validateRequiredParameters, -} from '../Assertions.js'; +import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions.js'; import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder.js'; import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from '../SlashCommandSubcommands.js'; @@ -24,80 +10,8 @@ import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } fro export class SharedSlashCommandSubcommands< TypeAfterAddingSubcommands extends SharedSlashCommandSubcommands, > { - public readonly name: string = undefined!; - - public readonly name_localizations?: LocalizationMap; - - public readonly description: string = undefined!; - - public readonly description_localizations?: LocalizationMap; - public readonly options: ToAPIApplicationCommandOptions[] = []; - /** - * Sets whether the command is enabled by default when the application is added to a guild. - * - * @remarks - * If set to `false`, you will have to later `PUT` the permissions for this command. - * @param value - Whether or not to enable this command by default - * @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions} - * @deprecated Use {@link SharedSlashCommandSubcommands.setDefaultMemberPermissions} or {@link SharedSlashCommandSubcommands.setDMPermission} instead. - */ - public setDefaultPermission(value: boolean) { - // Assert the value matches the conditions - validateDefaultPermission(value); - - Reflect.set(this, 'default_permission', value); - - return this; - } - - /** - * Sets the default permissions a member should have in order to run the command. - * - * @remarks - * You can set this to `'0'` to disable the command by default. - * @param permissions - The permissions bit field to set - * @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions} - */ - public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) { - // Assert the value and parse it - const permissionValue = validateDefaultMemberPermissions(permissions); - - Reflect.set(this, 'default_member_permissions', permissionValue); - - return this; - } - - /** - * Sets if the command is available in direct messages with the application. - * - * @remarks - * By default, commands are visible. This method is only for global commands. - * @param enabled - Whether the command should be enabled in direct messages - * @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions} - */ - public setDMPermission(enabled: boolean | null | undefined) { - // Assert the value matches the conditions - validateDMPermission(enabled); - - Reflect.set(this, 'dm_permission', enabled); - - return this; - } - - /** - * Sets whether this command is NSFW. - * - * @param nsfw - Whether this command is NSFW - */ - public setNSFW(nsfw = true) { - // Assert the value matches the conditions - validateNSFW(nsfw); - Reflect.set(this, 'nsfw', nsfw); - return this; - } - /** * Adds a new subcommand group to this command. * @@ -149,23 +63,4 @@ export class SharedSlashCommandSubcommands< return this as unknown as TypeAfterAddingSubcommands; } - - /** - * Serializes this builder to API-compatible JSON data. - * - * @remarks - * This method runs validations on the data before serializing it. - * As such, it may throw an error if the data is invalid. - */ - public toJSON(): RESTPostAPIChatInputApplicationCommandsJSONBody { - validateRequiredParameters(this.name, this.description, this.options); - - validateLocalizationMap(this.name_localizations); - validateLocalizationMap(this.description_localizations); - - return { - ...this, - options: this.options.map((option) => option.toJSON()), - }; - } }