From d062a0da1bcaa211c5ae0d89c0fcbd78900ba648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Wed, 11 Oct 2023 18:57:51 -0500 Subject: [PATCH] feat: updated full stats --- src/commands/fullStats.tsx | 136 +++++++------- src/commands/stats.tsx | 9 +- .../GenericAllStats/components/Card.tsx | 1 + src/core/blitz/filterStats.ts | 2 +- src/core/blitz/tankopedia.ts | 2 +- src/core/blitzkrieg/getTierWeights.ts | 22 +++ src/renderers/fullStats.tsx | 173 ------------------ 7 files changed, 99 insertions(+), 246 deletions(-) create mode 100644 src/core/blitzkrieg/getTierWeights.ts delete mode 100644 src/renderers/fullStats.tsx diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 2350d7fc3..67ed931c9 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -1,23 +1,68 @@ import { SlashCommandBuilder } from 'discord.js'; -import { TreeTypeString } from '../components/Tanks'; -import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID'; -import getWargamingResponse from '../core/blitz/getWargamingResponse'; -import resolveTankId from '../core/blitz/resolveTankId'; +import GenericAllStats from '../components/GenericAllStats'; +import NoData, { NoDataType } from '../components/NoData'; +import TierWeights from '../components/TierWeights'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; +import { StatFilters, filterStats } from '../core/blitz/filterStats'; +import { getAccountInfo } from '../core/blitz/getAccountInfo'; +import { getClanLogo } from '../core/blitz/getClanLogo'; +import { playerClanInfo } from '../core/blitz/playerClanInfo'; +import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription'; +import { getTierWeights } from '../core/blitzkrieg/getTierWeights'; +import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; +import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteTanks from '../core/discord/autocompleteTanks'; import autocompleteUsername from '../core/discord/autocompleteUsername'; +import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams'; +import { getFiltersFromButton } from '../core/discord/getFiltersFromButton'; +import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand'; import interactionToURL from '../core/discord/interactionToURL'; -import linkButton from '../core/discord/linkButton'; -import primaryButton from '../core/discord/primaryButton'; +import { refreshButton } from '../core/discord/refreshButton'; import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton'; -import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand'; +import resolvePeriodFromCommand, { + ResolvedPeriod, +} from '../core/discord/resolvePeriodFromCommand'; import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import resolvePlayerFromCommand, { + ResolvedPlayer, +} from '../core/discord/resolvePlayerFromCommand'; import { CommandRegistryRaw } from '../events/interactionCreate'; -import fullStats from '../renderers/fullStats'; -import { MultiTankFilters, StatType } from '../renderers/stats'; -import { AccountInfo } from '../types/accountInfo'; + +async function render( + { region, id }: ResolvedPlayer, + { start, end, name }: ResolvedPeriod, + filters: StatFilters, +) { + const nickname = (await getAccountInfo(region, id))[id].nickname; + const clan = (await playerClanInfo(region, id))[id]?.clan; + const clanImage = clan ? getClanLogo(clan.emblem_set_id) : undefined; + const diffedTankStats = await getDiffedTankStats(region, id, start, end); + const { stats, supplementary, filteredOrder } = await filterStats( + diffedTankStats, + filters, + ); + const filterDescriptions = await filtersToDescription(filters); + const tierWeights = await getTierWeights(diffedTankStats.diff, filteredOrder); + + return ( + + + + {stats.battles === 0 && } + {stats.battles > 0 && } + {stats.battles > 0 && ( + + )} + + ); +} export const fullStatsCommand = new Promise( async (resolve) => { @@ -35,59 +80,23 @@ export const fullStatsCommand = new Promise( command, async handler(interaction) { - const commandGroup = interaction.options.getSubcommandGroup( - true, - ) as StatType; - const player = await resolvePlayerFromCommand(interaction); + const resolvedPlayer = await resolvePlayerFromCommand(interaction); const resolvedPeriod = resolvePeriodFromCommand( - player.region, + resolvedPlayer.region, interaction, ); - const period = interaction.options.getString('period'); - 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 filters = getFiltersFromCommand(interaction); const path = interactionToURL(interaction, { - ...player, - tankId, - start, - end, - period, + ...resolvedPlayer, + ...getCustomPeriodParams(interaction), + ...filters, }); - const { nickname } = ( - await getWargamingResponse( - `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`, - ) - )[player.id]; - const tierOption = interaction.options.getString('tier'); - - return [ - 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: tierOption ? parseInt(tierOption) : undefined, - 'tree-type': (interaction.options.getString('tree-type') ?? - undefined) as TreeTypeString | undefined, - } satisfies Partial) - : null, - ), - primaryButton(path, 'Refresh'), - linkButton( - `https://www.blitzstars.com/player/${player.region}/${nickname}`, - 'BlitzStars', - ), - ]; + return Promise.all([ + render(resolvedPlayer, resolvedPeriod, filters), + refreshButton(interaction, path), + getBlitzStarsLinkButton(resolvedPlayer.region, resolvedPlayer.id), + ]); }, autocomplete: (interaction) => { @@ -96,18 +105,11 @@ export const fullStatsCommand = new Promise( }, async button(interaction) { - const url = new URL(`https://example.com/${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); + const filters = getFiltersFromButton(interaction); - return await fullStats( - commandGroup, - period, - player, - parseInt(url.searchParams.get('tankId')!), - ); + return await render(player, period, filters); }, }); }, diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index b6f3e48f2..b670cd098 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -29,7 +29,7 @@ import resolvePlayerFromCommand, { } from '../core/discord/resolvePlayerFromCommand'; import { CommandRegistryRaw } from '../events/interactionCreate'; -export default async function render( +async function render( { region, id }: ResolvedPlayer, { start, end, name }: ResolvedPeriod, filters: StatFilters, @@ -39,17 +39,18 @@ export default async function render( const clanImage = clan ? getClanLogo(clan.emblem_set_id) : undefined; const diffedTankStats = await getDiffedTankStats(region, id, start, end); const { stats, supplementary } = await filterStats(diffedTankStats, filters); + const filterDescriptions = await filtersToDescription(filters); return ( - {!stats?.battles && } - {stats?.battles > 0 && ( + {!stats.battles && } + {stats.battles > 0 && ( )} diff --git a/src/components/GenericAllStats/components/Card.tsx b/src/components/GenericAllStats/components/Card.tsx index ac42bb83e..0b570a837 100644 --- a/src/components/GenericAllStats/components/Card.tsx +++ b/src/components/GenericAllStats/components/Card.tsx @@ -44,6 +44,7 @@ export function Card({ items, title }: CardProps) { > {items.map((item) => (
, + order: number[] = Object.keys(stats).map(parseInt), +) { + const awaitedTankopedia = await tankopedia; + const tierWeights: TierWeightsRecord = {}; + + order.map((id) => { + const entry = awaitedTankopedia[id]; + + if (!entry) return; + if (!tierWeights[entry.tier]) tierWeights[entry.tier] = 0; + + tierWeights[entry.tier]! += stats[id].battles; + }); + + return tierWeights; +} diff --git a/src/renderers/fullStats.tsx b/src/renderers/fullStats.tsx deleted file mode 100644 index 067d6b0b1..000000000 --- a/src/renderers/fullStats.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import GenericAllStats from '../components/GenericAllStats'; -import NoData, { NoDataType } from '../components/NoData'; -import TierWeights, { TierWeightsRecord } from '../components/TierWeights'; -import TitleBar from '../components/TitleBar'; -import Wrapper from '../components/Wrapper'; -import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID'; -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 getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; -import { tankAverages } from '../core/blitzstars/tankAverages'; -import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand'; -import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand'; -import { - AccountInfo, - AllStats, - SupplementaryStats, -} from '../types/accountInfo'; -import { PlayerClanInfo } from '../types/playerClanData'; -import { MultiTankFilters, StatType } from './stats'; - -export default async function fullStats( - type: Type, - { start, end, name: statsName }: ResolvedPeriod, - { region: server, id }: ResolvedPlayer, - filters: Type extends 'tank' - ? number - : Type extends 'multi-tank' - ? Partial - : null, -) { - let nameDiscriminator: string | undefined; - let image: string | undefined; - - if (type === 'player' || type === 'multi-tank') { - const clan = ( - await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, - ) - )[id]?.clan; - - if (clan) nameDiscriminator = `[${clan.tag}]`; - image = clan - ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` - : undefined; - } else if (type === 'tank') { - nameDiscriminator = `(${await resolveTankName(filters as number)})`; - image = (await tankopedia)[filters as number]?.images.normal; - } - - const { diff: 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' || 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: - filteredOrder.reduce( - (accumulator, id) => - awaitedTankAverages[id] - ? accumulator + - calculateWN8(awaitedTankAverages[id].all, diffed[id]) * - diffed[id].battles - : accumulator, - 0, - ) / battlesOfTanksWithAverages, - tier: - filteredOrder.reduce( - (accumulator, id) => - awaitedTankopedia[id] - ? accumulator + awaitedTankopedia[id]!.tier * diffed[id].battles - : accumulator, - 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)!]; - - supplementaryStats = { - WN8: stats - ? calculateWN8( - (await tankAverages)[(filters as number)!].all, - diffed[(filters as number)!], - ) - : undefined, - tier: (await tankopedia)[(filters as number)!]?.tier, - }; - } - - const accountInfo = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, - ); - - return ( - - - - {stats.battles === 0 && } - {stats.battles > 0 && type === 'player' && ( - - )} - {stats.battles > 0 && ( - - )} - - ); -}