From 2cabcc57f43690784c1a143b1f7fa3ffc7466be9 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Mon, 31 Jul 2023 01:32:31 +0000 Subject: [PATCH 01/36] feat: moved ratings out of preview --- package.json | 2 +- src/commands/ratings.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 0430bdf53..7b94e956d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blitzkrieg", - "version": "1.10.2", + "version": "1.12.0", "description": "", "main": "src/server.ts", "type": "module", diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 948440aec..9a782f21f 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -123,7 +123,6 @@ export const ratingsCommand: CommandRegistry = { inProduction: true, inDevelopment: true, inPublic: true, - inPreview: true, command: new SlashCommandBuilder() .setName('ratings') From 23dd088466e6091b20f0e7ab8be6238977aaa81a Mon Sep 17 00:00:00 2001 From: tresabhi Date: Fri, 4 Aug 2023 02:13:21 +0000 Subject: [PATCH 02/36] feat: better subcommand organiztion --- .github/workflows/cd-host.yml | 2 +- package.dist.json | 5 +- package.json | 2 +- scripts/build.ts | 9 +- src/commands/evolution.tsx | 138 +-- src/commands/fullStats.tsx | 134 +-- src/commands/ratings.tsx | 3 +- src/commands/stats.tsx | 120 +-- src/core/blitz/tankopedia.ts | 19 + src/core/blitzstars/getPeriodStart.ts | 4 +- src/core/discord/addPeriodSubCommands.ts | 37 +- .../discord/addStatTypeSubCommandGroups.ts | 61 +- src/core/discord/resolvePeriodFromCommand.ts | 14 +- src/events/interactionCreate/index.ts | 117 ++- yarn.lock | 951 +++++++----------- 15 files changed, 749 insertions(+), 867 deletions(-) diff --git a/.github/workflows/cd-host.yml b/.github/workflows/cd-host.yml index 76b9262d7..16f856001 100644 --- a/.github/workflows/cd-host.yml +++ b/.github/workflows/cd-host.yml @@ -29,4 +29,4 @@ jobs: unzip dist.zip - name: Run bot - run: NODE_ENV=production GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} WARGAMING_APPLICATION_ID=${{ secrets.WARGAMING_APPLICATION_ID }} node --enable-source-maps --trace-warnings bot.cjs + run: NODE_ENV=production GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} WARGAMING_APPLICATION_ID=${{ secrets.WARGAMING_APPLICATION_ID }} node --enable-source-maps --trace-warnings bot.js diff --git a/package.dist.json b/package.dist.json index 8c2a9203f..fedbb602d 100644 --- a/package.dist.json +++ b/package.dist.json @@ -1,6 +1,7 @@ { - "main": "server.cjs", + "main": "server.js", + "type": "module", "scripts": { - "start": "node --enable-source-maps --trace-warnings server.cjs" + "start": "node --enable-source-maps --trace-warnings server.js" } } diff --git a/package.json b/package.json index 7b94e956d..8fd18ffc0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@radix-ui/colors": "^0.1.8", "@resvg/resvg-js": "^2.4.1", "bepaint": "^0.1.0-beta.3", - "discord.js": "^14.9.0", + "discord.js": "^14.12.0", "esbuild": "^0.18.10", "escape-html": "^1.0.3", "express": "^4.18.2", diff --git a/scripts/build.ts b/scripts/build.ts index db54175b2..9ba893d13 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -1,5 +1,5 @@ import { BuildOptions, build } from 'esbuild'; -import { copyFile, mkdir, rm } from 'fs/promises'; +import { copyFile, mkdirSync, rmSync } from 'fs'; import { argv } from 'process'; const isProd = !argv.includes('--dev'); @@ -10,15 +10,15 @@ const buildServer = argv.includes('--build=server') || buildAll; if (isProd) { // only remove in production to ensure no time is wasted in dev console.log('Removing dist...'); - await rm('dist', { recursive: true, force: true }); + rmSync('dist', { recursive: true, force: true }); console.log('Dist removed'); console.log('Creating dist...'); - await mkdir('dist'); + mkdirSync('dist'); console.log('Dist created'); console.log('Copying package.json...'); - copyFile('package.dist.json', 'dist/package.json').then(() => + copyFile('package.dist.json', 'dist/package.json', () => console.log('package.json copied'), ); } @@ -29,6 +29,7 @@ const commonOptions: BuildOptions = { '.node': 'copy', '.ttf': 'file', }, + format: 'cjs', bundle: true, sourcemap: true, diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 9354f2689..5da0c83b5 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -13,75 +13,81 @@ import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand'; import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { secrets } from '../core/node/secrets'; -import { CommandRegistry } from '../events/interactionCreate'; +import { CommandRegistryRaw } from '../events/interactionCreate'; import evolution from '../renderers/evolution'; import { StatType } from '../renderers/fullStats'; import { AccountInfo } from '../types/accountInfo'; -export const evolutionCommand: CommandRegistry = { - inProduction: true, - inDevelopment: false, - inPublic: true, - - command: addStatTypeSubCommandGroups( - new SlashCommandBuilder() - .setName('evolution') - .setDescription('Evolution of statistics'), - ), - - async handler(interaction) { - const commandGroup = interaction.options.getSubcommandGroup( - true, - ) as StatType; - const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(player.region, interaction); - const tankIdRaw = interaction.options.getString('tank')!; - const tankId = - commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; - const start = interaction.options.getInteger('start'); - const end = interaction.options.getInteger('end'); - const { nickname } = ( - await getWargamingResponse( - `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`, - ) - )[player.id]; - const path = interactionToURL(interaction, { - ...player, - tankId, - start, - end, - }); - - return [ - await evolution(commandGroup, period, player, tankId), - primaryButton(path, 'Refresh'), - // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), - linkButton( - `https://www.blitzstars.com/player/${player.region}/${nickname}${ - commandGroup === 'tank' ? `/tank/${tankId!}` : '' - }`, - 'BlitzStars', - ), - ]; - }, - - autocomplete: (interaction) => { - autocompleteUsername(interaction); - autocompleteTanks(interaction); - }, - - async button(interaction) { - const url = new URL(`${CYCLIC_API}/${interaction.customId}`); - const path = url.pathname.split('/').filter(Boolean); - const commandGroup = path[1] as StatType; - const player = await resolvePlayerFromButton(interaction); - const period = resolvePeriodFromButton(player.region, interaction); - - return await evolution( - commandGroup, - period, - player, - parseInt(url.searchParams.get('tankId')!), +export const evolutionCommand = new Promise( + async (resolve) => { + const command = await addStatTypeSubCommandGroups( + new SlashCommandBuilder() + .setName('evolution') + .setDescription('Evolution of statistics'), ); + + resolve({ + inProduction: true, + inDevelopment: false, + inPublic: true, + + command, + + async handler(interaction) { + const commandGroup = interaction.options.getSubcommandGroup( + true, + ) as StatType; + const player = await resolvePlayerFromCommand(interaction); + const period = resolvePeriodFromCommand(player.region, interaction); + const tankIdRaw = interaction.options.getString('tank')!; + const tankId = + commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; + const start = interaction.options.getInteger('start'); + const end = interaction.options.getInteger('end'); + const { nickname } = ( + await getWargamingResponse( + `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`, + ) + )[player.id]; + const path = interactionToURL(interaction, { + ...player, + tankId, + start, + end, + }); + + return [ + await evolution(commandGroup, period, player, tankId), + primaryButton(path, 'Refresh'), + // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), + linkButton( + `https://www.blitzstars.com/player/${player.region}/${nickname}${ + commandGroup === 'tank' ? `/tank/${tankId!}` : '' + }`, + 'BlitzStars', + ), + ]; + }, + + autocomplete: (interaction) => { + autocompleteUsername(interaction); + autocompleteTanks(interaction); + }, + + async button(interaction) { + const url = new URL(`${CYCLIC_API}/${interaction.customId}`); + const path = url.pathname.split('/').filter(Boolean); + const commandGroup = path[1] as StatType; + const player = await resolvePlayerFromButton(interaction); + const period = resolvePeriodFromButton(player.region, interaction); + + return await evolution( + commandGroup, + period, + player, + parseInt(url.searchParams.get('tankId')!), + ); + }, + }); }, -}; +); diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 8b30f58d9..83a3ef076 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -13,72 +13,78 @@ import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand'; import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { secrets } from '../core/node/secrets'; -import { CommandRegistry } from '../events/interactionCreate'; +import { CommandRegistryRaw } from '../events/interactionCreate'; import fullStats, { StatType } from '../renderers/fullStats'; import { AccountInfo } from '../types/accountInfo'; -export const fullStatsCommand: CommandRegistry = { - inProduction: true, - inDevelopment: false, - inPublic: true, - - command: addStatTypeSubCommandGroups( - new SlashCommandBuilder() - .setName('fullstats') - .setDescription('Full in-game statistics'), - ), - - async handler(interaction) { - const commandGroup = interaction.options.getSubcommandGroup( - true, - ) as StatType; - const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(player.region, interaction); - const tankIdRaw = interaction.options.getString('tank')!; - const tankId = - commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; - const start = interaction.options.getInteger('start'); - const end = interaction.options.getInteger('end'); - const path = interactionToURL(interaction, { - ...player, - tankId, - start, - end, - }); - const { nickname } = ( - await getWargamingResponse( - `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`, - ) - )[player.id]; - - return [ - await fullStats(commandGroup, period, player, tankId), - primaryButton(path, 'Refresh'), - // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), - linkButton( - `https://www.blitzstars.com/player/${player.region}/${nickname}`, - 'BlitzStars', - ), - ]; - }, - - autocomplete: (interaction) => { - autocompleteUsername(interaction); - autocompleteTanks(interaction); - }, - - async button(interaction) { - const url = new URL(`${CYCLIC_API}/${interaction.customId}`); - const path = url.pathname.split('/').filter(Boolean); - const commandGroup = path[1] as StatType; - const player = await resolvePlayerFromButton(interaction); - const period = resolvePeriodFromButton(player.region, interaction); - - return await fullStats( - commandGroup, - period, - player, - parseInt(url.searchParams.get('tankId')!), +export const fullStatsCommand = new Promise( + async (resolve) => { + const command = await addStatTypeSubCommandGroups( + new SlashCommandBuilder() + .setName('fullstats') + .setDescription('Full in-game statistics'), ); + + resolve({ + inProduction: true, + inDevelopment: false, + inPublic: true, + + command, + + async handler(interaction) { + const commandGroup = interaction.options.getSubcommandGroup( + true, + ) as StatType; + const player = await resolvePlayerFromCommand(interaction); + const period = resolvePeriodFromCommand(player.region, interaction); + const tankIdRaw = interaction.options.getString('tank')!; + const tankId = + commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; + const start = interaction.options.getInteger('start'); + const end = interaction.options.getInteger('end'); + const path = interactionToURL(interaction, { + ...player, + tankId, + start, + end, + }); + const { nickname } = ( + await getWargamingResponse( + `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`, + ) + )[player.id]; + + return [ + await fullStats(commandGroup, period, player, tankId), + primaryButton(path, 'Refresh'), + // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), + linkButton( + `https://www.blitzstars.com/player/${player.region}/${nickname}`, + 'BlitzStars', + ), + ]; + }, + + autocomplete: (interaction) => { + autocompleteUsername(interaction); + autocompleteTanks(interaction); + }, + + async button(interaction) { + const url = new URL(`${CYCLIC_API}/${interaction.customId}`); + const path = url.pathname.split('/').filter(Boolean); + const commandGroup = path[1] as StatType; + const player = await resolvePlayerFromButton(interaction); + const period = resolvePeriodFromButton(player.region, interaction); + + return await fullStats( + commandGroup, + period, + player, + parseInt(url.searchParams.get('tankId')!), + ); + }, + }); }, -}; +); diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 9a782f21f..98eac5b01 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -121,7 +121,7 @@ export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; export const ratingsCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() @@ -165,7 +165,6 @@ export const ratingsCommand: CommandRegistry = { ), ), ), - async handler(interaction) { const subcommandGroup = interaction.options.getSubcommandGroup(true); diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index a366eeb93..cf6ca65f5 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -13,73 +13,77 @@ import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand'; import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { secrets } from '../core/node/secrets'; -import { CommandRegistry } from '../events/interactionCreate'; +import { CommandRegistryRaw } from '../events/interactionCreate'; import { StatType } from '../renderers/fullStats'; import stats from '../renderers/stats'; import { AccountInfo } from '../types/accountInfo'; -export const statsCommand: CommandRegistry = { - inProduction: true, - inDevelopment: false, - inPublic: true, - - command: addStatTypeSubCommandGroups( +export const statsCommand = new Promise(async (resolve) => { + const command = await addStatTypeSubCommandGroups( new SlashCommandBuilder() .setName('stats') .setDescription('Regular battles statistics'), - ), + ); - async handler(interaction) { - const commandGroup = interaction.options.getSubcommandGroup( - true, - ) as StatType; - const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(player.region, interaction); - const tankIdRaw = interaction.options.getString('tank')!; - const tankId = - commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; - const start = interaction.options.getInteger('start'); - const end = interaction.options.getInteger('end'); - const path = interactionToURL(interaction, { - ...player, - tankId, - start, - end, - }); - const { nickname } = ( - await getWargamingResponse( - `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`, - ) - )[player.id]; + resolve({ + inProduction: true, + inDevelopment: true, + inPublic: true, - return [ - await stats(commandGroup, period, player, tankId), - primaryButton(path, 'Refresh'), - linkButton(`${CYCLIC_API}/${path}`, 'Embed'), - linkButton( - `https://www.blitzstars.com/player/${player.region}/${nickname}`, - 'BlitzStars', - ), - ]; - }, + command, - autocomplete: (interaction) => { - autocompleteUsername(interaction); - autocompleteTanks(interaction); - }, + async handler(interaction) { + const commandGroup = interaction.options.getSubcommandGroup( + true, + ) as StatType; + const player = await resolvePlayerFromCommand(interaction); + const period = resolvePeriodFromCommand(player.region, interaction); + const tankIdRaw = interaction.options.getString('tank')!; + const tankId = + commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; + const start = interaction.options.getInteger('start'); + const end = interaction.options.getInteger('end'); + const path = interactionToURL(interaction, { + ...player, + tankId, + start, + end, + }); + const { nickname } = ( + await getWargamingResponse( + `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`, + ) + )[player.id]; - async button(interaction) { - const url = new URL(`${CYCLIC_API}/${interaction.customId}`); - const path = url.pathname.split('/').filter(Boolean); - const commandGroup = path[1] as StatType; - const player = await resolvePlayerFromButton(interaction); - const period = resolvePeriodFromButton(player.region, interaction); + return [ + await stats(commandGroup, period, player, tankId), + primaryButton(path, 'Refresh'), + linkButton(`${CYCLIC_API}/${path}`, 'Embed'), + linkButton( + `https://www.blitzstars.com/player/${player.region}/${nickname}`, + 'BlitzStars', + ), + ]; + }, - return await stats( - commandGroup, - period, - player, - parseInt(url.searchParams.get('tankId')!), - ); - }, -}; + autocomplete: (interaction) => { + autocompleteUsername(interaction); + autocompleteTanks(interaction); + }, + + async button(interaction) { + const url = new URL(`${CYCLIC_API}/${interaction.customId}`); + const path = url.pathname.split('/').filter(Boolean); + const commandGroup = path[1] as StatType; + const player = await resolvePlayerFromButton(interaction); + const period = resolvePeriodFromButton(player.region, interaction); + + return await stats( + commandGroup, + period, + player, + parseInt(url.searchParams.get('tankId')!), + ); + }, + }); +}); diff --git a/src/core/blitz/tankopedia.ts b/src/core/blitz/tankopedia.ts index 557f3a4e6..1a51edbe8 100644 --- a/src/core/blitz/tankopedia.ts +++ b/src/core/blitz/tankopedia.ts @@ -1,3 +1,6 @@ +import { secrets } from '../node/secrets'; +import getWargamingResponse from './getWargamingResponse'; + export interface TankopediaEntry { name: string; nation: string; @@ -32,6 +35,22 @@ export const TANK_NAMES = new Promise(async (resolve) => { resolve((await entries).map(({ name }) => name)); }); +export interface TankopediaInfo { + achievement_sections: Record; + tanks_updated_at: number; + languages: Record; + vehicle_types: Record; + vehicle_nations: Record; + game_version: string; +} + +// this is blocking because info is needed for command creation +console.log('Caching tankopedia info...'); +export const tankopediaInfo = getWargamingResponse( + `https://api.wotblitz.com/wotb/encyclopedia/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}`, +); +console.log('Cached tankopedia info'); + export const TANK_ICONS: Record = { 'AT-SPG': 'https://i.imgur.com/BIHSEH0.png', lightTank: 'https://i.imgur.com/CSNha5V.png', diff --git a/src/core/blitzstars/getPeriodStart.ts b/src/core/blitzstars/getPeriodStart.ts index 1ad1ff478..60cf2099e 100644 --- a/src/core/blitzstars/getPeriodStart.ts +++ b/src/core/blitzstars/getPeriodStart.ts @@ -4,13 +4,13 @@ import getTimeDaysAgo from './getTimeDaysAgo'; export default function getPeriodStart( server: Region, - period: Exclude, + period: Exclude, ) { if (period === 'career') { return -Infinity; } else if (period === 'today') { return getTimeDaysAgo(server, 1); } else { - return getTimeDaysAgo(server, parseInt(period)); + return getTimeDaysAgo(server, parseInt(period.match(/\d+/)?.[0]!)); } } diff --git a/src/core/discord/addPeriodSubCommands.ts b/src/core/discord/addPeriodSubCommands.ts index 35d7a4dda..32020e707 100644 --- a/src/core/discord/addPeriodSubCommands.ts +++ b/src/core/discord/addPeriodSubCommands.ts @@ -4,7 +4,7 @@ import { } from 'discord.js'; import { PERIOD_NAMES } from '../discord/resolvePeriodFromCommand'; -export type Period = 'today' | '30' | '60' | '90' | 'career' | 'custom'; +export type Period = 'period' | 'customperiod'; export default function addPeriodSubCommands( option: SlashCommandSubcommandGroupBuilder, @@ -14,25 +14,30 @@ export default function addPeriodSubCommands( ) { return option .addSubcommand((option) => - extra(option.setName('today').setDescription(PERIOD_NAMES.today)), - ) - .addSubcommand((option) => - extra(option.setName('30').setDescription(PERIOD_NAMES[30])), - ) - .addSubcommand((option) => - extra(option.setName('60').setDescription(PERIOD_NAMES[60])), - ) - .addSubcommand((option) => - extra(option.setName('90').setDescription(PERIOD_NAMES[90])), - ) - .addSubcommand((option) => - extra(option.setName('career').setDescription(PERIOD_NAMES.career)), + extra( + option + .setName('period' satisfies Period) + .setDescription(PERIOD_NAMES.period) + .addStringOption((option) => + option + .setName('days') + .setDescription('A preset period') + .setChoices( + { name: 'Today', value: '1' }, + { name: '30 days', value: '30' }, + { name: '60 days', value: '60' }, + { name: '90 days', value: '90' }, + { name: 'Career', value: 'Infinity' }, + ) + .setRequired(true), + ), + ), ) .addSubcommand((option) => extra( option - .setName('custom') - .setDescription(PERIOD_NAMES.custom) + .setName('customperiod' satisfies Period) + .setDescription(PERIOD_NAMES.customperiod) .addIntegerOption((option) => option .setName('start') diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommandGroups.ts index 66fe7f586..c96518e83 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommandGroups.ts @@ -1,18 +1,21 @@ import { SlashCommandBuilder } from 'discord.js'; +import { tankopediaInfo } from '../blitz/tankopedia'; import addPeriodSubCommands from './addPeriodSubCommands'; import addTankChoices from './addTankChoices'; import addUsernameChoices from './addUsernameChoices'; -export default function addStatTypeSubCommandGroups( +export default async function addStatTypeSubCommandGroups( option: SlashCommandBuilder, ) { + const awaitedTankopediaInfo = await tankopediaInfo; + return option .addSubcommandGroup((option) => addPeriodSubCommands(option, (option) => option.addStringOption(addUsernameChoices), ) .setName('player') - .setDescription("Player's statistics"), + .setDescription('Player statistics'), ) .addSubcommandGroup((option) => addPeriodSubCommands(option, (option) => @@ -21,6 +24,58 @@ export default function addStatTypeSubCommandGroups( .addStringOption(addUsernameChoices), ) .setName('tank') - .setDescription("Tank's statistics"), + .setDescription('Tank statistics'), + ) + .addSubcommandGroup((option) => + addPeriodSubCommands(option, (option) => + option + .addStringOption((option) => { + option + .setName('nation') + .setDescription('Nation') + .addChoices( + ...Object.entries(awaitedTankopediaInfo.vehicle_nations).map( + ([type, name]) => ({ name, value: type }), + ), + ); + + return option; + }) + .addStringOption((option) => + option + .setName('tanktype') + .setDescription('Type') + .addChoices( + ...Object.entries(awaitedTankopediaInfo.vehicle_types).map( + ([type, name]) => ({ + name: name.replace(' Tank', ''), + value: type, + }), + ), + ) + .setRequired(false), + ) + .addIntegerOption((option) => + option + .setName('tier') + .setDescription('Tier') + .setMinValue(1) + .setMaxValue(10) + .setRequired(false), + ) + .addStringOption((option) => + option + .setName('treetype') + .setDescription('Tech tree type') + .addChoices( + { name: 'Tech Tree', value: 'techtree' }, + { name: 'Premium', value: 'premium' }, + { name: 'Collector', value: 'collector' }, + ) + .setRequired(false), + ), + ) + .setName('tanks') + .setDescription('Multiple tanks'), ); } diff --git a/src/core/discord/resolvePeriodFromCommand.ts b/src/core/discord/resolvePeriodFromCommand.ts index 95210715d..65819e16b 100644 --- a/src/core/discord/resolvePeriodFromCommand.ts +++ b/src/core/discord/resolvePeriodFromCommand.ts @@ -7,20 +7,16 @@ import { Period } from '../discord/addPeriodSubCommands'; export const PERIOD_NAMES: Record = { today: "Today's statistics", - 30: "30 days' statistics", - 60: "60 days' statistics", - 90: "90 days' statistics", + period: "Set number of days' statistics", career: 'Career statistics', - custom: 'Custom period', + customperiod: 'Custom period', }; export const EVOLUTION_PERIOD_NAMES: Record = { today: "Today's evolution", - 30: "30 days' evolution", - 60: "60 days' evolution", - 90: "90 days' evolution", + period: "Set number of days' evolution", career: 'Career evolution', - custom: 'Custom period', + customperiod: 'Custom period', }; export interface ResolvedPeriod { @@ -40,7 +36,7 @@ export default function resolvePeriodFromCommand( let end: number; const period = interaction.options.getSubcommand() as Period; - if (period === 'custom') { + if (period === 'customperiod') { const startRaw = interaction.options.getInteger('start')!; const endRaw = interaction.options.getInteger('end')!; const startMin = Math.min(startRaw, endRaw); diff --git a/src/events/interactionCreate/index.ts b/src/events/interactionCreate/index.ts index b1d0ab354..6664952cc 100644 --- a/src/events/interactionCreate/index.ts +++ b/src/events/interactionCreate/index.ts @@ -54,22 +54,30 @@ export interface Registry { inProduction: boolean; } -export interface CommandRegistry - extends Registry { +export type CommandRegistryRaw = Registry & { inPublic: boolean; inPreview?: boolean; - handlesInteraction?: HandlesInteraction; + command: | SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder | Omit; - handler: ( - interaction: ChatInputCommandInteraction, - ) => HandlesInteraction extends true ? void : InteractionReturnable; autocomplete?: (interaction: AutocompleteInteraction) => void; button?: (interaction: ButtonInteraction) => InteractionReturnable; -} +} & ( + | { + handlesInteraction: true; + handler: (interaction: ChatInputCommandInteraction) => void; + } + | { + handlesInteraction?: false; + handler: ( + interaction: ChatInputCommandInteraction, + ) => InteractionReturnable; + } + ); +export type CommandRegistry = CommandRegistryRaw | Promise; const rest = new REST().setToken(secrets.DISCORD_TOKEN); @@ -92,12 +100,19 @@ export const COMMANDS_RAW: CommandRegistry[] = [ statsCommand, ratingsCommand, ]; -export const commands: Record = COMMANDS_RAW.reduce( - (accumulator, registry) => { - if (isDev()) registry.command.setDefaultMemberPermissions(0); - return { ...accumulator, [registry.command.name]: registry }; - }, - {}, + +export const commands = Promise.all(COMMANDS_RAW).then((rawCommands) => + rawCommands.reduce>( + (accumulator, registry) => { + if (isDev()) registry.command.setDefaultMemberPermissions(0); + + return { + ...accumulator, + [registry.command.name]: registry, + }; + }, + {}, + ), ); export const guildCommands: RESTPostAPIChatInputApplicationCommandsJSONBody[] = @@ -105,48 +120,50 @@ export const guildCommands: RESTPostAPIChatInputApplicationCommandsJSONBody[] = export const publicCommands: RESTPostAPIChatInputApplicationCommandsJSONBody[] = []; -Object.entries(commands).forEach(([, registry]) => { - if (isDev() ? registry.inDevelopment : registry.inProduction) { - const json = registry.command.toJSON(); +commands.then((awaitedCommands) => { + Object.entries(awaitedCommands).forEach(([, registry]) => { + if (isDev() ? registry.inDevelopment : registry.inProduction) { + const json = registry.command.toJSON(); - if (registry.inPublic) { - publicCommands.push(json); - } else { - guildCommands.push(json); + if (registry.inPublic) { + publicCommands.push(json); + } else { + guildCommands.push(json); + } } + }); + + try { + console.log(`Refreshing ${publicCommands.length} public command(s).`); + rest + .put(Routes.applicationCommands(getClientId()), { body: publicCommands }) + .then((publicData) => { + console.log( + `Successfully refreshed ${ + (publicData as RESTPostAPIChatInputApplicationCommandsJSONBody[]) + .length + } public command(s).`, + ); + }); + + console.log(`Refreshing ${guildCommands.length} guild command(s).`); + rest + .put( + Routes.applicationGuildCommands(getClientId(), discord.tres_guild_id), + { body: guildCommands }, + ) + .then((guildData) => { + console.log( + `Successfully refreshed ${ + (guildData as unknown[]).length + } guild command(s).`, + ); + }); + } catch (error) { + console.error(error); } }); -try { - console.log(`Refreshing ${publicCommands.length} public command(s).`); - rest - .put(Routes.applicationCommands(getClientId()), { body: publicCommands }) - .then((publicData) => { - console.log( - `Successfully refreshed ${ - (publicData as RESTPostAPIChatInputApplicationCommandsJSONBody[]) - .length - } public command(s).`, - ); - }); - - console.log(`Refreshing ${guildCommands.length} guild command(s).`); - rest - .put( - Routes.applicationGuildCommands(getClientId(), discord.tres_guild_id), - { body: guildCommands }, - ) - .then((guildData) => { - console.log( - `Successfully refreshed ${ - (guildData as unknown[]).length - } guild command(s).`, - ); - }); -} catch (error) { - console.error(error); -} - export default async function interactionCreate( interaction: Interaction, ) { diff --git a/yarn.lock b/yarn.lock index 0ebee1b61..3aab71936 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,132 +29,132 @@ resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz#2e22e830d36f0a9cf2c0ccd3c7f6d59435b77dfa" integrity sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ== -"@algolia/cache-browser-local-storage@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.18.0.tgz#7bc0c9d8d346ed01384f4cf0dfaf6ba29ad5c20c" - integrity sha512-rUAs49NLlO8LVLgGzM4cLkw8NJLKguQLgvFmBEe3DyzlinoqxzQMHfKZs6TSq4LZfw/z8qHvRo8NcTAAUJQLcw== +"@algolia/cache-browser-local-storage@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.19.1.tgz#d29f42775ed4d117182897ac164519c593faf399" + integrity sha512-FYAZWcGsFTTaSAwj9Std8UML3Bu8dyWDncM7Ls8g+58UOe4XYdlgzXWbrIgjaguP63pCCbMoExKr61B+ztK3tw== dependencies: - "@algolia/cache-common" "4.18.0" + "@algolia/cache-common" "4.19.1" -"@algolia/cache-common@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.18.0.tgz#aac33afac53e191c595d14a4bb7e6d81aae4836f" - integrity sha512-BmxsicMR4doGbeEXQu8yqiGmiyvpNvejYJtQ7rvzttEAMxOPoWEHrWyzBQw4x7LrBY9pMrgv4ZlUaF8PGzewHg== +"@algolia/cache-common@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.19.1.tgz#faa5eeacaffd6023c2cf26e9866bdb06193f9b26" + integrity sha512-XGghi3l0qA38HiqdoUY+wvGyBsGvKZ6U3vTiMBT4hArhP3fOGLXpIINgMiiGjTe4FVlTa5a/7Zf2bwlIHfRqqg== -"@algolia/cache-in-memory@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.18.0.tgz#9a40294c734819724a1b4e86afd5a7d4be9bcc2f" - integrity sha512-evD4dA1nd5HbFdufBxLqlJoob7E2ozlqJZuV3YlirNx5Na4q1LckIuzjNYZs2ddLzuTc/Xd5O3Ibf7OwPskHxw== +"@algolia/cache-in-memory@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.19.1.tgz#afe4f0f21149800358379871089e0141fb72415b" + integrity sha512-+PDWL+XALGvIginigzu8oU6eWw+o76Z8zHbBovWYcrtWOEtinbl7a7UTt3x3lthv+wNuFr/YD1Gf+B+A9V8n5w== dependencies: - "@algolia/cache-common" "4.18.0" + "@algolia/cache-common" "4.19.1" -"@algolia/client-account@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.18.0.tgz#202d9e06b41e06e45cb919107bed0a65106883cc" - integrity sha512-XsDnlROr3+Z1yjxBJjUMfMazi1V155kVdte6496atvBgOEtwCzTs3A+qdhfsAnGUvaYfBrBkL0ThnhMIBCGcew== +"@algolia/client-account@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.19.1.tgz#1fa65881baab79ad35af6bcf44646a13b8d5edc9" + integrity sha512-Oy0ritA2k7AMxQ2JwNpfaEcgXEDgeyKu0V7E7xt/ZJRdXfEpZcwp9TOg4TJHC7Ia62gIeT2Y/ynzsxccPw92GA== dependencies: - "@algolia/client-common" "4.18.0" - "@algolia/client-search" "4.18.0" - "@algolia/transporter" "4.18.0" + "@algolia/client-common" "4.19.1" + "@algolia/client-search" "4.19.1" + "@algolia/transporter" "4.19.1" -"@algolia/client-analytics@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.18.0.tgz#030b026bd9c13cb15437e35e4456bde25b0f1298" - integrity sha512-chEUSN4ReqU7uRQ1C8kDm0EiPE+eJeAXiWcBwLhEynfNuTfawN9P93rSZktj7gmExz0C8XmkbBU19IQ05wCNrQ== +"@algolia/client-analytics@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.19.1.tgz#e6ed79acd4de5a0284c9696bf4e1c25278ba34db" + integrity sha512-5QCq2zmgdZLIQhHqwl55ZvKVpLM3DNWjFI4T+bHr3rGu23ew2bLO4YtyxaZeChmDb85jUdPDouDlCumGfk6wOg== dependencies: - "@algolia/client-common" "4.18.0" - "@algolia/client-search" "4.18.0" - "@algolia/requester-common" "4.18.0" - "@algolia/transporter" "4.18.0" + "@algolia/client-common" "4.19.1" + "@algolia/client-search" "4.19.1" + "@algolia/requester-common" "4.19.1" + "@algolia/transporter" "4.19.1" -"@algolia/client-common@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.18.0.tgz#e080c393e1becdd5f5f008815c57d3248d3a8483" - integrity sha512-7N+soJFP4wn8tjTr3MSUT/U+4xVXbz4jmeRfWfVAzdAbxLAQbHa0o/POSdTvQ8/02DjCLelloZ1bb4ZFVKg7Wg== +"@algolia/client-common@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.19.1.tgz#40a8387316fa61d62ad1091beb3a8e227f008e75" + integrity sha512-3kAIVqTcPrjfS389KQvKzliC559x+BDRxtWamVJt8IVp7LGnjq+aVAXg4Xogkur1MUrScTZ59/AaUd5EdpyXgA== dependencies: - "@algolia/requester-common" "4.18.0" - "@algolia/transporter" "4.18.0" + "@algolia/requester-common" "4.19.1" + "@algolia/transporter" "4.19.1" -"@algolia/client-personalization@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.18.0.tgz#9042ce2773120158ad25e1dfb28d706cebb48dc2" - integrity sha512-+PeCjODbxtamHcPl+couXMeHEefpUpr7IHftj4Y4Nia1hj8gGq4VlIcqhToAw8YjLeCTfOR7r7xtj3pJcYdP8A== +"@algolia/client-personalization@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.19.1.tgz#fe362e0684dc74c3504c3641c5a7488c6ae02e07" + integrity sha512-8CWz4/H5FA+krm9HMw2HUQenizC/DxUtsI5oYC0Jxxyce1vsr8cb1aEiSJArQT6IzMynrERif1RVWLac1m36xw== dependencies: - "@algolia/client-common" "4.18.0" - "@algolia/requester-common" "4.18.0" - "@algolia/transporter" "4.18.0" + "@algolia/client-common" "4.19.1" + "@algolia/requester-common" "4.19.1" + "@algolia/transporter" "4.19.1" -"@algolia/client-search@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.18.0.tgz#83b37aacbe254fd7892154fe7a8f0395bd01c682" - integrity sha512-F9xzQXTjm6UuZtnsLIew6KSraXQ0AzS/Ee+OD+mQbtcA/K1sg89tqb8TkwjtiYZ0oij13u3EapB3gPZwm+1Y6g== +"@algolia/client-search@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.19.1.tgz#5e54601aa5f5cea790cec3f2cde4af9d6403871e" + integrity sha512-mBecfMFS4N+yK/p0ZbK53vrZbL6OtWMk8YmnOv1i0LXx4pelY8TFhqKoTit3NPVPwoSNN0vdSN9dTu1xr1XOVw== dependencies: - "@algolia/client-common" "4.18.0" - "@algolia/requester-common" "4.18.0" - "@algolia/transporter" "4.18.0" + "@algolia/client-common" "4.19.1" + "@algolia/requester-common" "4.19.1" + "@algolia/transporter" "4.19.1" -"@algolia/logger-common@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.18.0.tgz#0e6a14e8b91fcb7861595169e1ca57cf219f8255" - integrity sha512-46etYgSlkoKepkMSyaoriSn2JDgcrpc/nkOgou/lm0y17GuMl9oYZxwKKTSviLKI5Irk9nSKGwnBTQYwXOYdRg== +"@algolia/logger-common@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.19.1.tgz#0e46a11510f3e94e1afc0ac780ae52e9597be78f" + integrity sha512-i6pLPZW/+/YXKis8gpmSiNk1lOmYCmRI6+x6d2Qk1OdfvX051nRVdalRbEcVTpSQX6FQAoyeaui0cUfLYW5Elw== -"@algolia/logger-console@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.18.0.tgz#3636e4b3e2154ee2ee2db2e5be2857203c9f7047" - integrity sha512-3P3VUYMl9CyJbi/UU1uUNlf6Z8N2ltW3Oqhq/nR7vH0CjWv32YROq3iGWGxB2xt3aXobdUPXs6P0tHSKRmNA6g== +"@algolia/logger-console@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.19.1.tgz#656a6f4ebb5de39af6ef7095c398d9ab3cceb87d" + integrity sha512-jj72k9GKb9W0c7TyC3cuZtTr0CngLBLmc8trzZlXdfvQiigpUdvTi1KoWIb2ZMcRBG7Tl8hSb81zEY3zI2RlXg== dependencies: - "@algolia/logger-common" "4.18.0" + "@algolia/logger-common" "4.19.1" -"@algolia/requester-browser-xhr@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.18.0.tgz#90ac575946e0ab196cdd87593b3fed563a32a9af" - integrity sha512-/AcWHOBub2U4TE/bPi4Gz1XfuLK6/7dj4HJG+Z2SfQoS1RjNLshZclU3OoKIkFp8D2NC7+BNsPvr9cPLyW8nyQ== +"@algolia/requester-browser-xhr@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.19.1.tgz#7341ea2f980b8980a2976110142026721e452187" + integrity sha512-09K/+t7lptsweRTueHnSnmPqIxbHMowejAkn9XIcJMLdseS3zl8ObnS5GWea86mu3vy4+8H+ZBKkUN82Zsq/zg== dependencies: - "@algolia/requester-common" "4.18.0" + "@algolia/requester-common" "4.19.1" -"@algolia/requester-common@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.18.0.tgz#12984aa4b10679ffa863536ceeae33cdd0ee4d42" - integrity sha512-xlT8R1qYNRBCi1IYLsx7uhftzdfsLPDGudeQs+xvYB4sQ3ya7+ppolB/8m/a4F2gCkEO6oxpp5AGemM7kD27jA== +"@algolia/requester-common@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.19.1.tgz#f3396c77631b9d36e8d4d6f819a2c27f9ddbf7a1" + integrity sha512-BisRkcWVxrDzF1YPhAckmi2CFYK+jdMT60q10d7z3PX+w6fPPukxHRnZwooiTUrzFe50UBmLItGizWHP5bDzVQ== -"@algolia/requester-node-http@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.18.0.tgz#8046b141c784cc7778bcf51e8a7888cce463754b" - integrity sha512-TGfwj9aeTVgOUhn5XrqBhwUhUUDnGIKlI0kCBMdR58XfXcfdwomka+CPIgThRbfYw04oQr31A6/95ZH2QVJ9UQ== +"@algolia/requester-node-http@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.19.1.tgz#ea210de9642628b3bdda1dd7fcd1fcb686da442e" + integrity sha512-6DK52DHviBHTG2BK/Vv2GIlEw7i+vxm7ypZW0Z7vybGCNDeWzADx+/TmxjkES2h15+FZOqVf/Ja677gePsVItA== dependencies: - "@algolia/requester-common" "4.18.0" + "@algolia/requester-common" "4.19.1" -"@algolia/transporter@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.18.0.tgz#18de645c20fc5703196b2ad4fec55e98c315a1d8" - integrity sha512-xbw3YRUGtXQNG1geYFEDDuFLZt4Z8YNKbamHPkzr3rWc6qp4/BqEeXcI2u/P/oMq2yxtXgMxrCxOPA8lyIe5jw== +"@algolia/transporter@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.19.1.tgz#b5787299740c4bec9ba05502d98c14b5999860c8" + integrity sha512-nkpvPWbpuzxo1flEYqNIbGz7xhfhGOKGAZS7tzC+TELgEmi7z99qRyTfNSUlW7LZmB3ACdnqAo+9A9KFBENviQ== dependencies: - "@algolia/cache-common" "4.18.0" - "@algolia/logger-common" "4.18.0" - "@algolia/requester-common" "4.18.0" + "@algolia/cache-common" "4.19.1" + "@algolia/logger-common" "4.19.1" + "@algolia/requester-common" "4.19.1" "@babel/parser@^7.20.15", "@babel/parser@^7.21.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" - integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== -"@discordjs/builders@^1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.3.tgz#994b4fe57e77b47096f74bb5a1f664870a930a43" - integrity sha512-CTCh8NqED3iecTNuiz49mwSsrc2iQb4d0MjMdmS/8pb69Y4IlzJ/DIy/p5GFlgOrFbNO2WzMHkWKQSiJ3VNXaw== +"@discordjs/builders@^1.6.4": + version "1.6.4" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.4.tgz#d99f4e76684ef9b1c3b9e1c4d0bc17fafb19b960" + integrity sha512-ARFKvmAkLhfkQQiNxqi0YIWqwUExvBRtvdtMFVJXvJoibsGkFrB/DWTf9byU7BTVUfsmW8w7NM55tYXR5S/iSg== dependencies: "@discordjs/formatters" "^0.3.1" - "@discordjs/util" "^0.3.1" - "@sapphire/shapeshift" "^3.8.2" - discord-api-types "^0.37.41" + "@discordjs/util" "^1.0.0" + "@sapphire/shapeshift" "^3.9.2" + discord-api-types "^0.37.50" fast-deep-equal "^3.1.3" ts-mixer "^6.0.3" - tslib "^2.5.0" + tslib "^2.6.1" -"@discordjs/collection@^1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.1.tgz#bc7ca557838dc29247bf19860426637f103bc383" - integrity sha512-aWEc9DCf3TMDe9iaJoOnO2+JVAjeRNuRxPZQA6GVvBf+Z3gqUuWYBy2NWh4+5CLYq5uoc3MOvUQ5H5m8CJBqOA== +"@discordjs/collection@^1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.2.tgz#3ba34c216e920999b5075f8fdc62f70bb2a7e0fb" + integrity sha512-LDplPy8SPbc8MYkuCdnLRGWqygAX97E8NH7gA9uz+NZ/hXknUKJHuxsOmhC6pmHnF9Zmg0kvfwrDjGsRIljt9g== "@discordjs/formatters@^0.3.1": version "0.3.1" @@ -163,46 +163,47 @@ dependencies: discord-api-types "^0.37.41" -"@discordjs/rest@^1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.7.1.tgz#eeef0e71a37c95fa27962129729b2aa9de8e3752" - integrity sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ== +"@discordjs/rest@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.0.0.tgz#d82d4035d93bc860f9f34b07af3178dc12a296c4" + integrity sha512-CW9ldfzsRzUbHcS4Oqu5+Moo+yrQ5qQ9groKNxPOzcoq2nuXa/fXOXkuQtQHcTeSVXsC9cmJ56M8gBDBUyLgGA== dependencies: - "@discordjs/collection" "^1.5.1" - "@discordjs/util" "^0.3.0" + "@discordjs/collection" "^1.5.2" + "@discordjs/util" "^1.0.0" "@sapphire/async-queue" "^1.5.0" - "@sapphire/snowflake" "^3.4.2" - discord-api-types "^0.37.41" - file-type "^18.3.0" - tslib "^2.5.0" - undici "^5.22.0" + "@sapphire/snowflake" "^3.5.1" + "@vladfrangu/async_event_emitter" "^2.2.2" + discord-api-types "^0.37.50" + magic-bytes.js "^1.0.15" + tslib "^2.6.1" + undici "^5.22.1" -"@discordjs/util@^0.3.0", "@discordjs/util@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-0.3.1.tgz#4e8737e1dcff7e9f5eccc3116fb44755b65b1e97" - integrity sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA== +"@discordjs/util@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.0.0.tgz#8b4d3756ee725f6fc1a4999834d6ca7c4a353837" + integrity sha512-U2Iiab0mo8cFe+o4ZY4GROoAetGjFYA1PhhxiXEW82LuPUjOU/seHZDtVjDpOf6n3rz4IRm84wNtgHdpqRY5CA== -"@discordjs/ws@^0.8.3": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-0.8.3.tgz#77db8d563b731a2198c1b40f63b1ef8d230504f7" - integrity sha512-hcYtppanjHecbdNyCKQNH2I4RP9UrphDgmRgLYrATEQF1oo4sYSve7ZmGsBEXSzH72MO2tBPdWSThunbxUVk0g== +"@discordjs/ws@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.0.0.tgz#99b6aef63374ae406b481fae2e76a2666e95a1c6" + integrity sha512-POiImjuQJzwCxjJs4JCtDcTjzvjVsVQbnsaoW/F03yTVdrj/xSpmgv4383AnpNEYXI+CA6ggkz37phZDsZQ1NQ== dependencies: - "@discordjs/collection" "^1.5.1" - "@discordjs/rest" "^1.7.1" - "@discordjs/util" "^0.3.1" + "@discordjs/collection" "^1.5.2" + "@discordjs/rest" "^2.0.0" + "@discordjs/util" "^1.0.0" "@sapphire/async-queue" "^1.5.0" - "@types/ws" "^8.5.4" - "@vladfrangu/async_event_emitter" "^2.2.1" - discord-api-types "^0.37.41" - tslib "^2.5.0" + "@types/ws" "^8.5.5" + "@vladfrangu/async_event_emitter" "^2.2.2" + discord-api-types "^0.37.50" + tslib "^2.6.1" ws "^8.13.0" -"@docsearch/css@3.5.1", "@docsearch/css@^3.5.0": +"@docsearch/css@3.5.1", "@docsearch/css@^3.5.1": version "3.5.1" resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.5.1.tgz#4adf9884735bbfea621c3716e80ea97baa419b73" integrity sha512-2Pu9HDg/uP/IT10rbQ+4OrTQuxIWdKVUEdcw9/w7kZJv9NeHS6skJx1xuRiFyoGKwAzcHXnLp7csE99sj+O1YA== -"@docsearch/js@^3.5.0": +"@docsearch/js@^3.5.1": version "3.5.1" resolved "https://registry.yarnpkg.com/@docsearch/js/-/js-3.5.1.tgz#6d8de3b4fcf7de94462c0e592e333efa9ebbbabd" integrity sha512-EXi8de5njxgP6TV3N9ytnGRLG9zmBNTEZjR4VzwPcpPLbZxxTLG2gaFyJyKiFVQxHW/DPlMrDJA3qoRRGEkgZw== @@ -220,227 +221,117 @@ "@docsearch/css" "3.5.1" algoliasearch "^4.0.0" -"@esbuild/android-arm64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" - integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== +"@esbuild/android-arm64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.17.tgz#9e00eb6865ed5f2dbe71a1e96f2c52254cd92903" + integrity sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg== -"@esbuild/android-arm64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz#fa6f0cc7105367cb79cc0a8bf32bf50cb1673e45" - integrity sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw== +"@esbuild/android-arm@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.17.tgz#1aa013b65524f4e9f794946b415b32ae963a4618" + integrity sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg== -"@esbuild/android-arm@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" - integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== +"@esbuild/android-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.17.tgz#c2bd0469b04ded352de011fae34a7a1d4dcecb79" + integrity sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw== -"@esbuild/android-arm@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.11.tgz#ae84a410696c9f549a15be94eaececb860bacacb" - integrity sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q== +"@esbuild/darwin-arm64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.17.tgz#0c21a59cb5bd7a2cec66c7a42431dca42aefeddd" + integrity sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g== -"@esbuild/android-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" - integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== +"@esbuild/darwin-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.17.tgz#92f8763ff6f97dff1c28a584da7b51b585e87a7b" + integrity sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g== -"@esbuild/android-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.11.tgz#0e58360bbc789ad0d68174d32ba20e678c2a16b6" - integrity sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw== +"@esbuild/freebsd-arm64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.17.tgz#934f74bdf4022e143ba2f21d421b50fd0fead8f8" + integrity sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ== -"@esbuild/darwin-arm64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" - integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== +"@esbuild/freebsd-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.17.tgz#16b6e90ba26ecc865eab71c56696258ec7f5d8bf" + integrity sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA== -"@esbuild/darwin-arm64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz#fcdcd2ef76ca656540208afdd84f284072f0d1f9" - integrity sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w== +"@esbuild/linux-arm64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.17.tgz#179a58e8d4c72116eb068563629349f8f4b48072" + integrity sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ== -"@esbuild/darwin-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" - integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== +"@esbuild/linux-arm@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.17.tgz#9d78cf87a310ae9ed985c3915d5126578665c7b5" + integrity sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg== -"@esbuild/darwin-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz#c5ac602ec0504a8ff81e876bc8a9811e94d69d37" - integrity sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw== +"@esbuild/linux-ia32@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.17.tgz#6fed202602d37361bca376c9d113266a722a908c" + integrity sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg== -"@esbuild/freebsd-arm64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" - integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== +"@esbuild/linux-loong64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.17.tgz#cdc60304830be1e74560c704bfd72cab8a02fa06" + integrity sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg== -"@esbuild/freebsd-arm64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz#7012fb06ee3e6e0d5560664a65f3fefbcc46db2e" - integrity sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A== +"@esbuild/linux-mips64el@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.17.tgz#c367b2855bb0902f5576291a2049812af2088086" + integrity sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ== -"@esbuild/freebsd-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" - integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== +"@esbuild/linux-ppc64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.17.tgz#7fdc0083d42d64a4651711ee0a7964f489242f45" + integrity sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ== -"@esbuild/freebsd-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz#c5de1199f70e1f97d5c8fca51afa9bf9a2af5969" - integrity sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q== +"@esbuild/linux-riscv64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.17.tgz#5198a417f3f5b86b10c95647b8bc032e5b6b2b1c" + integrity sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g== -"@esbuild/linux-arm64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" - integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== +"@esbuild/linux-s390x@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.17.tgz#7459c2fecdee2d582f0697fb76a4041f4ad1dd1e" + integrity sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg== -"@esbuild/linux-arm64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz#2a6d3a74e0b8b5f294e22b4515b29f76ebd42660" - integrity sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog== +"@esbuild/linux-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.17.tgz#948cdbf46d81c81ebd7225a7633009bc56a4488c" + integrity sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ== -"@esbuild/linux-arm@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" - integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== +"@esbuild/netbsd-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.17.tgz#6bb89668c0e093c5a575ded08e1d308bd7fd63e7" + integrity sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ== -"@esbuild/linux-arm@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz#5175bd61b793b436e4aece6328aa0d9be07751e1" - integrity sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg== +"@esbuild/openbsd-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.17.tgz#abac2ae75fef820ef6c2c48da4666d092584c79d" + integrity sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA== -"@esbuild/linux-ia32@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" - integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== +"@esbuild/sunos-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.17.tgz#74a45fe1db8ea96898f1a9bb401dcf1dadfc8371" + integrity sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g== -"@esbuild/linux-ia32@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz#20ee6cfd65a398875f321a485e7b2278e5f6f67b" - integrity sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw== +"@esbuild/win32-arm64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.17.tgz#fd95ffd217995589058a4ed8ac17ee72a3d7f615" + integrity sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw== -"@esbuild/linux-loong64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" - integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== +"@esbuild/win32-ia32@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.17.tgz#9b7ef5d0df97593a80f946b482e34fcba3fa4aaf" + integrity sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg== -"@esbuild/linux-loong64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz#8e7b251dede75083bf44508dab5edce3f49d052b" - integrity sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw== +"@esbuild/win32-x64@0.18.17": + version "0.18.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.17.tgz#bcb2e042631b3c15792058e189ed879a22b2968b" + integrity sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA== -"@esbuild/linux-mips64el@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" - integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== - -"@esbuild/linux-mips64el@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz#a3125eb48538ac4932a9d05089b157f94e443165" - integrity sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg== - -"@esbuild/linux-ppc64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" - integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== - -"@esbuild/linux-ppc64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz#842abadb7a0995bd539adee2be4d681b68279499" - integrity sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ== - -"@esbuild/linux-riscv64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" - integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== - -"@esbuild/linux-riscv64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz#7ce6e6cee1c72d5b4d2f4f8b6fcccf4a9bea0e28" - integrity sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w== - -"@esbuild/linux-s390x@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" - integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== - -"@esbuild/linux-s390x@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz#98fbc794363d02ded07d300df2e535650b297b96" - integrity sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg== - -"@esbuild/linux-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" - integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== - -"@esbuild/linux-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz#f8458ec8cf74c8274e4cacd00744d8446cac52eb" - integrity sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA== - -"@esbuild/netbsd-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" - integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== - -"@esbuild/netbsd-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz#a7b2f991b8293748a7be42eac1c4325faf0c7cca" - integrity sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q== - -"@esbuild/openbsd-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" - integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== - -"@esbuild/openbsd-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz#3e50923de84c54008f834221130fd23646072b2f" - integrity sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ== - -"@esbuild/sunos-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" - integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== - -"@esbuild/sunos-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz#ae47a550b0cd395de03606ecfba03cc96c7c19e2" - integrity sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng== - -"@esbuild/win32-arm64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" - integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== - -"@esbuild/win32-arm64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz#05d364582b7862d7fbf4698ef43644f7346dcfcc" - integrity sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg== - -"@esbuild/win32-ia32@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" - integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== - -"@esbuild/win32-ia32@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz#a3372095a4a1939da672156a3c104f8ce85ee616" - integrity sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg== - -"@esbuild/win32-x64@0.17.19": - version "0.17.19" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" - integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== - -"@esbuild/win32-x64@0.18.11": - version "0.18.11" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz#6526c7e1b40d5b9f0a222c6b767c22f6fb97aa57" - integrity sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA== - -"@jridgewell/sourcemap-codec@^1.4.13": +"@jridgewell/sourcemap-codec@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -515,9 +406,9 @@ once "^1.4.0" "@octokit/request@^8.0.1", "@octokit/request@^8.0.2": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.0.tgz#3b0fd15faeda1545024af09c31ac2689f4d65e7d" - integrity sha512-0gg/NwewU0iXctYBale0VVcCPqOtoW5lsog8cNBJgzV/CyTHa2gicUBOlNnzOk6pJkuwXI34qkq+uRm40PmD4A== + version "8.1.1" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.1.tgz#23b4d3f164e973f4c1a0f24f68256f1646c00620" + integrity sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ== dependencies: "@octokit/endpoint" "^9.0.0" "@octokit/request-error" "^5.0.0" @@ -630,7 +521,7 @@ resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8" integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== -"@sapphire/shapeshift@^3.8.2": +"@sapphire/shapeshift@^3.9.2": version "3.9.2" resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.2.tgz#a9c12cd51e1bc467619bb56df804450dd14871ac" integrity sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA== @@ -638,7 +529,7 @@ fast-deep-equal "^3.1.3" lodash "^4.17.21" -"@sapphire/snowflake@^3.4.2": +"@sapphire/snowflake@^3.5.1": version "3.5.1" resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.1.tgz#254521c188b49e8b2d4cc048b475fb2b38737fec" integrity sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA== @@ -651,11 +542,6 @@ fflate "^0.7.3" string.prototype.codepointat "^0.2.1" -"@tokenizer/token@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" - integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== - "@total-typescript/ts-reset@^0.4.2": version "0.4.2" resolved "https://registry.yarnpkg.com/@total-typescript/ts-reset/-/ts-reset-0.4.2.tgz#c564c173ba09973968e1046c93965b7a257878a4" @@ -707,9 +593,9 @@ integrity sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ== "@types/lodash@^4.14.195": - version "4.14.195" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" - integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== + version "4.14.196" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.196.tgz#a7c3d6fc52d8d71328b764e28e080b4169ec7a95" + integrity sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ== "@types/markdown-escape@^1.1.0": version "1.1.0" @@ -727,9 +613,9 @@ integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/node@*", "@types/node@^20.2.1": - version "20.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.3.tgz#329842940042d2b280897150e023e604d11657d6" - integrity sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw== + version "20.4.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.5.tgz#9dc0a5cb1ccce4f7a731660935ab70b9c00a5d69" + integrity sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg== "@types/prop-types@*": version "15.7.5" @@ -747,9 +633,9 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react@^18.2.7": - version "18.2.14" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" - integrity sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g== + version "18.2.18" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.18.tgz#c8b233919eef1bdc294f6f34b37f9727ad677516" + integrity sha512-da4NTSeBv/P34xoZPhtcLkmZuJ+oYaCxHmyHzwaDQo9RQPBeXV+06gEk2FpqEcsX9XrnNLvRpVh6bdavDSjtiQ== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -782,7 +668,7 @@ resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz#5c9f3c617f64a9735d7b72a7cc671e166d900c40" integrity sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA== -"@types/ws@^8.5.4": +"@types/ws@^8.5.5": version "8.5.5" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== @@ -794,7 +680,7 @@ resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz#ee0b6dfcc62fe65364e6395bf38fa2ba10bb44b6" integrity sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw== -"@vladfrangu/async_event_emitter@^2.2.1": +"@vladfrangu/async_event_emitter@^2.2.2": version "2.2.2" resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz#84c5a3f8d648842cec5cc649b88df599af32ed88" integrity sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ== @@ -894,34 +780,34 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.4.tgz#06e83c5027f464eef861c329be81454bc8b70780" integrity sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ== -"@vueuse/core@10.2.1", "@vueuse/core@^10.1.2": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.2.1.tgz#a62b54cdaf1496138a9f8a7df7f9d644892b421f" - integrity sha512-c441bfMbkAwTNwVRHQ0zdYZNETK//P84rC01aP2Uy/aRFCiie9NE/k9KdIXbno0eDYP5NPUuWv0aA/I4Unr/7w== +"@vueuse/core@10.3.0", "@vueuse/core@^10.2.1": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.3.0.tgz#b2dab7821ef206811b925fc935163c38056fd82b" + integrity sha512-BEM5yxcFKb5btFjTSAFjTu5jmwoW66fyV9uJIP4wUXXU8aR5Hl44gndaaXp7dC5HSObmgbnR2RN+Un1p68Mf5Q== dependencies: "@types/web-bluetooth" "^0.0.17" - "@vueuse/metadata" "10.2.1" - "@vueuse/shared" "10.2.1" + "@vueuse/metadata" "10.3.0" + "@vueuse/shared" "10.3.0" vue-demi ">=0.14.5" -"@vueuse/integrations@^10.1.2": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-10.2.1.tgz#abc44b35f909f6b5e1a66a2d69a257bb4b087536" - integrity sha512-FDP5lni+z9FjHE9H3xuvwSjoRV9U8jmDvJpmHPCBjUgPGYRynwb60eHWXCFJXLUtb4gSIHy0e+iaEbrKdalCkQ== +"@vueuse/integrations@^10.2.1": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-10.3.0.tgz#765e9505358590f21531998194c6e60a8b23655c" + integrity sha512-Jgiv7oFyIgC6BxmDtiyG/fxyGysIds00YaY7sefwbhCZ2/tjEx1W/1WcsISSJPNI30in28+HC2J4uuU8184ekg== dependencies: - "@vueuse/core" "10.2.1" - "@vueuse/shared" "10.2.1" + "@vueuse/core" "10.3.0" + "@vueuse/shared" "10.3.0" vue-demi ">=0.14.5" -"@vueuse/metadata@10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.2.1.tgz#0865066def62ed97629c417ec79678d508d22934" - integrity sha512-3Gt68mY/i6bQvFqx7cuGBzrCCQu17OBaGWS5JdwISpMsHnMKKjC2FeB5OAfMcCQ0oINfADP3i9A4PPRo0peHdQ== +"@vueuse/metadata@10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.3.0.tgz#14fe6cc909573785f73a56e4d9351edf3830b796" + integrity sha512-Ema3YhNOa4swDsV0V7CEY5JXvK19JI/o1szFO1iWxdFg3vhdFtCtSTP26PCvbUpnUtNHBY2wx5y3WDXND5Pvnw== -"@vueuse/shared@10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.2.1.tgz#0fd0a5dd014b7713b7fe03347b30fad69bb15b30" - integrity sha512-QWHq2bSuGptkcxx4f4M/fBYC3Y8d3M2UYyLsyzoPgEoVzJURQ0oJeWXu79OiLlBb8gTKkqe4mO85T/sf39mmiw== +"@vueuse/shared@10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.3.0.tgz#ce6b4b6860e14aaa293025dcf0cbe1036a25869f" + integrity sha512-kGqCTEuFPMK4+fNWy6dUOiYmxGcUbtznMwBZLC1PubidF4VZY05B+Oht7Jh7/6x4VOWGpvu3R37WHi81cKpiqg== dependencies: vue-demi ">=0.14.5" @@ -939,29 +825,29 @@ acorn@^8.9.0: integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== algoliasearch@^4.0.0: - version "4.18.0" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.18.0.tgz#1183ad0384a5b2c14f381c3a361da611acc8edb3" - integrity sha512-pCuVxC1SVcpc08ENH32T4sLKSyzoU7TkRIDBMwSLfIiW+fq4znOmWDkAygHZ6pRcO9I1UJdqlfgnV7TRj+MXrA== + version "4.19.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.19.1.tgz#18111fb422eaf841737adb92d5ab12133d244218" + integrity sha512-IJF5b93b2MgAzcE/tuzW0yOPnuUyRgGAtaPv5UUywXM8kzqfdwZTO4sPJBzoGz1eOy6H9uEchsJsBFTELZSu+g== dependencies: - "@algolia/cache-browser-local-storage" "4.18.0" - "@algolia/cache-common" "4.18.0" - "@algolia/cache-in-memory" "4.18.0" - "@algolia/client-account" "4.18.0" - "@algolia/client-analytics" "4.18.0" - "@algolia/client-common" "4.18.0" - "@algolia/client-personalization" "4.18.0" - "@algolia/client-search" "4.18.0" - "@algolia/logger-common" "4.18.0" - "@algolia/logger-console" "4.18.0" - "@algolia/requester-browser-xhr" "4.18.0" - "@algolia/requester-common" "4.18.0" - "@algolia/requester-node-http" "4.18.0" - "@algolia/transporter" "4.18.0" + "@algolia/cache-browser-local-storage" "4.19.1" + "@algolia/cache-common" "4.19.1" + "@algolia/cache-in-memory" "4.19.1" + "@algolia/client-account" "4.19.1" + "@algolia/client-analytics" "4.19.1" + "@algolia/client-common" "4.19.1" + "@algolia/client-personalization" "4.19.1" + "@algolia/client-search" "4.19.1" + "@algolia/logger-common" "4.19.1" + "@algolia/logger-console" "4.19.1" + "@algolia/requester-browser-xhr" "4.19.1" + "@algolia/requester-common" "4.19.1" + "@algolia/requester-node-http" "4.19.1" + "@algolia/transporter" "4.19.1" ansi-sequence-parser@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz#4d790f31236ac20366b23b3916b789e1bde39aed" - integrity sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ== + version "1.1.1" + resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf" + integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== array-flatten@1.1.1: version "1.1.1" @@ -1121,29 +1007,29 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -discord-api-types@^0.37.41: - version "0.37.47" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.47.tgz#d622d5f5629a71ca2cd534442ae2800844e1888d" - integrity sha512-rNif8IAv6duS2z47BMXq/V9kkrLfkAoiwpFY3sLxxbyKprk065zqf3HLTg4bEoxRSmi+Lhc7yqGDrG8C3j8GFA== +discord-api-types@^0.37.41, discord-api-types@^0.37.50: + version "0.37.51" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.51.tgz#a9fe2ceddde1c9c2e4d29987d45241cd746b9319" + integrity sha512-tdmdH98t3zgjQF6zwOHl0OB/PCBiE4aVsNTuN7m0PfU2jOLx3lNoU6xTeFALntUtlIxN80GTr9RPQR4t7msjSg== -discord.js@^14.9.0: - version "14.11.0" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.11.0.tgz#6529d49f30d10fc5a9ff8e6796661aa998769afe" - integrity sha512-CkueWYFQ28U38YPR8HgsBR/QT35oPpMbEsTNM30Fs8loBIhnA4s70AwQEoy6JvLcpWWJO7GY0y2BUzZmuBMepQ== +discord.js@^14.12.0: + version "14.12.0" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.12.0.tgz#e476c7f171ecc3add62781583223b522d12bd1fd" + integrity sha512-rvqwvcu73p8QCsy90klZ6n8HLfaN9SCCKyGGrgVIHcJ3ywUzr1hU67Zm/0c7h2PR45ySZqSHV8x2mIfJ1+n/OA== dependencies: - "@discordjs/builders" "^1.6.3" - "@discordjs/collection" "^1.5.1" + "@discordjs/builders" "^1.6.4" + "@discordjs/collection" "^1.5.2" "@discordjs/formatters" "^0.3.1" - "@discordjs/rest" "^1.7.1" - "@discordjs/util" "^0.3.1" - "@discordjs/ws" "^0.8.3" - "@sapphire/snowflake" "^3.4.2" - "@types/ws" "^8.5.4" - discord-api-types "^0.37.41" + "@discordjs/rest" "^2.0.0" + "@discordjs/util" "^1.0.0" + "@discordjs/ws" "^1.0.0" + "@sapphire/snowflake" "^3.5.1" + "@types/ws" "^8.5.5" + discord-api-types "^0.37.50" fast-deep-equal "^3.1.3" lodash.snakecase "^4.1.1" - tslib "^2.5.0" - undici "^5.22.0" + tslib "^2.6.1" + undici "^5.22.1" ws "^8.13.0" ee-first@1.1.1: @@ -1161,61 +1047,33 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -esbuild@^0.17.5: - version "0.17.19" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" - integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== - optionalDependencies: - "@esbuild/android-arm" "0.17.19" - "@esbuild/android-arm64" "0.17.19" - "@esbuild/android-x64" "0.17.19" - "@esbuild/darwin-arm64" "0.17.19" - "@esbuild/darwin-x64" "0.17.19" - "@esbuild/freebsd-arm64" "0.17.19" - "@esbuild/freebsd-x64" "0.17.19" - "@esbuild/linux-arm" "0.17.19" - "@esbuild/linux-arm64" "0.17.19" - "@esbuild/linux-ia32" "0.17.19" - "@esbuild/linux-loong64" "0.17.19" - "@esbuild/linux-mips64el" "0.17.19" - "@esbuild/linux-ppc64" "0.17.19" - "@esbuild/linux-riscv64" "0.17.19" - "@esbuild/linux-s390x" "0.17.19" - "@esbuild/linux-x64" "0.17.19" - "@esbuild/netbsd-x64" "0.17.19" - "@esbuild/openbsd-x64" "0.17.19" - "@esbuild/sunos-x64" "0.17.19" - "@esbuild/win32-arm64" "0.17.19" - "@esbuild/win32-ia32" "0.17.19" - "@esbuild/win32-x64" "0.17.19" - esbuild@^0.18.10: - version "0.18.11" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.11.tgz#cbf94dc3359d57f600a0dbf281df9b1d1b4a156e" - integrity sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA== + version "0.18.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.17.tgz#2aaf6bc6759b0c605777fdc435fea3969e091cad" + integrity sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg== optionalDependencies: - "@esbuild/android-arm" "0.18.11" - "@esbuild/android-arm64" "0.18.11" - "@esbuild/android-x64" "0.18.11" - "@esbuild/darwin-arm64" "0.18.11" - "@esbuild/darwin-x64" "0.18.11" - "@esbuild/freebsd-arm64" "0.18.11" - "@esbuild/freebsd-x64" "0.18.11" - "@esbuild/linux-arm" "0.18.11" - "@esbuild/linux-arm64" "0.18.11" - "@esbuild/linux-ia32" "0.18.11" - "@esbuild/linux-loong64" "0.18.11" - "@esbuild/linux-mips64el" "0.18.11" - "@esbuild/linux-ppc64" "0.18.11" - "@esbuild/linux-riscv64" "0.18.11" - "@esbuild/linux-s390x" "0.18.11" - "@esbuild/linux-x64" "0.18.11" - "@esbuild/netbsd-x64" "0.18.11" - "@esbuild/openbsd-x64" "0.18.11" - "@esbuild/sunos-x64" "0.18.11" - "@esbuild/win32-arm64" "0.18.11" - "@esbuild/win32-ia32" "0.18.11" - "@esbuild/win32-x64" "0.18.11" + "@esbuild/android-arm" "0.18.17" + "@esbuild/android-arm64" "0.18.17" + "@esbuild/android-x64" "0.18.17" + "@esbuild/darwin-arm64" "0.18.17" + "@esbuild/darwin-x64" "0.18.17" + "@esbuild/freebsd-arm64" "0.18.17" + "@esbuild/freebsd-x64" "0.18.17" + "@esbuild/linux-arm" "0.18.17" + "@esbuild/linux-arm64" "0.18.17" + "@esbuild/linux-ia32" "0.18.17" + "@esbuild/linux-loong64" "0.18.17" + "@esbuild/linux-mips64el" "0.18.17" + "@esbuild/linux-ppc64" "0.18.17" + "@esbuild/linux-riscv64" "0.18.17" + "@esbuild/linux-s390x" "0.18.17" + "@esbuild/linux-x64" "0.18.17" + "@esbuild/netbsd-x64" "0.18.17" + "@esbuild/openbsd-x64" "0.18.17" + "@esbuild/sunos-x64" "0.18.17" + "@esbuild/win32-arm64" "0.18.17" + "@esbuild/win32-ia32" "0.18.17" + "@esbuild/win32-x64" "0.18.17" escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" @@ -1284,15 +1142,6 @@ fflate@^0.7.3: resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== -file-type@^18.3.0: - version "18.5.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-18.5.0.tgz#604a001ba0d32577d4c3fa420ee104d656b914d2" - integrity sha512-yvpl5U868+V6PqXHMmsESpg6unQ5GfnPssl4dxdJudBrr9qy7Fddt7EVX1VLlddFfe8Gj9N7goCZH22FXuSQXQ== - dependencies: - readable-web-to-node-stream "^3.0.2" - strtok3 "^7.0.0" - token-types "^5.0.1" - finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -1306,12 +1155,12 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" -focus-trap@^7.4.3: - version "7.4.3" - resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-7.4.3.tgz#a3dae73d44df359eb92bbf37b18e173e813b16c5" - integrity sha512-BgSSbK4GPnS2VbtZ50VtOv1Sti6DIkj3+LkVjiWMNjLeAp1SH1UlLx3ULu/DCu4vq5R4/uvTm+zrvsMsuYmGLg== +focus-trap@^7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-7.5.2.tgz#e5ee678d10a18651f2591ffb66c949fb098d57cf" + integrity sha512-p6vGNNWLDGwJCiEjkSK6oERj/hEyI9ITsSwIUICBoKLlWiTWXJRfQibCwcoi50rTZdbi87qDtUlMCmQwsGSgPw== dependencies: - tabbable "^6.1.2" + tabbable "^6.2.0" forwarded@0.2.0: version "0.2.0" @@ -1388,12 +1237,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -inherits@2.0.4, inherits@^2.0.3: +inherits@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1443,12 +1287,17 @@ loose-envify@^1.1.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +magic-bytes.js@^1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.0.15.tgz#3c9d2b7d45bb8432482646b5f74bbf6725274616" + integrity sha512-bpRmwbRHqongRhA+mXzbLWjVy7ylqmfMBYaQkSs6pac0z6hBTvsgrH0r4FBYd/UYVJBmS6Rp/O+oCCQVLzKV1g== + magic-string@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.0.tgz#fd58a4748c5c4547338a424e90fa5dd17f4de529" - integrity sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ== + version "0.30.2" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.2.tgz#dcf04aad3d0d1314bc743d076c50feb29b3c7aca" + integrity sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug== dependencies: - "@jridgewell/sourcemap-codec" "^1.4.13" + "@jridgewell/sourcemap-codec" "^1.4.15" mark.js@8.11.1: version "8.11.1" @@ -1579,11 +1428,6 @@ pathe@^1.1.0, pathe@^1.1.1: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== -peek-readable@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec" - integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -1603,28 +1447,19 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.1.10, postcss@^8.4.23: - version "8.4.24" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" - integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== - dependencies: - nanoid "^3.3.6" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -postcss@^8.4.25: - version "8.4.26" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.26.tgz#1bc62ab19f8e1e5463d98cf74af39702a00a9e94" - integrity sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw== +postcss@^8.1.10, postcss@^8.4.26: + version "8.4.27" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.27.tgz#234d7e4b72e34ba5a92c29636734349e0d9c3057" + integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" source-map-js "^1.0.2" preact@^10.0.0: - version "10.15.1" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.15.1.tgz#a1de60c9fc0c79a522d969c65dcaddc5d994eede" - integrity sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g== + version "10.16.0" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.16.0.tgz#68a06d70b191b8a313ea722d61e09c6b2a79a37e" + integrity sha512-XTSj3dJ4roKIC93pald6rWuB2qQJO9gO2iLLyTe87MrjQN+HklueLsmskbywEWqCHlclgz3/M4YLL2iBr9UmMA== prettier@^3.0.0: version "3.0.0" @@ -1668,37 +1503,14 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" -readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-web-to-node-stream@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" - integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== - dependencies: - readable-stream "^3.6.0" - -rollup@^3.21.0: - version "3.26.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.26.0.tgz#9f2e0316a4ca641911cefd8515c562a9124e6130" - integrity sha512-YzJH0eunH2hr3knvF3i6IkLO/jTjAEwU4HoMUbQl4//Tnl3ou0e7P5SjxdDr8HQJdeUJShlbEHXrrnEHy1l7Yg== - optionalDependencies: - fsevents "~2.3.2" - rollup@^3.25.2: - version "3.26.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c" - integrity sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA== + version "3.27.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.27.0.tgz#15bd07e2e1cbfa9255bf6a3f04a432621c2f3550" + integrity sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg== optionalDependencies: fsevents "~2.3.2" -safe-buffer@5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -1757,7 +1569,7 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shiki@^0.14.2: +shiki@^0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.3.tgz#d1a93c463942bdafb9866d74d619a4347d0bbf64" integrity sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g== @@ -1796,22 +1608,7 @@ string.prototype.codepointat@^0.2.1: resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc" integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg== -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -strtok3@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5" - integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ== - dependencies: - "@tokenizer/token" "^0.3.0" - peek-readable "^5.0.0" - -tabbable@^6.1.2: +tabbable@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew== @@ -1826,23 +1623,15 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -token-types@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" - integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg== - dependencies: - "@tokenizer/token" "^0.3.0" - ieee754 "^1.2.1" - ts-mixer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.3.tgz#69bd50f406ff39daa369885b16c77a6194c7cae6" integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ== -tslib@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" - integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== +tslib@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" + integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig== type-is@~1.6.18: version "1.6.18" @@ -1858,11 +1647,11 @@ typescript@^5.0.4: integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== ufo@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76" - integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ== + version "1.2.0" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.2.0.tgz#28d127a087a46729133fdc89cb1358508b3f80ba" + integrity sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg== -undici@^5.22.0: +undici@^5.22.1: version "5.22.1" resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b" integrity sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw== @@ -1887,11 +1676,6 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -1914,50 +1698,39 @@ vite-node@^0.33.0: picocolors "^1.0.0" vite "^3.0.0 || ^4.0.0" -"vite@^3.0.0 || ^4.0.0": - version "4.4.4" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.4.tgz#b76e6049c0e080cb54e735ad2d18287753752118" - integrity sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg== +"vite@^3.0.0 || ^4.0.0", vite@^4.4.7: + version "4.4.7" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.7.tgz#71b8a37abaf8d50561aca084dbb77fa342824154" + integrity sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw== dependencies: esbuild "^0.18.10" - postcss "^8.4.25" + postcss "^8.4.26" rollup "^3.25.2" optionalDependencies: fsevents "~2.3.2" -vite@^4.3.9: - version "4.3.9" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d" - integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg== - dependencies: - esbuild "^0.17.5" - postcss "^8.4.23" - rollup "^3.21.0" - optionalDependencies: - fsevents "~2.3.2" - vitepress-plugin-auto-sidebar@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vitepress-plugin-auto-sidebar/-/vitepress-plugin-auto-sidebar-1.1.0.tgz#d8131e31143e99b6e142cb7d23b9323a578a862b" integrity sha512-LgkpjKVlNlQS54PFv5R/Y4+CHtHoXsJ3xgyVTKNi0sG1fXUmViKCg3Hnl9eo/SfNn894qsNRXoNneQmRBP2Lmw== vitepress@^1.0.0-beta.3: - version "1.0.0-beta.3" - resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-1.0.0-beta.3.tgz#1c03bd3cff5193b888d202258d1dd0a60cfe52eb" - integrity sha512-GR5Pvr/o343NN1M4Na1shhDYZRrQbjmLq7WE0lla0H8iDPAsHE8agTHLWfu3FWx+3q2KA29sv16+0O9RQKGjlA== + version "1.0.0-beta.7" + resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-1.0.0-beta.7.tgz#edf7f0806e700ed9b08d84e48131d78e453d3a19" + integrity sha512-P9Rw+FXatKIU4fVdtKxqwHl6fby8E/8zE3FIfep6meNgN4BxbWqoKJ6yfuuQQR9IrpQqwnyaBh4LSabyll6tWg== dependencies: - "@docsearch/css" "^3.5.0" - "@docsearch/js" "^3.5.0" + "@docsearch/css" "^3.5.1" + "@docsearch/js" "^3.5.1" "@vitejs/plugin-vue" "^4.2.3" "@vue/devtools-api" "^6.5.0" - "@vueuse/core" "^10.1.2" - "@vueuse/integrations" "^10.1.2" + "@vueuse/core" "^10.2.1" + "@vueuse/integrations" "^10.2.1" body-scroll-lock "4.0.0-beta.0" - focus-trap "^7.4.3" + focus-trap "^7.5.2" mark.js "8.11.1" minisearch "^6.1.0" - shiki "^0.14.2" - vite "^4.3.9" + shiki "^0.14.3" + vite "^4.4.7" vue "^3.3.4" vscode-oniguruma@^1.7.0: From 91f364b76af5f03029fe73717df4744d5fe1b8ef Mon Sep 17 00:00:00 2001 From: tresabhi Date: Fri, 4 Aug 2023 02:24:14 +0000 Subject: [PATCH 03/36] feat: used dahses for command readibility --- src/commands/fullStats.tsx | 2 +- src/commands/ownedTanks.tsx | 2 +- src/commands/playerAchievements.ts | 2 +- src/commands/playerInfo.ts | 2 +- src/commands/searchClans.ts | 2 +- src/commands/searchPlayers.ts | 2 +- src/commands/searchTanks.ts | 2 +- src/core/blitzstars/getPeriodStart.ts | 15 +++---------- src/core/blitzstars/getTimeDaysAgo.ts | 4 ++-- src/core/discord/addPeriodSubCommands.ts | 21 +++++++++--------- .../discord/addStatTypeSubCommandGroups.ts | 6 ++--- src/core/discord/resolvePeriodFromCommand.ts | 22 ++++++++----------- src/core/express/resolvePeriodFromURL.ts | 4 ++-- 13 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 83a3ef076..689d958ba 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -21,7 +21,7 @@ export const fullStatsCommand = new Promise( async (resolve) => { const command = await addStatTypeSubCommandGroups( new SlashCommandBuilder() - .setName('fullstats') + .setName('full-stats') .setDescription('Full in-game statistics'), ); diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index f5bddbb84..82e338d6d 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -49,7 +49,7 @@ export const ownedTanksCommand: CommandRegistry = { inPublic: true, command: new SlashCommandBuilder() - .setName('ownedtanks') + .setName('owned-tanks') .setDescription("Shows a player's owned tanks") .addStringOption((option) => option diff --git a/src/commands/playerAchievements.ts b/src/commands/playerAchievements.ts index 904b8534a..60cdf86fa 100644 --- a/src/commands/playerAchievements.ts +++ b/src/commands/playerAchievements.ts @@ -20,7 +20,7 @@ export const playerAchievementsCommand: CommandRegistry = { inPublic: true, command: new SlashCommandBuilder() - .setName('playerachievements') + .setName('player-achievements') .setDescription("All the player's achievements") .addStringOption(addUsernameChoices) .addStringOption((option) => diff --git a/src/commands/playerInfo.ts b/src/commands/playerInfo.ts index e11a66ebe..704daf3b0 100644 --- a/src/commands/playerInfo.ts +++ b/src/commands/playerInfo.ts @@ -16,7 +16,7 @@ export const playerInfoCommand: CommandRegistry = { inPublic: true, command: new SlashCommandBuilder() - .setName('playerinfo') + .setName('player-info') .setDescription('Basic information about a player') .addStringOption(addUsernameChoices), diff --git a/src/commands/searchClans.ts b/src/commands/searchClans.ts index 7d7436d3b..01ca6061d 100644 --- a/src/commands/searchClans.ts +++ b/src/commands/searchClans.ts @@ -15,7 +15,7 @@ export const searchClansCommand: CommandRegistry = { inPublic: true, command: new SlashCommandBuilder() - .setName('searchclans') + .setName('search-clans') .setDescription('Search clans') .addStringOption(addRegionChoices) .addStringOption((option) => addClanChoices(option).setAutocomplete(false)) diff --git a/src/commands/searchPlayers.ts b/src/commands/searchPlayers.ts index c68d8a0e6..536d5fb18 100644 --- a/src/commands/searchPlayers.ts +++ b/src/commands/searchPlayers.ts @@ -16,7 +16,7 @@ export const searchPlayersCommand: CommandRegistry = { inPublic: true, command: new SlashCommandBuilder() - .setName('searchplayers') + .setName('search-players') .setDescription('Search players in a Blitz server') .addStringOption(addRegionChoices) .addStringOption((option) => diff --git a/src/commands/searchTanks.ts b/src/commands/searchTanks.ts index 7f1e87ab3..f02b95c05 100644 --- a/src/commands/searchTanks.ts +++ b/src/commands/searchTanks.ts @@ -12,7 +12,7 @@ export const searchTanksCommand: CommandRegistry = { inPublic: true, command: new SlashCommandBuilder() - .setName('searchtanks') + .setName('search-tanks') .setDescription('Search tanks') .addStringOption((option) => addTankChoices(option).setAutocomplete(false)) .addIntegerOption((option) => diff --git a/src/core/blitzstars/getPeriodStart.ts b/src/core/blitzstars/getPeriodStart.ts index 60cf2099e..330997209 100644 --- a/src/core/blitzstars/getPeriodStart.ts +++ b/src/core/blitzstars/getPeriodStart.ts @@ -1,16 +1,7 @@ import { Region } from '../../constants/regions'; -import { Period } from '../discord/addPeriodSubCommands'; +import { PeriodOption } from '../discord/addPeriodSubCommands'; import getTimeDaysAgo from './getTimeDaysAgo'; -export default function getPeriodStart( - server: Region, - period: Exclude, -) { - if (period === 'career') { - return -Infinity; - } else if (period === 'today') { - return getTimeDaysAgo(server, 1); - } else { - return getTimeDaysAgo(server, parseInt(period.match(/\d+/)?.[0]!)); - } +export default function getPeriodStart(region: Region, period: PeriodOption) { + return period === 'career' ? 0 : getTimeDaysAgo(region, parseInt(period)); } diff --git a/src/core/blitzstars/getTimeDaysAgo.ts b/src/core/blitzstars/getTimeDaysAgo.ts index 652b28fea..485561e9d 100644 --- a/src/core/blitzstars/getTimeDaysAgo.ts +++ b/src/core/blitzstars/getTimeDaysAgo.ts @@ -6,13 +6,13 @@ export const TIME_ZONE_MAPPINGS: Record = { asia: +7, // Central Asia Standard Time }; -export default function getTimeDaysAgo(server: Region, daysAgo: number) { +export default function getTimeDaysAgo(region: Region, daysAgo: number) { const now = new Date(); if (daysAgo === 0) now.getTime() / 1000; const time = new Date(); - time.setUTCHours(-TIME_ZONE_MAPPINGS[server], 0, 0, 0); + time.setUTCHours(-TIME_ZONE_MAPPINGS[region], 0, 0, 0); if (time > now) time.setUTCDate(time.getUTCDate() - 1); time.setUTCDate(time.getUTCDate() - daysAgo + 1); diff --git a/src/core/discord/addPeriodSubCommands.ts b/src/core/discord/addPeriodSubCommands.ts index 32020e707..f4261acc4 100644 --- a/src/core/discord/addPeriodSubCommands.ts +++ b/src/core/discord/addPeriodSubCommands.ts @@ -4,7 +4,8 @@ import { } from 'discord.js'; import { PERIOD_NAMES } from '../discord/resolvePeriodFromCommand'; -export type Period = 'period' | 'customperiod'; +export type PeriodSubcommand = 'period' | 'custom'; +export type PeriodOption = `${number}` | 'career'; export default function addPeriodSubCommands( option: SlashCommandSubcommandGroupBuilder, @@ -16,18 +17,18 @@ export default function addPeriodSubCommands( .addSubcommand((option) => extra( option - .setName('period' satisfies Period) + .setName('period' satisfies PeriodSubcommand) .setDescription(PERIOD_NAMES.period) .addStringOption((option) => option - .setName('days') + .setName('period') .setDescription('A preset period') .setChoices( - { name: 'Today', value: '1' }, - { name: '30 days', value: '30' }, - { name: '60 days', value: '60' }, - { name: '90 days', value: '90' }, - { name: 'Career', value: 'Infinity' }, + { name: 'Today', value: '1' satisfies PeriodOption }, + { name: '30 days', value: '30' satisfies PeriodOption }, + { name: '60 days', value: '60' satisfies PeriodOption }, + { name: '90 days', value: '90' satisfies PeriodOption }, + { name: 'Career', value: 'career' satisfies PeriodOption }, ) .setRequired(true), ), @@ -36,8 +37,8 @@ export default function addPeriodSubCommands( .addSubcommand((option) => extra( option - .setName('customperiod' satisfies Period) - .setDescription(PERIOD_NAMES.customperiod) + .setName('custom' satisfies PeriodSubcommand) + .setDescription(PERIOD_NAMES.custom) .addIntegerOption((option) => option .setName('start') diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommandGroups.ts index c96518e83..86c91d0b8 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommandGroups.ts @@ -43,7 +43,7 @@ export default async function addStatTypeSubCommandGroups( }) .addStringOption((option) => option - .setName('tanktype') + .setName('tank-type') .setDescription('Type') .addChoices( ...Object.entries(awaitedTankopediaInfo.vehicle_types).map( @@ -65,7 +65,7 @@ export default async function addStatTypeSubCommandGroups( ) .addStringOption((option) => option - .setName('treetype') + .setName('tree-type') .setDescription('Tech tree type') .addChoices( { name: 'Tech Tree', value: 'techtree' }, @@ -75,7 +75,7 @@ export default async function addStatTypeSubCommandGroups( .setRequired(false), ), ) - .setName('tanks') + .setName('multi-tank') .setDescription('Multiple tanks'), ); } diff --git a/src/core/discord/resolvePeriodFromCommand.ts b/src/core/discord/resolvePeriodFromCommand.ts index 65819e16b..db4fb1c7f 100644 --- a/src/core/discord/resolvePeriodFromCommand.ts +++ b/src/core/discord/resolvePeriodFromCommand.ts @@ -3,20 +3,16 @@ import { Region } from '../../constants/regions'; import getPeriodNow from '../blitzstars/getPeriodNow'; import getPeriodStart from '../blitzstars/getPeriodStart'; import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; -import { Period } from '../discord/addPeriodSubCommands'; +import { PeriodSubcommand } from '../discord/addPeriodSubCommands'; -export const PERIOD_NAMES: Record = { - today: "Today's statistics", - period: "Set number of days' statistics", - career: 'Career statistics', - customperiod: 'Custom period', +export const PERIOD_NAMES: Record = { + period: "Preset period's statistics", + custom: "Custom period's statistics", }; -export const EVOLUTION_PERIOD_NAMES: Record = { - today: "Today's evolution", - period: "Set number of days' evolution", - career: 'Career evolution', - customperiod: 'Custom period', +export const EVOLUTION_PERIOD_NAMES: Record = { + period: "Preset period's evolution", + custom: "Custom period's evolution", }; export interface ResolvedPeriod { @@ -34,9 +30,9 @@ export default function resolvePeriodFromCommand( let evolutionName: string; let start: number; let end: number; - const period = interaction.options.getSubcommand() as Period; + const period = interaction.options.getSubcommand() as PeriodSubcommand; - if (period === 'customperiod') { + if (period === 'custom') { const startRaw = interaction.options.getInteger('start')!; const endRaw = interaction.options.getInteger('end')!; const startMin = Math.min(startRaw, endRaw); diff --git a/src/core/express/resolvePeriodFromURL.ts b/src/core/express/resolvePeriodFromURL.ts index 035f68916..a4bee04fe 100644 --- a/src/core/express/resolvePeriodFromURL.ts +++ b/src/core/express/resolvePeriodFromURL.ts @@ -2,7 +2,7 @@ import { Region } from '../../constants/regions'; import getPeriodNow from '../blitzstars/getPeriodNow'; import getPeriodStart from '../blitzstars/getPeriodStart'; import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; -import { Period } from '../discord/addPeriodSubCommands'; +import { PeriodSubcommand } from '../discord/addPeriodSubCommands'; import { EVOLUTION_PERIOD_NAMES, PERIOD_NAMES, @@ -19,7 +19,7 @@ export default function resolvePeriodFromURL( let end: number; const url = new URL(urlString); const path = url.pathname.split('/').filter(Boolean); - const period = path[path.length - 1] as Period; + const period = path[path.length - 1] as PeriodSubcommand; if (period === 'custom') { const startRaw = parseInt(url!.searchParams.get('start')!); From 91dc45c8809da991a76cf2b522677a91ada4bdcf Mon Sep 17 00:00:00 2001 From: tresabhi Date: Fri, 4 Aug 2023 18:24:20 +0000 Subject: [PATCH 04/36] feat: added more mature periods --- src/commands/debug.ts | 2 +- src/commands/eligible.ts | 2 +- src/commands/evolution.tsx | 2 +- src/commands/fullStats.tsx | 2 +- src/commands/help.ts | 9 ++++--- src/commands/inactive.tsx | 2 +- src/commands/ownedTanks.tsx | 2 +- src/commands/ping.ts | 4 +-- src/commands/playerAchievements.ts | 2 +- src/commands/playerInfo.ts | 2 +- src/commands/ratings.tsx | 2 +- src/commands/searchClans.ts | 2 +- src/commands/searchPlayers.ts | 2 +- src/commands/searchTanks.ts | 2 +- src/commands/stats.tsx | 9 +++++-- src/commands/today.tsx | 2 +- src/commands/verify.ts | 2 +- src/core/blitzstars/getPeriodStart.ts | 4 +-- src/core/discord/addPeriodSubCommands.ts | 27 +++++++++---------- .../discord/addStatTypeSubCommandGroups.ts | 1 + src/core/discord/resolvePeriodFromCommand.ts | 25 +++++++---------- src/core/express/resolvePeriodFromURL.ts | 16 +++++------ .../handlers/autocomplete.ts | 4 +-- .../interactionCreate/handlers/button.tsx | 2 +- .../handlers/chatInputCommand.ts | 2 +- src/routes/stats.tsx | 2 +- src/routes/today.tsx | 2 +- 27 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/commands/debug.ts b/src/commands/debug.ts index 76bd7c807..fa7240883 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -12,7 +12,7 @@ const executionStart = new Date().getTime(); export const debugCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index 06ad066fd..eadb83c0b 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -26,7 +26,7 @@ export const SKILLED_CLANS: Record = { export const eligibleCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: false, command: new SlashCommandBuilder() diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 5da0c83b5..f2b1c0e50 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -28,7 +28,7 @@ export const evolutionCommand = new Promise( resolve({ inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command, diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 689d958ba..7b3162f9e 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -27,7 +27,7 @@ export const fullStatsCommand = new Promise( resolve({ inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command, diff --git a/src/commands/help.ts b/src/commands/help.ts index 2052fd12a..a85d83319 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -30,7 +30,7 @@ function addDocsSubcommands(option: SlashCommandSubcommandsOnlyBuilder) { export const helpCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: addDocsSubcommands( @@ -44,13 +44,14 @@ export const helpCommand: CommandRegistry = { ), ), - handler(interaction) { + async handler(interaction) { const subcommand = interaction.options.getSubcommand(); if (subcommand === 'commands') { - return `# Commands\n\nBlitzkrieg offers the following commands:\n\n${COMMANDS_RAW.filter( - (registry) => registry.inPublic && registry.inProduction, + return `# Commands\n\nBlitzkrieg offers the following commands:\n\n${( + await Promise.all(COMMANDS_RAW) ) + .filter((registry) => registry.inPublic && registry.inProduction) .map( (registry) => `- \`/${registry.command.name}\`: ${registry.command.description}`, diff --git a/src/commands/inactive.tsx b/src/commands/inactive.tsx index 8784e03d7..1e8e4b947 100644 --- a/src/commands/inactive.tsx +++ b/src/commands/inactive.tsx @@ -18,7 +18,7 @@ const DEFAULT_THRESHOLD = 7; export const inactiveCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 82e338d6d..33e3031e4 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -45,7 +45,7 @@ const COMP_TANKS = [ export const ownedTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 2005400f0..a9fcd5146 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,9 +1,9 @@ import { SlashCommandBuilder } from 'discord.js'; import { CommandRegistry } from '../events/interactionCreate'; -export const pingCommand: CommandRegistry = { +export const pingCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, handlesInteraction: true, diff --git a/src/commands/playerAchievements.ts b/src/commands/playerAchievements.ts index 60cdf86fa..2a81a2d2d 100644 --- a/src/commands/playerAchievements.ts +++ b/src/commands/playerAchievements.ts @@ -16,7 +16,7 @@ type SortBy = 'name' | 'count'; export const playerAchievementsCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/playerInfo.ts b/src/commands/playerInfo.ts index 704daf3b0..ce26234c5 100644 --- a/src/commands/playerInfo.ts +++ b/src/commands/playerInfo.ts @@ -12,7 +12,7 @@ import { AccountInfo } from '../types/accountInfo'; export const playerInfoCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 98eac5b01..9c129dbed 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -121,7 +121,7 @@ export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; export const ratingsCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchClans.ts b/src/commands/searchClans.ts index 01ca6061d..a7ca4e6f8 100644 --- a/src/commands/searchClans.ts +++ b/src/commands/searchClans.ts @@ -11,7 +11,7 @@ import { ClanList } from '../types/clanList'; export const searchClansCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchPlayers.ts b/src/commands/searchPlayers.ts index 536d5fb18..b6c62b6d2 100644 --- a/src/commands/searchPlayers.ts +++ b/src/commands/searchPlayers.ts @@ -12,7 +12,7 @@ import { AccountList } from '../types/accountList'; export const searchPlayersCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchTanks.ts b/src/commands/searchTanks.ts index f02b95c05..f2330aad5 100644 --- a/src/commands/searchTanks.ts +++ b/src/commands/searchTanks.ts @@ -8,7 +8,7 @@ import { CommandRegistry } from '../events/interactionCreate'; export const searchTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index cf6ca65f5..c4de71558 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -37,7 +37,11 @@ export const statsCommand = new Promise(async (resolve) => { true, ) as StatType; const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(player.region, interaction); + const resolvedPeriod = resolvePeriodFromCommand( + player.region, + interaction, + ); + const period = interaction.options.getString('period'); const tankIdRaw = interaction.options.getString('tank')!; const tankId = commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; @@ -48,6 +52,7 @@ export const statsCommand = new Promise(async (resolve) => { tankId, start, end, + period, }); const { nickname } = ( await getWargamingResponse( @@ -56,7 +61,7 @@ export const statsCommand = new Promise(async (resolve) => { )[player.id]; return [ - await stats(commandGroup, period, player, tankId), + await stats(commandGroup, resolvedPeriod, player, tankId), primaryButton(path, 'Refresh'), linkButton(`${CYCLIC_API}/${path}`, 'Embed'), linkButton( diff --git a/src/commands/today.tsx b/src/commands/today.tsx index 9a5a7a037..f8505c682 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -15,7 +15,7 @@ import { AccountInfo } from '../types/accountInfo'; export const todayCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 1dea0918f..c6682c1cf 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -14,7 +14,7 @@ import { PlayerClanData } from '../types/playerClanData'; export const verifyCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/core/blitzstars/getPeriodStart.ts b/src/core/blitzstars/getPeriodStart.ts index 330997209..118084a35 100644 --- a/src/core/blitzstars/getPeriodStart.ts +++ b/src/core/blitzstars/getPeriodStart.ts @@ -1,7 +1,7 @@ import { Region } from '../../constants/regions'; -import { PeriodOption } from '../discord/addPeriodSubCommands'; +import { PeriodSize } from '../discord/addPeriodSubCommands'; import getTimeDaysAgo from './getTimeDaysAgo'; -export default function getPeriodStart(region: Region, period: PeriodOption) { +export default function getPeriodStart(region: Region, period: PeriodSize) { return period === 'career' ? 0 : getTimeDaysAgo(region, parseInt(period)); } diff --git a/src/core/discord/addPeriodSubCommands.ts b/src/core/discord/addPeriodSubCommands.ts index f4261acc4..cd28a4c4b 100644 --- a/src/core/discord/addPeriodSubCommands.ts +++ b/src/core/discord/addPeriodSubCommands.ts @@ -2,10 +2,9 @@ import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder, } from 'discord.js'; -import { PERIOD_NAMES } from '../discord/resolvePeriodFromCommand'; -export type PeriodSubcommand = 'period' | 'custom'; -export type PeriodOption = `${number}` | 'career'; +export type PeriodType = 'period' | 'custom'; +export type PeriodSize = `${number}` | 'career'; export default function addPeriodSubCommands( option: SlashCommandSubcommandGroupBuilder, @@ -17,18 +16,18 @@ export default function addPeriodSubCommands( .addSubcommand((option) => extra( option - .setName('period' satisfies PeriodSubcommand) - .setDescription(PERIOD_NAMES.period) + .setName('period' satisfies PeriodType) + .setDescription("Preset period's statistics") .addStringOption((option) => option .setName('period') .setDescription('A preset period') .setChoices( - { name: 'Today', value: '1' satisfies PeriodOption }, - { name: '30 days', value: '30' satisfies PeriodOption }, - { name: '60 days', value: '60' satisfies PeriodOption }, - { name: '90 days', value: '90' satisfies PeriodOption }, - { name: 'Career', value: 'career' satisfies PeriodOption }, + { name: 'Today', value: '1' satisfies PeriodSize }, + { name: '30 days', value: '30' satisfies PeriodSize }, + { name: '60 days', value: '60' satisfies PeriodSize }, + { name: '90 days', value: '90' satisfies PeriodSize }, + { name: 'Career', value: 'career' satisfies PeriodSize }, ) .setRequired(true), ), @@ -37,19 +36,19 @@ export default function addPeriodSubCommands( .addSubcommand((option) => extra( option - .setName('custom' satisfies PeriodSubcommand) - .setDescription(PERIOD_NAMES.custom) + .setName('custom' satisfies PeriodType) + .setDescription("Custom period's statistics") .addIntegerOption((option) => option .setName('start') - .setDescription('Start of period in days ago from today') + .setDescription('Days ago from today') .setMinValue(0) .setRequired(true), ) .addIntegerOption((option) => option .setName('end') - .setDescription('End of period in days ago from today') + .setDescription('Days ago from today') .setMinValue(0) .setRequired(true), ), diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommandGroups.ts index 86c91d0b8..e9e5dbb01 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommandGroups.ts @@ -29,6 +29,7 @@ export default async function addStatTypeSubCommandGroups( .addSubcommandGroup((option) => addPeriodSubCommands(option, (option) => option + .addStringOption(addUsernameChoices) .addStringOption((option) => { option .setName('nation') diff --git a/src/core/discord/resolvePeriodFromCommand.ts b/src/core/discord/resolvePeriodFromCommand.ts index db4fb1c7f..dc172292b 100644 --- a/src/core/discord/resolvePeriodFromCommand.ts +++ b/src/core/discord/resolvePeriodFromCommand.ts @@ -3,17 +3,11 @@ import { Region } from '../../constants/regions'; import getPeriodNow from '../blitzstars/getPeriodNow'; import getPeriodStart from '../blitzstars/getPeriodStart'; import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; -import { PeriodSubcommand } from '../discord/addPeriodSubCommands'; +import { PeriodSize, PeriodType } from '../discord/addPeriodSubCommands'; -export const PERIOD_NAMES: Record = { - period: "Preset period's statistics", - custom: "Custom period's statistics", -}; - -export const EVOLUTION_PERIOD_NAMES: Record = { - period: "Preset period's evolution", - custom: "Custom period's evolution", -}; +export function getPeriodOptionName(period: PeriodSize) { + return period === 'career' ? 'Career' : `${period}-day`; +} export interface ResolvedPeriod { statsName: string; @@ -30,9 +24,10 @@ export default function resolvePeriodFromCommand( let evolutionName: string; let start: number; let end: number; - const period = interaction.options.getSubcommand() as PeriodSubcommand; + const periodSubcommand = interaction.options.getSubcommand() as PeriodType; + const periodOption = interaction.options.getString('period') as PeriodSize; - if (period === 'custom') { + if (periodSubcommand === 'custom') { const startRaw = interaction.options.getInteger('start')!; const endRaw = interaction.options.getInteger('end')!; const startMin = Math.min(startRaw, endRaw); @@ -43,9 +38,9 @@ export default function resolvePeriodFromCommand( start = getTimeDaysAgo(server, startMin); end = getTimeDaysAgo(server, endMax); } else { - statsName = PERIOD_NAMES[period]; - evolutionName = EVOLUTION_PERIOD_NAMES[period]; - start = getPeriodStart(server, period); + statsName = `${getPeriodOptionName(periodOption)} Statistics`; + evolutionName = `${getPeriodOptionName(periodOption)} Evolution`; + start = getPeriodStart(server, periodOption); end = getPeriodNow(); } diff --git a/src/core/express/resolvePeriodFromURL.ts b/src/core/express/resolvePeriodFromURL.ts index a4bee04fe..d9cfc564e 100644 --- a/src/core/express/resolvePeriodFromURL.ts +++ b/src/core/express/resolvePeriodFromURL.ts @@ -2,11 +2,10 @@ import { Region } from '../../constants/regions'; import getPeriodNow from '../blitzstars/getPeriodNow'; import getPeriodStart from '../blitzstars/getPeriodStart'; import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; -import { PeriodSubcommand } from '../discord/addPeriodSubCommands'; +import { PeriodSize, PeriodType } from '../discord/addPeriodSubCommands'; import { - EVOLUTION_PERIOD_NAMES, - PERIOD_NAMES, ResolvedPeriod, + getPeriodOptionName, } from '../discord/resolvePeriodFromCommand'; export default function resolvePeriodFromURL( @@ -19,9 +18,10 @@ export default function resolvePeriodFromURL( let end: number; const url = new URL(urlString); const path = url.pathname.split('/').filter(Boolean); - const period = path[path.length - 1] as PeriodSubcommand; + const periodSubcommand = path[path.length - 1] as PeriodType; + const periodOption = url.searchParams.get('period') as PeriodSize; - if (period === 'custom') { + if (periodSubcommand === 'custom') { const startRaw = parseInt(url!.searchParams.get('start')!); const endRaw = parseInt(url!.searchParams.get('end')!); const startMin = Math.min(startRaw, endRaw); @@ -32,9 +32,9 @@ export default function resolvePeriodFromURL( start = getTimeDaysAgo(server, startMin); end = getTimeDaysAgo(server, endMax); } else { - statsName = PERIOD_NAMES[period]; - evolutionName = EVOLUTION_PERIOD_NAMES[period]; - start = getPeriodStart(server, period); + statsName = `${getPeriodOptionName(periodOption)} Statistics`; + evolutionName = `${getPeriodOptionName(periodOption)} Evolution`; + start = getPeriodStart(server, periodOption); end = getPeriodNow(); } diff --git a/src/events/interactionCreate/handlers/autocomplete.ts b/src/events/interactionCreate/handlers/autocomplete.ts index af4b81bed..c688cd491 100644 --- a/src/events/interactionCreate/handlers/autocomplete.ts +++ b/src/events/interactionCreate/handlers/autocomplete.ts @@ -1,10 +1,10 @@ import { AutocompleteInteraction, CacheType } from 'discord.js'; import { commands } from '..'; -export default function handleAutocomplete( +export default async function handleAutocomplete( interaction: AutocompleteInteraction, ) { - const command = commands[interaction.commandName]; + const command = (await commands)[interaction.commandName]; if (!command) { console.error(`No command matching ${interaction.commandName} was found.`); diff --git a/src/events/interactionCreate/handlers/button.tsx b/src/events/interactionCreate/handlers/button.tsx index 7fc55ae21..cac6fddb5 100644 --- a/src/events/interactionCreate/handlers/button.tsx +++ b/src/events/interactionCreate/handlers/button.tsx @@ -11,7 +11,7 @@ export default async function handleButton( const url = new URL(`${CYCLIC_API}/${interaction.customId}`); const commandName = url.pathname.split('/')[1]; - const button = commands[commandName]?.button; + const button = (await commands)[commandName]?.button; if (!button) { throwError(`Button handler not found for "${commandName}"`); diff --git a/src/events/interactionCreate/handlers/chatInputCommand.ts b/src/events/interactionCreate/handlers/chatInputCommand.ts index 18b81dcb6..e603b4048 100644 --- a/src/events/interactionCreate/handlers/chatInputCommand.ts +++ b/src/events/interactionCreate/handlers/chatInputCommand.ts @@ -16,7 +16,7 @@ import { handleError } from '../../error'; export default async function handleChatInputCommand( interaction: ChatInputCommandInteraction, ) { - const registry = commands[interaction.commandName]; + const registry = (await commands)[interaction.commandName]; console.log(interaction.toString()); await interaction.deferReply(); diff --git a/src/routes/stats.tsx b/src/routes/stats.tsx index b06a87a7a..a096d4181 100644 --- a/src/routes/stats.tsx +++ b/src/routes/stats.tsx @@ -6,7 +6,7 @@ import { RouteRegistry } from '../server'; export const statsRoute: RouteRegistry = { route: '/stats/:type/:period', inProduction: true, - inDevelopment: false, + inDevelopment: true, async handler(req) { const type = req.params.type as StatType; diff --git a/src/routes/today.tsx b/src/routes/today.tsx index 8a5f193ed..82f2631a7 100644 --- a/src/routes/today.tsx +++ b/src/routes/today.tsx @@ -5,7 +5,7 @@ import { RouteRegistry } from '../server'; export const todayRoute: RouteRegistry = { route: '/today', inProduction: true, - inDevelopment: false, + inDevelopment: true, async handler(req) { const player = resolvePlayerFromRequest(req); From a734a71d2f09e6b9c8f9f98682c5e76611ae6b69 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Fri, 4 Aug 2023 18:25:20 +0000 Subject: [PATCH 05/36] fix: duplicate efficienty cards --- src/components/GenericAllStats/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GenericAllStats/index.tsx b/src/components/GenericAllStats/index.tsx index 2a7784a4b..3919b7c14 100644 --- a/src/components/GenericAllStats/index.tsx +++ b/src/components/GenericAllStats/index.tsx @@ -96,7 +96,7 @@ export default function GenericAllStats({ ]} /> Date: Fri, 4 Aug 2023 19:49:40 +0000 Subject: [PATCH 06/36] feat: filter functionality --- src/commands/stats.tsx | 33 +++- src/components/Tanks/components/Item.tsx | 18 ++- src/core/blitz/getTreeType.ts | 8 +- .../discord/addStatTypeSubCommandGroups.ts | 20 ++- src/renderers/evolution.tsx | 2 +- src/renderers/fullStats.tsx | 3 +- src/renderers/stats.tsx | 150 ++++++++++-------- 7 files changed, 143 insertions(+), 91 deletions(-) diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index c4de71558..b66eb48cf 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -1,4 +1,5 @@ import { SlashCommandBuilder } from 'discord.js'; +import { TreeTypeString } from '../components/Tanks'; import { CYCLIC_API } from '../constants/cyclic'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankId from '../core/blitz/resolveTankId'; @@ -14,8 +15,7 @@ import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { secrets } from '../core/node/secrets'; import { CommandRegistryRaw } from '../events/interactionCreate'; -import { StatType } from '../renderers/fullStats'; -import stats from '../renderers/stats'; +import stats, { MultiTankFilters, StatType } from '../renderers/stats'; import { AccountInfo } from '../types/accountInfo'; export const statsCommand = new Promise(async (resolve) => { @@ -61,7 +61,23 @@ export const statsCommand = new Promise(async (resolve) => { )[player.id]; return [ - await stats(commandGroup, resolvedPeriod, player, tankId), + await stats( + commandGroup, + resolvedPeriod, + player, + commandGroup === 'tank' + ? tankId + : commandGroup === 'multi-tank' + ? ({ + 'tank-type': + interaction.options.getString('tank-type') ?? undefined, + nation: interaction.options.getString('nation') ?? undefined, + tier: interaction.options.getInteger('tier') ?? undefined, + 'tree-type': (interaction.options.getString('tree-type') ?? + undefined) as TreeTypeString | undefined, + } satisfies Partial) + : null, + ), primaryButton(path, 'Refresh'), linkButton(`${CYCLIC_API}/${path}`, 'Embed'), linkButton( @@ -87,7 +103,16 @@ export const statsCommand = new Promise(async (resolve) => { commandGroup, period, player, - parseInt(url.searchParams.get('tankId')!), + commandGroup === 'tank' + ? parseInt(url.searchParams.get('tankId')!) + : commandGroup === 'multi-tank' + ? ({ + 'tank-type': url.searchParams.get('tank-type')!, + nation: url.searchParams.get('nation')!, + tier: parseInt(url.searchParams.get('tier')!), + 'tree-type': url.searchParams.get('tree-type') as TreeTypeString, + } satisfies MultiTankFilters) + : null, ); }, }); diff --git a/src/components/Tanks/components/Item.tsx b/src/components/Tanks/components/Item.tsx index d7d2cd546..9d9b52ef7 100644 --- a/src/components/Tanks/components/Item.tsx +++ b/src/components/Tanks/components/Item.tsx @@ -5,29 +5,31 @@ import { } from '../../../core/blitz/tankopedia'; import { theme } from '../../../stitches.config'; -export enum TreeType { +export enum TreeTypeEnum { TechTree, Premium, Collector, } +export type TreeTypeString = 'techtree' | 'premium' | 'collector'; + export interface ItemProps { image?: string; tankType?: string; name: string; - treeType: TreeType; + treeType: TreeTypeEnum; } const TREE_TYPE_COLOR = { - [TreeType.TechTree]: '', - [TreeType.Premium]: '_amber', - [TreeType.Collector]: '_blue', + [TreeTypeEnum.TechTree]: '', + [TreeTypeEnum.Premium]: '_amber', + [TreeTypeEnum.Collector]: '_blue', } as const; const TREE_TYPE_ICONS = { - [TreeType.TechTree]: TANK_ICONS, - [TreeType.Premium]: TANK_ICONS_PREMIUM, - [TreeType.Collector]: TANK_ICONS_COLLECTOR, + [TreeTypeEnum.TechTree]: TANK_ICONS, + [TreeTypeEnum.Premium]: TANK_ICONS_PREMIUM, + [TreeTypeEnum.Collector]: TANK_ICONS_COLLECTOR, }; export function Item({ image, tankType, name, treeType }: ItemProps) { diff --git a/src/core/blitz/getTreeType.ts b/src/core/blitz/getTreeType.ts index bdbf303be..5c5e4f1ec 100644 --- a/src/core/blitz/getTreeType.ts +++ b/src/core/blitz/getTreeType.ts @@ -1,11 +1,11 @@ -import { TreeType } from '../../components/Tanks'; +import { TreeTypeEnum } from '../../components/Tanks'; import { tankopedia } from './tankopedia'; export default async function getTreeType(id: number) { const entry = (await tankopedia)[id]; - if (entry?.is_collectible) return TreeType.Collector; - if (entry?.is_premium) return TreeType.Premium; + if (entry?.is_collectible) return TreeTypeEnum.Collector; + if (entry?.is_premium) return TreeTypeEnum.Premium; - return TreeType.TechTree; + return TreeTypeEnum.TechTree; } diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommandGroups.ts index e9e5dbb01..e182d0ad8 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommandGroups.ts @@ -1,4 +1,6 @@ import { SlashCommandBuilder } from 'discord.js'; +import { TreeTypeString } from '../../components/Tanks'; +import { StatType } from '../../renderers/stats'; import { tankopediaInfo } from '../blitz/tankopedia'; import addPeriodSubCommands from './addPeriodSubCommands'; import addTankChoices from './addTankChoices'; @@ -14,7 +16,7 @@ export default async function addStatTypeSubCommandGroups( addPeriodSubCommands(option, (option) => option.addStringOption(addUsernameChoices), ) - .setName('player') + .setName('player' satisfies StatType) .setDescription('Player statistics'), ) .addSubcommandGroup((option) => @@ -23,7 +25,7 @@ export default async function addStatTypeSubCommandGroups( .addStringOption(addTankChoices) .addStringOption(addUsernameChoices), ) - .setName('tank') + .setName('tank' satisfies StatType) .setDescription('Tank statistics'), ) .addSubcommandGroup((option) => @@ -69,14 +71,20 @@ export default async function addStatTypeSubCommandGroups( .setName('tree-type') .setDescription('Tech tree type') .addChoices( - { name: 'Tech Tree', value: 'techtree' }, - { name: 'Premium', value: 'premium' }, - { name: 'Collector', value: 'collector' }, + { + name: 'Tech Tree', + value: 'techtree' satisfies TreeTypeString, + }, + { name: 'Premium', value: 'premium' satisfies TreeTypeString }, + { + name: 'Collector', + value: 'collector' satisfies TreeTypeString, + }, ) .setRequired(false), ), ) - .setName('multi-tank') + .setName('multi-tank' satisfies StatType) .setDescription('Multiple tanks'), ); } diff --git a/src/renderers/evolution.tsx b/src/renderers/evolution.tsx index 1267cf92c..70d4ab111 100644 --- a/src/renderers/evolution.tsx +++ b/src/renderers/evolution.tsx @@ -15,7 +15,7 @@ import { secrets } from '../core/node/secrets'; import { AccountInfo } from '../types/accountInfo'; import { Histories } from '../types/histories'; import { PlayerClanData } from '../types/playerClanData'; -import { StatType } from './fullStats'; +import { StatType } from './stats'; export default async function evolution( type: Type, diff --git a/src/renderers/fullStats.tsx b/src/renderers/fullStats.tsx index c6fc3a6d2..da8e26853 100644 --- a/src/renderers/fullStats.tsx +++ b/src/renderers/fullStats.tsx @@ -22,8 +22,7 @@ import { } from '../types/accountInfo'; import { PlayerClanData } from '../types/playerClanData'; import { PossiblyPromise } from '../types/possiblyPromise'; - -export type StatType = 'player' | 'tank'; +import { StatType } from './stats'; export default async function fullStats( type: Type, diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index 8c0287cc4..40e0dacda 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -1,6 +1,7 @@ import AllStatsOverview from '../components/AllStatsOverview'; import NoData, { NoDataType } from '../components/NoData'; import PoweredBy, { PoweredByType } from '../components/PoweredBy'; +import { TreeTypeString } from '../components/Tanks'; import { TierWeightsRecord } from '../components/TierWeights'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; @@ -22,15 +23,25 @@ import { SupplementaryStats, } from '../types/accountInfo'; import { PlayerClanData } from '../types/playerClanData'; -import { PossiblyPromise } from '../types/possiblyPromise'; -export type StatType = 'player' | 'tank'; +export type StatType = 'player' | 'tank' | 'multi-tank'; + +export interface MultiTankFilters { + nation: string; + 'tank-type': string; + tier: number; + 'tree-type': TreeTypeString; +} export default async function stats( type: Type, { start, end, statsName }: ResolvedPeriod, { region: server, id }: ResolvedPlayer, - tankId: Type extends 'tank' ? number : null, + filters: Type extends 'tank' + ? number + : Type extends 'multi-tank' + ? Partial + : null, naked = false, ) { let nameDiscriminator: string | undefined; @@ -48,91 +59,98 @@ export default async function stats( ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` : undefined; } else if (type === 'tank') { - nameDiscriminator = `(${await resolveTankName(tankId!)})`; - image = (await tankopedia)[tankId!]?.images.normal; + nameDiscriminator = `(${await resolveTankName(filters as number)})`; + image = (await tankopedia)[filters as number]?.images.normal; + } else { } - const { diffed } = await getDiffedTankStats(server, id, start, end); + const { diffed, order } = await getDiffedTankStats(server, id, start, end); + const awaitedTankopedia = await tankopedia; let stats: AllStats | undefined; let supplementaryStats: SupplementaryStats; let tierWeights: TierWeightsRecord; - if (type === 'player') { - const entries = Object.entries(diffed); - stats = sumStats(entries.map(([, stats]) => stats)); - const battlesOfTanksWithAverages = await entries.reduce< - PossiblyPromise - >(async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankAverage = (await tankAverages)[tankId]; - - return tankAverage ? (await accumulator) + stats.battles : accumulator; - }, 0); - const battlesOfTanksWithTankopediaEntry = await entries.reduce< - PossiblyPromise - >(async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankopediaEntry = (await tankopedia)[tankId]; - - return tankopediaEntry - ? (await accumulator) + stats.battles - : accumulator; - }, 0); + if (type === 'player' || type === 'multi-tank') { + const typedFilters = filters as Partial | null; + const filteredOrder = order.filter( + (id) => + typedFilters === null || + ((typedFilters.nation === undefined || + awaitedTankopedia[id]?.nation === typedFilters.nation) && + (typedFilters['tank-type'] === undefined || + awaitedTankopedia[id]?.type === typedFilters['tank-type']) && + (typedFilters.tier === undefined || + awaitedTankopedia[id]?.tier === typedFilters.tier) && + (typedFilters['tree-type'] === undefined || + (typedFilters['tree-type'] === 'collector' && + awaitedTankopedia[id]?.is_collectible) || + (typedFilters['tree-type'] === 'premium' && + awaitedTankopedia[id]?.is_premium) || + (typedFilters['tree-type'] === 'techtree' && + !awaitedTankopedia[id]?.is_collectible && + !awaitedTankopedia[id]?.is_premium))), + ); + const awaitedTankAverages = await tankAverages; + stats = sumStats(filteredOrder.map((id) => diffed[id])); + const battlesOfTanksWithAverages = filteredOrder.reduce( + (accumulator, id) => + awaitedTankAverages[id] + ? accumulator + diffed[id].battles + : accumulator, + 0, + ); + const battlesOfTanksWithTankopediaEntry = filteredOrder.reduce( + (accumulator, id) => + awaitedTankopedia[id] ? accumulator + diffed[id].battles : accumulator, + 0, + ); supplementaryStats = { WN8: - (await entries.reduce>( - async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankAverage = (await tankAverages)[tankId]; - - return tankAverage - ? (await accumulator) + - calculateWN8(tankAverage.all, stats) * stats.battles - : accumulator; - }, + filteredOrder.reduce( + (accumulator, id) => + awaitedTankAverages[id] + ? accumulator + + calculateWN8(awaitedTankAverages[id].all, diffed[id]) * + diffed[id].battles + : accumulator, 0, - )) / battlesOfTanksWithAverages, + ) / battlesOfTanksWithAverages, tier: - (await entries.reduce>( - async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankopediaEntry = (await tankopedia)[tankId]; - - return tankopediaEntry - ? (await accumulator) + tankopediaEntry.tier * stats.battles - : accumulator; - }, + filteredOrder.reduce( + (accumulator, id) => + awaitedTankopedia[id] + ? accumulator + awaitedTankopedia[id]!.tier * diffed[id].battles + : accumulator, 0, - )) / battlesOfTanksWithTankopediaEntry, + ) / battlesOfTanksWithTankopediaEntry, }; - tierWeights = await entries.reduce>( - async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankopediaEntry = (await tankopedia)[tankId]; + tierWeights = filteredOrder.reduce((accumulator, id) => { + const tankopediaEntry = awaitedTankopedia[id]; - if (!tankopediaEntry) return accumulator; + if (!tankopediaEntry) return accumulator; - const tier = tankopediaEntry.tier as Tier; + const tier = tankopediaEntry.tier as Tier; - if ((await accumulator)[tier]) { - (await accumulator)[tier]! += stats.battles; - } else { - (await accumulator)[tier] = stats.battles; - } + if (accumulator[tier]) { + accumulator[tier]! += diffed[id].battles; + } else { + accumulator[tier] = diffed[id].battles; + } - return accumulator; - }, - {}, - ); + return accumulator; + }, {}); } else { - stats = diffed[tankId!]; + stats = diffed[(filters as number)!]; supplementaryStats = { WN8: stats - ? calculateWN8((await tankAverages)[tankId!].all, diffed[tankId!]) + ? calculateWN8( + (await tankAverages)[(filters as number)!].all, + diffed[(filters as number)!], + ) : undefined, - tier: (await tankopedia)[tankId!]?.tier, + tier: (await tankopedia)[(filters as number)!]?.tier, }; } From af54f12ec3157f461fd2bcc2bb787fe109042996 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Sun, 6 Aug 2023 18:38:17 +0000 Subject: [PATCH 07/36] feat: set width to 480px --- src/commands/debug.ts | 2 +- src/commands/eligible.ts | 2 +- src/commands/evolution.tsx | 2 +- src/commands/fullStats.tsx | 2 +- src/commands/help.ts | 2 +- src/commands/inactive.tsx | 2 +- src/commands/ownedTanks.tsx | 2 +- src/commands/ping.ts | 2 +- src/commands/playerAchievements.ts | 2 +- src/commands/playerInfo.ts | 2 +- src/commands/ratings.tsx | 2 +- src/commands/searchClans.ts | 2 +- src/commands/searchPlayers.ts | 2 +- src/commands/searchTanks.ts | 2 +- src/commands/today.tsx | 2 +- src/commands/verify.ts | 2 +- src/components/Wrapper.tsx | 2 +- src/core/node/jsxToSvg.ts | 2 +- src/routes/stats.tsx | 2 +- src/routes/today.tsx | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/commands/debug.ts b/src/commands/debug.ts index fa7240883..76bd7c807 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -12,7 +12,7 @@ const executionStart = new Date().getTime(); export const debugCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index eadb83c0b..06ad066fd 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -26,7 +26,7 @@ export const SKILLED_CLANS: Record = { export const eligibleCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: false, command: new SlashCommandBuilder() diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index f2b1c0e50..5da0c83b5 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -28,7 +28,7 @@ export const evolutionCommand = new Promise( resolve({ inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command, diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 7b3162f9e..689d958ba 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -27,7 +27,7 @@ export const fullStatsCommand = new Promise( resolve({ inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command, diff --git a/src/commands/help.ts b/src/commands/help.ts index a85d83319..ce8d76f93 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -30,7 +30,7 @@ function addDocsSubcommands(option: SlashCommandSubcommandsOnlyBuilder) { export const helpCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: addDocsSubcommands( diff --git a/src/commands/inactive.tsx b/src/commands/inactive.tsx index 1e8e4b947..8784e03d7 100644 --- a/src/commands/inactive.tsx +++ b/src/commands/inactive.tsx @@ -18,7 +18,7 @@ const DEFAULT_THRESHOLD = 7; export const inactiveCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 33e3031e4..82e338d6d 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -45,7 +45,7 @@ const COMP_TANKS = [ export const ownedTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ping.ts b/src/commands/ping.ts index a9fcd5146..83b65d115 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -3,7 +3,7 @@ import { CommandRegistry } from '../events/interactionCreate'; export const pingCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, handlesInteraction: true, diff --git a/src/commands/playerAchievements.ts b/src/commands/playerAchievements.ts index 2a81a2d2d..60cdf86fa 100644 --- a/src/commands/playerAchievements.ts +++ b/src/commands/playerAchievements.ts @@ -16,7 +16,7 @@ type SortBy = 'name' | 'count'; export const playerAchievementsCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/playerInfo.ts b/src/commands/playerInfo.ts index ce26234c5..704daf3b0 100644 --- a/src/commands/playerInfo.ts +++ b/src/commands/playerInfo.ts @@ -12,7 +12,7 @@ import { AccountInfo } from '../types/accountInfo'; export const playerInfoCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 9c129dbed..98eac5b01 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -121,7 +121,7 @@ export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; export const ratingsCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchClans.ts b/src/commands/searchClans.ts index a7ca4e6f8..01ca6061d 100644 --- a/src/commands/searchClans.ts +++ b/src/commands/searchClans.ts @@ -11,7 +11,7 @@ import { ClanList } from '../types/clanList'; export const searchClansCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchPlayers.ts b/src/commands/searchPlayers.ts index b6c62b6d2..536d5fb18 100644 --- a/src/commands/searchPlayers.ts +++ b/src/commands/searchPlayers.ts @@ -12,7 +12,7 @@ import { AccountList } from '../types/accountList'; export const searchPlayersCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchTanks.ts b/src/commands/searchTanks.ts index f2330aad5..f02b95c05 100644 --- a/src/commands/searchTanks.ts +++ b/src/commands/searchTanks.ts @@ -8,7 +8,7 @@ import { CommandRegistry } from '../events/interactionCreate'; export const searchTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/today.tsx b/src/commands/today.tsx index f8505c682..9a5a7a037 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -15,7 +15,7 @@ import { AccountInfo } from '../types/accountInfo'; export const todayCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/verify.ts b/src/commands/verify.ts index c6682c1cf..1dea0918f 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -14,7 +14,7 @@ import { PlayerClanData } from '../types/playerClanData'; export const verifyCommand: CommandRegistry = { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/components/Wrapper.tsx b/src/components/Wrapper.tsx index 41fadd94a..ce3aad029 100644 --- a/src/components/Wrapper.tsx +++ b/src/components/Wrapper.tsx @@ -14,7 +14,7 @@ export default function Wrapper({ children, naked }: WrapperProps) { flexDirection: 'column', padding: 16, gap: 32, - width: 640, + width: 480, color: theme.colors.textHighContrast, ...(!naked && { diff --git a/src/core/node/jsxToSvg.ts b/src/core/node/jsxToSvg.ts index 5c6aa1290..eaffb3e5a 100644 --- a/src/core/node/jsxToSvg.ts +++ b/src/core/node/jsxToSvg.ts @@ -32,7 +32,7 @@ Promise.all( export default async function jsxToSvg(jsx: JSX.Element) { return await satori(jsx, { - width: 640, + width: 480, fonts: [ { data: roboto, name: FONT_NAME, weight: 400 }, { data: robotoBold, name: FONT_NAME, weight: 700 }, diff --git a/src/routes/stats.tsx b/src/routes/stats.tsx index a096d4181..b06a87a7a 100644 --- a/src/routes/stats.tsx +++ b/src/routes/stats.tsx @@ -6,7 +6,7 @@ import { RouteRegistry } from '../server'; export const statsRoute: RouteRegistry = { route: '/stats/:type/:period', inProduction: true, - inDevelopment: true, + inDevelopment: false, async handler(req) { const type = req.params.type as StatType; diff --git a/src/routes/today.tsx b/src/routes/today.tsx index 82f2631a7..8a5f193ed 100644 --- a/src/routes/today.tsx +++ b/src/routes/today.tsx @@ -5,7 +5,7 @@ import { RouteRegistry } from '../server'; export const todayRoute: RouteRegistry = { route: '/today', inProduction: true, - inDevelopment: true, + inDevelopment: false, async handler(req) { const player = resolvePlayerFromRequest(req); From e6fb17918430994997e8288c47e26aea7bda7101 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Sun, 6 Aug 2023 18:38:49 +0000 Subject: [PATCH 08/36] feat: removed self-advert from title bar --- src/components/TitleBar.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 7b5f88a7d..84d332c59 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -1,4 +1,3 @@ -import isDev from '../core/node/isDev'; import { theme } from '../stitches.config'; export interface TitleBarProps { @@ -70,15 +69,6 @@ export default function TitleBar({ - - ); } From d5de7150328a863055c99081a612e8aea0a8e9e2 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Sun, 6 Aug 2023 18:39:23 +0000 Subject: [PATCH 09/36] fix: title bar spacing is now 4px just like figma --- src/components/TitleBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 84d332c59..3b44769c9 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -30,7 +30,7 @@ export default function TitleBar({ )}
Date: Sun, 6 Aug 2023 18:41:59 +0000 Subject: [PATCH 10/36] feat: removed region text in title bar --- src/commands/inactive.tsx | 5 +---- src/commands/ownedTanks.tsx | 5 +---- src/commands/ratings.tsx | 6 ++---- src/renderers/evolution.tsx | 5 +---- src/renderers/fullStats.tsx | 5 +---- src/renderers/stats.tsx | 5 +---- src/renderers/today.tsx | 5 +---- 7 files changed, 8 insertions(+), 28 deletions(-) diff --git a/src/commands/inactive.tsx b/src/commands/inactive.tsx index 8784e03d7..bb4ecf25c 100644 --- a/src/commands/inactive.tsx +++ b/src/commands/inactive.tsx @@ -4,7 +4,6 @@ import NoData, { NoDataType } from '../components/NoData'; import PoweredBy, { PoweredByType } from '../components/PoweredBy'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { REGION_NAMES } from '../constants/regions'; import getBlitzClan from '../core/blitz/getBlitzClan'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import addClanChoices from '../core/discord/addClanChoices'; @@ -71,9 +70,7 @@ export const inactiveCommand: CommandRegistry = { name={clanData.name} nameDiscriminator="(Inactivity)" image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData.emblem_set_id}.png`} - description={`${threshold}+ Days • ${new Date().toDateString()} • ${ - REGION_NAMES[server] - }`} + description={`${threshold}+ Days • ${new Date().toDateString()}`} /> {!hasInactiveMembers && } diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 82e338d6d..91d9764ee 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -4,7 +4,6 @@ import PoweredBy, { PoweredByType } from '../components/PoweredBy'; import * as Tanks from '../components/Tanks'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { REGION_NAMES } from '../constants/regions'; import getTankStats from '../core/blitz/getTankStats'; import getTreeType from '../core/blitz/getTreeType'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; @@ -104,9 +103,7 @@ export const ownedTanksCommand: CommandRegistry = { ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData[id]?.clan?.emblem_set_id}.png` : undefined } - description={`Owned tanks • ${new Date().toDateString()} • ${ - REGION_NAMES[server] - }`} + description={`Owned tanks • ${new Date().toDateString()}`} /> {tanks.length === 0 && } diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 98eac5b01..6b5c8e97f 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -4,7 +4,7 @@ import { octokit } from '../bot'; import * as Leaderboard from '../components/Leaderboard'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { REGION_NAMES, Region } from '../constants/regions'; +import { Region } from '../constants/regions'; import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain'; import addRegionChoices from '../core/discord/addRegionChoices'; import { CommandRegistry } from '../events/interactionCreate'; @@ -241,9 +241,7 @@ export const ratingsCommand: CommandRegistry = { ? leagueInfo.big_icon : `https:${leagueInfo.big_icon}` } - description={`Top ${limit} players • ${new Date().toDateString()} • ${ - REGION_NAMES[region] - }`} + description={`Top ${limit} players • ${new Date().toDateString()}`} /> diff --git a/src/renderers/evolution.tsx b/src/renderers/evolution.tsx index 70d4ab111..e40dde18c 100644 --- a/src/renderers/evolution.tsx +++ b/src/renderers/evolution.tsx @@ -3,7 +3,6 @@ import NoData, { NoDataType } from '../components/NoData'; import PoweredBy, { PoweredByType } from '../components/PoweredBy'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { REGION_NAMES } from '../constants/regions'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankName from '../core/blitz/resolveTankName'; import { tankopedia } from '../core/blitz/tankopedia'; @@ -88,9 +87,7 @@ export default async function evolution( name={accountInfo[id].nickname} nameDiscriminator={nameDiscriminator} image={image} - description={`${evolutionName} • ${new Date().toDateString()} • ${ - REGION_NAMES[server] - }`} + description={`${evolutionName} • ${new Date().toDateString()}`} /> {/* goofy ahh bug forces me to call them as functions */} diff --git a/src/renderers/fullStats.tsx b/src/renderers/fullStats.tsx index da8e26853..9b6041152 100644 --- a/src/renderers/fullStats.tsx +++ b/src/renderers/fullStats.tsx @@ -4,7 +4,6 @@ import PoweredBy, { PoweredByType } from '../components/PoweredBy'; import TierWeights, { TierWeightsRecord } from '../components/TierWeights'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { REGION_NAMES } from '../constants/regions'; import calculateWN8 from '../core/blitz/calculateWN8'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankName from '../core/blitz/resolveTankName'; @@ -141,9 +140,7 @@ export default async function fullStats( name={accountInfo[id].nickname} nameDiscriminator={nameDiscriminator} image={image} - description={`${statsName} • ${new Date().toDateString()} • ${ - REGION_NAMES[server] - }`} + description={`${statsName} • ${new Date().toDateString()}`} /> {stats.battles === 0 && } diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index 40e0dacda..df8c3a599 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -5,7 +5,6 @@ import { TreeTypeString } from '../components/Tanks'; import { TierWeightsRecord } from '../components/TierWeights'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { REGION_NAMES } from '../constants/regions'; import calculateWN8 from '../core/blitz/calculateWN8'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankName from '../core/blitz/resolveTankName'; @@ -181,9 +180,7 @@ export default async function stats( name={accountInfo[id].nickname} nameDiscriminator={nameDiscriminator} image={image} - description={`${statsName} • ${new Date().toDateString()} • ${ - REGION_NAMES[server] - }`} + description={`${statsName} • ${new Date().toDateString()}`} /> {!stats?.battles && } diff --git a/src/renderers/today.tsx b/src/renderers/today.tsx index d3afd0ba1..e231cb280 100644 --- a/src/renderers/today.tsx +++ b/src/renderers/today.tsx @@ -3,7 +3,6 @@ import NoData, { NoDataType } from '../components/NoData'; import PoweredBy, { PoweredByType } from '../components/PoweredBy'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { REGION_NAMES } from '../constants/regions'; import calculateWN8 from '../core/blitz/calculateWN8'; import getTankStats from '../core/blitz/getTankStats'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; @@ -161,9 +160,7 @@ export default async function today( ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData[id]?.clan?.emblem_set_id}.png` : undefined } - description={`Today's breakdown • ${new Date().toDateString()} • ${ - REGION_NAMES[server] - }`} + description={`Today's breakdown • ${new Date().toDateString()}`} /> {rows.length === 0 && } From 92149b0f001d2e84b650684187aafe5c49a2ddf7 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Sun, 6 Aug 2023 18:53:57 +0000 Subject: [PATCH 11/36] feat: matched titlebar style to figma --- src/components/TitleBar.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 3b44769c9..05c7bb831 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -18,10 +18,11 @@ export default function TitleBar({ style={{ display: 'flex', alignItems: 'center', - gap: 16, + justifyContent: 'center', + overflow: 'hidden', }} > -
+
{image && (
{name} @@ -52,7 +60,6 @@ export default function TitleBar({ {nameDiscriminator && ( Date: Sun, 6 Aug 2023 19:00:00 +0000 Subject: [PATCH 12/36] fix: import types from stats instead of fullstats --- src/commands/evolution.tsx | 2 +- src/commands/fullStats.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 5da0c83b5..761623808 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -15,7 +15,7 @@ import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { secrets } from '../core/node/secrets'; import { CommandRegistryRaw } from '../events/interactionCreate'; import evolution from '../renderers/evolution'; -import { StatType } from '../renderers/fullStats'; +import { StatType } from '../renderers/stats'; import { AccountInfo } from '../types/accountInfo'; export const evolutionCommand = new Promise( diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 689d958ba..c86e97515 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -14,7 +14,8 @@ import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { secrets } from '../core/node/secrets'; import { CommandRegistryRaw } from '../events/interactionCreate'; -import fullStats, { StatType } from '../renderers/fullStats'; +import fullStats from '../renderers/fullStats'; +import { StatType } from '../renderers/stats'; import { AccountInfo } from '../types/accountInfo'; export const fullStatsCommand = new Promise( From cac82f3124f2c89df8e60f86654edeb1b6966cf6 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Sun, 6 Aug 2023 19:00:32 +0000 Subject: [PATCH 13/36] fix: renamed statsfull to fullstats in power by bar --- src/renderers/stats.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index df8c3a599..3b6455c76 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -168,7 +168,7 @@ export default async function stats( gap: 4, }} > - /statsfull for all statistics + /fullstats for all statistics ); From 7823b8ad2e088e7fcdf23671c8c2062fd7fd7d8a Mon Sep 17 00:00:00 2001 From: tresabhi Date: Sun, 6 Aug 2023 19:07:44 +0000 Subject: [PATCH 14/36] feat: updated /evolution to new width --- src/commands/debug.ts | 2 +- src/commands/eligible.ts | 2 +- src/commands/evolution.tsx | 2 +- src/commands/fullStats.tsx | 2 +- src/commands/help.ts | 2 +- src/commands/inactive.tsx | 2 +- src/commands/ownedTanks.tsx | 2 +- src/commands/ping.ts | 2 +- src/commands/playerAchievements.ts | 2 +- src/commands/playerInfo.ts | 2 +- src/commands/ratings.tsx | 2 +- src/commands/searchClans.ts | 2 +- src/commands/searchPlayers.ts | 2 +- src/commands/searchTanks.ts | 2 +- src/commands/today.tsx | 2 +- src/commands/verify.ts | 2 +- src/components/Graph/components/Root.tsx | 30 +++++++++++++----------- src/renderers/evolution.tsx | 1 - src/routes/stats.tsx | 2 +- src/routes/today.tsx | 2 +- 20 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/commands/debug.ts b/src/commands/debug.ts index 76bd7c807..fa7240883 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -12,7 +12,7 @@ const executionStart = new Date().getTime(); export const debugCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index 06ad066fd..eadb83c0b 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -26,7 +26,7 @@ export const SKILLED_CLANS: Record = { export const eligibleCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: false, command: new SlashCommandBuilder() diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 761623808..797fdb002 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -28,7 +28,7 @@ export const evolutionCommand = new Promise( resolve({ inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command, diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index c86e97515..e0b87937e 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -28,7 +28,7 @@ export const fullStatsCommand = new Promise( resolve({ inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command, diff --git a/src/commands/help.ts b/src/commands/help.ts index ce8d76f93..a85d83319 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -30,7 +30,7 @@ function addDocsSubcommands(option: SlashCommandSubcommandsOnlyBuilder) { export const helpCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: addDocsSubcommands( diff --git a/src/commands/inactive.tsx b/src/commands/inactive.tsx index bb4ecf25c..9fc709e57 100644 --- a/src/commands/inactive.tsx +++ b/src/commands/inactive.tsx @@ -17,7 +17,7 @@ const DEFAULT_THRESHOLD = 7; export const inactiveCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 91d9764ee..53869e605 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -44,7 +44,7 @@ const COMP_TANKS = [ export const ownedTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 83b65d115..a9fcd5146 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -3,7 +3,7 @@ import { CommandRegistry } from '../events/interactionCreate'; export const pingCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, handlesInteraction: true, diff --git a/src/commands/playerAchievements.ts b/src/commands/playerAchievements.ts index 60cdf86fa..2a81a2d2d 100644 --- a/src/commands/playerAchievements.ts +++ b/src/commands/playerAchievements.ts @@ -16,7 +16,7 @@ type SortBy = 'name' | 'count'; export const playerAchievementsCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/playerInfo.ts b/src/commands/playerInfo.ts index 704daf3b0..ce26234c5 100644 --- a/src/commands/playerInfo.ts +++ b/src/commands/playerInfo.ts @@ -12,7 +12,7 @@ import { AccountInfo } from '../types/accountInfo'; export const playerInfoCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 6b5c8e97f..ae9a64238 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -121,7 +121,7 @@ export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; export const ratingsCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchClans.ts b/src/commands/searchClans.ts index 01ca6061d..a7ca4e6f8 100644 --- a/src/commands/searchClans.ts +++ b/src/commands/searchClans.ts @@ -11,7 +11,7 @@ import { ClanList } from '../types/clanList'; export const searchClansCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchPlayers.ts b/src/commands/searchPlayers.ts index 536d5fb18..b6c62b6d2 100644 --- a/src/commands/searchPlayers.ts +++ b/src/commands/searchPlayers.ts @@ -12,7 +12,7 @@ import { AccountList } from '../types/accountList'; export const searchPlayersCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/searchTanks.ts b/src/commands/searchTanks.ts index f02b95c05..f2330aad5 100644 --- a/src/commands/searchTanks.ts +++ b/src/commands/searchTanks.ts @@ -8,7 +8,7 @@ import { CommandRegistry } from '../events/interactionCreate'; export const searchTanksCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/today.tsx b/src/commands/today.tsx index 9a5a7a037..f8505c682 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -15,7 +15,7 @@ import { AccountInfo } from '../types/accountInfo'; export const todayCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 1dea0918f..c6682c1cf 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -14,7 +14,7 @@ import { PlayerClanData } from '../types/playerClanData'; export const verifyCommand: CommandRegistry = { inProduction: true, - inDevelopment: false, + inDevelopment: true, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/components/Graph/components/Root.tsx b/src/components/Graph/components/Root.tsx index cc5d6df8a..7f838ace8 100644 --- a/src/components/Graph/components/Root.tsx +++ b/src/components/Graph/components/Root.tsx @@ -3,7 +3,7 @@ import { ReactNode } from 'react'; import { theme } from '../../../stitches.config'; import { Margin, MarginOrientation } from './Margin'; -export const GRAPH_WIDTH = 544; +export const GRAPH_WIDTH = 384; export const GRAPH_HEIGHT = 320; export interface MarginInputProps { @@ -20,7 +20,7 @@ export interface RootProps { separations?: number; xMinLabel: string; xMaxLabel: string; - xTitle: string; + xTitle?: string; } export function Root({ @@ -91,18 +91,20 @@ export function Root({ /> )} -
- - {xTitle} - -
+ {xTitle !== undefined && ( +
+ + {xTitle} + +
+ )}
); } diff --git a/src/renderers/evolution.tsx b/src/renderers/evolution.tsx index e40dde18c..5d4a7833a 100644 --- a/src/renderers/evolution.tsx +++ b/src/renderers/evolution.tsx @@ -93,7 +93,6 @@ export default async function evolution( {/* goofy ahh bug forces me to call them as functions */} {plot.length > 0 && ( Date: Sun, 6 Aug 2023 19:08:02 +0000 Subject: [PATCH 15/36] feat: made footer center aligned when without footer text --- src/components/PoweredBy.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/PoweredBy.tsx b/src/components/PoweredBy.tsx index 8b224b1d7..ef080ddce 100644 --- a/src/components/PoweredBy.tsx +++ b/src/components/PoweredBy.tsx @@ -24,8 +24,13 @@ const POWERED_BY_NAMES: Record = { export default function PoweredBy({ type, footer }: PoweredByProps) { return ( -
-
{footer}
+
+ {footer !== undefined &&
{footer}
}
From b455e3380fc11fd10bd5320d1c3dbba994c5e711 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Sun, 6 Aug 2023 19:13:28 +0000 Subject: [PATCH 16/36] feat: added minimum character limit to clan autocomplete --- src/core/discord/autocompleteClan.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/discord/autocompleteClan.ts b/src/core/discord/autocompleteClan.ts index 6f0efb31e..91277e8a0 100644 --- a/src/core/discord/autocompleteClan.ts +++ b/src/core/discord/autocompleteClan.ts @@ -11,6 +11,7 @@ export default async function autocompleteClan( ) { const focusedOption = interaction.options.getFocused(true); if (focusedOption.name !== 'clan') return; + if (focusedOption.value.length < 2) return interaction.respond([]); const players = await listClans(focusedOption.value); await interaction.respond( From 338cec4f18bcbae8eac1c46fa841005e397898ef Mon Sep 17 00:00:00 2001 From: tresabhi Date: Mon, 7 Aug 2023 22:51:04 +0000 Subject: [PATCH 17/36] feat: updated full-stats to new subcommands --- src/commands/fullStats.tsx | 28 ++++++- src/renderers/fullStats.tsx | 146 +++++++++++++++++++----------------- 2 files changed, 104 insertions(+), 70 deletions(-) diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index e0b87937e..a622487b9 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -1,4 +1,5 @@ import { SlashCommandBuilder } from 'discord.js'; +import { TreeTypeString } from '../components/Tanks'; import { CYCLIC_API } from '../constants/cyclic'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankId from '../core/blitz/resolveTankId'; @@ -15,7 +16,7 @@ import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { secrets } from '../core/node/secrets'; import { CommandRegistryRaw } from '../events/interactionCreate'; import fullStats from '../renderers/fullStats'; -import { StatType } from '../renderers/stats'; +import { MultiTankFilters, StatType } from '../renderers/stats'; import { AccountInfo } from '../types/accountInfo'; export const fullStatsCommand = new Promise( @@ -38,7 +39,11 @@ export const fullStatsCommand = new Promise( true, ) as StatType; const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(player.region, interaction); + const resolvedPeriod = resolvePeriodFromCommand( + player.region, + interaction, + ); + const period = interaction.options.getString('period'); const tankIdRaw = interaction.options.getString('tank')!; const tankId = commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; @@ -49,6 +54,7 @@ export const fullStatsCommand = new Promise( tankId, start, end, + period, }); const { nickname } = ( await getWargamingResponse( @@ -57,7 +63,23 @@ export const fullStatsCommand = new Promise( )[player.id]; return [ - await fullStats(commandGroup, period, player, tankId), + await fullStats( + commandGroup, + resolvedPeriod, + player, + commandGroup === 'tank' + ? tankId + : commandGroup === 'multi-tank' + ? ({ + 'tank-type': + interaction.options.getString('tank-type') ?? undefined, + nation: interaction.options.getString('nation') ?? undefined, + tier: interaction.options.getInteger('tier') ?? undefined, + 'tree-type': (interaction.options.getString('tree-type') ?? + undefined) as TreeTypeString | undefined, + } satisfies Partial) + : null, + ), primaryButton(path, 'Refresh'), // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), linkButton( diff --git a/src/renderers/fullStats.tsx b/src/renderers/fullStats.tsx index 9b6041152..6196b0c89 100644 --- a/src/renderers/fullStats.tsx +++ b/src/renderers/fullStats.tsx @@ -20,14 +20,17 @@ import { SupplementaryStats, } from '../types/accountInfo'; import { PlayerClanData } from '../types/playerClanData'; -import { PossiblyPromise } from '../types/possiblyPromise'; -import { StatType } from './stats'; +import { MultiTankFilters, StatType } from './stats'; export default async function fullStats( type: Type, { start, end, statsName }: ResolvedPeriod, { region: server, id }: ResolvedPlayer, - tankId: Type extends 'tank' ? number : null, + filters: Type extends 'tank' + ? number + : Type extends 'multi-tank' + ? Partial + : null, ) { let nameDiscriminator: string | undefined; let image: string | undefined; @@ -44,89 +47,98 @@ export default async function fullStats( ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` : undefined; } else if (type === 'tank') { - nameDiscriminator = `(${await resolveTankName(tankId!)})`; - image = (await tankopedia)[tankId!]?.images.normal; + nameDiscriminator = `(${await resolveTankName(filters as number)})`; + image = (await tankopedia)[filters as number]?.images.normal; + } else { } - const { diffed } = await getDiffedTankStats(server, id, start, end); - let stats: AllStats; + const { diffed, order } = await getDiffedTankStats(server, id, start, end); + const awaitedTankopedia = await tankopedia; + let stats: AllStats | undefined; let supplementaryStats: SupplementaryStats; let tierWeights: TierWeightsRecord; - if (type === 'player') { - const entries = Object.entries(diffed); - stats = sumStats(entries.map(([, stats]) => stats)); - const battlesOfTanksWithAverages = await entries.reduce< - PossiblyPromise - >(async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankAverage = (await tankAverages)[tankId]; - - return tankAverage ? (await accumulator) + stats.battles : accumulator; - }, 0); - const battlesOfTanksWithTankopediaEntry = await entries.reduce< - PossiblyPromise - >(async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankopediaEntry = (await tankopedia)[tankId]; - - return tankopediaEntry - ? (await accumulator) + stats.battles - : accumulator; - }, 0); + if (type === 'player' || type === 'multi-tank') { + const typedFilters = filters as Partial | null; + const filteredOrder = order.filter( + (id) => + typedFilters === null || + ((typedFilters.nation === undefined || + awaitedTankopedia[id]?.nation === typedFilters.nation) && + (typedFilters['tank-type'] === undefined || + awaitedTankopedia[id]?.type === typedFilters['tank-type']) && + (typedFilters.tier === undefined || + awaitedTankopedia[id]?.tier === typedFilters.tier) && + (typedFilters['tree-type'] === undefined || + (typedFilters['tree-type'] === 'collector' && + awaitedTankopedia[id]?.is_collectible) || + (typedFilters['tree-type'] === 'premium' && + awaitedTankopedia[id]?.is_premium) || + (typedFilters['tree-type'] === 'techtree' && + !awaitedTankopedia[id]?.is_collectible && + !awaitedTankopedia[id]?.is_premium))), + ); + const awaitedTankAverages = await tankAverages; + stats = sumStats(filteredOrder.map((id) => diffed[id])); + const battlesOfTanksWithAverages = filteredOrder.reduce( + (accumulator, id) => + awaitedTankAverages[id] + ? accumulator + diffed[id].battles + : accumulator, + 0, + ); + const battlesOfTanksWithTankopediaEntry = filteredOrder.reduce( + (accumulator, id) => + awaitedTankopedia[id] ? accumulator + diffed[id].battles : accumulator, + 0, + ); supplementaryStats = { WN8: - (await entries.reduce>( - async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankAverage = (await tankAverages)[tankId]; - - return tankAverage - ? (await accumulator) + - calculateWN8(tankAverage.all, stats) * stats.battles - : accumulator; - }, + filteredOrder.reduce( + (accumulator, id) => + awaitedTankAverages[id] + ? accumulator + + calculateWN8(awaitedTankAverages[id].all, diffed[id]) * + diffed[id].battles + : accumulator, 0, - )) / battlesOfTanksWithAverages, + ) / battlesOfTanksWithAverages, tier: - (await entries.reduce>( - async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankopediaEntry = (await tankopedia)[tankId]; - - return tankopediaEntry - ? (await accumulator) + tankopediaEntry.tier * stats.battles - : accumulator; - }, + filteredOrder.reduce( + (accumulator, id) => + awaitedTankopedia[id] + ? accumulator + awaitedTankopedia[id]!.tier * diffed[id].battles + : accumulator, 0, - )) / battlesOfTanksWithTankopediaEntry, + ) / battlesOfTanksWithTankopediaEntry, }; - tierWeights = await entries.reduce>( - async (accumulator, [tankIdString, stats]) => { - const tankId = parseInt(tankIdString); - const tankopediaEntry = (await tankopedia)[tankId]; + tierWeights = filteredOrder.reduce((accumulator, id) => { + const tankopediaEntry = awaitedTankopedia[id]; - if (!tankopediaEntry) return accumulator; + if (!tankopediaEntry) return accumulator; - const tier = tankopediaEntry.tier as Tier; + const tier = tankopediaEntry.tier as Tier; - if ((await accumulator)[tier]) { - (await accumulator)[tier]! += stats.battles; - } else { - (await accumulator)[tier] = stats.battles; - } + if (accumulator[tier]) { + accumulator[tier]! += diffed[id].battles; + } else { + accumulator[tier] = diffed[id].battles; + } - return accumulator; - }, - {}, - ); + return accumulator; + }, {}); } else { - stats = diffed[tankId!]; + stats = diffed[(filters as number)!]; supplementaryStats = { - WN8: calculateWN8((await tankAverages)[tankId!].all, diffed[tankId!]), - tier: (await tankopedia)[tankId!]?.tier, + WN8: stats + ? calculateWN8( + (await tankAverages)[(filters as number)!].all, + diffed[(filters as number)!], + ) + : undefined, + tier: (await tankopedia)[(filters as number)!]?.tier, }; } From 81bc9f9ba6f39af9393572734fbaa0d36dafd97a Mon Sep 17 00:00:00 2001 From: tresabhi Date: Mon, 7 Aug 2023 22:54:55 +0000 Subject: [PATCH 18/36] feat: updated /inactive --- src/commands/inactive.tsx | 3 +-- src/components/TitleBar.tsx | 2 +- src/renderers/stats.tsx | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/commands/inactive.tsx b/src/commands/inactive.tsx index 9fc709e57..d0eb9b6dd 100644 --- a/src/commands/inactive.tsx +++ b/src/commands/inactive.tsx @@ -68,9 +68,8 @@ export const inactiveCommand: CommandRegistry = { {!hasInactiveMembers && } diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 05c7bb831..277ec1c38 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -51,7 +51,7 @@ export default function TitleBar({ color: theme.colors.textHighContrast, fontWeight: 900, whiteSpace: 'nowrap', - maxWidth: 240, + maxWidth: nameDiscriminator === undefined ? Infinity : 240, overflow: 'hidden', }} > diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index 3b6455c76..d832a7a9c 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -168,7 +168,7 @@ export default async function stats( gap: 4, }} > - /fullstats for all statistics + /full-stats for all statistics ); From 270a3051b7c065a2bc613b1e6c53fddbc6019015 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 00:19:50 +0000 Subject: [PATCH 19/36] feat: updated /owned-tanks to new width --- src/commands/ownedTanks.tsx | 110 ++++++++++++++-------- src/components/Tanks/components/Root.tsx | 3 +- src/components/Tanks/components/Row.tsx | 18 ++++ src/components/Tanks/components/Title.tsx | 19 ++++ src/components/Tanks/index.ts | 2 + tsconfig.json | 2 +- 6 files changed, 113 insertions(+), 41 deletions(-) create mode 100644 src/components/Tanks/components/Row.tsx create mode 100644 src/components/Tanks/components/Title.tsx diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 53869e605..341e40f65 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -10,8 +10,10 @@ import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankName from '../core/blitz/resolveTankName'; import { TIER_ROMAN_NUMERALS, + TankopediaEntry, Tier, tankopedia, + tankopediaInfo, } from '../core/blitz/tankopedia'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteUsername from '../core/discord/autocompleteUsername'; @@ -78,66 +80,96 @@ export const ownedTanksCommand: CommandRegistry = { `https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`, ); const tankStats = await getTankStats(server, id); - const tanks = ( + const filteredTanks = ( await Promise.all( tankStats.map(async (tankData) => ({ - ...(await tankopedia)[tankData.tank_id], + tankopedia: (await tankopedia)[tankData.tank_id]!, tank_id: tankData.tank_id, })), ) - ).filter((tank) => tank?.tier === tier); + ).filter((tank) => tank.tankopedia?.tier === tier); const clanData = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ); - const leftColumnSize = Math.ceil(tanks.length / 2); - const leftColumn = tanks.slice(0, leftColumnSize); - const rightColumn = tanks.slice(leftColumnSize); + const groupedTanks: Record = {}; + const nations: string[] = []; + + filteredTanks.forEach((tank) => { + if (groupedTanks[tank.tankopedia.nation] === undefined) { + groupedTanks[tank.tankopedia.nation] = [tank.tankopedia]; + nations.push(tank.tankopedia.nation); + } else { + groupedTanks[tank.tankopedia.nation].push(tank.tankopedia); + } + }); + + nations.sort(); return ( - {tanks.length === 0 && } + {filteredTanks.length === 0 && } - {tanks.length > 0 && ( - - - {await Promise.all( - leftColumn.map(async (tank) => ( - - )), - )} - - - {await Promise.all( - rightColumn.map(async (tank) => ( - - )), - )} - - - )} + {filteredTanks.length > 0 && + (await Promise.all( + nations.map(async (nation) => { + const tanks = groupedTanks[nation]; + const leftColumnSize = Math.ceil(tanks.length / 2); + const leftColumn = tanks.slice(0, leftColumnSize); + const rightColumn = tanks.slice(leftColumnSize); + + return ( + + + {(await tankopediaInfo).vehicle_nations[nation]} + + + + + {await Promise.all( + leftColumn.map(async (tank) => ( + + )), + )} + + + {await Promise.all( + rightColumn.map(async (tank) => ( + + )), + )} + + + + ); + }), + ))} diff --git a/src/components/Tanks/components/Root.tsx b/src/components/Tanks/components/Root.tsx index 9d47b099f..8b85ce492 100644 --- a/src/components/Tanks/components/Root.tsx +++ b/src/components/Tanks/components/Root.tsx @@ -9,7 +9,8 @@ export function Root({ children }: RootProps) {
{children} diff --git a/src/components/Tanks/components/Row.tsx b/src/components/Tanks/components/Row.tsx new file mode 100644 index 000000000..115cee4b1 --- /dev/null +++ b/src/components/Tanks/components/Row.tsx @@ -0,0 +1,18 @@ +import { ReactNode } from 'react'; + +export interface RowProps { + children: ReactNode; +} + +export function Row({ children }: RowProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/Tanks/components/Title.tsx b/src/components/Tanks/components/Title.tsx new file mode 100644 index 000000000..b7c649b4b --- /dev/null +++ b/src/components/Tanks/components/Title.tsx @@ -0,0 +1,19 @@ +import { theme } from '../../../stitches.config'; + +export interface TitleProps { + children: string; +} + +export function Title({ children }: TitleProps) { + return ( + + {children} + + ); +} diff --git a/src/components/Tanks/index.ts b/src/components/Tanks/index.ts index 25362d8dd..7042919a3 100644 --- a/src/components/Tanks/index.ts +++ b/src/components/Tanks/index.ts @@ -1,3 +1,5 @@ export * from './components/Column'; export * from './components/Item'; export * from './components/Root'; +export * from './components/Row'; +export * from './components/Title'; diff --git a/tsconfig.json b/tsconfig.json index e6d1941ec..65c75994f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,5 +11,5 @@ "resolveJsonModule": true, "jsx": "react-jsx" }, - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "src/components/Tanks/components/Row.tsx"] } From d511cfa4cc635d14c9d7c6b1367485f0cd1a0160 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 00:35:33 +0000 Subject: [PATCH 20/36] fix: fixed ratings reward alignment --- src/commands/ownedTanks.tsx | 14 ++++++++++++++ src/components/Leaderboard/components/Item.tsx | 5 +++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 341e40f65..3aa828d8f 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -178,3 +178,17 @@ export const ownedTanksCommand: CommandRegistry = { autocomplete: autocompleteUsername, }; + +// TODO: make this work in the future +// console.log('Caching tank images...'); +// TANKS.then((tanks) => +// jsxToSvg( +//
+// {tanks.map(({ images, tank_id }) => { +// return ( +// images.normal && +// ); +// })} +//
, +// ), +// ).then(() => console.log('Cached tank images')); diff --git a/src/components/Leaderboard/components/Item.tsx b/src/components/Leaderboard/components/Item.tsx index 77fc18097..93e855372 100644 --- a/src/components/Leaderboard/components/Item.tsx +++ b/src/components/Leaderboard/components/Item.tsx @@ -109,7 +109,7 @@ export function Item({ flex: 1, display: 'flex', alignItems: 'center', - justifyContent: 'center', + justifyContent: 'flex-end', gap: 4, }} > @@ -140,7 +140,8 @@ export function Item({ display: 'flex', gap: 4, alignItems: 'center', - justifyContent: 'center', + width: 96, + justifyContent: 'flex-end', }} > {deltaScore !== 0 && deltaScore !== undefined && ( From f7eadb08a980dbf5ba0b755d2fc81d9b4a317479 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 00:47:15 +0000 Subject: [PATCH 21/36] feat: made /ratings data-driven --- src/commands/ratings.tsx | 278 +++++++++++++++++++-------------------- 1 file changed, 133 insertions(+), 145 deletions(-) diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index ae9a64238..c3aa6d6be 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -1,4 +1,4 @@ -import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js'; +import { SlashCommandBuilder } from 'discord.js'; import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard'; import { octokit } from '../bot'; import * as Leaderboard from '../components/Leaderboard'; @@ -7,7 +7,7 @@ import Wrapper from '../components/Wrapper'; import { Region } from '../constants/regions'; import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain'; import addRegionChoices from '../core/discord/addRegionChoices'; -import { CommandRegistry } from '../events/interactionCreate'; +import { CommandRegistryRaw } from '../events/interactionCreate'; export interface RatingsPlayer { spa_id: number; @@ -102,157 +102,145 @@ const ratingsInfo = fetch( return info; }); -function optionalParameters(option: SlashCommandSubcommandBuilder) { - return option - .addStringOption(addRegionChoices) - .addIntegerOption((option) => - option - .setName('limit') - .setDescription('How many rows to display (default: 10)') - .setRequired(false) - .setMinValue(5) - .setMaxValue(30), - ); -} - -const LEAGUE_INDEXES = ['diamond', 'platinum', 'gold', 'silver', 'bronze']; - export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; -export const ratingsCommand: CommandRegistry = { - inProduction: true, - inDevelopment: true, - inPublic: true, +const LEAGUE_SUBCOMMAND_TO_INDEX: Record = {}; - command: new SlashCommandBuilder() - .setName('ratings') - .setDescription('Ratings battles statistics') - .addSubcommandGroup((option) => - option - .setName('league') - .setDescription('Top league players') - .addSubcommand((option) => - optionalParameters( - option - .setName('diamond') - .setDescription('Top Diamond League players'), - ), - ) - .addSubcommand((option) => - optionalParameters( - option - .setName('platinum') - .setDescription('Top Platinum League players'), - ), - ) - .addSubcommand((option) => - optionalParameters( - option.setName('gold').setDescription('Top Gold League players'), - ), - ) - .addSubcommand((option) => - optionalParameters( - option - .setName('silver') - .setDescription('Top Silver League players'), - ), - ) - .addSubcommand((option) => - optionalParameters( - option - .setName('bronze') - .setDescription('Top Bronze League players'), - ), - ), - ), - async handler(interaction) { - const subcommandGroup = interaction.options.getSubcommandGroup(true); +export const ratingsCommand = new Promise( + async (resolve) => { + const awaitedRatingsInfo = await ratingsInfo; - if (subcommandGroup === 'league') { - const limit = interaction.options.getInteger('limit') ?? 10; - const subcommand = interaction.options.getSubcommand(true); - const leagueIndex = LEAGUE_INDEXES.indexOf(subcommand); - const region = interaction.options.getString('region') as Region; - const regionSubdomain = regionToRegionSubdomain(region); - const result = await fetch( - `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`, - ) - .then((response) => response.json() as Promise) - .then(({ result }) => result.slice(0, limit)); - const awaitedInfo = await ratingsInfo; - const leagueInfo = awaitedInfo.leagues[leagueIndex]; - const midnightLeaderboard = await octokit.repos - .getContent({ - ...DATABASE_REPO, - path: `${region}/ratings/${awaitedInfo.current_season}/midnight.json`, - }) - .then(({ data }) => { - if (!Array.isArray(data) && data.type === 'file') { - const content = Buffer.from(data.content, 'base64').toString(); - const jsonContent = JSON.parse( - content, - ) as BlitzkriegRatingsLeaderboard; + const command = new SlashCommandBuilder() + .setName('ratings') + .setDescription('Ratings battles statistics') + .addSubcommandGroup((option) => { + const subcommandGroup = option + .setName('league') + .setDescription('Top league players'); - return jsonContent; - } - }) - .catch(() => console.warn('No midnight leaderboard found')); - const items = await Promise.all( - result.map(async (player) => { - const reward = awaitedInfo.rewards.find( - (reward) => - player.number >= reward.from_position && - player.number <= reward.to_position, + awaitedRatingsInfo.leagues.forEach((league) => { + subcommandGroup.addSubcommand((option) => { + const subcommandName = league.title.split(' ')[0].toLowerCase(); + LEAGUE_SUBCOMMAND_TO_INDEX[subcommandName] = league.index; + + return option + .setName(subcommandName) + .setDescription(`Top ${league.title} players`) + .addStringOption(addRegionChoices) + .addIntegerOption((option) => + option + .setName('limit') + .setDescription('How many rows to display (default: 10)') + .setRequired(false) + .setMinValue(5) + .setMaxValue(30), + ); + }); + }); + + return subcommandGroup; + }); + + resolve({ + inProduction: true, + inDevelopment: true, + inPublic: true, + + command, + + async handler(interaction) { + const subcommandGroup = interaction.options.getSubcommandGroup(true); + + if (subcommandGroup === 'league') { + const limit = interaction.options.getInteger('limit') ?? 10; + const subcommand = interaction.options.getSubcommand(true); + const leagueIndex = LEAGUE_SUBCOMMAND_TO_INDEX[subcommand]; + const region = interaction.options.getString('region') as Region; + const regionSubdomain = regionToRegionSubdomain(region); + const result = await fetch( + `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`, + ) + .then((response) => response.json() as Promise) + .then(({ result }) => result.slice(0, limit)); + const awaitedInfo = await ratingsInfo; + const leagueInfo = awaitedInfo.leagues[leagueIndex]; + const midnightLeaderboard = await octokit.repos + .getContent({ + ...DATABASE_REPO, + path: `${region}/ratings/${awaitedInfo.current_season}/midnight.json`, + }) + .then(({ data }) => { + if (!Array.isArray(data) && data.type === 'file') { + const content = Buffer.from(data.content, 'base64').toString(); + const jsonContent = JSON.parse( + content, + ) as BlitzkriegRatingsLeaderboard; + + return jsonContent; + } + }) + .catch(() => console.warn('No midnight leaderboard found')); + const items = await Promise.all( + result.map(async (player) => { + const reward = awaitedInfo.rewards.find( + (reward) => + player.number >= reward.from_position && + player.number <= reward.to_position, + ); + const midnightIndex = midnightLeaderboard?.findIndex( + (item) => item.id === player.spa_id, + ); + const midnightScore = + midnightIndex === undefined + ? player.score + : midnightLeaderboard![midnightIndex].score; + const midnightPosition = + midnightIndex === undefined ? player.number : midnightIndex + 1; + + return ( + + ); + }), ); - const midnightIndex = midnightLeaderboard?.findIndex( - (item) => item.id === player.spa_id, - ); - const midnightScore = - midnightIndex === undefined - ? player.score - : midnightLeaderboard![midnightIndex].score; - const midnightPosition = - midnightIndex === undefined ? player.number : midnightIndex + 1; + + const playersBefore = result[0].number - 1; + const playersAfter = + awaitedInfo.count - result[result.length - 1].number; return ( - + + + + + {playersBefore > 0 && ( + + )} + {items} + {playersAfter > 0 && } + + ); - }), - ); + } - const playersBefore = result[0].number - 1; - const playersAfter = awaitedInfo.count - result[result.length - 1].number; - - return ( - - - - - {playersBefore > 0 && } - {items} - {playersAfter > 0 && } - - - ); - } - - return []; + return []; + }, + }); }, -}; +); From 9e4094f3a91901c23132329456b65f9d8a8f7b85 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 17:13:26 +0000 Subject: [PATCH 22/36] feat: added ratings neighbours --- src/commands/ratings.tsx | 275 +++++++++++------- .../Leaderboard/components/Item.tsx | 44 ++- src/constants/regions.ts | 7 + src/core/database/getMidnightLeaderboard.ts | 26 ++ src/renderers/stats.tsx | 2 +- 5 files changed, 234 insertions(+), 120 deletions(-) create mode 100644 src/core/database/getMidnightLeaderboard.ts diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index c3aa6d6be..33ef3cef1 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -1,13 +1,23 @@ -import { SlashCommandBuilder } from 'discord.js'; +import { + APIApplicationCommandOptionChoice, + SlashCommandBuilder, +} from 'discord.js'; import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard'; -import { octokit } from '../bot'; import * as Leaderboard from '../components/Leaderboard'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { Region } from '../constants/regions'; +import { REGION_NAMES_SHORT, Region } from '../constants/regions'; +import getWargamingResponse from '../core/blitz/getWargamingResponse'; import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain'; +import getMidnightLeaderboard from '../core/database/getMidnightLeaderboard'; import addRegionChoices from '../core/discord/addRegionChoices'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; +import autocompleteUsername from '../core/discord/autocompleteUsername'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import { secrets } from '../core/node/secrets'; import { CommandRegistryRaw } from '../events/interactionCreate'; +import { AccountInfo } from '../types/accountInfo'; +import { PlayerClanData } from '../types/playerClanData'; export interface RatingsPlayer { spa_id: number; @@ -102,10 +112,6 @@ const ratingsInfo = fetch( return info; }); -export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; - -const LEAGUE_SUBCOMMAND_TO_INDEX: Record = {}; - export const ratingsCommand = new Promise( async (resolve) => { const awaitedRatingsInfo = await ratingsInfo; @@ -113,33 +119,49 @@ export const ratingsCommand = new Promise( const command = new SlashCommandBuilder() .setName('ratings') .setDescription('Ratings battles statistics') - .addSubcommandGroup((option) => { - const subcommandGroup = option + .addSubcommand((option) => + option .setName('league') - .setDescription('Top league players'); - - awaitedRatingsInfo.leagues.forEach((league) => { - subcommandGroup.addSubcommand((option) => { - const subcommandName = league.title.split(' ')[0].toLowerCase(); - LEAGUE_SUBCOMMAND_TO_INDEX[subcommandName] = league.index; - - return option - .setName(subcommandName) - .setDescription(`Top ${league.title} players`) - .addStringOption(addRegionChoices) - .addIntegerOption((option) => - option - .setName('limit') - .setDescription('How many rows to display (default: 10)') - .setRequired(false) - .setMinValue(5) - .setMaxValue(30), - ); - }); - }); - - return subcommandGroup; - }); + .setDescription('Top league players') + .addStringOption((option) => + option + .setName('league') + .setDescription('The league to display') + .addChoices( + ...awaitedRatingsInfo.leagues.map( + (league) => + ({ + name: league.title.split(' ')[0].toLowerCase(), + value: `${league.index}`, + }) satisfies APIApplicationCommandOptionChoice, + ), + ) + .setRequired(true), + ) + .addStringOption(addRegionChoices) + .addIntegerOption((option) => + option + .setName('limit') + .setDescription('How many rows to display (default: 10)') + .setRequired(false) + .setMinValue(5) + .setMaxValue(30), + ), + ) + .addSubcommand((option) => + option + .setName('neighbours') + .setDescription('Your position and neighbours') + .addStringOption(addUsernameChoices) + .addIntegerOption((option) => + option + .setName('limit') + .setDescription('How many neighbours to display (default: 10)') + .setRequired(false) + .setMinValue(5) + .setMaxValue(30), + ), + ); resolve({ inProduction: true, @@ -149,12 +171,22 @@ export const ratingsCommand = new Promise( command, async handler(interaction) { - const subcommandGroup = interaction.options.getSubcommandGroup(true); + const subcommand = interaction.options.getSubcommand(true); + const limit = interaction.options.getInteger('limit') ?? 10; + let titleName: string; + let titleImage: string | undefined; + let titleNameDiscriminator: string | undefined; + let titleDescription: string; + let playersBefore: number; + let playersAfter: number; + let players: RatingsPlayer[]; + let midnightLeaderboard: BlitzkriegRatingsLeaderboard | undefined; + let playerId: number | undefined; - if (subcommandGroup === 'league') { - const limit = interaction.options.getInteger('limit') ?? 10; - const subcommand = interaction.options.getSubcommand(true); - const leagueIndex = LEAGUE_SUBCOMMAND_TO_INDEX[subcommand]; + if (subcommand === 'league') { + const leagueIndex = parseInt( + interaction.options.getString('league')!, + ); const region = interaction.options.getString('region') as Region; const regionSubdomain = regionToRegionSubdomain(region); const result = await fetch( @@ -162,85 +194,106 @@ export const ratingsCommand = new Promise( ) .then((response) => response.json() as Promise) .then(({ result }) => result.slice(0, limit)); - const awaitedInfo = await ratingsInfo; - const leagueInfo = awaitedInfo.leagues[leagueIndex]; - const midnightLeaderboard = await octokit.repos - .getContent({ - ...DATABASE_REPO, - path: `${region}/ratings/${awaitedInfo.current_season}/midnight.json`, - }) - .then(({ data }) => { - if (!Array.isArray(data) && data.type === 'file') { - const content = Buffer.from(data.content, 'base64').toString(); - const jsonContent = JSON.parse( - content, - ) as BlitzkriegRatingsLeaderboard; - - return jsonContent; - } - }) - .catch(() => console.warn('No midnight leaderboard found')); - const items = await Promise.all( - result.map(async (player) => { - const reward = awaitedInfo.rewards.find( - (reward) => - player.number >= reward.from_position && - player.number <= reward.to_position, - ); - const midnightIndex = midnightLeaderboard?.findIndex( - (item) => item.id === player.spa_id, - ); - const midnightScore = - midnightIndex === undefined - ? player.score - : midnightLeaderboard![midnightIndex].score; - const midnightPosition = - midnightIndex === undefined ? player.number : midnightIndex + 1; - - return ( - - ); - }), + const leagueInfo = awaitedRatingsInfo.leagues[leagueIndex]; + midnightLeaderboard = await getMidnightLeaderboard( + region, + awaitedRatingsInfo.current_season, ); - const playersBefore = result[0].number - 1; - const playersAfter = - awaitedInfo.count - result[result.length - 1].number; - - return ( - - - - - {playersBefore > 0 && ( - - )} - {items} - {playersAfter > 0 && } - - + players = result; + playersBefore = result[0].number - 1; + playersAfter = + awaitedRatingsInfo.count - result[result.length - 1].number; + titleName = `${leagueInfo.title} - ${REGION_NAMES_SHORT[region]}`; + titleImage = leagueInfo.big_icon.startsWith('http') + ? leagueInfo.big_icon + : `https:${leagueInfo.big_icon}`; + titleDescription = `Top ${limit} players`; + } else { + const { region, id } = await resolvePlayerFromCommand(interaction); + const accountInfo = await getWargamingResponse( + `https://api.wotblitz.${region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`, ); + const clan = ( + await getWargamingResponse( + `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, + ) + )[id]?.clan; + const regionSubdomain = regionToRegionSubdomain(region); + const { neighbors } = await fetch( + `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${Math.round( + limit / 2, + )}`, + ).then((response) => response.json() as Promise); + midnightLeaderboard = await getMidnightLeaderboard( + region, + awaitedRatingsInfo.current_season, + ); + + if (clan) titleNameDiscriminator = `[${clan.tag}]`; + + players = neighbors; + titleImage = clan + ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` + : undefined; + titleName = accountInfo[id].nickname; + titleDescription = 'Ratings neighbours'; + playersBefore = neighbors[0].number - 1; + playersAfter = + awaitedRatingsInfo.count - neighbors[neighbors.length - 1].number; + playerId = id; } - return []; + const items = players.map((player) => { + const reward = awaitedRatingsInfo.rewards.find( + (reward) => + player.number >= reward.from_position && + player.number <= reward.to_position, + ); + const midnightIndex = midnightLeaderboard?.findIndex( + (item) => item.id === player.spa_id, + ); + const midnightScore = + midnightIndex === undefined + ? player.score + : midnightLeaderboard![midnightIndex].score; + const midnightPosition = + midnightIndex === undefined ? player.number : midnightIndex + 1; + + return ( + + ); + }); + + return ( + + + + + {playersBefore > 0 && } + {items} + {playersAfter > 0 && } + + + ); }, + + autocomplete: autocompleteUsername, }); }, ); diff --git a/src/components/Leaderboard/components/Item.tsx b/src/components/Leaderboard/components/Item.tsx index 93e855372..72e45260e 100644 --- a/src/components/Leaderboard/components/Item.tsx +++ b/src/components/Leaderboard/components/Item.tsx @@ -9,6 +9,7 @@ export interface ItemProps { reward?: RatingsReward; nickname: string; clan?: string; + highlight?: boolean; } function normalizeImage(url: string) { @@ -23,13 +24,16 @@ export function Item({ reward, nickname, clan, + highlight, }: ItemProps) { return (
@@ -55,7 +61,9 @@ export function Item({ > @@ -64,7 +72,9 @@ export function Item({ {clan && ( @@ -95,7 +105,9 @@ export function Item({ /> @@ -126,7 +138,9 @@ export function Item({ {reward && reward.count > 1 && ( @@ -145,7 +159,14 @@ export function Item({ }} > {deltaScore !== 0 && deltaScore !== undefined && ( - + {Math.abs(deltaScore).toLocaleString()} )} @@ -162,7 +183,14 @@ export function Item({ } /> )} - + {score.toLocaleString()}
diff --git a/src/constants/regions.ts b/src/constants/regions.ts index 43118a767..218394efe 100644 --- a/src/constants/regions.ts +++ b/src/constants/regions.ts @@ -7,6 +7,13 @@ export const REGION_NAMES: Record = { asia: 'Asia', }; +// https://planetarynames.wr.usgs.gov/Abbreviations +export const REGION_NAMES_SHORT: Record = { + com: 'NA', + eu: 'EU', + asia: 'AS', +}; + export const REGION_SUBDOMAIN_NAMES: Record = { na: 'North America', eu: 'Europe', diff --git a/src/core/database/getMidnightLeaderboard.ts b/src/core/database/getMidnightLeaderboard.ts new file mode 100644 index 000000000..b4032bb9d --- /dev/null +++ b/src/core/database/getMidnightLeaderboard.ts @@ -0,0 +1,26 @@ +import { BlitzkriegRatingsLeaderboard } from '../../../scripts/buildRatingsLeaderboard'; +import { octokit } from '../../bot'; +import { Region } from '../../constants/regions'; + +export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; + +export default async function getMidnightLeaderboard( + region: Region, + season: number, +) { + try { + const { data } = await octokit.repos.getContent({ + ...DATABASE_REPO, + path: `${region}/ratings/${season}/midnight.json`, + }); + + if (!Array.isArray(data) && data.type === 'file') { + const content = Buffer.from(data.content, 'base64').toString(); + const jsonContent = JSON.parse(content) as BlitzkriegRatingsLeaderboard; + + return jsonContent; + } + } catch (error) { + console.warn('No midnight leaderboard found'); + } +} diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index d832a7a9c..6182d0add 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -178,8 +178,8 @@ export default async function stats( From ffebe6c696ea27160ac49b7d6de76af4d2f2b5f8 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 17:20:03 +0000 Subject: [PATCH 23/36] fix: use regional season info instead of hard-coded NA --- src/commands/ratings.tsx | 28 ++++++++++++++-------------- src/core/blitz/getRatingsInfo.ts | 12 ++++++++++++ 2 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 src/core/blitz/getRatingsInfo.ts diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 33ef3cef1..6493edcc3 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -7,6 +7,7 @@ import * as Leaderboard from '../components/Leaderboard'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; import { REGION_NAMES_SHORT, Region } from '../constants/regions'; +import getRatingsInfo from '../core/blitz/getRatingsInfo'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain'; import getMidnightLeaderboard from '../core/database/getMidnightLeaderboard'; @@ -103,14 +104,10 @@ export interface RatingsNeighbors { } console.log('Caching ratings info...'); -const ratingsInfo = fetch( - 'https://na.wotblitz.com/en/api/rating-leaderboards/season/', -) - .then((response) => response.json() as Promise) - .then((info) => { - console.log('Cached ratings info'); - return info; - }); +const ratingsInfo = getRatingsInfo('com').then((info) => { + console.log('Cached ratings info'); + return info; +}); export const ratingsCommand = new Promise( async (resolve) => { @@ -182,28 +179,30 @@ export const ratingsCommand = new Promise( let players: RatingsPlayer[]; let midnightLeaderboard: BlitzkriegRatingsLeaderboard | undefined; let playerId: number | undefined; + let regionRatingsInfo: RatingsInfo; if (subcommand === 'league') { const leagueIndex = parseInt( interaction.options.getString('league')!, ); const region = interaction.options.getString('region') as Region; + regionRatingsInfo = await getRatingsInfo(region); const regionSubdomain = regionToRegionSubdomain(region); const result = await fetch( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`, ) .then((response) => response.json() as Promise) .then(({ result }) => result.slice(0, limit)); - const leagueInfo = awaitedRatingsInfo.leagues[leagueIndex]; + const leagueInfo = regionRatingsInfo.leagues[leagueIndex]; midnightLeaderboard = await getMidnightLeaderboard( region, - awaitedRatingsInfo.current_season, + regionRatingsInfo.current_season, ); players = result; playersBefore = result[0].number - 1; playersAfter = - awaitedRatingsInfo.count - result[result.length - 1].number; + regionRatingsInfo.count - result[result.length - 1].number; titleName = `${leagueInfo.title} - ${REGION_NAMES_SHORT[region]}`; titleImage = leagueInfo.big_icon.startsWith('http') ? leagueInfo.big_icon @@ -211,6 +210,7 @@ export const ratingsCommand = new Promise( titleDescription = `Top ${limit} players`; } else { const { region, id } = await resolvePlayerFromCommand(interaction); + regionRatingsInfo = await getRatingsInfo(region); const accountInfo = await getWargamingResponse( `https://api.wotblitz.${region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`, ); @@ -227,7 +227,7 @@ export const ratingsCommand = new Promise( ).then((response) => response.json() as Promise); midnightLeaderboard = await getMidnightLeaderboard( region, - awaitedRatingsInfo.current_season, + regionRatingsInfo.current_season, ); if (clan) titleNameDiscriminator = `[${clan.tag}]`; @@ -240,12 +240,12 @@ export const ratingsCommand = new Promise( titleDescription = 'Ratings neighbours'; playersBefore = neighbors[0].number - 1; playersAfter = - awaitedRatingsInfo.count - neighbors[neighbors.length - 1].number; + regionRatingsInfo.count - neighbors[neighbors.length - 1].number; playerId = id; } const items = players.map((player) => { - const reward = awaitedRatingsInfo.rewards.find( + const reward = regionRatingsInfo.rewards.find( (reward) => player.number >= reward.from_position && player.number <= reward.to_position, diff --git a/src/core/blitz/getRatingsInfo.ts b/src/core/blitz/getRatingsInfo.ts new file mode 100644 index 000000000..bf3b809ef --- /dev/null +++ b/src/core/blitz/getRatingsInfo.ts @@ -0,0 +1,12 @@ +import { RatingsInfo } from '../../commands/ratings'; +import { Region } from '../../constants/regions'; +import regionToRegionSubdomain from './regionToRegionSubdomain'; + +export default async function getRatingsInfo(region: Region) { + const response = await fetch( + `https://${regionToRegionSubdomain( + region, + )}.wotblitz.com/en/api/rating-leaderboards/season/`, + ); + return (await response.json()) as Promise; +} From d2319688a531bacfec3c7e9f7b15c0e9e81a6c29 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 17:25:05 +0000 Subject: [PATCH 24/36] fix: removed multi-tank from evolution --- src/commands/evolution.tsx | 1 + src/core/discord/addStatTypeSubCommandGroups.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 797fdb002..31c4db287 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -24,6 +24,7 @@ export const evolutionCommand = new Promise( new SlashCommandBuilder() .setName('evolution') .setDescription('Evolution of statistics'), + false, ); resolve({ diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommandGroups.ts index e182d0ad8..06ad4f0d2 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommandGroups.ts @@ -8,10 +8,11 @@ import addUsernameChoices from './addUsernameChoices'; export default async function addStatTypeSubCommandGroups( option: SlashCommandBuilder, + addMultiTank = true, ) { const awaitedTankopediaInfo = await tankopediaInfo; - return option + option .addSubcommandGroup((option) => addPeriodSubCommands(option, (option) => option.addStringOption(addUsernameChoices), @@ -27,8 +28,10 @@ export default async function addStatTypeSubCommandGroups( ) .setName('tank' satisfies StatType) .setDescription('Tank statistics'), - ) - .addSubcommandGroup((option) => + ); + + if (addMultiTank) + option.addSubcommandGroup((option) => addPeriodSubCommands(option, (option) => option .addStringOption(addUsernameChoices) @@ -87,4 +90,6 @@ export default async function addStatTypeSubCommandGroups( .setName('multi-tank' satisfies StatType) .setDescription('Multiple tanks'), ); + + return option; } From 254392370f134501c1eb45f376ad4e25b8e3cf57 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 17:27:38 +0000 Subject: [PATCH 25/36] feat: only interpret correctly formatter usernames --- src/core/discord/resolvePlayerFromCommand.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/discord/resolvePlayerFromCommand.ts b/src/core/discord/resolvePlayerFromCommand.ts index 1452e6bc8..3cfee5ccb 100644 --- a/src/core/discord/resolvePlayerFromCommand.ts +++ b/src/core/discord/resolvePlayerFromCommand.ts @@ -1,6 +1,7 @@ import { CacheType, ChatInputCommandInteraction } from 'discord.js'; import { Region } from '../../constants/regions'; import listPlayers, { + usernamePattern, usernamePatternWithoutPosition, } from '../blitz/listPlayers'; import throwError from '../node/throwError'; @@ -19,7 +20,7 @@ export default async function resolvePlayerFromCommand( const displayName = interaction.guild?.members.cache .get(interaction.user.id) ?.displayName.match(usernamePatternWithoutPosition)?.[0]; - const username = commandUsername ?? displayName; + const username = commandUsername?.match(usernamePattern)?.[0] ?? displayName; if (username === undefined) { throw throwError( From 8253c27333bd0c660041c97a632124097258aa4f Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 17:39:21 +0000 Subject: [PATCH 26/36] fix: search parameters are now passed correctly --- src/core/express/resolvePeriodFromRequest.ts | 2 +- src/core/express/resolvePeriodFromURL.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/core/express/resolvePeriodFromRequest.ts b/src/core/express/resolvePeriodFromRequest.ts index bf88d3ea7..701e41e26 100644 --- a/src/core/express/resolvePeriodFromRequest.ts +++ b/src/core/express/resolvePeriodFromRequest.ts @@ -7,5 +7,5 @@ export default function resolvePeriodFromRequest( server: Region, request: Request, ) { - return resolvePeriodFromURL(server, `${CYCLIC_API}${request.path}`); + return resolvePeriodFromURL(server, `${CYCLIC_API}${request.url}`); } diff --git a/src/core/express/resolvePeriodFromURL.ts b/src/core/express/resolvePeriodFromURL.ts index d9cfc564e..5f6242022 100644 --- a/src/core/express/resolvePeriodFromURL.ts +++ b/src/core/express/resolvePeriodFromURL.ts @@ -38,10 +38,5 @@ export default function resolvePeriodFromURL( end = getPeriodNow(); } - return { - statsName, - evolutionName, - start, - end, - } satisfies ResolvedPeriod; + return { statsName, evolutionName, start, end } satisfies ResolvedPeriod; } From e806f0ede2448ab154590652f41e1d4de4c8321d Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 17:51:19 +0000 Subject: [PATCH 27/36] fix: swapped min and max for start and end --- src/core/discord/resolvePeriodFromCommand.ts | 20 ++++++++++---------- src/renderers/stats.tsx | 19 +------------------ 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/core/discord/resolvePeriodFromCommand.ts b/src/core/discord/resolvePeriodFromCommand.ts index dc172292b..360c54a12 100644 --- a/src/core/discord/resolvePeriodFromCommand.ts +++ b/src/core/discord/resolvePeriodFromCommand.ts @@ -17,7 +17,7 @@ export interface ResolvedPeriod { } export default function resolvePeriodFromCommand( - server: Region, + region: Region, interaction: ChatInputCommandInteraction, ) { let statsName: string; @@ -28,19 +28,19 @@ export default function resolvePeriodFromCommand( const periodOption = interaction.options.getString('period') as PeriodSize; if (periodSubcommand === 'custom') { - const startRaw = interaction.options.getInteger('start')!; - const endRaw = interaction.options.getInteger('end')!; - const startMin = Math.min(startRaw, endRaw); - const endMax = Math.max(startRaw, endRaw); + const startRaw = interaction.options.getInteger('start', true); + const endRaw = interaction.options.getInteger('end', true); + const startDaysAgoMax = Math.min(startRaw, endRaw); + const endDaysAgoMin = Math.max(startRaw, endRaw); - statsName = `${startMin} to ${endMax} days' statistics`; - evolutionName = `${startMin} to ${endMax} days' evolution`; - start = getTimeDaysAgo(server, startMin); - end = getTimeDaysAgo(server, endMax); + statsName = `${startDaysAgoMax} to ${endDaysAgoMin} days' statistics`; + evolutionName = `${startDaysAgoMax} to ${endDaysAgoMin} days' evolution`; + start = getTimeDaysAgo(region, endDaysAgoMin); + end = getTimeDaysAgo(region, startDaysAgoMax); } else { statsName = `${getPeriodOptionName(periodOption)} Statistics`; evolutionName = `${getPeriodOptionName(periodOption)} Evolution`; - start = getPeriodStart(server, periodOption); + start = getPeriodStart(region, periodOption); end = getPeriodNow(); } diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index 6182d0add..831087e2a 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -2,14 +2,13 @@ import AllStatsOverview from '../components/AllStatsOverview'; import NoData, { NoDataType } from '../components/NoData'; import PoweredBy, { PoweredByType } from '../components/PoweredBy'; import { TreeTypeString } from '../components/Tanks'; -import { TierWeightsRecord } from '../components/TierWeights'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; import calculateWN8 from '../core/blitz/calculateWN8'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankName from '../core/blitz/resolveTankName'; import sumStats from '../core/blitz/sumStats'; -import { Tier, tankopedia } from '../core/blitz/tankopedia'; +import { tankopedia } from '../core/blitz/tankopedia'; import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; import { tankAverages } from '../core/blitzstars/tankAverages'; import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand'; @@ -67,7 +66,6 @@ export default async function stats( const awaitedTankopedia = await tankopedia; let stats: AllStats | undefined; let supplementaryStats: SupplementaryStats; - let tierWeights: TierWeightsRecord; if (type === 'player' || type === 'multi-tank') { const typedFilters = filters as Partial | null; @@ -124,21 +122,6 @@ export default async function stats( 0, ) / battlesOfTanksWithTankopediaEntry, }; - tierWeights = filteredOrder.reduce((accumulator, id) => { - const tankopediaEntry = awaitedTankopedia[id]; - - if (!tankopediaEntry) return accumulator; - - const tier = tankopediaEntry.tier as Tier; - - if (accumulator[tier]) { - accumulator[tier]! += diffed[id].battles; - } else { - accumulator[tier] = diffed[id].battles; - } - - return accumulator; - }, {}); } else { stats = diffed[(filters as number)!]; From d448101b3202deb3cb29ca697df86ea489a5abc3 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 17:56:35 +0000 Subject: [PATCH 28/36] fix: swapped start and end min and max --- src/core/discord/resolvePeriodFromCommand.ts | 12 ++++++------ src/core/express/resolvePeriodFromURL.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/core/discord/resolvePeriodFromCommand.ts b/src/core/discord/resolvePeriodFromCommand.ts index 360c54a12..564226944 100644 --- a/src/core/discord/resolvePeriodFromCommand.ts +++ b/src/core/discord/resolvePeriodFromCommand.ts @@ -30,13 +30,13 @@ export default function resolvePeriodFromCommand( if (periodSubcommand === 'custom') { const startRaw = interaction.options.getInteger('start', true); const endRaw = interaction.options.getInteger('end', true); - const startDaysAgoMax = Math.min(startRaw, endRaw); - const endDaysAgoMin = Math.max(startRaw, endRaw); + const startDaysAgoMin = Math.min(startRaw, endRaw); + const endDaysAgoMax = Math.max(startRaw, endRaw); - statsName = `${startDaysAgoMax} to ${endDaysAgoMin} days' statistics`; - evolutionName = `${startDaysAgoMax} to ${endDaysAgoMin} days' evolution`; - start = getTimeDaysAgo(region, endDaysAgoMin); - end = getTimeDaysAgo(region, startDaysAgoMax); + statsName = `${startDaysAgoMin} to ${endDaysAgoMax} days' statistics`; + evolutionName = `${startDaysAgoMin} to ${endDaysAgoMax} days' evolution`; + start = getTimeDaysAgo(region, endDaysAgoMax); + end = getTimeDaysAgo(region, startDaysAgoMin); } else { statsName = `${getPeriodOptionName(periodOption)} Statistics`; evolutionName = `${getPeriodOptionName(periodOption)} Evolution`; diff --git a/src/core/express/resolvePeriodFromURL.ts b/src/core/express/resolvePeriodFromURL.ts index 5f6242022..5fba7141f 100644 --- a/src/core/express/resolvePeriodFromURL.ts +++ b/src/core/express/resolvePeriodFromURL.ts @@ -24,13 +24,13 @@ export default function resolvePeriodFromURL( if (periodSubcommand === 'custom') { const startRaw = parseInt(url!.searchParams.get('start')!); const endRaw = parseInt(url!.searchParams.get('end')!); - const startMin = Math.min(startRaw, endRaw); - const endMax = Math.max(startRaw, endRaw); + const startDaysAgoMin = Math.min(startRaw, endRaw); + const endDaysAgoMax = Math.max(startRaw, endRaw); - statsName = `${startMin} to ${endMax} days' statistics`; - evolutionName = `${startMin} to ${endMax} days' evolution`; - start = getTimeDaysAgo(server, startMin); - end = getTimeDaysAgo(server, endMax); + statsName = `${startDaysAgoMin} to ${endDaysAgoMax} days' statistics`; + evolutionName = `${startDaysAgoMin} to ${endDaysAgoMax} days' evolution`; + start = getTimeDaysAgo(server, endDaysAgoMax); + end = getTimeDaysAgo(server, startDaysAgoMin); } else { statsName = `${getPeriodOptionName(periodOption)} Statistics`; evolutionName = `${getPeriodOptionName(periodOption)} Evolution`; From aa1c7639f29f1e80a9b7d99cbef1b7b663c5b3f1 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 18:00:23 +0000 Subject: [PATCH 29/36] feat: auto refresh rate changed to every 10 seconds --- src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index 998ff4d8f..902319491 100644 --- a/src/server.ts +++ b/src/server.ts @@ -31,7 +31,7 @@ const app = express(); if (registry.handlesInteraction) return; res.send( - withAutoRefresh(await normalizeRouteReturnable(returnable), 60 * 1000), + withAutoRefresh(await normalizeRouteReturnable(returnable), 10 * 1000), ); }); }); From c81de2d75826b88f3e609ab299a775fc919eeb52 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Wed, 9 Aug 2023 18:02:15 +0000 Subject: [PATCH 30/36] feat: removed interaction based search param inference --- src/core/discord/interactionToURL.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core/discord/interactionToURL.ts b/src/core/discord/interactionToURL.ts index 25e903f5d..f05d03cb3 100644 --- a/src/core/discord/interactionToURL.ts +++ b/src/core/discord/interactionToURL.ts @@ -19,12 +19,7 @@ export default function interactionToURL( ] .filter(Boolean) .join('/'); - const params = [ - ...interaction.options.data.map( - (option) => [option.name, option.value] as const, - ), - ...Object.entries(parameters ?? {}), - ] + const params = (parameters ? Object.entries(parameters) : []) .filter(([, value]) => value !== undefined && value !== null) .map(([key, value]) => `${key}=${value}`) .join('&'); From a9cf07480a6f716449e34f5ba1dd82b32234721f Mon Sep 17 00:00:00 2001 From: tresabhi Date: Thu, 10 Aug 2023 00:37:54 +0000 Subject: [PATCH 31/36] feat: added arabic numerals to owned tanks options --- src/commands/ownedTanks.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index 3aa828d8f..0c48e106f 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -57,16 +57,16 @@ export const ownedTanksCommand: CommandRegistry = { .setName('tier') .setDescription('The tier you want to see') .setChoices( - { name: 'Tier I', value: '1' }, - { name: 'Tier II', value: '2' }, - { name: 'Tier III', value: '3' }, - { name: 'Tier IV', value: '4' }, - { name: 'Tier V', value: '5' }, - { name: 'Tier VI', value: '6' }, - { name: 'Tier VII', value: '7' }, - { name: 'Tier VIII', value: '8' }, - { name: 'Tier IX', value: '9' }, - { name: 'Tier X', value: '10' }, + { name: 'Tier I (1)', value: '1' }, + { name: 'Tier II (2)', value: '2' }, + { name: 'Tier III (3)', value: '3' }, + { name: 'Tier IV (4)', value: '4' }, + { name: 'Tier V (5)', value: '5' }, + { name: 'Tier VI (6)', value: '6' }, + { name: 'Tier VII (7)', value: '7' }, + { name: 'Tier VIII (8)', value: '8' }, + { name: 'Tier IX (9)', value: '9' }, + { name: 'Tier X (10)', value: '10' }, ) .setRequired(true), ) From ab5668932623d3ca86f20b90dabc4270c72ad0ae Mon Sep 17 00:00:00 2001 From: tresabhi Date: Thu, 10 Aug 2023 01:03:53 +0000 Subject: [PATCH 32/36] feat: updated /today with figma --- src/components/Breakdown/components/Row.tsx | 111 ++++++++++++------ .../Breakdown/components/RowDiscriminator.tsx | 51 -------- .../Breakdown/components/RowStat.tsx | 1 + src/components/Breakdown/index.ts | 2 +- src/components/Tanks/components/Item.tsx | 5 +- src/core/blitz/tankopedia.ts | 10 +- src/renderers/today.tsx | 26 ++-- 7 files changed, 100 insertions(+), 106 deletions(-) delete mode 100644 src/components/Breakdown/components/RowDiscriminator.tsx diff --git a/src/components/Breakdown/components/Row.tsx b/src/components/Breakdown/components/Row.tsx index c92f28ced..c99ed1e91 100644 --- a/src/components/Breakdown/components/Row.tsx +++ b/src/components/Breakdown/components/Row.tsx @@ -1,6 +1,7 @@ import getWN8Percentile from '../../../core/blitz/getWN8Percentile'; +import { TankType } from '../../../core/blitz/tankopedia'; import { theme } from '../../../stitches.config'; -import { RowDiscriminator } from './RowDiscriminator'; +import { TREE_TYPE_ICONS, TreeTypeEnum } from '../../Tanks'; import { RowStat } from './RowStat'; export interface RowProps { @@ -13,14 +14,14 @@ export interface RowProps { careerDamage: number; battles: number; careerBattles: number; - icon?: string; minimized: boolean; - isListing: boolean; - naked?: boolean; + isTank: boolean; + treeType?: TreeTypeEnum; + tankType?: TankType; } export function Row({ - isListing, + isTank, minimized, name, winrate, @@ -31,45 +32,85 @@ export function Row({ careerDamage, battles, careerBattles, - icon, - naked, + treeType, + tankType, }: RowProps) { return (
- +
+ {isTank && treeType !== undefined && tankType !== undefined && ( + + )} + + {name} + +
- - - - +
+ + + + +
); } diff --git a/src/components/Breakdown/components/RowDiscriminator.tsx b/src/components/Breakdown/components/RowDiscriminator.tsx deleted file mode 100644 index 04bc4c2d3..000000000 --- a/src/components/Breakdown/components/RowDiscriminator.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { theme } from '../../../stitches.config'; - -export interface RowDiscriminatorProps { - name: string; - icon?: string; - minimized: boolean; -} - -export function RowDiscriminator({ - name, - icon, - minimized, -}: RowDiscriminatorProps) { - return ( -
- {!minimized && icon && ( - - )} - - - {name} - -
- ); -} diff --git a/src/components/Breakdown/components/RowStat.tsx b/src/components/Breakdown/components/RowStat.tsx index cf58e3a14..43ce8e133 100644 --- a/src/components/Breakdown/components/RowStat.tsx +++ b/src/components/Breakdown/components/RowStat.tsx @@ -53,6 +53,7 @@ export function RowStat({ diff --git a/src/components/Breakdown/index.ts b/src/components/Breakdown/index.ts index 8bb31e1ff..c2782754b 100644 --- a/src/components/Breakdown/index.ts +++ b/src/components/Breakdown/index.ts @@ -1,4 +1,4 @@ export * from './components/Root'; export * from './components/Row'; -export * from './components/RowDiscriminator'; export * from './components/RowStat'; + diff --git a/src/components/Tanks/components/Item.tsx b/src/components/Tanks/components/Item.tsx index 9d9b52ef7..4aa2e77ea 100644 --- a/src/components/Tanks/components/Item.tsx +++ b/src/components/Tanks/components/Item.tsx @@ -2,6 +2,7 @@ import { TANK_ICONS, TANK_ICONS_COLLECTOR, TANK_ICONS_PREMIUM, + TankType, } from '../../../core/blitz/tankopedia'; import { theme } from '../../../stitches.config'; @@ -15,7 +16,7 @@ export type TreeTypeString = 'techtree' | 'premium' | 'collector'; export interface ItemProps { image?: string; - tankType?: string; + tankType?: TankType; name: string; treeType: TreeTypeEnum; } @@ -26,7 +27,7 @@ const TREE_TYPE_COLOR = { [TreeTypeEnum.Collector]: '_blue', } as const; -const TREE_TYPE_ICONS = { +export const TREE_TYPE_ICONS = { [TreeTypeEnum.TechTree]: TANK_ICONS, [TreeTypeEnum.Premium]: TANK_ICONS_PREMIUM, [TreeTypeEnum.Collector]: TANK_ICONS_COLLECTOR, diff --git a/src/core/blitz/tankopedia.ts b/src/core/blitz/tankopedia.ts index 1a51edbe8..ffb2cf6c8 100644 --- a/src/core/blitz/tankopedia.ts +++ b/src/core/blitz/tankopedia.ts @@ -1,6 +1,8 @@ import { secrets } from '../node/secrets'; import getWargamingResponse from './getWargamingResponse'; +export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank'; + export interface TankopediaEntry { name: string; nation: string; @@ -10,7 +12,7 @@ export interface TankopediaEntry { cost: { price_credit: number; price_gold: number }; images: { preview: string; normal: string }; tank_id: number; - type: string; + type: TankType; description: string; } @@ -51,21 +53,21 @@ export const tankopediaInfo = getWargamingResponse( ); console.log('Cached tankopedia info'); -export const TANK_ICONS: Record = { +export const TANK_ICONS: Record = { 'AT-SPG': 'https://i.imgur.com/BIHSEH0.png', lightTank: 'https://i.imgur.com/CSNha5V.png', mediumTank: 'https://i.imgur.com/wvf3ltm.png', heavyTank: 'https://i.imgur.com/ECeqlZa.png', }; -export const TANK_ICONS_PREMIUM: Record = { +export const TANK_ICONS_PREMIUM: Record = { 'AT-SPG': 'https://i.imgur.com/TCu3EdR.png', lightTank: 'https://i.imgur.com/zdkpTRb.png', mediumTank: 'https://i.imgur.com/3z7eHX6.png', heavyTank: 'https://i.imgur.com/P3vbmyA.png', }; -export const TANK_ICONS_COLLECTOR: Record = { +export const TANK_ICONS_COLLECTOR: Record = { 'AT-SPG': 'https://i.imgur.com/WTjeirB.png', lightTank: 'https://i.imgur.com/EwhtKkU.png', mediumTank: 'https://i.imgur.com/u8YDMBh.png', diff --git a/src/renderers/today.tsx b/src/renderers/today.tsx index e231cb280..d378cbcb2 100644 --- a/src/renderers/today.tsx +++ b/src/renderers/today.tsx @@ -5,6 +5,7 @@ import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; import calculateWN8 from '../core/blitz/calculateWN8'; import getTankStats from '../core/blitz/getTankStats'; +import getTreeType from '../core/blitz/getTreeType'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankName from '../core/blitz/resolveTankName'; import sumStats from '../core/blitz/sumStats'; @@ -117,28 +118,27 @@ export default async function today( // this code unreadable on god will forget tomorrow no cap ...(order.length === 0 ? [] : [0]), ...(isFinite(limit) ? order.slice(0, limit) : order), - ].map(async (tankId, index) => { - const tankStats = diffed[tankId]; - const career = careerStats[tankId]; + ].map(async (id, index) => { + const tankStats = diffed[id]; + const career = careerStats[id]; + const tankopediaEntry = (await tankopedia)[id]; return ( 4} - key={tankId} - name={tankId === 0 ? 'Total' : await resolveTankName(tankId)} + key={id} + name={id === 0 ? 'Total' : await resolveTankName(id)} winrate={tankStats.wins / tankStats.battles} careerWinrate={career.wins / career.battles} - WN8={isNaN(todayWN8s[tankId]) ? undefined : todayWN8s[tankId]} - careerWN8={isNaN(careerWN8s[tankId]) ? undefined : careerWN8s[tankId]} + WN8={isNaN(todayWN8s[id]) ? undefined : todayWN8s[id]} + careerWN8={isNaN(careerWN8s[id]) ? undefined : careerWN8s[id]} damage={tankStats.damage_dealt / tankStats.battles} careerDamage={career.damage_dealt / career.battles} battles={tankStats.battles} careerBattles={career.battles} - icon={ - tankId === 0 ? undefined : (await tankopedia)[tankId]?.images.normal - } /> ); }), @@ -146,7 +146,7 @@ export default async function today( return naked ? ( - {rows.slice(1)} + {rows} ) : ( From bb2450eb3cca850bd654fdf4e88282dd8fb59596 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Thu, 10 Aug 2023 01:31:05 +0000 Subject: [PATCH 33/36] feat: added the new /today embeds --- src/commands/today.tsx | 30 +++++++++++++-- src/renderers/today.tsx | 85 +++++++++++++++++++++++------------------ src/routes/today.tsx | 4 +- src/server.ts | 2 +- 4 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/commands/today.tsx b/src/commands/today.tsx index f8505c682..12cf88dd1 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -24,26 +24,48 @@ export const todayCommand: CommandRegistry = { .addStringOption(addUsernameChoices) .addIntegerOption((option) => option - .setName('limit') + .setName('cutoff') .setDescription( 'The maximum number of tanks to display (default: Infinity)', ) .setRequired(false) .setMinValue(1), + ) + .addIntegerOption((option) => + option + .setName('maximized') + .setDescription( + 'The number of rows with full stats after which all are collapsed (default: 4)', + ) + .setMinValue(0) + .setRequired(false), + ) + .addBooleanOption((option) => + option + .setName('show-total') + .setDescription('Show the total stats at the top (default: true)') + .setRequired(false), ), async handler(interaction) { const player = await resolvePlayerFromCommand(interaction); - const limit = interaction.options.getInteger('limit') ?? Infinity; + const cutoff = interaction.options.getInteger('cutoff') ?? undefined; + const maximized = interaction.options.getInteger('maximized') ?? undefined; + const showTotal = interaction.options.getBoolean('show-total') ?? undefined; const { nickname } = ( await getWargamingResponse( `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`, ) )[player.id]; - const path = interactionToURL(interaction, player); + const path = interactionToURL(interaction, { + ...player, + cutoff, + maximized, + 'show-total': showTotal, + }); return [ - await today(player, limit, false), + await today(player, cutoff, maximized, showTotal, false), primaryButton(path, 'Refresh'), linkButton(`${CYCLIC_API}/${path}`, 'Embed'), linkButton( diff --git a/src/renderers/today.tsx b/src/renderers/today.tsx index d378cbcb2..a23421c26 100644 --- a/src/renderers/today.tsx +++ b/src/renderers/today.tsx @@ -22,9 +22,13 @@ import { PossiblyPromise } from '../types/possiblyPromise'; export default async function today( { region: server, id }: ResolvedPlayer, - limit = Infinity, + cutoff = Infinity, + maximized = 4, + showTotal = true, naked?: boolean, ) { + console.log(maximized); + const { diffed, order } = await getDiffedTankStats( server, id, @@ -38,9 +42,11 @@ export default async function today( `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ); const careerTankStatsRaw = await getTankStats(server, id); - const careerStats: Record = { - 0: accountInfo[id].statistics.all, - }; + const careerStats: Record = showTotal + ? { + 0: accountInfo[id].statistics.all, + } + : {}; const allStatsToAccumulate: AllStats[] = []; Object.entries(diffed).forEach(([, tankStats]) => { @@ -52,7 +58,7 @@ export default async function today( const accumulatedStats = sumStats(allStatsToAccumulate); - if (Object.keys(diffed).length > 0) { + if (showTotal && Object.keys(diffed).length > 0) { diffed[0] = accumulatedStats; } @@ -62,7 +68,7 @@ export default async function today( >(async (accumulator, [tankIdString, tankStats]) => { const tankId = parseInt(tankIdString); - return tankId === 0 || !(await tankAverages)[tankId] + return (showTotal && tankId === 0) || !(await tankAverages)[tankId] ? accumulator : { ...(await accumulator), @@ -73,7 +79,8 @@ export default async function today( const careerWN8s = await careerTankStatsRaw.reduce< PossiblyPromise> >(async (accumulator, { tank_id }) => { - return tank_id === 0 || (await tankAverages)[tank_id] === undefined + return (showTotal && tank_id === 0) || + (await tankAverages)[tank_id] === undefined ? accumulator : { ...(await accumulator), @@ -86,38 +93,40 @@ export default async function today( const todayWN8sEntries = Object.entries(todayWN8s); const careerWN8sEntries = Object.entries(careerWN8s); - todayWN8s[0] = - todayWN8sEntries.reduce( - (accumulator, [tankIdString, wn8]) => - accumulator + wn8 * diffed[Number(tankIdString)].battles, - 0, - ) / - todayWN8sEntries.reduce( - (accumulator, [tankIdString]) => - accumulator + diffed[Number(tankIdString)].battles, - 0, - ); - careerWN8s[0] = - careerWN8sEntries.reduce( - (accumulator, [tankIdString, WN8]) => - isNaN(WN8) - ? accumulator - : accumulator + WN8 * careerStats[Number(tankIdString)].battles, - 0, - ) / - careerWN8sEntries.reduce( - (accumulator, [tankIdString, WN8]) => - isNaN(WN8) - ? accumulator - : accumulator + careerStats[Number(tankIdString)].battles, - 0, - ); + if (showTotal) { + todayWN8s[0] = + todayWN8sEntries.reduce( + (accumulator, [tankIdString, wn8]) => + accumulator + wn8 * diffed[Number(tankIdString)].battles, + 0, + ) / + todayWN8sEntries.reduce( + (accumulator, [tankIdString]) => + accumulator + diffed[Number(tankIdString)].battles, + 0, + ); + careerWN8s[0] = + careerWN8sEntries.reduce( + (accumulator, [tankIdString, WN8]) => + isNaN(WN8) + ? accumulator + : accumulator + WN8 * careerStats[Number(tankIdString)].battles, + 0, + ) / + careerWN8sEntries.reduce( + (accumulator, [tankIdString, WN8]) => + isNaN(WN8) + ? accumulator + : accumulator + careerStats[Number(tankIdString)].battles, + 0, + ); + } const rows = await Promise.all( [ // this code unreadable on god will forget tomorrow no cap - ...(order.length === 0 ? [] : [0]), - ...(isFinite(limit) ? order.slice(0, limit) : order), + ...(order.length === 0 || !showTotal ? [] : [0]), + ...(isFinite(cutoff) ? order.slice(0, cutoff) : order), ].map(async (id, index) => { const tankStats = diffed[id]; const career = careerStats[id]; @@ -125,12 +134,12 @@ export default async function today( return ( 4} + minimized={showTotal ? index > maximized : index + 1 > maximized} key={id} - name={id === 0 ? 'Total' : await resolveTankName(id)} + name={showTotal && id === 0 ? 'Total' : await resolveTankName(id)} winrate={tankStats.wins / tankStats.battles} careerWinrate={career.wins / career.battles} WN8={isNaN(todayWN8s[id]) ? undefined : todayWN8s[id]} diff --git a/src/routes/today.tsx b/src/routes/today.tsx index 82f2631a7..bb66e12d7 100644 --- a/src/routes/today.tsx +++ b/src/routes/today.tsx @@ -12,7 +12,9 @@ export const todayRoute: RouteRegistry = { return await today( player, - req.query.limit ? parseInt(req.query.limit as string) : undefined, + req.query.cutoff ? parseInt(req.query.cutoff as string) : undefined, + req.query.maximized ? parseInt(req.query.maximized as string) : undefined, + req.query['show-total'] ? req.query['show-total'] === 'true' : undefined, true, ); }, diff --git a/src/server.ts b/src/server.ts index 902319491..1d249786f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -31,7 +31,7 @@ const app = express(); if (registry.handlesInteraction) return; res.send( - withAutoRefresh(await normalizeRouteReturnable(returnable), 10 * 1000), + withAutoRefresh(await normalizeRouteReturnable(returnable), 30 * 1000), ); }); }); From 70575fa9cfc19fc623763aa3a232a7cb8740e0a3 Mon Sep 17 00:00:00 2001 From: tresabhi Date: Thu, 10 Aug 2023 01:46:39 +0000 Subject: [PATCH 34/36] feat: accent-tolerant tank search --- src/commands/searchTanks.ts | 9 +++++---- src/core/blitz/tankopedia.ts | 15 +++++++++++++++ src/core/discord/autocompleteTanks.ts | 15 ++++++--------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/commands/searchTanks.ts b/src/commands/searchTanks.ts index f2330aad5..20e5ff5c5 100644 --- a/src/commands/searchTanks.ts +++ b/src/commands/searchTanks.ts @@ -1,7 +1,7 @@ import { SlashCommandBuilder } from 'discord.js'; import { go } from 'fuzzysort'; import markdownEscape from 'markdown-escape'; -import { TANK_NAMES } from '../core/blitz/tankopedia'; +import { TANK_NAMES_DIACRITICS } from '../core/blitz/tankopedia'; import addTankChoices from '../core/discord/addTankChoices'; import embedInfo from '../core/discord/embedInfo'; import { CommandRegistry } from '../events/interactionCreate'; @@ -26,9 +26,10 @@ export const searchTanksCommand: CommandRegistry = { async handler(interaction) { const tank = interaction.options.getString('tank')!; const limit = interaction.options.getInteger('limit') ?? 25; - const results = go(tank, await TANK_NAMES, { limit }).map( - (result) => result.target, - ); + const results = go(tank, await TANK_NAMES_DIACRITICS, { + limit, + keys: ['combined'], + }).map((result) => result.obj.original); return embedInfo( `Tank search for "${markdownEscape(tank)}"`, diff --git a/src/core/blitz/tankopedia.ts b/src/core/blitz/tankopedia.ts index ffb2cf6c8..bbd798099 100644 --- a/src/core/blitz/tankopedia.ts +++ b/src/core/blitz/tankopedia.ts @@ -1,3 +1,4 @@ +import { deburr } from 'lodash'; import { secrets } from '../node/secrets'; import getWargamingResponse from './getWargamingResponse'; @@ -36,6 +37,20 @@ export const TANKS = new Promise(async (resolve) => { export const TANK_NAMES = new Promise(async (resolve) => { resolve((await entries).map(({ name }) => name)); }); +export const TANK_NAMES_DIACRITICS = TANK_NAMES.then((tankNames) => + Promise.all( + tankNames.map(async (tankName, index) => { + const name = tankName ?? `Unknown Tank ${(await TANKS)[index].tank_id}`; + const diacriticless = deburr(name); + + return { + original: name, + diacriticless, + combined: `${name}${diacriticless}`, + }; + }), + ), +); export interface TankopediaInfo { achievement_sections: Record; diff --git a/src/core/discord/autocompleteTanks.ts b/src/core/discord/autocompleteTanks.ts index a7a1cdbaa..2af8adfed 100644 --- a/src/core/discord/autocompleteTanks.ts +++ b/src/core/discord/autocompleteTanks.ts @@ -4,8 +4,7 @@ import { CacheType, } from 'discord.js'; import { go } from 'fuzzysort'; -import resolveTankName from '../blitz/resolveTankName'; -import { TANKS } from '../blitz/tankopedia'; +import { TANKS, TANK_NAMES_DIACRITICS } from '../blitz/tankopedia'; export default async function autocompleteTanks( interaction: AutocompleteInteraction, @@ -16,16 +15,14 @@ export default async function autocompleteTanks( await interaction.respond( focusedOption.value ? await Promise.all( - go(focusedOption.value, await TANKS, { - keys: ['name'], + go(focusedOption.value, await TANK_NAMES_DIACRITICS, { + keys: ['combined'], limit: 10, }).map( - async (item) => + async (item, index) => ({ - name: item.obj.name - ? item.obj.name - : await resolveTankName(item.obj.tank_id), - value: `${item.obj.tank_id}`, + name: item.obj.original, + value: `${(await TANKS)[index].tank_id}`, }) satisfies ApplicationCommandOptionChoiceData, ), ) From 893110d3ef81d1e6b6deab5278da296b5b410eaf Mon Sep 17 00:00:00 2001 From: tresabhi Date: Thu, 10 Aug 2023 01:49:05 +0000 Subject: [PATCH 35/36] feat: added 1 more decimal of precision in evolution --- src/renderers/evolution.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/evolution.tsx b/src/renderers/evolution.tsx index 5d4a7833a..aaacd4c34 100644 --- a/src/renderers/evolution.tsx +++ b/src/renderers/evolution.tsx @@ -97,7 +97,7 @@ export default async function evolution( min: minY, max: maxY, suffix: '%', - precision: 1, + precision: 2, }} horizontalMargin={{ min: minX, From 76a69f08b4a6c70817f8eefd38abd40cff24e01c Mon Sep 17 00:00:00 2001 From: tresabhi Date: Thu, 10 Aug 2023 18:51:22 +0000 Subject: [PATCH 36/36] feat: docs --- docs/changelogs/1.11.0.md | 2 +- docs/changelogs/1.12.0.md | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 docs/changelogs/1.12.0.md diff --git a/docs/changelogs/1.11.0.md b/docs/changelogs/1.11.0.md index 8cbf7e077..a075c4bf9 100644 --- a/docs/changelogs/1.11.0.md +++ b/docs/changelogs/1.11.0.md @@ -12,7 +12,7 @@ I am going on vacation so I thought I'd publish this a bit prematurely without a - Blitzkrieg now internally generates and keeps a permanent record of all leaderboards updated every `UTC+0` midnight - This approach will allow us to capture the absolute latest state of the leaderboard before it is wiped for the next season -## Fixed +## Fixes - Removed region "Europe" in `/verify` example diff --git a/docs/changelogs/1.12.0.md b/docs/changelogs/1.12.0.md new file mode 100644 index 000000000..4fbfb2ab8 --- /dev/null +++ b/docs/changelogs/1.12.0.md @@ -0,0 +1,54 @@ +# Blitzkrieg 1.12.0 + +_Houston, permission to return to Earth? A new update dropped._ + +## Features + +- Added `multi-tank` filters to `/stats` and `/full-stats` + - This lets you pull up the stats by combining filters of `nation`, `tank-type`, `tier`, and `tree-type` +- Added `/ratings neighbours` + - Lets you see your rank and the neighbours around you +- Added a bunch more `/today` parameters (thank you for the suggestions, HisRoyalFatness!) + - `cutoff`: the max number of rows to show (excluding the total row) + - `maximized`: the max number of rows that show the full non-collapsed stats (default: `4`) + - `show-total`: show the total row or not (default: `true`) +- Tank search is now accent-tolerant + +## Changes + +- `/ratings` has been moved out of preview and is now accessible to all +- `/today` now has a more appealing look with more efficient space management +- `/stats`, `/full-stats`, and `/evolution` now have less convoluted commands + - All periods now fall under `period` and `custom` + - `period` has preset periods like `today`, `career`, `30`, `60`, `90` + - `custom` lets you set a start and end day +- Added dashes to command names, subcommands, options, etc. to make them more readable +- Titlebar + - Removed the Blitzkrieg logo + - Removed region + - Center aligned items +- Footers are now center alinged when they do no have footer text on the left +- Moved several title discriminators into the description +- `/ratings` is now data-driven +- Embeds now auto refresh every `30` seconds instead of `60` +- Added numbers besides roman numerals in tier choice + - I forgot to implement this in places other than `/owned-tanks`... oh well! +- Increased precision of winrate in `/evolution` to `2` decimals + +## Fixes + +- Removed duplicate "Efficiency" cards in `/full-stats` +- Titlebar gap is now `4px` just like Figma +- Renamed `/statsfull` to `/full-stats` in the `/stats` footer +- Rewards in `/ratings` are now properly aligned +- `/ratings` now uses the correct source of information per region instead of getting it from a cached North American source +- Search parameters are now passed onto the URL parser correctly +- Custom date ranges now work correctly once again as the start is now the maximum days ago from today and the end is the minimum + +## Technical Changes + +- All generated images are now `480px` wide instead of `640px` to combat the unnecessary amounts of negative space +- Changed all relevant typings to be sourced from `stats` instead of `full-stats` +- Added minimum character limit to clan search +- Username auto-completing now only takes in regex-accepted parts of the input +- Search parameters are now longer automatically generated by inferring command interactions