mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: random localizations
This commit is contained in:
1 parent
796aca21cf
commit
3325536bbc
17 files changed
+423
-166
No files matched your search
Vendored
+1
@@ -131,6 +131,7 @@
|
|||||||
"TYPELESS",
|
"TYPELESS",
|
||||||
"ubjson",
|
"ubjson",
|
||||||
"Unicum",
|
"Unicum",
|
||||||
|
"UNLOCALIZED",
|
||||||
"UNORM",
|
"UNORM",
|
||||||
"Unregistering",
|
"Unregistering",
|
||||||
"vickers",
|
"vickers",
|
||||||
|
|||||||
@@ -20,7 +20,11 @@ import PageWrapper from '../../../components/PageWrapper';
|
|||||||
import { Skeleton } from '../../../components/Skeleton';
|
import { Skeleton } from '../../../components/Skeleton';
|
||||||
import { LEAGUES } from '../../../constants/leagues';
|
import { LEAGUES } from '../../../constants/leagues';
|
||||||
import { FIRST_ARCHIVED_RATINGS_SEASON } from '../../../constants/ratings';
|
import { FIRST_ARCHIVED_RATINGS_SEASON } from '../../../constants/ratings';
|
||||||
import { REGIONS, REGION_NAMES, Region } from '../../../constants/regions';
|
import {
|
||||||
|
REGIONS,
|
||||||
|
Region,
|
||||||
|
UNLOCALIZED_REGION_NAMES,
|
||||||
|
} from '../../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../../../core/blitz/fetchBlitz';
|
import fetchBlitz from '../../../core/blitz/fetchBlitz';
|
||||||
import { getClanAccountInfo } from '../../../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../../../core/blitz/getClanAccountInfo';
|
||||||
@@ -556,7 +560,7 @@ export default function Page() {
|
|||||||
<Select.Content>
|
<Select.Content>
|
||||||
{REGIONS.map((region) => (
|
{REGIONS.map((region) => (
|
||||||
<Select.Item key={region} value={region}>
|
<Select.Item key={region} value={region}>
|
||||||
{REGION_NAMES[region]}
|
{UNLOCALIZED_REGION_NAMES[region]}
|
||||||
</Select.Item>
|
</Select.Item>
|
||||||
))}
|
))}
|
||||||
</Select.Content>
|
</Select.Content>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||||
import PageWrapper from '../../../components/PageWrapper';
|
import PageWrapper from '../../../components/PageWrapper';
|
||||||
import { REGION_NAMES } from '../../../constants/regions';
|
import { UNLOCALIZED_REGION_NAMES } from '../../../constants/regions';
|
||||||
import searchPlayersAcrossRegions, {
|
import searchPlayersAcrossRegions, {
|
||||||
AccountListWithServer,
|
AccountListWithServer,
|
||||||
} from '../../../core/blitz/searchPlayersAcrossRegions';
|
} from '../../../core/blitz/searchPlayersAcrossRegions';
|
||||||
@@ -107,7 +107,7 @@ export default function Page() {
|
|||||||
setShowSearchResults(false);
|
setShowSearchResults(false);
|
||||||
setSession(region, id, nickname);
|
setSession(region, id, nickname);
|
||||||
}}
|
}}
|
||||||
shortcut={REGION_NAMES[region]}
|
shortcut={UNLOCALIZED_REGION_NAMES[region]}
|
||||||
>
|
>
|
||||||
{nickname}
|
{nickname}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ async function render(
|
|||||||
max: maxX,
|
max: maxX,
|
||||||
precision: 0,
|
precision: 0,
|
||||||
}}
|
}}
|
||||||
xMinLabel={new Date(minTime * 1000).toDateString()}
|
xMinLabel={new Date(minTime * 1000).toLocaleDateString(locale)}
|
||||||
xMaxLabel={new Date(maxTime * 1000).toDateString()}
|
xMaxLabel={new Date(maxTime * 1000).toLocaleDateString(locale)}
|
||||||
>
|
>
|
||||||
{Graph.Line({
|
{Graph.Line({
|
||||||
color: Graph.LineColor.Red,
|
color: Graph.LineColor.Red,
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ export const inactiveCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
([name, days]) =>
|
([name, days]) =>
|
||||||
[
|
[
|
||||||
name,
|
name,
|
||||||
translate('bot.commands.inactive.body.listing', [`${days}`]),
|
translate('bot.commands.inactive.body.listing', [
|
||||||
|
days.toFixed(0),
|
||||||
|
]),
|
||||||
] as [string, string],
|
] as [string, string],
|
||||||
);
|
);
|
||||||
const hasInactiveMembers = inactive.length > 0;
|
const hasInactiveMembers = inactive.length > 0;
|
||||||
|
|||||||
+21
-14
@@ -1,18 +1,19 @@
|
|||||||
import { teal } from '@radix-ui/colors';
|
import { teal } from '@radix-ui/colors';
|
||||||
import { GuildMemberRoleManager, SlashCommandBuilder } from 'discord.js';
|
import { GuildMemberRoleManager } from 'discord.js';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import discord from '../../discord.json' assert { type: 'json' };
|
import discord from '../../discord.json' assert { type: 'json' };
|
||||||
import { Glow } from '../components/AllStatsOverview/components/WN8Display/components/Glow';
|
import { Glow } from '../components/AllStatsOverview/components/WN8Display/components/Glow';
|
||||||
import CommandWrapper from '../components/CommandWrapper';
|
import CommandWrapper from '../components/CommandWrapper';
|
||||||
import { REGION_NAMES } from '../constants/regions';
|
|
||||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||||
import { linkBlitzAndDiscord } from '../core/blitzkrieg/discordBlitz';
|
import { linkBlitzAndDiscord } from '../core/blitzkrieg/discordBlitz';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
import embedInfo from '../core/discord/embedInfo';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
import embedNegative from '../core/discord/embedNegative';
|
import embedNegative from '../core/discord/embedNegative';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
|
import { translator } from '../core/localization/translator';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { theme } from '../stitches.config';
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
@@ -21,14 +22,12 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
inProduction: true,
|
inProduction: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: createLocalizedCommand('link').addStringOption((option) =>
|
||||||
.setName('link')
|
addUsernameChoices(option).setRequired(true),
|
||||||
.setDescription('Links your Blitz and Discord account')
|
),
|
||||||
.addStringOption((option) =>
|
|
||||||
addUsernameChoices(option).setRequired(true),
|
|
||||||
),
|
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
|
const { t, translate } = translator(interaction.locale);
|
||||||
const { id, region } = await resolvePlayerFromCommand(interaction);
|
const { id, region } = await resolvePlayerFromCommand(interaction);
|
||||||
const discordId = parseInt(interaction.user.id);
|
const discordId = parseInt(interaction.user.id);
|
||||||
const accountInfo = await getAccountInfo(region, id);
|
const accountInfo = await getAccountInfo(region, id);
|
||||||
@@ -39,8 +38,14 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
if (interaction.guildId === discord.sklld_guild_id) {
|
if (interaction.guildId === discord.sklld_guild_id) {
|
||||||
if (!interaction.guild?.members.me?.permissions.has('ManageRoles')) {
|
if (!interaction.guild?.members.me?.permissions.has('ManageRoles')) {
|
||||||
return embedNegative(
|
return embedNegative(
|
||||||
`${markdownEscape(interaction.user.username)} failed to verify`,
|
translate(translate('bot.commands.link.body.no_role_permissions'), [
|
||||||
"I don't have the permission to change your manage roles.",
|
markdownEscape(interaction.user.username),
|
||||||
|
]),
|
||||||
|
translate(
|
||||||
|
translate(
|
||||||
|
'bot.commands.link.body.no_role_permissions.description',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +145,9 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
height: 16,
|
height: 16,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ fontSize: 16 }}>Accounts linked</span>
|
<span
|
||||||
|
style={{ fontSize: 16 }}
|
||||||
|
>{t`bot.commands.link.body.accounts_linked`}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -191,7 +198,7 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
{clanAccountInfo?.clan
|
{clanAccountInfo?.clan
|
||||||
? `[${clanAccountInfo?.clan?.tag}] • `
|
? `[${clanAccountInfo?.clan?.tag}] • `
|
||||||
: ''}
|
: ''}
|
||||||
{REGION_NAMES[region]}
|
{translate(`common.regions.normal.${region}`)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -202,8 +209,8 @@ export const verifyCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
</CommandWrapper>,
|
</CommandWrapper>,
|
||||||
|
|
||||||
embedInfo(
|
embedInfo(
|
||||||
'Incorrect account?',
|
t`bot.commands.link.embed.title`,
|
||||||
'Use the command again and select a username from the search results.',
|
t`bot.commands.link.embed.description`,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
+135
-84
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
APIApplicationCommandOptionChoice,
|
APIApplicationCommandOptionChoice,
|
||||||
SlashCommandBuilder,
|
Locale,
|
||||||
SlashCommandSubcommandBuilder,
|
SlashCommandSubcommandBuilder,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { range } from 'lodash';
|
import { range } from 'lodash';
|
||||||
@@ -11,7 +11,7 @@ import * as Leaderboard from '../components/Leaderboard';
|
|||||||
import TitleBar from '../components/TitleBar';
|
import TitleBar from '../components/TitleBar';
|
||||||
import { LEAGUES } from '../constants/leagues';
|
import { LEAGUES } from '../constants/leagues';
|
||||||
import { FIRST_ARCHIVED_RATINGS_SEASON } from '../constants/ratings';
|
import { FIRST_ARCHIVED_RATINGS_SEASON } from '../constants/ratings';
|
||||||
import { REGION_NAMES_SHORT, Region } from '../constants/regions';
|
import { Region } from '../constants/regions';
|
||||||
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
|
||||||
import getRatingsInfo from '../core/blitz/getRatingsInfo';
|
import getRatingsInfo from '../core/blitz/getRatingsInfo';
|
||||||
@@ -25,8 +25,10 @@ import getArchivedRatingsMidnightLeaderboard from '../core/blitzkrieg/getArchive
|
|||||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||||
import embedNegative from '../core/discord/embedNegative';
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
|
import { localizationObject } from '../core/discord/localizationObject';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
|
import { translator } from '../core/localization/translator';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { UserError } from '../hooks/userError';
|
import { UserError } from '../hooks/userError';
|
||||||
|
|
||||||
@@ -117,10 +119,15 @@ export interface RatingsNeighbors {
|
|||||||
neighbors: RatingsPlayer[];
|
neighbors: RatingsPlayer[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const noOngoingSeason = embedNegative(
|
interface SimplifiedPlayer {
|
||||||
'No ongoing season',
|
id: number;
|
||||||
"Wargaming didn't provide any data for this season.",
|
score: number;
|
||||||
);
|
position: number;
|
||||||
|
clan: string | undefined;
|
||||||
|
nickname: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_LIMIT = 10;
|
||||||
|
|
||||||
export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
||||||
const latestArchivedSeasonNumber = await getArchivedLatestSeasonNumber();
|
const latestArchivedSeasonNumber = await getArchivedLatestSeasonNumber();
|
||||||
@@ -130,89 +137,131 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1,
|
latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
function addSubcommands(option: SlashCommandSubcommandBuilder) {
|
function addOptions(option: SlashCommandSubcommandBuilder) {
|
||||||
|
const { t, translate } = translator(Locale.EnglishUS);
|
||||||
|
|
||||||
return option
|
return option
|
||||||
.addStringOption(addUsernameChoices)
|
|
||||||
.addIntegerOption((option) =>
|
.addIntegerOption((option) =>
|
||||||
option
|
option
|
||||||
.setName('limit')
|
.setName(t`bot.commands.ratings.options.limit`)
|
||||||
.setDescription('How many neighbors to display (default: 10)')
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.commands.ratings.options.limit'),
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
translate('bot.commands.ratings.options.limit.description', [
|
||||||
|
`${DEFAULT_LIMIT}`,
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
localizationObject(
|
||||||
|
'bot.commands.ratings.options.limit.description',
|
||||||
|
[`${DEFAULT_LIMIT}`],
|
||||||
|
),
|
||||||
|
)
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
.setMinValue(5)
|
.setMinValue(5)
|
||||||
.setMaxValue(30),
|
.setMaxValue(30),
|
||||||
)
|
)
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
option
|
option
|
||||||
.setName('season')
|
.setName(t`bot.commands.ratings.options.season`)
|
||||||
.setDescription('Season number')
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.commands.ratings.options.season'),
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
translate('bot.commands.ratings.options.season.description'),
|
||||||
|
)
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
localizationObject(
|
||||||
|
'bot.commands.ratings.options.season.description',
|
||||||
|
),
|
||||||
|
)
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
.addChoices(
|
.addChoices(
|
||||||
...seasonNumbers
|
...seasonNumbers
|
||||||
.map(
|
.map((number, index) => {
|
||||||
(number, index) =>
|
const current =
|
||||||
({
|
index === seasonNumbers.length - 1 && onGoingSeason;
|
||||||
name: `${number}${index === seasonNumbers.length - 1 && onGoingSeason ? ' (current)' : ''}`,
|
|
||||||
value: `${number}`,
|
return {
|
||||||
}) satisfies APIApplicationCommandOptionChoice<string>,
|
name: current
|
||||||
)
|
? translate(
|
||||||
|
'bot.commands.ratings.options.season.choices.current',
|
||||||
|
[`${number}`],
|
||||||
|
)
|
||||||
|
: `${number}`,
|
||||||
|
value: `${number}`,
|
||||||
|
name_localizations: current
|
||||||
|
? localizationObject(
|
||||||
|
'bot.commands.ratings.options.season.choices.current',
|
||||||
|
[`${number}`],
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
|
} satisfies APIApplicationCommandOptionChoice<string>;
|
||||||
|
})
|
||||||
.reverse(),
|
.reverse(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const command = new SlashCommandBuilder()
|
|
||||||
.setName('ratings')
|
|
||||||
.setDescription('Ratings battles statistics')
|
|
||||||
.addSubcommand((option) =>
|
|
||||||
addSubcommands(
|
|
||||||
option
|
|
||||||
.setName('neighbors')
|
|
||||||
.setDescription('Shows neighboring players on the leaderboard'),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.addSubcommand((option) =>
|
|
||||||
addSubcommands(
|
|
||||||
option
|
|
||||||
.setName('league')
|
|
||||||
.setDescription('Shows top players from a league')
|
|
||||||
.addStringOption(addRegionChoices)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName('league')
|
|
||||||
.setDescription('The league to display')
|
|
||||||
.addChoices(
|
|
||||||
...LEAGUES.map(
|
|
||||||
({ name }, value) =>
|
|
||||||
({
|
|
||||||
name,
|
|
||||||
value: `${value}`,
|
|
||||||
}) satisfies APIApplicationCommandOptionChoice<string>,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.setRequired(true),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command,
|
command: createLocalizedCommand('ratings', [
|
||||||
|
{
|
||||||
|
subcommand: 'neighbors',
|
||||||
|
modify(option: SlashCommandSubcommandBuilder) {
|
||||||
|
option.addStringOption(addUsernameChoices);
|
||||||
|
addOptions(option);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subcommand: 'league',
|
||||||
|
modify(option: SlashCommandSubcommandBuilder) {
|
||||||
|
option
|
||||||
|
.addStringOption((option) => {
|
||||||
|
const { t, translate } = translator(Locale.EnglishUS);
|
||||||
|
|
||||||
|
return option
|
||||||
|
.setName(t`bot.commands.ratings.options.league`)
|
||||||
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.commands.ratings.options.league'),
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
translate('bot.commands.ratings.options.league.description'),
|
||||||
|
)
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
localizationObject(
|
||||||
|
'bot.commands.ratings.options.league.description',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.addChoices(
|
||||||
|
...LEAGUES.map(
|
||||||
|
({ name }, value) =>
|
||||||
|
({
|
||||||
|
name: translate(`common.leagues.${name}`),
|
||||||
|
value: `${value}`,
|
||||||
|
name_localizations: localizationObject(
|
||||||
|
`common.leagues.${name}`,
|
||||||
|
),
|
||||||
|
}) satisfies APIApplicationCommandOptionChoice<string>,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.setRequired(true);
|
||||||
|
})
|
||||||
|
.addStringOption(addRegionChoices);
|
||||||
|
addOptions(option);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
interface SimplifiedPlayer {
|
const { t, translate } = translator(interaction.locale);
|
||||||
id: number;
|
|
||||||
score: number;
|
|
||||||
position: number;
|
|
||||||
clan: string | undefined;
|
|
||||||
nickname: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subcommand = interaction.options.getSubcommand(true) as
|
const subcommand = interaction.options.getSubcommand(true) as
|
||||||
| 'league'
|
| 'league'
|
||||||
| 'neighbors';
|
| 'neighbors';
|
||||||
const limit = interaction.options.getInteger('limit') ?? 10;
|
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
|
||||||
const seasonOption = interaction.options.getString('season');
|
const seasonOption = interaction.options.getString('season');
|
||||||
const season = seasonOption
|
const season = seasonOption
|
||||||
? parseInt(seasonOption)
|
? parseInt(seasonOption)
|
||||||
@@ -223,7 +272,6 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
let titleName: string;
|
let titleName: string;
|
||||||
let titleImage: string | undefined;
|
let titleImage: string | undefined;
|
||||||
let titleDescription: string;
|
let titleDescription: string;
|
||||||
let playersBefore: number;
|
|
||||||
let playersAfter: number;
|
let playersAfter: number;
|
||||||
let players: SimplifiedPlayer[] | undefined;
|
let players: SimplifiedPlayer[] | undefined;
|
||||||
let midnightLeaderboard: BlitzkriegRatingsLeaderboard | undefined;
|
let midnightLeaderboard: BlitzkriegRatingsLeaderboard | undefined;
|
||||||
@@ -237,7 +285,8 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
? getRatingsInfo(region)
|
? getRatingsInfo(region)
|
||||||
: getArchivedRatingsInfo(region, season!));
|
: getArchivedRatingsInfo(region, season!));
|
||||||
|
|
||||||
if (regionRatingsInfo.detail !== undefined) return noOngoingSeason;
|
if (regionRatingsInfo.detail !== undefined)
|
||||||
|
return t`bot.commands.ratings.body.no_ongoing_season`;
|
||||||
|
|
||||||
const result = isCurrentSeason
|
const result = isCurrentSeason
|
||||||
? await getRatingsLeague(region, leagueIndex).then(async (result) =>
|
? await getRatingsLeague(region, leagueIndex).then(async (result) =>
|
||||||
@@ -280,7 +329,10 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
position: firstPlayerIndex + index + 1,
|
position: firstPlayerIndex + index + 1,
|
||||||
clan: clanData[player.id]?.clan?.tag,
|
clan: clanData[player.id]?.clan?.tag,
|
||||||
nickname:
|
nickname:
|
||||||
clanData[index]?.account_name ?? `Deleted ${player.id}`,
|
clanData[index]?.account_name ??
|
||||||
|
translate('bot.commands.ratings.body.deleted_player', [
|
||||||
|
`${player.id}`,
|
||||||
|
]),
|
||||||
}) satisfies SimplifiedPlayer,
|
}) satisfies SimplifiedPlayer,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -294,23 +346,25 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
: await getArchivedRatingsMidnightLeaderboard(region, season!);
|
: await getArchivedRatingsMidnightLeaderboard(region, season!);
|
||||||
|
|
||||||
players = result;
|
players = result;
|
||||||
playersBefore = result ? result[0].position - 1 : 0;
|
|
||||||
playersAfter =
|
playersAfter =
|
||||||
regionRatingsInfo.count - result[result.length - 1].position;
|
regionRatingsInfo.count - result[result.length - 1].position;
|
||||||
titleName = `${
|
titleName = `${
|
||||||
leagueInfo ? leagueInfo.title : 'No Ongoing Season'
|
leagueInfo ? leagueInfo.title : ''
|
||||||
} - ${REGION_NAMES_SHORT[region]}`;
|
} - ${translate(`common.regions.short.${region}`)}`;
|
||||||
titleImage = leagueInfo.big_icon.startsWith('http')
|
titleImage = leagueInfo.big_icon.startsWith('http')
|
||||||
? leagueInfo.big_icon
|
? leagueInfo.big_icon
|
||||||
: `https:${leagueInfo.big_icon}`;
|
: `https:${leagueInfo.big_icon}`;
|
||||||
titleDescription = `Top ${limit} players`;
|
titleDescription = translate('bot.commands.ratings.body.top_players', [
|
||||||
|
`${limit}`,
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
const { region, id } = await resolvePlayerFromCommand(interaction);
|
const { region, id } = await resolvePlayerFromCommand(interaction);
|
||||||
regionRatingsInfo = isCurrentSeason
|
regionRatingsInfo = isCurrentSeason
|
||||||
? await getRatingsInfo(region)
|
? await getRatingsInfo(region)
|
||||||
: await getArchivedRatingsInfo(region, season!);
|
: await getArchivedRatingsInfo(region, season!);
|
||||||
|
|
||||||
if (regionRatingsInfo.detail !== undefined) return noOngoingSeason;
|
if (regionRatingsInfo.detail !== undefined)
|
||||||
|
return t`bot.commands.ratings.body.no_ongoing_season`;
|
||||||
|
|
||||||
const accountInfo = await getAccountInfo(region, id);
|
const accountInfo = await getAccountInfo(region, id);
|
||||||
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
|
||||||
@@ -336,7 +390,10 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
|
|
||||||
if (playerIndex === -1) {
|
if (playerIndex === -1) {
|
||||||
throw new UserError(
|
throw new UserError(
|
||||||
`# ${markdownEscape(accountInfo.nickname)} didn't play ratings in season ${season}\nThis player did not participate in this season or did not get past calibration.`,
|
translate('bot.commands.ratings.body.no_participation', [
|
||||||
|
markdownEscape(accountInfo.nickname),
|
||||||
|
`${season}`,
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,11 +430,8 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
titleImage = clan
|
titleImage = clan
|
||||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
||||||
: undefined;
|
: undefined;
|
||||||
titleName = neighbors
|
titleName = neighbors ? accountInfo.nickname : '';
|
||||||
? accountInfo.nickname
|
titleDescription = t`bot.commands.ratings.body.neighbors`;
|
||||||
: `No Ongoing Season - ${REGION_NAMES_SHORT[region]}`;
|
|
||||||
titleDescription = 'Ratings neighbors';
|
|
||||||
playersBefore = neighbors ? neighbors[0].position - 1 : 0;
|
|
||||||
playersAfter =
|
playersAfter =
|
||||||
regionRatingsInfo.count - neighbors[neighbors.length - 1].position;
|
regionRatingsInfo.count - neighbors[neighbors.length - 1].position;
|
||||||
playerId = id;
|
playerId = id;
|
||||||
@@ -428,15 +482,12 @@ export const ratingsCommand = new Promise<CommandRegistry>(async (resolve) => {
|
|||||||
|
|
||||||
{items && (
|
{items && (
|
||||||
<Leaderboard.Root>
|
<Leaderboard.Root>
|
||||||
{playersBefore > 0 && (
|
|
||||||
<Leaderboard.Gap
|
|
||||||
message={`+ ${playersBefore.toLocaleString()} more`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{items}
|
{items}
|
||||||
{playersAfter > 0 && (
|
{playersAfter > 0 && (
|
||||||
<Leaderboard.Gap
|
<Leaderboard.Gap
|
||||||
message={`+ ${playersAfter.toLocaleString()} more`}
|
message={translate('bot.commands.ratings.body.more', [
|
||||||
|
`${playersAfter.toLocaleString(interaction.locale)}`,
|
||||||
|
])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Leaderboard.Root>
|
</Leaderboard.Root>
|
||||||
|
|||||||
+36
-14
@@ -1,46 +1,68 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import { REGION_NAMES, Region } from '../constants/regions';
|
import { Region } from '../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../core/blitz/fetchBlitz';
|
import fetchBlitz from '../core/blitz/fetchBlitz';
|
||||||
import addClanChoices from '../core/discord/addClanChoices';
|
import addClanChoices from '../core/discord/addClanChoices';
|
||||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||||
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
import embedInfo from '../core/discord/embedInfo';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
|
import { localizationObject } from '../core/discord/localizationObject';
|
||||||
|
import { translator } from '../core/localization/translator';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
import { ClanList } from '../types/clanList';
|
import { ClanList } from '../types/clanList';
|
||||||
|
|
||||||
|
const DEFAULT_LIMIT = 25;
|
||||||
|
|
||||||
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
export const searchClansCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: createLocalizedCommand('search-clans')
|
||||||
.setName('search-clans')
|
|
||||||
.setDescription('Search clans')
|
|
||||||
.addStringOption(addRegionChoices)
|
.addStringOption(addRegionChoices)
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
addClanChoices(option).setAutocomplete(false),
|
addClanChoices(option).setAutocomplete(false),
|
||||||
)
|
)
|
||||||
.addIntegerOption((option) =>
|
.addIntegerOption((option) => {
|
||||||
option
|
const { t, translate } = translator(Locale.EnglishUS);
|
||||||
.setName('limit')
|
|
||||||
.setDescription('The size of the search result (default: 25)')
|
return option
|
||||||
|
.setName(t`bot.commands.search_clans.options.limit`)
|
||||||
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.commands.search_clans.options.limit'),
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
translate('bot.commands.search_clans.options.limit.description', [
|
||||||
|
`${DEFAULT_LIMIT}`,
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
localizationObject(
|
||||||
|
'bot.commands.search_clans.options.limit.description',
|
||||||
|
[`${DEFAULT_LIMIT}`],
|
||||||
|
),
|
||||||
|
)
|
||||||
.setMinValue(1)
|
.setMinValue(1)
|
||||||
.setMaxValue(100),
|
.setMaxValue(100);
|
||||||
),
|
}),
|
||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
|
const { translate } = translator(interaction.locale);
|
||||||
const server = interaction.options.getString('region') as Region;
|
const server = interaction.options.getString('region') as Region;
|
||||||
const clan = interaction.options.getString('clan')!;
|
const clan = interaction.options.getString('clan')!;
|
||||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT;
|
||||||
const clanList = await fetchBlitz<ClanList>(
|
const clanList = await fetchBlitz<ClanList>(
|
||||||
`https://api.wotblitz.${server}/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${clan}&limit=${limit}`,
|
`https://api.wotblitz.${server}/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${clan}&limit=${limit}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return embedInfo(
|
return embedInfo(
|
||||||
`Clan search for "${markdownEscape(clan)}" in ${REGION_NAMES[server]}`,
|
translate('bot.commands.search_clans.body.title', [
|
||||||
|
markdownEscape(clan),
|
||||||
|
translate(`common.regions.normal.${server}`),
|
||||||
|
]),
|
||||||
clanList.length === 0
|
clanList.length === 0
|
||||||
? 'No clans found.'
|
? translate('bot.commands.search_clans.body.no_results')
|
||||||
: `\`\`\`\n${clanList
|
: `\`\`\`\n${clanList
|
||||||
.map((clan) => `${clan.name} [${clan.tag}]`)
|
.map((clan) => `${clan.name} [${clan.tag}]`)
|
||||||
.join('\n')}\n\`\`\``,
|
.join('\n')}\n\`\`\``,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { Locale } from 'discord.js';
|
||||||
import markdownEscape from 'markdown-escape';
|
import markdownEscape from 'markdown-escape';
|
||||||
import { REGION_NAMES, Region } from '../constants/regions';
|
import { Region } from '../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
|
||||||
import fetchBlitz from '../core/blitz/fetchBlitz';
|
import fetchBlitz from '../core/blitz/fetchBlitz';
|
||||||
import {
|
import {
|
||||||
@@ -9,25 +9,43 @@ import {
|
|||||||
} from '../core/blitz/searchPlayersAcrossRegions';
|
} from '../core/blitz/searchPlayersAcrossRegions';
|
||||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
import embedInfo from '../core/discord/embedInfo';
|
import embedInfo from '../core/discord/embedInfo';
|
||||||
|
import { localizationObject } from '../core/discord/localizationObject';
|
||||||
|
import { translator } from '../core/localization/translator';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
|
|
||||||
|
const DEFAULT_LIMIT = 25;
|
||||||
|
|
||||||
export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
|
const { t, translate } = translator(Locale.EnglishUS);
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: createLocalizedCommand('search-players')
|
||||||
.setName('search-players')
|
|
||||||
.setDescription('Search players in a Blitz server')
|
|
||||||
.addStringOption(addRegionChoices)
|
.addStringOption(addRegionChoices)
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
addUsernameChoices(option).setAutocomplete(false).setRequired(true),
|
addUsernameChoices(option).setAutocomplete(false).setRequired(true),
|
||||||
)
|
)
|
||||||
.addIntegerOption((option) =>
|
.addIntegerOption((option) =>
|
||||||
option
|
option
|
||||||
.setName('limit')
|
.setName(t`bot.commands.search_players.options.limit`)
|
||||||
.setDescription('The size of the search result (default: 25)')
|
.setNameLocalizations(
|
||||||
|
localizationObject('bot.commands.search_players.options.limit'),
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
translate('bot.commands.search_players.options.limit.description', [
|
||||||
|
`${DEFAULT_LIMIT}`,
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
localizationObject(
|
||||||
|
'bot.commands.search_players.options.limit.description',
|
||||||
|
[`${DEFAULT_LIMIT}`],
|
||||||
|
),
|
||||||
|
)
|
||||||
.setMinValue(1)
|
.setMinValue(1)
|
||||||
.setMaxValue(100),
|
.setMaxValue(100),
|
||||||
),
|
),
|
||||||
@@ -46,10 +64,13 @@ export const searchPlayersCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
return embedInfo(
|
return embedInfo(
|
||||||
`Player search for "${markdownEscape(name)}" in ${REGION_NAMES[server]}`,
|
translate('bot.commands.search_players.body.title', [
|
||||||
|
markdownEscape(trimmedSearch),
|
||||||
|
translate(`common.regions.normal.${server}`),
|
||||||
|
]),
|
||||||
`\`\`\`${
|
`\`\`\`${
|
||||||
players.length === 0
|
players.length === 0
|
||||||
? 'No players found.'
|
? translate('bot.commands.search_players.body.no_results')
|
||||||
: players.map((player) => player.nickname).join('\n')
|
: players.map((player) => player.nickname).join('\n')
|
||||||
}\`\`\``,
|
}\`\`\``,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const LEAGUES = [
|
export const LEAGUES = [
|
||||||
{ name: 'Diamond', minScore: 5000 },
|
{ name: 'diamond', minScore: 5000 },
|
||||||
{ name: 'Platinum', minScore: 4000 },
|
{ name: 'platinum', minScore: 4000 },
|
||||||
{ name: 'Gold', minScore: 3000 },
|
{ name: 'gold', minScore: 3000 },
|
||||||
{ name: 'Silver', minScore: 2000 },
|
{ name: 'silver', minScore: 2000 },
|
||||||
{ name: 'Bronze', minScore: 0 },
|
{ name: 'bronze', minScore: 0 },
|
||||||
];
|
];
|
||||||
@@ -3,21 +3,11 @@ export const REGIONS = ['com', 'eu', 'asia'] as const;
|
|||||||
export type Region = (typeof REGIONS)[number];
|
export type Region = (typeof REGIONS)[number];
|
||||||
export type RegionSubdomain = 'na' | 'eu' | 'asia';
|
export type RegionSubdomain = 'na' | 'eu' | 'asia';
|
||||||
|
|
||||||
export const REGION_NAMES: Record<Region, string> = {
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
export const UNLOCALIZED_REGION_NAMES: Record<Region, string> = {
|
||||||
com: 'North America',
|
com: 'North America',
|
||||||
eu: 'Europe',
|
|
||||||
asia: 'Asia',
|
asia: 'Asia',
|
||||||
};
|
|
||||||
|
|
||||||
// https://planetarynames.wr.usgs.gov/Abbreviations
|
|
||||||
export const REGION_NAMES_SHORT: Record<Region, string> = {
|
|
||||||
com: 'NA',
|
|
||||||
eu: 'EU',
|
|
||||||
asia: 'ASIA',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const REGION_SUBDOMAIN_NAMES: Record<RegionSubdomain, string> = {
|
|
||||||
na: 'North America',
|
|
||||||
eu: 'Europe',
|
eu: 'Europe',
|
||||||
asia: 'Asia',
|
|
||||||
};
|
};
|
||||||
@@ -1,14 +1,33 @@
|
|||||||
import { SlashCommandStringOption } from 'discord.js';
|
import {
|
||||||
import { REGION_NAMES } from '../../constants/regions';
|
APIApplicationCommandOptionChoice,
|
||||||
|
Locale,
|
||||||
|
SlashCommandStringOption,
|
||||||
|
} from 'discord.js';
|
||||||
|
import { REGIONS } from '../../constants/regions';
|
||||||
|
import { translator } from '../localization/translator';
|
||||||
|
import { localizationObject } from './localizationObject';
|
||||||
|
|
||||||
export default function addRegionChoices(option: SlashCommandStringOption) {
|
export default function addRegionChoices(option: SlashCommandStringOption) {
|
||||||
|
const { t, translate } = translator(Locale.EnglishUS);
|
||||||
|
|
||||||
return option
|
return option
|
||||||
.setName('region')
|
.setName(t`bot.common.options.region`)
|
||||||
.setDescription('The Blitz region you are in')
|
.setNameLocalizations(localizationObject('bot.common.options.region'))
|
||||||
|
.setDescription(t`bot.common.options.region.description`)
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
localizationObject('bot.common.options.region.description'),
|
||||||
|
)
|
||||||
.addChoices(
|
.addChoices(
|
||||||
{ name: REGION_NAMES.com, value: 'com' },
|
...REGIONS.map(
|
||||||
{ name: REGION_NAMES.eu, value: 'eu' },
|
(region) =>
|
||||||
{ name: REGION_NAMES.asia, value: 'asia' },
|
({
|
||||||
|
value: region,
|
||||||
|
name: translate(`common.regions.normal.${region}`),
|
||||||
|
name_localizations: localizationObject(
|
||||||
|
`common.regions.normal.${region}`,
|
||||||
|
),
|
||||||
|
}) satisfies APIApplicationCommandOptionChoice,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.setRequired(true);
|
.setRequired(true);
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { REGION_NAMES_SHORT } from '../../constants/regions';
|
|
||||||
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
|
import searchClansAcrossRegions from '../blitz/searchClansAcrossRegions';
|
||||||
|
import { translator } from '../localization/translator';
|
||||||
|
|
||||||
export const DISCORD_CHOICES_MAX_NAME_SIZE = 25;
|
export const DISCORD_CHOICES_MAX_NAME_SIZE = 25;
|
||||||
export const OVERFLOW_SUFFIX = '…';
|
export const OVERFLOW_SUFFIX = '…';
|
||||||
@@ -12,6 +12,7 @@ export const OVERFLOW_SUFFIX = '…';
|
|||||||
export default async function autocompleteClan(
|
export default async function autocompleteClan(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
) {
|
) {
|
||||||
|
const { translate } = translator(interaction.locale);
|
||||||
const focusedOption = interaction.options.getFocused(true);
|
const focusedOption = interaction.options.getFocused(true);
|
||||||
if (focusedOption.name !== 'clan') return;
|
if (focusedOption.name !== 'clan') return;
|
||||||
if (focusedOption.value.length < 2) return interaction.respond([]);
|
if (focusedOption.value.length < 2) return interaction.respond([]);
|
||||||
@@ -20,7 +21,9 @@ export default async function autocompleteClan(
|
|||||||
await interaction.respond(
|
await interaction.respond(
|
||||||
clans
|
clans
|
||||||
? clans.map((clan) => {
|
? clans.map((clan) => {
|
||||||
let name = `${clan.name} (${REGION_NAMES_SHORT[clan.region]})`;
|
let name = `${clan.name} (${translate(
|
||||||
|
`common.regions.short.${clan.region}`,
|
||||||
|
)})`;
|
||||||
|
|
||||||
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
||||||
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
||||||
@@ -28,7 +31,9 @@ export default async function autocompleteClan(
|
|||||||
name = `${clan.name.slice(
|
name = `${clan.name.slice(
|
||||||
0,
|
0,
|
||||||
clan.name.length - overSize - OVERFLOW_SUFFIX.length,
|
clan.name.length - overSize - OVERFLOW_SUFFIX.length,
|
||||||
)}${OVERFLOW_SUFFIX} (${REGION_NAMES_SHORT[clan.region]})`;
|
)}${OVERFLOW_SUFFIX} (${translate(
|
||||||
|
`common.regions.short.${clan.region}`,
|
||||||
|
)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
CacheType,
|
CacheType,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { REGION_NAMES_SHORT } from '../../constants/regions';
|
|
||||||
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
|
import searchPlayersAcrossRegions from '../blitz/searchPlayersAcrossRegions';
|
||||||
|
import { translator } from '../localization/translator';
|
||||||
import {
|
import {
|
||||||
DISCORD_CHOICES_MAX_NAME_SIZE,
|
DISCORD_CHOICES_MAX_NAME_SIZE,
|
||||||
OVERFLOW_SUFFIX,
|
OVERFLOW_SUFFIX,
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
export default async function autocompleteUsername(
|
export default async function autocompleteUsername(
|
||||||
interaction: AutocompleteInteraction<CacheType>,
|
interaction: AutocompleteInteraction<CacheType>,
|
||||||
) {
|
) {
|
||||||
|
const { translate } = translator(interaction.locale);
|
||||||
const focusedOption = interaction.options.getFocused(true);
|
const focusedOption = interaction.options.getFocused(true);
|
||||||
if (focusedOption.name !== 'username') return;
|
if (focusedOption.name !== 'username') return;
|
||||||
const players = await searchPlayersAcrossRegions(focusedOption.value);
|
const players = await searchPlayersAcrossRegions(focusedOption.value);
|
||||||
@@ -21,9 +22,9 @@ export default async function autocompleteUsername(
|
|||||||
await interaction.respond(
|
await interaction.respond(
|
||||||
players
|
players
|
||||||
? players.map((player) => {
|
? players.map((player) => {
|
||||||
let name = `${player.nickname} (${
|
let name = `${player.nickname} (${translate(
|
||||||
REGION_NAMES_SHORT[player.region]
|
`common.regions.short.${player.region}`,
|
||||||
})`;
|
)})`;
|
||||||
|
|
||||||
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
if (name.length > DISCORD_CHOICES_MAX_NAME_SIZE) {
|
||||||
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
const overSize = name.length - DISCORD_CHOICES_MAX_NAME_SIZE;
|
||||||
@@ -31,7 +32,9 @@ export default async function autocompleteUsername(
|
|||||||
name = `${player.nickname.slice(
|
name = `${player.nickname.slice(
|
||||||
0,
|
0,
|
||||||
player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
|
player.nickname.length - overSize - OVERFLOW_SUFFIX.length,
|
||||||
)}${OVERFLOW_SUFFIX} (${REGION_NAMES_SHORT[player.region]})`;
|
)}${OVERFLOW_SUFFIX} (${translate(
|
||||||
|
`common.regions.short.${player.region}`,
|
||||||
|
)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { Locale } from 'discord.js';
|
|||||||
import { locales } from '../localization/strings';
|
import { locales } from '../localization/strings';
|
||||||
import { translator } from '../localization/translator';
|
import { translator } from '../localization/translator';
|
||||||
|
|
||||||
export function localizationObject(path: string) {
|
export function localizationObject(path: string, literals?: string[]) {
|
||||||
return locales.reduce<Partial<Record<Locale, string>>>(
|
return locales.reduce<Partial<Record<Locale, string>>>(
|
||||||
(localizations, locale) => {
|
(localizations, locale) => {
|
||||||
const { translate } = translator(locale);
|
const { translate } = translator(locale);
|
||||||
return {
|
return {
|
||||||
...localizations,
|
...localizations,
|
||||||
[locale]: translate(path),
|
[locale]: translate(path, literals),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
|||||||
@@ -101,8 +101,15 @@ export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
|
|||||||
|
|
||||||
export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
|
export const commands = Promise.allSettled(COMMANDS_RAW).then((rawCommands) => {
|
||||||
return rawCommands.reduce<Record<string, CommandRegistry>>(
|
return rawCommands.reduce<Record<string, CommandRegistry>>(
|
||||||
(commands, registry) => {
|
(commands, registry, index) => {
|
||||||
if (registry.status === 'rejected') return commands;
|
if (registry.status === 'rejected') {
|
||||||
|
console.warn(
|
||||||
|
`Command ${index} failed to load; skipping...`,
|
||||||
|
registry.reason,
|
||||||
|
);
|
||||||
|
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
if (isDev()) registry.value.command.setDefaultMemberPermissions(0);
|
if (isDev()) registry.value.command.setDefaultMemberPermissions(0);
|
||||||
return { ...commands, [registry.value.command.name]: registry.value };
|
return { ...commands, [registry.value.command.name]: registry.value };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,6 +23,27 @@
|
|||||||
"researchable": "Tech tree",
|
"researchable": "Tech tree",
|
||||||
"premium": "Premium",
|
"premium": "Premium",
|
||||||
"collector": "Collector"
|
"collector": "Collector"
|
||||||
|
},
|
||||||
|
|
||||||
|
"regions": {
|
||||||
|
"normal": {
|
||||||
|
"com": "North America",
|
||||||
|
"eu": "Europe",
|
||||||
|
"asia": "Asia"
|
||||||
|
},
|
||||||
|
"short": {
|
||||||
|
"com": "NA",
|
||||||
|
"eu": "EU",
|
||||||
|
"asia": "ASIA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"leagues": {
|
||||||
|
"diamond": "Diamond",
|
||||||
|
"platinum": "Platinum",
|
||||||
|
"gold": "Gold",
|
||||||
|
"silver": "Silver",
|
||||||
|
"bronze": "Bronze"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -109,6 +130,11 @@
|
|||||||
"$": "tree-type",
|
"$": "tree-type",
|
||||||
"description": "Tree type"
|
"description": "Tree type"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"region": {
|
||||||
|
"$": "region",
|
||||||
|
"description": "Region"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -261,6 +287,105 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"link": {
|
||||||
|
"$": "link",
|
||||||
|
"description": "Links your Blitz and Discord account",
|
||||||
|
|
||||||
|
"body": {
|
||||||
|
"accounts_linked": "Accounts linked",
|
||||||
|
"no_role_permissions": {
|
||||||
|
"$": "%s failed to verify",
|
||||||
|
"description": "I don't have the permission to change your manage roles."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"embed": {
|
||||||
|
"title": "Incorrect account?",
|
||||||
|
"description": "Use the command again and select a username from the search results."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"search_clans": {
|
||||||
|
"$": "search-clans",
|
||||||
|
"description": "Search clans",
|
||||||
|
|
||||||
|
"options": {
|
||||||
|
"limit": {
|
||||||
|
"$": "limit",
|
||||||
|
"description": "The max number of clan results (default: %s)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"body": {
|
||||||
|
"title": "Clan search for %s in region %s",
|
||||||
|
"no_results": "No clans found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"search_players": {
|
||||||
|
"$": "search-players",
|
||||||
|
"description": "Search players",
|
||||||
|
|
||||||
|
"options": {
|
||||||
|
"limit": {
|
||||||
|
"$": "limit",
|
||||||
|
"description": "The max number of player results (default: %s)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"body": {
|
||||||
|
"title": "Player search for %s in region %s",
|
||||||
|
"no_results": "No players found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ratings": {
|
||||||
|
"$": "ratings",
|
||||||
|
"description": "Ratings battles statistics",
|
||||||
|
|
||||||
|
"subcommands": {
|
||||||
|
"neighbors": {
|
||||||
|
"$": "neighbors",
|
||||||
|
"description": "Neighboring players on the leaderboard"
|
||||||
|
},
|
||||||
|
|
||||||
|
"league": {
|
||||||
|
"$": "league",
|
||||||
|
"description": "Top players of a league"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"options": {
|
||||||
|
"limit": {
|
||||||
|
"$": "limit",
|
||||||
|
"description": "How many neighbors to display (default: %s)"
|
||||||
|
},
|
||||||
|
|
||||||
|
"season": {
|
||||||
|
"$": "season",
|
||||||
|
"description": "Season number (default: current)",
|
||||||
|
|
||||||
|
"choices": {
|
||||||
|
"current": "%s (current)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"league": {
|
||||||
|
"$": "league",
|
||||||
|
"description": "The league to display"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"body": {
|
||||||
|
"top_players": "Top %s players",
|
||||||
|
"no_participation": "# %s didn't play ratings in season %s\n\nThis player did not participate in this season or did not get past calibration.",
|
||||||
|
"no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running ratings season.",
|
||||||
|
"deleted_player": "Deleted %s",
|
||||||
|
"neighbors": "Ratings neighbors",
|
||||||
|
"more": "+%s more"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user