feat: Slash command localization for builders (#7683)

* feat: add slash command localizations

* chore: make requested changes

* chore: make requested changes

* fix: prevent unnecessary spread

* chore: make requested changes

* chore: don't allow maps
This commit is contained in:
Suneet Tipirneni
2022-04-17 04:58:20 -04:00
committed by GitHub
parent ab4c608b97
commit 40b9a1d67d
6 changed files with 172 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
import is from '@sindresorhus/is';
import type { APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
import { type APIApplicationCommandOptionChoice, Locale } from 'discord-api-types/v10';
import { s } from '@sapphire/shapeshift';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
@@ -15,12 +15,16 @@ export function validateName(name: unknown): asserts name is string {
}
const descriptionPredicate = s.string.lengthGe(1).lengthLe(100);
const localePredicate = s.nativeEnum(Locale);
export function validateDescription(description: unknown): asserts description is string {
descriptionPredicate.parse(description);
}
const maxArrayLengthPredicate = s.unknown.array.lengthLe(25);
export function validateLocale(locale: unknown) {
return localePredicate.parse(locale);
}
export function validateMaxOptionsLength(options: unknown): asserts options is ToAPIApplicationCommandOptions[] {
maxArrayLengthPredicate.parse(options);

View File

@@ -1,4 +1,8 @@
import type { APIApplicationCommandOption, RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10';
import type {
APIApplicationCommandOption,
LocalizationMap,
RESTPostAPIApplicationCommandsJSONBody,
} from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import {
assertReturnOfBuilder,
@@ -17,11 +21,21 @@ export class SlashCommandBuilder {
*/
public readonly name: string = undefined!;
/**
* The localized names for this command
*/
public readonly name_localizations?: LocalizationMap;
/**
* The description of this slash command
*/
public readonly description: string = undefined!;
/**
* The localized descriptions for this command
*/
public readonly description_localizations?: LocalizationMap;
/**
* The options of this slash command
*/
@@ -44,7 +58,9 @@ export class SlashCommandBuilder {
return {
name: this.name,
name_localizations: this.name_localizations,
description: this.description,
description_localizations: this.description_localizations,
options: this.options.map((option) => option.toJSON()),
default_permission: this.defaultPermission,
};

View File

@@ -1,8 +1,11 @@
import { validateDescription, validateName } from '../Assertions';
import type { LocaleString, LocalizationMap } from 'discord-api-types/v10';
import { validateDescription, validateLocale, validateName } from '../Assertions';
export class SharedNameAndDescription {
public readonly name!: string;
public readonly name_localizations?: LocalizationMap;
public readonly description!: string;
public readonly description_localizations?: LocalizationMap;
/**
* Sets the name
@@ -31,4 +34,85 @@ export class SharedNameAndDescription {
return this;
}
/**
* Sets a name localization
*
* @param locale The locale to set a description for
* @param localizedName The localized description for the given locale
*/
public setNameLocalization(locale: LocaleString, localizedName: string | null) {
if (!this.name_localizations) {
Reflect.set(this, 'name_localizations', {});
}
if (localizedName === null) {
this.name_localizations![locale] = null;
return this;
}
validateName(localizedName);
this.name_localizations![validateLocale(locale)] = localizedName;
return this;
}
/**
* Sets the name localizations
*
* @param localizedNames The dictionary of localized descriptions to set
*/
public setNameLocalizations(localizedNames: LocalizationMap | null) {
if (localizedNames === null) {
Reflect.set(this, 'name_localizations', null);
return this;
}
Reflect.set(this, 'name_localizations', {});
Object.entries(localizedNames).forEach((args) =>
this.setNameLocalization(...(args as [LocaleString, string | null])),
);
return this;
}
/**
* Sets a description localization
*
* @param locale The locale to set a description for
* @param localizedDescription The localized description for the given locale
*/
public setDescriptionLocalization(locale: LocaleString, localizedDescription: string | null) {
if (!this.description_localizations) {
Reflect.set(this, 'description_localizations', {});
}
if (localizedDescription === null) {
this.description_localizations![locale] = null;
return this;
}
validateDescription(localizedDescription);
this.description_localizations![validateLocale(locale)] = localizedDescription;
return this;
}
/**
* Sets the description localizations
*
* @param localizedDescriptions The dictionary of localized descriptions to set
*/
public setDescriptionLocalizations(localizedDescriptions: LocalizationMap | null) {
if (localizedDescriptions === null) {
Reflect.set(this, 'description_localizations', null);
return this;
}
Reflect.set(this, 'description_localizations', {});
Object.entries(localizedDescriptions).forEach((args) =>
this.setDescriptionLocalization(...(args as [LocaleString, string | null])),
);
return this;
}
}