diff --git a/src/commands/today.tsx b/src/commands/today.tsx index 2de10c577..c549c4845 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -1,14 +1,214 @@ import { SlashCommandBuilder } from 'discord.js'; -import { getAccountInfo } from '../core/blitz/getAccountInfo'; +import * as Breakdown from '../components/Breakdown'; +import NoData, { NoDataType } from '../components/NoData'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; +import { AllStats, getAccountInfo } from '../core/blitz/getAccountInfo'; +import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; +import getTankStats from '../core/blitz/getTankStats'; +import getTreeType from '../core/blitz/getTreeType'; +import resolveTankName from '../core/blitz/resolveTankName'; +import getPeriodNow from '../core/blitzkrieg/getPeriodNow'; +import getTimeDaysAgo from '../core/blitzkrieg/getTimeDaysAgo'; +import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod'; +import { tankAverages } from '../core/blitzstars/tankAverages'; +import { tankopedia } from '../core/blitzstars/tankopedia'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteUsername from '../core/discord/autocompleteUsername'; import buttonLink from '../core/discord/buttonLink'; import buttonPrimary from '../core/discord/buttonPrimary'; import commandToURL from '../core/discord/commandToURL'; import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; +import resolvePlayerFromCommand, { + ResolvedPlayer, +} from '../core/discord/resolvePlayerFromCommand'; +import calculateWN8 from '../core/statistics/calculateWN8'; +import getWN8Percentile from '../core/statistics/getWN8Percentile'; +import sumStats from '../core/statistics/sumStats'; import { CommandRegistry } from '../events/interactionCreate'; -import today from '../renderers/today'; +import { PossiblyPromise } from '../types/possiblyPromise'; + +async function render( + { region: server, id }: ResolvedPlayer, + cutoff = Infinity, + maximized = 4, + showTotal = true, + naked?: boolean, +) { + const { diff: diffed, order } = await getStatsInPeriod( + server, + id, + getTimeDaysAgo(server, 1), + getPeriodNow(), + ); + const accountInfo = await getAccountInfo(server, id); + const clanData = await getClanAccountInfo(server, id); + const careerTankStatsRaw = await getTankStats(server, id); + const careerStats: Record = showTotal + ? { + 0: accountInfo.statistics.all, + } + : {}; + const allStatsToAccumulate: AllStats[] = []; + + Object.entries(diffed).forEach(([, tankStats]) => { + allStatsToAccumulate.push(tankStats); + }); + Object.entries(careerTankStatsRaw).forEach(([, tankStats]) => { + careerStats[tankStats.tank_id] = tankStats.all; + }); + + const accumulatedStats = sumStats(allStatsToAccumulate); + + if (showTotal && Object.keys(diffed).length > 0) { + diffed[0] = accumulatedStats; + } + + const tankStatsOverTimeEntries = Object.entries(diffed); + const todayWN8s = await tankStatsOverTimeEntries.reduce< + PossiblyPromise> + >(async (accumulator, [tankIdString, tankStats]) => { + const tankId = parseInt(tankIdString); + + return (showTotal && tankId === 0) || !(await tankAverages)[tankId] + ? accumulator + : { + ...(await accumulator), + [tankId]: calculateWN8((await tankAverages)[tankId].all, tankStats), + }; + }, {}); + + const careerWN8s = await careerTankStatsRaw.reduce< + PossiblyPromise> + >(async (accumulator, { tank_id }) => { + return (showTotal && tank_id === 0) || + (await tankAverages)[tank_id] === undefined + ? accumulator + : { + ...(await accumulator), + [tank_id]: calculateWN8( + (await tankAverages)[tank_id].all, + careerStats[tank_id], + ), + }; + }, {}); + const todayWN8sEntries = Object.entries(todayWN8s); + const careerWN8sEntries = Object.entries(careerWN8s); + + 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 || !showTotal ? [] : [0]), + ...(isFinite(cutoff) ? order.slice(0, cutoff) : order), + ].map(async (id, index) => { + const tankStats = diffed[id]; + const career = careerStats[id]; + const tankopediaEntry = (await tankopedia)[id]; + + return ( + maximized : index + 1 > maximized} + stats={[ + { + title: 'Battles', + current: tankStats.battles.toLocaleString(), + career: career.battles.toLocaleString(), + }, + { + title: 'Winrate', + current: `${(100 * (tankStats.wins / tankStats.battles)).toFixed( + 2, + )}%`, + career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`, + delta: + tankStats.wins / tankStats.battles - + career.wins / career.battles, + }, + { + title: 'WN8', + current: isNaN(todayWN8s[id]) + ? undefined + : Math.round(todayWN8s[id]).toLocaleString(), + career: isNaN(careerWN8s[id]) + ? undefined + : Math.round(careerWN8s[id]).toLocaleString(), + percentile: isNaN(todayWN8s[id]) + ? undefined + : getWN8Percentile(todayWN8s[id]), + }, + { + title: 'Damage', + current: Math.round( + tankStats.damage_dealt / tankStats.battles, + ).toLocaleString(), + career: Math.round( + career.damage_dealt / career.battles, + ).toLocaleString(), + delta: + tankStats.damage_dealt / tankStats.battles - + career.damage_dealt / career.battles, + }, + ]} + /> + ); + }), + ); + + return naked ? ( + + {rows} + + ) : ( + + + + {rows.length === 0 && } + {rows.length > 0 && {rows}} + + ); +} export const todayCommand: CommandRegistry = { inProduction: true, @@ -57,7 +257,7 @@ export const todayCommand: CommandRegistry = { }); return [ - await today(player, cutoff, maximized, showTotal, false), + await render(player, cutoff, maximized, showTotal, false), buttonPrimary(path, 'Refresh'), // linkButton(`https://example.com/${path}`, 'Embed'), buttonLink( @@ -72,6 +272,6 @@ export const todayCommand: CommandRegistry = { async button(interaction) { const player = await resolvePlayerFromButton(interaction); - return await today(player); + return await render(player); }, }; diff --git a/src/renderers/today.tsx b/src/renderers/today.tsx deleted file mode 100644 index 675497ca6..000000000 --- a/src/renderers/today.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import * as Breakdown from '../components/Breakdown'; -import NoData, { NoDataType } from '../components/NoData'; -import TitleBar from '../components/TitleBar'; -import Wrapper from '../components/Wrapper'; -import { AllStats, getAccountInfo } from '../core/blitz/getAccountInfo'; -import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; -import getTankStats from '../core/blitz/getTankStats'; -import getTreeType from '../core/blitz/getTreeType'; -import resolveTankName from '../core/blitz/resolveTankName'; -import getPeriodNow from '../core/blitzkrieg/getPeriodNow'; -import getTimeDaysAgo from '../core/blitzkrieg/getTimeDaysAgo'; -import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod'; -import { tankAverages } from '../core/blitzstars/tankAverages'; -import { tankopedia } from '../core/blitzstars/tankopedia'; -import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand'; -import calculateWN8 from '../core/statistics/calculateWN8'; -import getWN8Percentile from '../core/statistics/getWN8Percentile'; -import sumStats from '../core/statistics/sumStats'; -import { PossiblyPromise } from '../types/possiblyPromise'; - -export default async function today( - { region: server, id }: ResolvedPlayer, - cutoff = Infinity, - maximized = 4, - showTotal = true, - naked?: boolean, -) { - const { diff: diffed, order } = await getStatsInPeriod( - server, - id, - getTimeDaysAgo(server, 1), - getPeriodNow(), - ); - const accountInfo = await getAccountInfo(server, id); - const clanData = await getClanAccountInfo(server, id); - const careerTankStatsRaw = await getTankStats(server, id); - const careerStats: Record = showTotal - ? { - 0: accountInfo.statistics.all, - } - : {}; - const allStatsToAccumulate: AllStats[] = []; - - Object.entries(diffed).forEach(([, tankStats]) => { - allStatsToAccumulate.push(tankStats); - }); - Object.entries(careerTankStatsRaw).forEach(([, tankStats]) => { - careerStats[tankStats.tank_id] = tankStats.all; - }); - - const accumulatedStats = sumStats(allStatsToAccumulate); - - if (showTotal && Object.keys(diffed).length > 0) { - diffed[0] = accumulatedStats; - } - - const tankStatsOverTimeEntries = Object.entries(diffed); - const todayWN8s = await tankStatsOverTimeEntries.reduce< - PossiblyPromise> - >(async (accumulator, [tankIdString, tankStats]) => { - const tankId = parseInt(tankIdString); - - return (showTotal && tankId === 0) || !(await tankAverages)[tankId] - ? accumulator - : { - ...(await accumulator), - [tankId]: calculateWN8((await tankAverages)[tankId].all, tankStats), - }; - }, {}); - - const careerWN8s = await careerTankStatsRaw.reduce< - PossiblyPromise> - >(async (accumulator, { tank_id }) => { - return (showTotal && tank_id === 0) || - (await tankAverages)[tank_id] === undefined - ? accumulator - : { - ...(await accumulator), - [tank_id]: calculateWN8( - (await tankAverages)[tank_id].all, - careerStats[tank_id], - ), - }; - }, {}); - const todayWN8sEntries = Object.entries(todayWN8s); - const careerWN8sEntries = Object.entries(careerWN8s); - - 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 || !showTotal ? [] : [0]), - ...(isFinite(cutoff) ? order.slice(0, cutoff) : order), - ].map(async (id, index) => { - const tankStats = diffed[id]; - const career = careerStats[id]; - const tankopediaEntry = (await tankopedia)[id]; - - return ( - maximized : index + 1 > maximized} - stats={[ - { - title: 'Battles', - current: tankStats.battles.toLocaleString(), - career: career.battles.toLocaleString(), - }, - { - title: 'Winrate', - current: `${(100 * (tankStats.wins / tankStats.battles)).toFixed( - 2, - )}%`, - career: `${(100 * (career.wins / career.battles)).toFixed(2)}%`, - delta: - tankStats.wins / tankStats.battles - - career.wins / career.battles, - }, - { - title: 'WN8', - current: isNaN(todayWN8s[id]) - ? undefined - : Math.round(todayWN8s[id]).toLocaleString(), - career: isNaN(careerWN8s[id]) - ? undefined - : Math.round(careerWN8s[id]).toLocaleString(), - percentile: isNaN(todayWN8s[id]) - ? undefined - : getWN8Percentile(todayWN8s[id]), - }, - { - title: 'Damage', - current: Math.round( - tankStats.damage_dealt / tankStats.battles, - ).toLocaleString(), - career: Math.round( - career.damage_dealt / career.battles, - ).toLocaleString(), - delta: - tankStats.damage_dealt / tankStats.battles - - career.damage_dealt / career.battles, - }, - ]} - /> - ); - }), - ); - - return naked ? ( - - {rows} - - ) : ( - - - - {rows.length === 0 && } - {rows.length > 0 && {rows}} - - ); -}