Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: slashcommand builder type split #10253

Merged
merged 1 commit into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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();
});
});
});
});
2 changes: 1 addition & 1 deletion packages/builders/src/components/textInput/TextInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export class TextInputBuilder
}

/**
* {@inheritDoc Equatable.equals}
* Whether this is equal to another structure.
*/
public equals(other: APITextInputComponent | JSONEncodable<APITextInputComponent>): boolean {
if (isJSONEncodable(other)) {
Expand Down
1 change: 1 addition & 0 deletions packages/builders/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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;

Expand All @@ -63,22 +64,24 @@ export class SlashCommandBuilder {
export interface SlashCommandBuilder
extends SharedNameAndDescription,
SharedSlashCommandOptions<SlashCommandOptionsOnlyBuilder>,
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder> {}
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder>,
SharedSlashCommand {}

/**
* An interface specifically for slash command subcommands.
*/
export interface SlashCommandSubcommandsOnlyBuilder
extends SharedNameAndDescription,
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder> {}
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder>,
SharedSlashCommand {}

/**
* An interface specifically for slash command options.
*/
export interface SlashCommandOptionsOnlyBuilder
extends SharedNameAndDescription,
SharedSlashCommandOptions<SlashCommandOptionsOnlyBuilder>,
ToAPIApplicationCommandOptions {}
SharedSlashCommand {}

/**
* An interface that ensures the `toJSON()` call will return something
Expand Down
Original file line number Diff line number Diff line change
@@ -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()),
};
}
}
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -24,80 +10,8 @@ import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } fro
export class SharedSlashCommandSubcommands<
TypeAfterAddingSubcommands extends SharedSlashCommandSubcommands<TypeAfterAddingSubcommands>,
> {
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.
*
Expand Down Expand Up @@ -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()),
};
}
}
Loading