From 78e4c82f20ba5bc692bb4f312e5d964105644016 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:26:35 -0500 Subject: [PATCH] feat: simpler subcommands --- src/commands/eligible.ts | 2 +- src/commands/evolution.tsx | 7 +- src/commands/fullStats.tsx | 6 +- src/commands/stats.tsx | 162 +++++++++-------- src/commands/today.tsx | 2 +- src/components/Tanks/components/Item.tsx | 11 +- src/components/TitleBar.tsx | 24 +-- src/constants/cyclic.ts | 1 - src/core/blitz/filterStats.ts | 79 ++++++++ src/core/blitz/tankopedia.ts | 13 +- src/core/blitzkrieg/filtersToDescription.ts | 23 +++ .../blitzstars/getBlitzStarsLinkButton.ts | 18 ++ src/core/blitzstars/getDiffedTankStats.ts | 45 +++-- src/core/blitzstars/getPeriodStart.ts | 4 +- src/core/discord/addPeriodSubCommands.ts | 51 ++---- .../discord/addStatTypeSubCommandGroups.ts | 127 ++++++------- src/core/discord/interactionToURL.ts | 8 +- src/core/discord/refreshButton.ts | 16 ++ src/core/discord/resolvePeriodFromButton.ts | 6 +- src/core/discord/resolvePeriodFromCommand.ts | 30 ++- src/core/discord/resolvePeriodFromURL.ts | 30 +-- src/core/discord/resolvePlayerFromButton.ts | 3 +- .../interactionCreate/handlers/button.tsx | 3 +- .../handlers/chatInputCommand.ts | 1 - src/events/interactionCreate/index.ts | 3 +- src/renderers/fullStats.tsx | 9 +- src/renderers/stats.tsx | 172 ------------------ src/renderers/today.tsx | 2 +- 28 files changed, 397 insertions(+), 461 deletions(-) delete mode 100644 src/constants/cyclic.ts create mode 100644 src/core/blitz/filterStats.ts create mode 100644 src/core/blitzkrieg/filtersToDescription.ts create mode 100644 src/core/blitzstars/getBlitzStarsLinkButton.ts create mode 100644 src/core/discord/refreshButton.ts delete mode 100644 src/renderers/stats.tsx diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index bd89200d5..9f31732d4 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -58,7 +58,7 @@ export const eligibleCommand: CommandRegistry = { let body = ''; if (clan === 'SKLLD') { - const { diffed } = await getDiffedTankStats( + const { diff: diffed } = await getDiffedTankStats( server, id, getTimeDaysAgo(server, 30), diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 1b39ef3b9..1de62cce2 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -1,9 +1,9 @@ import { SlashCommandBuilder } from 'discord.js'; -import { CYCLIC_API } from '../constants/cyclic'; import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankId from '../core/blitz/resolveTankId'; 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 interactionToURL from '../core/discord/interactionToURL'; @@ -24,7 +24,7 @@ export const evolutionCommand = new Promise( new SlashCommandBuilder() .setName('evolution') .setDescription('Evolution of statistics'), - false, + (option) => option.addStringOption(addUsernameChoices), ); resolve({ @@ -59,7 +59,6 @@ export const evolutionCommand = new Promise( 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!}` : '' @@ -75,7 +74,7 @@ export const evolutionCommand = new Promise( }, async button(interaction) { - const url = new URL(`${CYCLIC_API}/${interaction.customId}`); + 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); diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index d1c10afb7..2350d7fc3 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -1,10 +1,10 @@ import { SlashCommandBuilder } from 'discord.js'; import { TreeTypeString } from '../components/Tanks'; -import { CYCLIC_API } from '../constants/cyclic'; import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankId from '../core/blitz/resolveTankId'; 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 interactionToURL from '../core/discord/interactionToURL'; @@ -25,6 +25,7 @@ export const fullStatsCommand = new Promise( new SlashCommandBuilder() .setName('full-stats') .setDescription('Full in-game statistics'), + (option) => option.addStringOption(addUsernameChoices), ); resolve({ @@ -82,7 +83,6 @@ export const fullStatsCommand = new Promise( : null, ), primaryButton(path, 'Refresh'), - // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), linkButton( `https://www.blitzstars.com/player/${player.region}/${nickname}`, 'BlitzStars', @@ -96,7 +96,7 @@ export const fullStatsCommand = new Promise( }, async button(interaction) { - const url = new URL(`${CYCLIC_API}/${interaction.customId}`); + 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); diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index 60f1dcc9c..fd1916db2 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -1,28 +1,77 @@ import { SlashCommandBuilder } from 'discord.js'; +import AllStatsOverview from '../components/AllStatsOverview'; +import NoData, { NoDataType } from '../components/NoData'; import { TreeTypeString } from '../components/Tanks'; -import { CYCLIC_API } from '../constants/cyclic'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID'; +import { StatFilters, filterStats } from '../core/blitz/filterStats'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; -import resolveTankId from '../core/blitz/resolveTankId'; +import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription'; +import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; +import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; +import { PeriodType } from '../core/discord/addPeriodSubCommands'; 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 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 stats, { MultiTankFilters, StatType } from '../renderers/stats'; import { AccountInfo } from '../types/accountInfo'; +import { PlayerClanData } from '../types/playerClanData'; + +export default async function render( + { region, id }: ResolvedPlayer, + { start, end, name: statsName }: ResolvedPeriod, + filters: StatFilters, +) { + const nickname = ( + await getWargamingResponse( + `https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, + ) + )[id].nickname; + const clan = ( + await getWargamingResponse( + `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, + ) + )[id]?.clan; + const clanImage = clan + ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` + : undefined; + const diffedTankStats = await getDiffedTankStats(region, id, start, end); + const { stats, supplementary } = await filterStats(diffedTankStats, filters); + + return ( + + + + {!stats?.battles && } + {stats?.battles > 0 && ( + + )} + + ); +} export const statsCommand = new Promise(async (resolve) => { const command = await addStatTypeSubCommandGroups( new SlashCommandBuilder() .setName('stats') .setDescription('Regular battles statistics'), + (option) => option.addStringOption(addUsernameChoices), ); resolve({ @@ -32,59 +81,38 @@ export const statsCommand = new Promise(async (resolve) => { 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 = { + nation: interaction.options.getString('nation') ?? undefined, + tier: + parseInt(interaction.options.getString('tier') ?? '0') || undefined, + tankType: interaction.options.getString('tank-type') ?? undefined, + treeType: + (interaction.options.getString('tree-type') as + | TreeTypeString + | undefined) ?? undefined, + tank: + parseInt(interaction.options.getString('tank') ?? '0') || undefined, + } satisfies StatFilters; const path = interactionToURL(interaction, { - ...player, - tankId, - start, - end, - period, + ...resolvedPlayer, + ...((interaction.options.getSubcommand() as PeriodType) === 'custom' + ? { + start: interaction.options.getInteger('start', true), + end: interaction.options.getInteger('end', true), + } + : undefined), + ...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 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: tierOption ? parseInt(tierOption) : undefined, - 'tree-type': (interaction.options.getString('tree-type') ?? - undefined) as TreeTypeString | undefined, - } satisfies Partial) - : null, - ), - primaryButton(path, 'Refresh'), - // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), - linkButton( - `https://www.blitzstars.com/player/${player.region}/${nickname}`, - 'BlitzStars', - ), + await render(resolvedPlayer, resolvedPeriod, filters), + refreshButton(interaction, path), + await getBlitzStarsLinkButton(resolvedPlayer.region, resolvedPlayer.id), ]; }, @@ -94,27 +122,19 @@ export const statsCommand = new Promise(async (resolve) => { }, 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 url = new URL(`https://example.com${interaction.customId}`); const player = await resolvePlayerFromButton(interaction); const period = resolvePeriodFromButton(player.region, interaction); - return await stats( - commandGroup, - period, - player, - 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, - ); + return await render(player, period, { + nation: url.searchParams.get('nation') ?? undefined, + tank: parseInt(url.searchParams.get('tank') ?? '0') || undefined, + tankType: url.searchParams.get('tank-type') ?? undefined, + treeType: + (url.searchParams.get('tree-type') as TreeTypeString | undefined) ?? + undefined, + tier: parseInt(url.searchParams.get('tier') ?? '0') || undefined, + }); }, }); }); diff --git a/src/commands/today.tsx b/src/commands/today.tsx index 48d658056..f1b77309e 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -65,7 +65,7 @@ export const todayCommand: CommandRegistry = { return [ await today(player, cutoff, maximized, showTotal, false), primaryButton(path, 'Refresh'), - // linkButton(`${CYCLIC_API}/${path}`, 'Embed'), + // linkButton(`https://example.com/${path}`, 'Embed'), linkButton( `https://www.blitzstars.com/player/${player.region}/${nickname}`, 'BlitzStars', diff --git a/src/components/Tanks/components/Item.tsx b/src/components/Tanks/components/Item.tsx index 4aa2e77ea..ae680fc26 100644 --- a/src/components/Tanks/components/Item.tsx +++ b/src/components/Tanks/components/Item.tsx @@ -2,7 +2,6 @@ import { TANK_ICONS, TANK_ICONS_COLLECTOR, TANK_ICONS_PREMIUM, - TankType, } from '../../../core/blitz/tankopedia'; import { theme } from '../../../stitches.config'; @@ -12,11 +11,17 @@ export enum TreeTypeEnum { Collector, } -export type TreeTypeString = 'techtree' | 'premium' | 'collector'; +export type TreeTypeString = 'tech-tree' | 'premium' | 'collector'; + +export const TREE_TYPE_NAMES: Record = { + 'tech-tree': 'Tech tree', + premium: 'Premium', + collector: 'Collector', +}; export interface ItemProps { image?: string; - tankType?: TankType; + tankType?: string; name: string; treeType: TreeTypeEnum; } diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 277ec1c38..b3e4da675 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -2,17 +2,11 @@ import { theme } from '../stitches.config'; export interface TitleBarProps { name: string; - nameDiscriminator?: string; image?: string; description?: string; } -export default function TitleBar({ - name, - nameDiscriminator, - image, - description, -}: TitleBarProps) { +export default function TitleBar({ name, image, description }: TitleBarProps) { return (
@@ -51,24 +44,11 @@ export default function TitleBar({ color: theme.colors.textHighContrast, fontWeight: 900, whiteSpace: 'nowrap', - maxWidth: nameDiscriminator === undefined ? Infinity : 240, overflow: 'hidden', }} > {name} - {nameDiscriminator && ( - - {nameDiscriminator} - - )}
diff --git a/src/constants/cyclic.ts b/src/constants/cyclic.ts deleted file mode 100644 index 4543b432e..000000000 --- a/src/constants/cyclic.ts +++ /dev/null @@ -1 +0,0 @@ -export const CYCLIC_API = 'https://blitzkrieg-embeds.cyclic.app'; diff --git a/src/core/blitz/filterStats.ts b/src/core/blitz/filterStats.ts new file mode 100644 index 000000000..149d2cd3b --- /dev/null +++ b/src/core/blitz/filterStats.ts @@ -0,0 +1,79 @@ +// BIG TODO: reorganize code folder + +import { TreeTypeString } from '../../components/Tanks'; +import { DiffedTankStats } from '../blitzstars/getDiffedTankStats'; +import { tankAverages } from '../blitzstars/tankAverages'; +import calculateWN8 from './calculateWN8'; +import sumStats from './sumStats'; +import { tankopedia } from './tankopedia'; + +export interface StatFilters { + nation?: string; + tier?: number; + tankType?: string; + treeType?: TreeTypeString; + tank?: number; +} + +export async function filterStats( + { diff, order }: DiffedTankStats, + filters: StatFilters, +) { + // tank, if provided, takes priority over all other filters + if (filters.tank) filters = { tank: filters.tank }; + + const awaitedTankopedia = await tankopedia; + const awaitedTankAverages = await tankAverages; + const filteredOrder = order.filter((id) => { + const entry = awaitedTankopedia[id]; + + return ( + entry && + (filters.nation === undefined || entry.nation === filters.nation) && + (filters.tier === undefined || entry.tier === filters.tier) && + (filters.tankType === undefined || entry.type === filters.tankType) && + (filters.treeType === undefined || + (filters.treeType === 'collector' && entry.is_collectible) || + (filters.treeType === 'premium' && + entry.is_premium && + !entry.is_collectible) || + (filters.treeType === 'tech-tree' && + !entry.is_collectible && + !entry.is_premium)) && + (filters.tank === undefined || entry.tank_id === filters.tank) + ); + }); + const stats = sumStats(filteredOrder.map((id) => diff[id])); + const battlesOfTanksWithAverages = filteredOrder.reduce( + (accumulator, id) => + awaitedTankAverages[id] ? accumulator + diff[id].battles : accumulator, + 0, + ); + const battlesOfTanksWithTankopediaEntry = filteredOrder.reduce( + (accumulator, id) => + awaitedTankopedia[id] ? accumulator + diff[id].battles : accumulator, + 0, + ); + const supplementary = { + WN8: + filteredOrder.reduce( + (accumulator, id) => + awaitedTankAverages[id] + ? accumulator + + calculateWN8(awaitedTankAverages[id].all, diff[id]) * + diff[id].battles + : accumulator, + 0, + ) / battlesOfTanksWithAverages, + tier: + filteredOrder.reduce( + (accumulator, id) => + awaitedTankopedia[id] + ? accumulator + awaitedTankopedia[id]!.tier * diff[id].battles + : accumulator, + 0, + ) / battlesOfTanksWithTankopediaEntry, + }; + + return { stats, supplementary }; +} diff --git a/src/core/blitz/tankopedia.ts b/src/core/blitz/tankopedia.ts index 77e84b852..9eeb6b41f 100644 --- a/src/core/blitz/tankopedia.ts +++ b/src/core/blitz/tankopedia.ts @@ -2,8 +2,7 @@ import { deburr } from 'lodash'; import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID'; import { context } from '../blitzkrieg/context'; import getWargamingResponse from './getWargamingResponse'; - -export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank'; +import { TreeTypeString } from '../../components/Tanks'; export interface TankopediaEntry { name: string; @@ -14,10 +13,10 @@ export interface TankopediaEntry { cost: { price_credit: number; price_gold: number }; images: { preview: string; normal: string }; tank_id: number; - type: TankType; + type: string; description: string; } - +type asd = TreeTypeString export interface Tankopedia { [id: number]: TankopediaEntry | undefined; } @@ -77,21 +76,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/core/blitzkrieg/filtersToDescription.ts b/src/core/blitzkrieg/filtersToDescription.ts new file mode 100644 index 000000000..fd3f0da2c --- /dev/null +++ b/src/core/blitzkrieg/filtersToDescription.ts @@ -0,0 +1,23 @@ +import { TREE_TYPE_NAMES } from '../../components/Tanks'; +import { StatFilters } from '../blitz/filterStats'; +import resolveTankName from '../blitz/resolveTankName'; +import { TIER_ROMAN_NUMERALS, Tier, tankopediaInfo } from '../blitz/tankopedia'; + +export async function filtersToDescription({ + nation, + tier, + tankType, + treeType, + tank, +}: StatFilters) { + const awaitedTankopediaInfo = await tankopediaInfo; + const info: string[] = []; + + if (tank) return await resolveTankName(tank); + if (nation) info.push(awaitedTankopediaInfo.vehicle_nations[nation]); + if (tier) info.push(`Tier ${TIER_ROMAN_NUMERALS[tier as Tier]}`); + if (tankType) info.push(awaitedTankopediaInfo.vehicle_types[tankType]); + if (treeType) info.push(TREE_TYPE_NAMES[treeType]); + + return info.length === 0 ? 'No filters' : info.join(', '); +} diff --git a/src/core/blitzstars/getBlitzStarsLinkButton.ts b/src/core/blitzstars/getBlitzStarsLinkButton.ts new file mode 100644 index 000000000..41817944c --- /dev/null +++ b/src/core/blitzstars/getBlitzStarsLinkButton.ts @@ -0,0 +1,18 @@ +import { Region } from '../../constants/regions'; +import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID'; +import { AccountInfo } from '../../types/accountInfo'; +import getWargamingResponse from '../blitz/getWargamingResponse'; +import linkButton from '../discord/linkButton'; + +export async function getBlitzStarsLinkButton(region: Region, id: number) { + const { nickname } = ( + await getWargamingResponse( + `https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, + ) + )[id]; + + return linkButton( + `https://www.blitzstars.com/player/${region}/${nickname}`, + 'BlitzStars', + ); +} diff --git a/src/core/blitzstars/getDiffedTankStats.ts b/src/core/blitzstars/getDiffedTankStats.ts index ccf0f2099..512a52692 100644 --- a/src/core/blitzstars/getDiffedTankStats.ts +++ b/src/core/blitzstars/getDiffedTankStats.ts @@ -34,6 +34,11 @@ export const emptyTankHistoryNode: TankHistoryRaw = { all: emptyAllStats, }; +export interface DiffedTankStats { + diff: Record; + order: number[]; +} + // BIG TODO: merge this neatly with diffNormalizedTankStats export default async function getDiffedTankStats( server: Region, @@ -66,7 +71,7 @@ export default async function getDiffedTankStats( const playedTanks: number[] = []; // fetch history node right before "start" and right before "end" - const diffed: Record = {}; + const diff: Record = {}; inRangeTanks.forEach((tankId) => { const sortedHistory = tankSortedHistory[tankId]; const previousIndex = @@ -77,30 +82,30 @@ export default async function getDiffedTankStats( const latest = sortedHistory[latestIndex] ?? sortedHistory[sortedHistory.length - 1]; - function diff(get: (allStats: AllStats) => number) { + function d(get: (allStats: AllStats) => number) { return get(latest.all) - get(previous.all); } // check if there was a change in battles as games in ratings do update last_battle_time - if (diff((a) => a.battles) > 0) { - diffed[tankId] = { - battles: diff((a) => a.battles), - capture_points: diff((a) => a.capture_points), - damage_dealt: diff((a) => a.damage_dealt), - damage_received: diff((a) => a.damage_received), - dropped_capture_points: diff((a) => a.dropped_capture_points), - frags: diff((a) => a.frags), - frags8p: diff((a) => a.frags8p), - hits: diff((a) => a.hits), - losses: diff((a) => a.losses), + if (d((a) => a.battles) > 0) { + diff[tankId] = { + battles: d((a) => a.battles), + capture_points: d((a) => a.capture_points), + damage_dealt: d((a) => a.damage_dealt), + damage_received: d((a) => a.damage_received), + dropped_capture_points: d((a) => a.dropped_capture_points), + frags: d((a) => a.frags), + frags8p: d((a) => a.frags8p), + hits: d((a) => a.hits), + losses: d((a) => a.losses), max_frags: latest.all.max_frags, max_xp: latest.all.max_xp, - shots: diff((a) => a.shots), - spotted: diff((a) => a.spotted), - survived_battles: diff((a) => a.survived_battles), - win_and_survived: diff((a) => a.win_and_survived), - wins: diff((a) => a.wins), - xp: diff((a) => a.xp), + shots: d((a) => a.shots), + spotted: d((a) => a.spotted), + survived_battles: d((a) => a.survived_battles), + win_and_survived: d((a) => a.win_and_survived), + wins: d((a) => a.wins), + xp: d((a) => a.xp), }; playedTanks.push(tankId); @@ -116,5 +121,5 @@ export default async function getDiffedTankStats( return bLatest.last_battle_time - aLatest.last_battle_time; }); - return { diffed, order }; + return { diff, order } satisfies DiffedTankStats; } diff --git a/src/core/blitzstars/getPeriodStart.ts b/src/core/blitzstars/getPeriodStart.ts index 118084a35..b60c19307 100644 --- a/src/core/blitzstars/getPeriodStart.ts +++ b/src/core/blitzstars/getPeriodStart.ts @@ -3,5 +3,7 @@ import { PeriodSize } from '../discord/addPeriodSubCommands'; import getTimeDaysAgo from './getTimeDaysAgo'; export default function getPeriodStart(region: Region, period: PeriodSize) { - return period === 'career' ? 0 : getTimeDaysAgo(region, parseInt(period)); + if (period === 'career') return 0; + if (period === 'today') return getTimeDaysAgo(region, 1); + return getTimeDaysAgo(region, parseInt(period)); } diff --git a/src/core/discord/addPeriodSubCommands.ts b/src/core/discord/addPeriodSubCommands.ts index cd28a4c4b..b8ca9c488 100644 --- a/src/core/discord/addPeriodSubCommands.ts +++ b/src/core/discord/addPeriodSubCommands.ts @@ -1,55 +1,42 @@ -import { - SlashCommandSubcommandBuilder, - SlashCommandSubcommandGroupBuilder, -} from 'discord.js'; +import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js'; -export type PeriodType = 'period' | 'custom'; -export type PeriodSize = `${number}` | 'career'; +export type PeriodType = 'today' | '30' | '60' | '90' | 'custom'; +export type PeriodSize = 'today' | `${number}` | 'career'; export default function addPeriodSubCommands( - option: SlashCommandSubcommandGroupBuilder, + option: SlashCommandBuilder, extra: ( option: SlashCommandSubcommandBuilder, ) => SlashCommandSubcommandBuilder = (option) => option, ) { - return option + option .addSubcommand((option) => - extra( - option - .setName('period' satisfies PeriodType) - .setDescription("Preset period's statistics") - .addStringOption((option) => - option - .setName('period') - .setDescription('A preset period') - .setChoices( - { 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), - ), - ), + extra(option.setName('today').setDescription('Today')), + ) + .addSubcommand((option) => + extra(option.setName('30').setDescription('30 days')), + ) + .addSubcommand((option) => + extra(option.setName('60').setDescription('60 days')), + ) + .addSubcommand((option) => + extra(option.setName('90').setDescription('90 days')), ) .addSubcommand((option) => extra( option - .setName('custom' satisfies PeriodType) - .setDescription("Custom period's statistics") + .setName('custom') + .setDescription('Custom range') .addIntegerOption((option) => option .setName('start') - .setDescription('Days ago from today') - .setMinValue(0) + .setDescription('Start in number of days ago from today') .setRequired(true), ) .addIntegerOption((option) => option .setName('end') - .setDescription('Days ago from today') - .setMinValue(0) + .setDescription('End in number of days') .setRequired(true), ), ), diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommandGroups.ts index e1866c441..5da5655e7 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommandGroups.ts @@ -1,91 +1,70 @@ -import { SlashCommandBuilder } from 'discord.js'; -import { TreeTypeString } from '../../components/Tanks'; -import { StatType } from '../../renderers/stats'; +import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js'; +import { TREE_TYPE_NAMES, TreeTypeString } from '../../components/Tanks'; import { tankopediaInfo } from '../blitz/tankopedia'; import addPeriodSubCommands from './addPeriodSubCommands'; import addTankChoices from './addTankChoices'; import addTierChoices from './addTierChoices'; -import addUsernameChoices from './addUsernameChoices'; export default async function addStatTypeSubCommandGroups( option: SlashCommandBuilder, - addMultiTank = true, + extra: ( + option: SlashCommandSubcommandBuilder, + ) => SlashCommandSubcommandBuilder = (option) => option, ) { const awaitedTankopediaInfo = await tankopediaInfo; - option - .addSubcommandGroup((option) => - addPeriodSubCommands(option, (option) => - option.addStringOption(addUsernameChoices), - ) - .setName('player' satisfies StatType) - .setDescription('Player statistics'), - ) - .addSubcommandGroup((option) => - addPeriodSubCommands(option, (option) => - option - .addStringOption(addTankChoices) - .addStringOption(addUsernameChoices), - ) - .setName('tank' satisfies StatType) - .setDescription('Tank statistics'), - ); + addPeriodSubCommands(option, (option) => { + extra(option); - if (addMultiTank) - option.addSubcommandGroup((option) => - addPeriodSubCommands(option, (option) => + return option + .addStringOption((option) => option - .addStringOption(addUsernameChoices) - .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('tank-type') - .setDescription('Type') - .addChoices( - ...Object.entries(awaitedTankopediaInfo.vehicle_types).map( - ([type, name]) => ({ - name: name.replace(' Tank', ''), - value: type, - }), - ), - ) - .setRequired(false), + .setName('nation') + .setDescription('Nation') + .addChoices( + ...Object.entries(awaitedTankopediaInfo.vehicle_nations).map( + ([type, name]) => ({ name, value: type }), + ), ) - .addStringOption((option) => - addTierChoices(option).setRequired(false), - ) - .addStringOption((option) => - option - .setName('tree-type') - .setDescription('Tech tree type') - .addChoices( - { - name: 'Tech Tree', - value: 'techtree' satisfies TreeTypeString, - }, - { name: 'Premium', value: 'premium' satisfies TreeTypeString }, - { - name: 'Collector', - value: 'collector' satisfies TreeTypeString, - }, - ) - .setRequired(false), - ), + .setRequired(false), ) - .setName('multi-tank' satisfies StatType) - .setDescription('Multiple tanks'), - ); + .addStringOption((option) => addTierChoices(option).setRequired(false)) + .addStringOption((option) => + option + .setName('tank-type') + .setDescription('Tank type') + .addChoices( + ...Object.entries(awaitedTankopediaInfo.vehicle_types).map( + ([type, name]) => ({ + name: name.replace(' Tank', ''), + value: type, + }), + ), + ) + .setRequired(false), + ) + .addStringOption((option) => + option + .setName('tree-type') + .setDescription('Tech tree type') + .addChoices( + { + name: TREE_TYPE_NAMES['tech-tree'], + value: 'tech-tree' satisfies TreeTypeString, + }, + { + name: TREE_TYPE_NAMES.premium, + value: 'premium' satisfies TreeTypeString, + }, + { + name: TREE_TYPE_NAMES.collector, + value: 'collector' satisfies TreeTypeString, + }, + ) + .setRequired(false), + ) + .addStringOption((option) => addTankChoices(option).setRequired(false)); + }); return option; } diff --git a/src/core/discord/interactionToURL.ts b/src/core/discord/interactionToURL.ts index f05d03cb3..c4dac3f10 100644 --- a/src/core/discord/interactionToURL.ts +++ b/src/core/discord/interactionToURL.ts @@ -1,11 +1,7 @@ -import { - CacheType, - ChatInputCommandInteraction, - CommandInteractionOption, -} from 'discord.js'; +import { CacheType, ChatInputCommandInteraction } from 'discord.js'; export interface Parameters { - [key: string]: CommandInteractionOption['value'] | undefined | null; + [key: string]: string | number | boolean | undefined | null; } export default function interactionToURL( diff --git a/src/core/discord/refreshButton.ts b/src/core/discord/refreshButton.ts new file mode 100644 index 000000000..61394afd5 --- /dev/null +++ b/src/core/discord/refreshButton.ts @@ -0,0 +1,16 @@ +import { ChatInputCommandInteraction } from 'discord.js'; +import primaryButton from './primaryButton'; + +export function refreshButton( + interaction: ChatInputCommandInteraction, + path: string, +) { + if ( + interaction.appPermissions?.has('AttachFiles') && + interaction.appPermissions?.has('ViewChannel') + ) { + return primaryButton(path, 'Refresh'); + } + + return null; +} diff --git a/src/core/discord/resolvePeriodFromButton.ts b/src/core/discord/resolvePeriodFromButton.ts index 8267c3dce..18f2ba18f 100644 --- a/src/core/discord/resolvePeriodFromButton.ts +++ b/src/core/discord/resolvePeriodFromButton.ts @@ -1,5 +1,4 @@ import { ButtonInteraction, CacheType } from 'discord.js'; -import { CYCLIC_API } from '../../constants/cyclic'; import { Region } from '../../constants/regions'; import resolvePeriodFromURL from './resolvePeriodFromURL'; @@ -7,5 +6,8 @@ export default function resolvePeriodFromButton( server: Region, interaction: ButtonInteraction, ) { - return resolvePeriodFromURL(server, `${CYCLIC_API}/${interaction.customId}`); + return resolvePeriodFromURL( + server, + `https://example.com/${interaction.customId}`, + ); } diff --git a/src/core/discord/resolvePeriodFromCommand.ts b/src/core/discord/resolvePeriodFromCommand.ts index 564226944..fbb7d1995 100644 --- a/src/core/discord/resolvePeriodFromCommand.ts +++ b/src/core/discord/resolvePeriodFromCommand.ts @@ -6,12 +6,13 @@ import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; import { PeriodSize, PeriodType } from '../discord/addPeriodSubCommands'; export function getPeriodOptionName(period: PeriodSize) { - return period === 'career' ? 'Career' : `${period}-day`; + if (period === 'career') return 'Career'; + if (period === 'today') return 'Today'; + return `${period} days`; } export interface ResolvedPeriod { - statsName: string; - evolutionName: string; + name: string; start: number; end: number; } @@ -20,33 +21,28 @@ export default function resolvePeriodFromCommand( region: Region, interaction: ChatInputCommandInteraction, ) { - let statsName: string; - let evolutionName: string; + let name: string; let start: number; let end: number; const periodSubcommand = interaction.options.getSubcommand() as PeriodType; - const periodOption = interaction.options.getString('period') as PeriodSize; if (periodSubcommand === 'custom') { - const startRaw = interaction.options.getInteger('start', true); - const endRaw = interaction.options.getInteger('end', true); - const startDaysAgoMin = Math.min(startRaw, endRaw); - const endDaysAgoMax = Math.max(startRaw, endRaw); + const startOption = interaction.options.getInteger('start', true); + const endOption = interaction.options.getInteger('end', true); + const startDaysAgoMin = Math.min(startOption, endOption); + const endDaysAgoMax = Math.max(startOption, endOption); - statsName = `${startDaysAgoMin} to ${endDaysAgoMax} days' statistics`; - evolutionName = `${startDaysAgoMin} to ${endDaysAgoMax} days' evolution`; + name = `${startDaysAgoMin} - ${endDaysAgoMax} days`; start = getTimeDaysAgo(region, endDaysAgoMax); end = getTimeDaysAgo(region, startDaysAgoMin); } else { - statsName = `${getPeriodOptionName(periodOption)} Statistics`; - evolutionName = `${getPeriodOptionName(periodOption)} Evolution`; - start = getPeriodStart(region, periodOption); + name = getPeriodOptionName(periodSubcommand); + start = getPeriodStart(region, periodSubcommand); end = getPeriodNow(); } return { - statsName, - evolutionName, + name, start, end, } satisfies ResolvedPeriod; diff --git a/src/core/discord/resolvePeriodFromURL.ts b/src/core/discord/resolvePeriodFromURL.ts index 2f275e4c3..0ddca139d 100644 --- a/src/core/discord/resolvePeriodFromURL.ts +++ b/src/core/discord/resolvePeriodFromURL.ts @@ -2,24 +2,22 @@ import { Region } from '../../constants/regions'; import getPeriodNow from '../blitzstars/getPeriodNow'; import getPeriodStart from '../blitzstars/getPeriodStart'; import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; -import { PeriodSize, PeriodType } from './addPeriodSubCommands'; +import { PeriodType } from './addPeriodSubCommands'; import { ResolvedPeriod, getPeriodOptionName, } from './resolvePeriodFromCommand'; export default function resolvePeriodFromURL( - server: Region, + region: Region, urlString: string, ) { - let statsName: string; - let evolutionName: string; + let name: string; let start: number; let end: number; const url = new URL(urlString); - const path = url.pathname.split('/').filter(Boolean); - const periodSubcommand = path[path.length - 1] as PeriodType; - const periodOption = url.searchParams.get('period') as PeriodSize; + const path = url.pathname.split('/'); + const periodSubcommand = path[2] as PeriodType; if (periodSubcommand === 'custom') { const startRaw = parseInt(url!.searchParams.get('start')!); @@ -27,16 +25,18 @@ export default function resolvePeriodFromURL( const startDaysAgoMin = Math.min(startRaw, endRaw); const endDaysAgoMax = Math.max(startRaw, endRaw); - statsName = `${startDaysAgoMin} to ${endDaysAgoMax} days' statistics`; - evolutionName = `${startDaysAgoMin} to ${endDaysAgoMax} days' evolution`; - start = getTimeDaysAgo(server, endDaysAgoMax); - end = getTimeDaysAgo(server, startDaysAgoMin); + name = `${startDaysAgoMin} - ${endDaysAgoMax} days`; + start = getTimeDaysAgo(region, endDaysAgoMax); + end = getTimeDaysAgo(region, startDaysAgoMin); } else { - statsName = `${getPeriodOptionName(periodOption)} Statistics`; - evolutionName = `${getPeriodOptionName(periodOption)} Evolution`; - start = getPeriodStart(server, periodOption); + name = getPeriodOptionName(periodSubcommand); + start = getPeriodStart(region, periodSubcommand); end = getPeriodNow(); } - return { statsName, evolutionName, start, end } satisfies ResolvedPeriod; + return { + name, + start, + end, + } satisfies ResolvedPeriod; } diff --git a/src/core/discord/resolvePlayerFromButton.ts b/src/core/discord/resolvePlayerFromButton.ts index 8369c4c1b..eb57e5120 100644 --- a/src/core/discord/resolvePlayerFromButton.ts +++ b/src/core/discord/resolvePlayerFromButton.ts @@ -1,9 +1,8 @@ import { ButtonInteraction, CacheType } from 'discord.js'; -import { CYCLIC_API } from '../../constants/cyclic'; import resolvePlayerFromURL from './resolvePlayerFromURL'; export default async function resolvePlayerFromButton( interaction: ButtonInteraction, ) { - return resolvePlayerFromURL(`${CYCLIC_API}/${interaction.customId}`); + return resolvePlayerFromURL(`https://exmaple.com/${interaction.customId}`); } diff --git a/src/events/interactionCreate/handlers/button.tsx b/src/events/interactionCreate/handlers/button.tsx index cac6fddb5..40c6d7a98 100644 --- a/src/events/interactionCreate/handlers/button.tsx +++ b/src/events/interactionCreate/handlers/button.tsx @@ -1,6 +1,5 @@ import { ButtonInteraction, CacheType } from 'discord.js'; import { commands } from '..'; -import { CYCLIC_API } from '../../../constants/cyclic'; import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable'; import throwError from '../../../core/node/throwError'; @@ -9,7 +8,7 @@ export default async function handleButton( ) { await interaction.deferUpdate(); - const url = new URL(`${CYCLIC_API}/${interaction.customId}`); + const url = new URL(`https://example.com/${interaction.customId}`); const commandName = url.pathname.split('/')[1]; const button = (await commands)[commandName]?.button; diff --git a/src/events/interactionCreate/handlers/chatInputCommand.ts b/src/events/interactionCreate/handlers/chatInputCommand.ts index 985ed45c8..605a82567 100644 --- a/src/events/interactionCreate/handlers/chatInputCommand.ts +++ b/src/events/interactionCreate/handlers/chatInputCommand.ts @@ -17,7 +17,6 @@ export default async function handleChatInputCommand( ) { const registry = (await commands)[interaction.commandName]; - console.log(interaction.toString()); await interaction.deferReply(); try { diff --git a/src/events/interactionCreate/index.ts b/src/events/interactionCreate/index.ts index dea18f7f5..f218d5c7a 100644 --- a/src/events/interactionCreate/index.ts +++ b/src/events/interactionCreate/index.ts @@ -44,7 +44,8 @@ export type InteractionRawReturnable = | EmbedBuilder | ButtonBuilder | AttachmentBuilder - | JSX.Element; + | JSX.Element + | null; export type InteractionIterableReturnable = | InteractionRawReturnable | InteractionRawReturnable[]; diff --git a/src/renderers/fullStats.tsx b/src/renderers/fullStats.tsx index e69f28c29..bcd5641b4 100644 --- a/src/renderers/fullStats.tsx +++ b/src/renderers/fullStats.tsx @@ -23,7 +23,7 @@ import { MultiTankFilters, StatType } from './stats'; export default async function fullStats( type: Type, - { start, end, statsName }: ResolvedPeriod, + { start, end, name: statsName }: ResolvedPeriod, { region: server, id }: ResolvedPlayer, filters: Type extends 'tank' ? number @@ -50,7 +50,12 @@ export default async function fullStats( image = (await tankopedia)[filters as number]?.images.normal; } - const { diffed, order } = await getDiffedTankStats(server, id, start, end); + const { diff: diffed, order } = await getDiffedTankStats( + server, + id, + start, + end, + ); const awaitedTankopedia = await tankopedia; let stats: AllStats | undefined; let supplementaryStats: SupplementaryStats; diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx deleted file mode 100644 index 5e1f457e4..000000000 --- a/src/renderers/stats.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import AllStatsOverview from '../components/AllStatsOverview'; -import NoData, { NoDataType } from '../components/NoData'; -import { TreeTypeString } from '../components/Tanks'; -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 { 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 { theme } from '../stitches.config'; -import { - AccountInfo, - AllStats, - SupplementaryStats, -} from '../types/accountInfo'; -import { PlayerClanData } from '../types/playerClanData'; - -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, - filters: Type extends 'tank' - ? number - : Type extends 'multi-tank' - ? Partial - : null, - naked = false, -) { - 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; - } else { - } - - const { diffed, order } = await getDiffedTankStats(server, id, start, end); - const awaitedTankopedia = await tankopedia; - let stats: AllStats | undefined; - let supplementaryStats: SupplementaryStats; - - 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, - }; - } 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}`, - ); - const overview = ( - - ); - const footer = ( - - /full-stats for all statistics - - ); - - return naked ? ( - {overview} - ) : ( - - - - {!stats?.battles && } - {stats?.battles > 0 && overview} - - ); -} diff --git a/src/renderers/today.tsx b/src/renderers/today.tsx index ff0c40bc6..c63afb8da 100644 --- a/src/renderers/today.tsx +++ b/src/renderers/today.tsx @@ -27,7 +27,7 @@ export default async function today( showTotal = true, naked?: boolean, ) { - const { diffed, order } = await getDiffedTankStats( + const { diff: diffed, order } = await getDiffedTankStats( server, id, getTimeDaysAgo(server, 1),