feat: update bot chunk 1

This commit is contained in:
tresabhi committed 2025-01-15 20:14:05 -06:00
1 parent bb557a7dd7
commit d2b24fc953
42 files changed
+370 -389

No files matched your search

@@ -16,39 +16,67 @@ export function addPeriodSubCommands(
option: SlashCommandSubcommandBuilder,
) => SlashCommandSubcommandBuilder = (option) => option,
) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
const subcommandNameLocalizations = {
today: localizationObject('bot.common.subcommands.today', undefined, true),
30: localizationObject('bot.common.subcommands.30', undefined, true),
60: localizationObject('bot.common.subcommands.60', undefined, true),
90: localizationObject('bot.common.subcommands.90', undefined, true),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.$,
undefined,
true,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].$,
undefined,
true,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].$,
undefined,
true,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].$,
undefined,
true,
),
career: localizationObject(
'bot.common.subcommands.career',
(strings) => strings.bot.common.subcommands.career.$,
undefined,
true,
),
custom: localizationObject(
'bot.common.subcommands.custom',
(strings) => strings.bot.common.subcommands.custom.$,
undefined,
true,
),
};
const subcommandDescriptionLocalizations = {
today: localizationObject('bot.common.subcommands.today.description'),
30: localizationObject('bot.common.subcommands.30.description'),
60: localizationObject('bot.common.subcommands.60.description'),
90: localizationObject('bot.common.subcommands.90.description'),
career: localizationObject('bot.common.subcommands.career.description'),
custom: localizationObject('bot.common.subcommands.custom.description'),
today: localizationObject(
(strings) => strings.bot.common.subcommands.today.description,
),
30: localizationObject(
(strings) => strings.bot.common.subcommands[30].description,
),
60: localizationObject(
(strings) => strings.bot.common.subcommands[60].description,
),
90: localizationObject(
(strings) => strings.bot.common.subcommands[90].description,
),
career: localizationObject(
(strings) => strings.bot.common.subcommands.career.description,
),
custom: localizationObject(
(strings) => strings.bot.common.subcommands.custom.description,
),
};
option
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.today'))
.setName(strings.bot.common.subcommands.today.$)
.setNameLocalizations(subcommandNameLocalizations.today)
.setDescription(translate('bot.common.subcommands.today.description'))
.setDescription(strings.bot.common.subcommands.today.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.today,
),
@@ -57,38 +85,36 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.30'))
.setName(strings.bot.common.subcommands[30].$)
.setNameLocalizations(subcommandNameLocalizations[30])
.setDescription(translate('bot.common.subcommands.30.description'))
.setDescription(strings.bot.common.subcommands[30].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[30]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.60'))
.setName(strings.bot.common.subcommands[60].$)
.setNameLocalizations(subcommandNameLocalizations[60])
.setDescription(translate('bot.common.subcommands.60.description'))
.setDescription(strings.bot.common.subcommands[60].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[60]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.90'))
.setName(strings.bot.common.subcommands[90].$)
.setNameLocalizations(subcommandNameLocalizations[90])
.setDescription(translate('bot.common.subcommands.90.description'))
.setDescription(strings.bot.common.subcommands[90].description)
.setDescriptionLocalizations(subcommandDescriptionLocalizations[90]),
),
)
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.career'))
.setName(strings.bot.common.subcommands.career.$)
.setNameLocalizations(subcommandNameLocalizations.career)
.setDescription(
translate('bot.common.subcommands.career.description'),
)
.setDescription(strings.bot.common.subcommands.career.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.career,
),
@@ -97,31 +123,25 @@ export function addPeriodSubCommands(
.addSubcommand((option) =>
extra(
option
.setName(translate('bot.common.subcommands.custom'))
.setName(strings.bot.common.subcommands.custom.$)
.setNameLocalizations(subcommandNameLocalizations.custom)
.setDescription(
translate('bot.common.subcommands.custom.description'),
)
.setDescription(strings.bot.common.subcommands.custom.description)
.setDescriptionLocalizations(
subcommandDescriptionLocalizations.custom,
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.start'))
.setName(strings.bot.common.subcommands.custom.options.start.$)
.setDescription(
translate(
'bot.common.subcommands.custom.options.start.description',
),
strings.bot.common.subcommands.custom.options.start.description,
)
.setRequired(true),
)
.addIntegerOption((option) =>
option
.setName(translate('bot.common.subcommands.custom.options.end'))
.setName(strings.bot.common.subcommands.custom.options.end.$)
.setDescription(
translate(
'bot.common.subcommands.custom.options.end.description',
),
strings.bot.common.subcommands.custom.options.end.description,
)
.setRequired(true),
),
@@ -8,25 +8,31 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addRegionChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.region`)
.setName(strings.bot.common.options.region.$)
.setNameLocalizations(
localizationObject('bot.common.options.region', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.region.$,
undefined,
true,
),
)
.setDescription(t`bot.common.options.region.description`)
.setDescription(strings.bot.common.options.region.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.region.description'),
localizationObject(
(strings) => strings.bot.common.options.region.description,
),
)
.addChoices(
...REGIONS.map(
(region) =>
({
value: region,
name: translate(`common.regions.normal.${region}`),
name: strings.common.regions.normal[region],
name_localizations: localizationObject(
`common.regions.normal.${region}`,
(strings) => strings.common.regions.normal[region],
),
}) satisfies APIApplicationCommandOptionChoice,
),
@@ -3,16 +3,22 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTankChoices(option: SlashCommandStringOption) {
const { translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(translate('bot.common.options.tank'))
.setName(strings.bot.common.options.tank.$)
.setNameLocalizations(
localizationObject('bot.common.options.tank', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.tank.$,
undefined,
true,
),
)
.setDescription(translate('bot.common.options.tank.description'))
.setDescription(strings.bot.common.options.tank.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tank.description'),
localizationObject(
(strings) => strings.bot.common.options.tank.description,
),
)
.setAutocomplete(true)
.setRequired(true);
@@ -1,4 +1,5 @@
import { numberToRomanNumeral } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import {
APIApplicationCommandOptionChoice,
Locale,
@@ -9,28 +10,34 @@ import { translator } from '../localization/translator';
import { localizationObject } from './localizationObject';
export function addTierChoices(option: SlashCommandStringOption) {
const { t, translate } = translator(Locale.EnglishUS);
const { strings } = translator(Locale.EnglishUS);
return option
.setName(t`bot.common.options.tier`)
.setName(strings.bot.common.options.tier.$)
.setNameLocalizations(
localizationObject('bot.common.options.tier', undefined, true),
localizationObject(
(strings) => strings.bot.common.options.tier.$,
undefined,
true,
),
)
.setDescription(t`bot.common.options.tier.description`)
.setDescription(strings.bot.common.options.tier.description)
.setDescriptionLocalizations(
localizationObject('bot.common.options.tier.description'),
localizationObject(
(strings) => strings.bot.common.options.tier.description,
),
)
.setChoices(
...range(10, 0).map(
(tier) =>
({
name: translate('bot.common.options.tier.choices.tier', [
name: literals(strings.bot.common.options.tier.choices.tier, [
`${tier}`,
numberToRomanNumeral(tier),
]),
value: `${tier}`,
name_localizations: localizationObject(
'bot.common.options.tier.choices.tier',
(strings) => strings.bot.common.options.tier.choices.tier,
[`${tier}`, numberToRomanNumeral(tier)],
),
}) satisfies APIApplicationCommandOptionChoice,
@@ -10,7 +10,7 @@ import { DISCORD_CHOICES_MAX_NAME_SIZE, OVERFLOW_SUFFIX } from './constants';
export async function autocompleteClan(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'clan') return;
if (focusedOption.value.length < 2) return interaction.respond([]);
@@ -19,9 +19,7 @@ export async function autocompleteClan(
await interaction.respond(
clans
? clans.map((clan) => {
let name = `${clan.name} (${translate(
`common.regions.short.${clan.region}`,
)})`;
let name = `${clan.name} (${strings.common.regions.short[clan.region]})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -29,9 +27,7 @@ export async function autocompleteClan(
name = `${clan.name.slice(
0,
clan.name.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${clan.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${strings.common.regions.short[clan.region]})`;
}
return {
@@ -13,7 +13,7 @@ import {
export async function autocompleteUsername(
interaction: AutocompleteInteraction<CacheType>,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true);
if (focusedOption.name !== 'username') return;
const players = await searchPlayersAcrossRegions(focusedOption.value);
@@ -22,9 +22,9 @@ export async function autocompleteUsername(
await interaction.respond(
players
? players.map((player) => {
let name = `${player.nickname} (${translate(
`common.regions.short.${player.region}`,
)})`;
let name = `${player.nickname} (${
strings.common.regions.short[player.region]
})`;
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
@@ -32,9 +32,9 @@ export async function autocompleteUsername(
name = `${player.nickname.slice(
0,
player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX} (${translate(
`common.regions.short.${player.region}`,
)})`;
)}${OVERFLOW_SUFFIX} (${
strings.common.regions.short[player.region]
})`;
}
return {
@@ -1,3 +1,4 @@
import { BlitzKitStrings } from '@blitzkit/i18n';
import {
Locale,
SlashCommandBuilder,
@@ -23,28 +24,47 @@ export function createLocalizedCommand(
| CreateLocalizedCommandSubcommand[]
| CreateLocalizedCommandSubcommandGroup[],
) {
const commandPathItem = command.replaceAll('-', '_');
const { translate } = translator(Locale.EnglishUS);
const commandPathItem = command.replaceAll(
'-',
'_',
) as keyof BlitzKitStrings['bot']['commands'];
const { strings } = translator(Locale.EnglishUS);
const slashCommand = new SlashCommandBuilder()
.setName(command)
.setNameLocalizations(
localizationObject(`bot.commands.${commandPathItem}`, undefined, true),
localizationObject(
(strings) => strings.bot.commands[commandPathItem].$,
undefined,
true,
),
)
.setDescription(translate(`bot.commands.${commandPathItem}.description`))
.setDescription(strings.bot.commands[commandPathItem].description)
.setDescriptionLocalizations(
localizationObject(`bot.commands.${commandPathItem}.description`),
localizationObject(
(strings) => strings.bot.commands[commandPathItem].description,
),
);
if (sub) {
sub.map((subItem) => {
if ('subcommand' in subItem) {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { $: string }>;
}
).subcommands[subItem.subcommand].$,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
);
slashCommand.addSubcommand((option) => {
@@ -54,20 +74,32 @@ export function createLocalizedCommand(
.setName(subItem.subcommand)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.subcommands.${subItem.subcommand}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
subcommands: Record<string, { description: string }>;
}
).subcommands[subItem.subcommand].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
} else {
const subcommandNameLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { $: string }>;
}
).groups[subItem.group].$,
undefined,
true,
);
const subcommandDescriptionLocalizations = localizationObject(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
(strings) =>
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
);
slashCommand.addSubcommandGroup((option) => {
@@ -77,9 +109,11 @@ export function createLocalizedCommand(
.setName(subItem.group)
.setNameLocalizations(subcommandNameLocalizations)
.setDescription(
translate(
`bot.commands.${commandPathItem}.groups.${subItem.group}.description`,
),
(
strings.bot.commands[commandPathItem] as unknown as {
groups: Record<string, { description: string }>;
}
).groups[subItem.group].description,
)
.setDescriptionLocalizations(subcommandDescriptionLocalizations);
});
@@ -1,10 +1,11 @@
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translator } from '../localization/translator';
import { PeriodSize } from './addPeriodSubCommands';
export function getPeriodOptionName(period: PeriodSize, locale: Locale) {
const { translate } = translator(locale);
if (period === 'career') return translate('bot.common.periods.career');
if (period === 'today') return translate('bot.common.periods.today');
return translate('bot.common.periods.days', [period]);
const { strings } = translator(locale);
if (period === 'career') return strings.bot.common.periods.career;
if (period === 'today') return strings.bot.common.periods.today;
return literals(strings.bot.common.periods.days, [period]);
}
@@ -1,3 +1,4 @@
import { BlitzKitStrings, literals as literalsFunc } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
import { translator } from '../localization/translator';
@@ -5,14 +6,14 @@ import { translator } from '../localization/translator';
const validNameRegex = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
export function localizationObject(
path: string,
literals?: string[],
path: (strings: BlitzKitStrings) => string,
literals: string[] = [],
strict = false,
) {
return SUPPORTED_LOCALES_DISCORD.reduce<Partial<Record<Locale, string>>>(
(localizations, locale) => {
const { translate } = translator(locale);
const translation = translate(path, literals);
const { strings } = translator(locale);
const translation = literalsFunc(path(strings), literals);
const isValid = !strict || validNameRegex.test(translation);
if (!isValid) {
@@ -1,4 +1,5 @@
import { Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchClansAcrossRegionsBotWrapper } from '../blitz/searchClansAcrossRegionsBotWrapper';
@@ -10,7 +11,7 @@ export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
export async function resolveClanFromCommand(
interaction: ChatInputCommandInteraction,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const clan = interaction.options.getString('clan', true);
if (serverAndIdPattern.test(clan)) {
@@ -26,7 +27,9 @@ export async function resolveClanFromCommand(
return { region: accounts[0].region, id: accounts[0].clan_id };
} else {
throw new UserError(
translate('bot.common.errors.clan_not_found', [markdownEscape(clan)]),
literals(strings.bot.common.errors.clan_not_found, [
markdownEscape(clan),
]),
);
}
}
@@ -1,4 +1,5 @@
import { getTimeDaysAgo, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import { getPeriodNow } from '../blitzkit/getPeriodNow';
import { getPeriodStart } from '../blitzkit/getPeriodStart';
@@ -17,7 +18,7 @@ export function resolvePeriodFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
forcedPeriod?: PeriodType,
) {
const { translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
let name: string;
let start: number;
let end: number;
@@ -31,7 +32,7 @@ export function resolvePeriodFromCommand(
const startDaysAgoMin = Math.min(startOption, endOption);
const endDaysAgoMax = Math.max(startOption, endOption);
name = translate('bot.common.periods.custom', [
name = literals(strings.bot.common.periods.custom, [
`${startDaysAgoMin}`,
`${endDaysAgoMax}`,
]);
@@ -1,4 +1,5 @@
import { idToRegion, Region } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { searchPlayersAcrossRegionsBotWrapper } from '../../blitz/searchPlayersAcrossRegionsBotWrapper';
@@ -14,7 +15,7 @@ export interface ResolvedPlayer {
export async function resolvePlayerFromCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
const { t, translate } = translator(interaction.locale);
const { strings } = translator(interaction.locale);
const commandUsername = interaction.options.getString('username');
if (commandUsername !== null) {
@@ -36,7 +37,7 @@ export async function resolvePlayerFromCommand(
} satisfies ResolvedPlayer;
} else {
throw new UserError(
translate('bot.common.errors.player_not_found', [
literals(strings.bot.common.errors.player_not_found, [
markdownEscape(commandUsername),
]),
);
@@ -47,6 +48,6 @@ export async function resolvePlayerFromCommand(
if (account) return account;
throw new UserError(t`bot.common.errors.player_not_linked`);
throw new UserError(strings.bot.common.errors.player_not_linked);
}
}
@@ -1,24 +0,0 @@
import { Locale } from 'discord.js';
import {
SUPPORTED_LOCALES_DISCORD,
SUPPORTED_LOCALES_DISCORD_MAP_INVERSE,
} from './constants';
export type TranslationFragmentTree = {
[key: string]: TranslationFragment;
} & {
$?: string;
};
export type TranslationFragment = string | TranslationFragmentTree;
export const translations = SUPPORTED_LOCALES_DISCORD.reduce<
Partial<Record<Locale, TranslationFragment>>
>(
(table, locale) => ({
...table,
[locale]: require(
`@blitzkit/i18n/strings/${SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]}.json`,
),
}),
{},
);
@@ -1,6 +1,5 @@
import { unwrapper } from '@blitzkit/i18n';
import { STRINGS, unwrapper } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { translations } from './strings';
import {
DEFAULT_LOCALE_DISCORD,
SUPPORTED_LOCALES_DISCORD,
@@ -13,70 +12,10 @@ export function translator(localeRaw: Locale) {
SUPPORTED_LOCALES_DISCORD.includes(localeRaw)
? localeRaw
: DEFAULT_LOCALE_DISCORD;
const strings = translations[locale]!;
function translate(path: string, literals?: string[]): string {
const pathArray = path.split('.');
let fragment = strings;
let resolvedString: string;
for (const pathItem of pathArray) {
if (typeof fragment === 'string') {
throw new SyntaxError(
`Attempted to access string with key "${pathItem}" in "${path}" from within a string instead of an object in locale "${locale}"`,
);
} else {
fragment = fragment[pathItem];
if (typeof fragment === 'undefined') {
const message = `Undefined translation at "${pathItem}" in "${path}" for locale "${locale}"`;
if (locale === DEFAULT_LOCALE_DISCORD) {
throw new Error(message);
} else {
console.warn(`${message} falling back to en-US`);
return translator(DEFAULT_LOCALE_DISCORD).translate(path, literals);
}
}
}
}
if (typeof fragment === 'string') {
resolvedString = fragment;
} else if (fragment.$) {
resolvedString = fragment.$!;
} else {
throw new Error(
`Unresolved tree ending for "${path}" in locale "${locale}"`,
);
}
if (literals) {
const chunks = resolvedString.split('%s');
let embeddedString = '';
chunks.forEach((chunk, index) => {
if (index !== 0) embeddedString += literals[index - 1];
embeddedString += chunk;
});
return embeddedString;
}
return resolvedString;
}
function t(paths: TemplateStringsArray, ...embeds: string[]) {
return paths
.map((path, index) =>
path.length === 0
? ''
: `${translate(path)}${index === embeds.length ? '' : embeds[index]}`,
)
.join('');
}
STRINGS;
const strings = STRINGS[SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]];
const unwrap = unwrapper(SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]);
return { locale, translate, t, unwrap };
return { locale, strings, unwrap };
}