feat: updated /tankstats

This commit is contained in:
TresAbhi committed 2023-05-30 17:24:24 +00:00
1 parent 50369e9225
commit 831300cf69
13 files changed
+300 -272

No files matched your search

+2 -2
View File
@@ -79,8 +79,8 @@ export default {
: tanks
.map(
(tank) =>
`${TANK_TYPE_EMOJIS[tank.type]} ${resolveTankName(
tank,
`${TANK_TYPE_EMOJIS[tank.type]} ${markdownEscape(
resolveTankName(tank.tank_id),
)} ${tank.is_premium ? '⭐' : ''}${
COMP_TANKS.includes(tank.tank_id) ? '🏆' : ''
}`,
+10 -33
View File
@@ -15,8 +15,14 @@ import usernameAutocomplete from '../core/autocomplete/username.js';
import getBlitzAccount from '../core/blitz/getBlitzAccount.js';
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import sumStats from '../core/blitz/sumStats.js';
import getPeriodNow from '../core/blitzstars/getPeriodNow.js';
import getPeriodicStart from '../core/blitzstars/getPeriodStart.js';
import getTankStatsOverTime from '../core/blitzstars/getTankStatsOverTime.js';
import cmdName from '../core/interaction/cmdName.js';
import addStatPeriodChoices, {
StatPeriod,
statPeriodNames,
} from '../core/options/addStatPeriodChoices.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { args } from '../core/process/args.js';
import render from '../core/ui/render.js';
@@ -24,36 +30,15 @@ import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo, AllStats } from '../types/accountInfo.js';
import { PlayerClanData } from '../types/playerClanData.js';
export type StatPeriod = 'today' | '30' | '60' | '90' | 'career';
export const statPeriodNames: Record<StatPeriod, string> = {
today: "Today's statistics",
30: '30-day statistics',
60: '60-day statistics',
90: '90-day statistics',
career: 'Career statistics',
};
export default {
inProduction: true,
inDevelopment: true,
inDevelopment: false,
inPublic: true,
command: new SlashCommandBuilder()
.setName(cmdName('stats'))
.setDescription("Gets the user's in-game statistics")
.addStringOption((option) =>
option
.setName('period')
.setDescription('The last number of days of stats')
.setChoices(
{ name: 'Today', value: 'today' satisfies StatPeriod },
{ name: '30 Days', value: '30' satisfies StatPeriod },
{ name: '60 Days', value: '60' satisfies StatPeriod },
{ name: '90 Days', value: '90' satisfies StatPeriod },
{ name: 'Career', value: 'career' satisfies StatPeriod },
)
.setRequired(true),
)
.addStringOption(addStatPeriodChoices)
.addStringOption(addUsernameOption),
async execute(interaction) {
@@ -72,19 +57,13 @@ export default {
if (period === 'career') {
stats = accountInfo[id].statistics.all;
} else {
const daysAgo = period === 'today' ? 0 : Number(period) - 1;
const periodStart = new Date();
periodStart.setDate(periodStart.getDate() - daysAgo);
periodStart.setHours(5, 0, 0, 0);
stats = sumStats(
Object.entries(
await getTankStatsOverTime(
server,
id,
periodStart.getTime() / 1000,
new Date().getTime() / 1000,
getPeriodicStart(period),
getPeriodNow(),
),
).map(([, stats]) => stats),
);
@@ -115,7 +94,6 @@ export default {
'Winrate',
`${(100 * (stats.wins / stats.battles)).toFixed(2)}%`,
],
// ['WN8', stats.wn8.toFixed(0)],
[
'Survival',
`${(100 * (stats.survived_battles / stats.battles)).toFixed(
@@ -135,7 +113,6 @@ export default {
['Average hits', (stats.hits / stats.battles).toFixed(2)],
['Average kills', (stats.frags / stats.battles).toFixed(2)],
['Average spots', (stats.spotted / stats.battles).toFixed(2)],
// ['Average tier', stats.avg_tier.toFixed(2)],
[
'Damage ratio',
(stats.damage_dealt / stats.damage_received).toFixed(2),
-174
View File
@@ -1,174 +0,0 @@
import { SlashCommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import tanksAutocomplete from '../core/autocomplete/tanks.js';
import usernameAutocomplete from '../core/autocomplete/username.js';
import getBlitzAccount from '../core/blitz/getBlitzAccount.js';
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import { tankopedia } from '../core/blitzstars/tankopedia.js';
import getPeriodicStats, {
Period,
} from '../core/blitztankstats/getPeriodicStats.js';
import cleanTable from '../core/interaction/cleanTable.js';
import cmdName from '../core/interaction/cmdName.js';
import errorEmbed from '../core/interaction/errorEmbed.js';
import infoEmbed from '../core/interaction/infoEmbed.js';
import addTankChoices from '../core/options/addTankChoices.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { args } from '../core/process/args.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo } from '../types/accountInfo.js';
import resolveTankName from '../utilities/resolveTankName.js';
type Periods = '30' | '60' | '90';
export default {
inProduction: true,
inDevelopment: false,
inPublic: true,
command: new SlashCommandBuilder()
.setName(cmdName('tankstats'))
.setDescription('Stats for a tank over a period')
.addStringOption(addTankChoices)
.addStringOption((option) =>
option
.setName('period')
.setDescription('The period to get stats for')
.setChoices(
{ name: '30 Days', value: '30' satisfies Periods },
{ name: '60 Days', value: '60' satisfies Periods },
{ name: '90 Days', value: '90' satisfies Periods },
)
.setRequired(true),
)
.addStringOption(addUsernameOption),
async execute(interaction) {
const name = interaction.options.getString('username')!;
const tankId = interaction.options.getString('tank')!;
const period = interaction.options.getString('period') as Periods;
const blitzAccount = await getBlitzAccount(interaction, name);
const { server, id } = blitzAccount;
const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
);
const tank = tankopedia[tankId as unknown as number];
if (tank) {
const periodicStats = (await getPeriodicStats(id, Number(period)))
.filter((stats) => stats.tank_id === tank.tank_id)
.filter((stats) => !stats.isRating);
function sum(summer: (period: Period) => number) {
return periodicStats.reduce(
(accumulative, period) => accumulative + summer(period),
0,
);
}
function weighted(summer: (period: Period) => number) {
return (
periodicStats.reduce(
(accumulative, period) =>
accumulative + summer(period) * period.battles,
0,
) / sum((period) => period.battles)
);
}
interaction.editReply({
embeds: [
infoEmbed(
`${period} day stats for ${markdownEscape(
accountInfo[id].nickname,
)}'s ${resolveTankName(tank)}`,
periodicStats.length > 0
? cleanTable([
[
'Winrate',
`${(
100 *
(sum((a) => a.wins) / sum((a) => a.battles))
).toFixed(2)}%`,
],
[
'Survival',
`${(
100 *
(sum((a) => a.survived_battles) / sum((a) => a.battles))
).toFixed(2)}%`,
],
[
'Accuracy',
`${(
100 *
(sum((a) => a.hits) / sum((a) => a.shots))
).toFixed(2)}%`,
],
['WN8', weighted((a) => a.wn8).toFixed(0)],
[],
['Battles', sum((a) => a.battles)],
['Wins', sum((a) => a.wins)],
['Losses', sum((a) => a.losses)],
[],
[
'Shots per battle',
(sum((a) => a.shots) / sum((a) => a.battles)).toFixed(2),
],
[
'Hits per battle',
(sum((a) => a.hits) / sum((a) => a.battles)).toFixed(2),
],
[
'Damage per battle',
(
sum((a) => a.damage_dealt) / sum((a) => a.battles)
).toFixed(0),
],
[
'Kills per battle',
(sum((a) => a.frags) / sum((a) => a.battles)).toFixed(2),
],
[
'Spots per battle',
(sum((a) => a.spotted) / sum((a) => a.battles)).toFixed(2),
],
[
'XP per battle',
(sum((a) => a.xp) / sum((a) => a.battles)).toFixed(0),
],
[],
[
'Damage ratio',
(
sum((a) => a.damage_dealt) / sum((a) => a.damage_received)
).toFixed(2),
],
[
'Kills to death ratio',
(
sum((a) => a.frags) /
sum((a) => a.battles - a.survived_battles)
).toFixed(2),
],
])
: 'No battles were played or recorded during this period.',
),
],
});
} else {
interaction.editReply({
embeds: [
errorEmbed(
'Tank not found',
`"${markdownEscape(tankId)}" does not exist in the game.`,
),
],
});
}
},
autocomplete: (interaction) => {
tanksAutocomplete(interaction);
usernameAutocomplete(interaction);
},
} satisfies CommandRegistry;
+156
View File
@@ -0,0 +1,156 @@
import {
ActionRowBuilder,
ButtonBuilder,
SlashCommandBuilder,
} from 'discord.js';
import GenericStats from '../components/GenericStats/index.js';
import NoBattlesInPeriod from '../components/NoBattlesInPeriod.js';
import PoweredByBlitzStars from '../components/PoweredByBlitzStars.js';
import TitleBar from '../components/TitleBar.js';
import Wrapper from '../components/Wrapper.js';
import { BLITZ_SERVERS } from '../constants/servers.js';
import fullBlitzStarsStats from '../core/actions/fullBlitzStarsStats.js';
import { supportBlitzStars } from '../core/actions/supportBlitzStars.js';
import tanksAutocomplete from '../core/autocomplete/tanks.js';
import usernameAutocomplete from '../core/autocomplete/username.js';
import getBlitzAccount from '../core/blitz/getBlitzAccount.js';
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import resolveTankId from '../core/blitz/resolveTankId.js';
import getPeriodNow from '../core/blitzstars/getPeriodNow.js';
import getPeriodicStart from '../core/blitzstars/getPeriodStart.js';
import getTankStatsOverTime from '../core/blitzstars/getTankStatsOverTime.js';
import { tankopedia } from '../core/blitzstars/tankopedia.js';
import cmdName from '../core/interaction/cmdName.js';
import addStatPeriodChoices, {
StatPeriod,
statPeriodNames,
} from '../core/options/addStatPeriodChoices.js';
import addTankChoices from '../core/options/addTankChoices.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { args } from '../core/process/args.js';
import render from '../core/ui/render.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo, AllStats } from '../types/accountInfo.js';
import { TanksStats } from '../types/tanksStats.js';
import resolveTankName from '../utilities/resolveTankName.js';
export default {
inProduction: true,
inDevelopment: true,
inPublic: true,
command: new SlashCommandBuilder()
.setName(cmdName('tankstats'))
.setDescription('Stats for a tank over a period')
.addStringOption(addTankChoices)
.addStringOption(addStatPeriodChoices)
.addStringOption(addUsernameOption),
async execute(interaction) {
const tank = await resolveTankId(
interaction,
interaction.options.getString('tank')!,
);
const period = interaction.options.getString('period') as StatPeriod;
const username = interaction.options.getString('username')!;
const blitzAccount = await getBlitzAccount(interaction, username);
const { id, server } = blitzAccount;
const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
);
const tankStats = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
);
let stats: AllStats;
if (period === 'career') {
stats = tankStats[id].find(
(stats) => stats.tank_id === Number(tank),
)!.all;
} else {
stats = (
await getTankStatsOverTime(
server,
id,
getPeriodicStart(period),
getPeriodNow(),
)
)[Number(tank)];
}
const image = await render(
<Wrapper>
<TitleBar
name={accountInfo[id].nickname}
nameDiscriminator={`(${resolveTankName(Number(tank))})`}
image={tankopedia[Number(tank)].images.normal}
description={`${
statPeriodNames[period]
} • ${new Date().toDateString()} • ${BLITZ_SERVERS[server]}`}
/>
{stats.battles === 0 && <NoBattlesInPeriod />}
{stats.battles > 0 && (
// TODO: GenericStats generator for AllStats
<GenericStats
stats={[
[
'Winrate',
`${(100 * (stats.wins / stats.battles)).toFixed(2)}%`,
],
[
'Survival',
`${(100 * (stats.survived_battles / stats.battles)).toFixed(
2,
)}%`,
],
['Accuracy', `${((stats.hits / stats.shots) * 100).toFixed(2)}%`],
['Battles', stats.battles],
['Wins', stats.wins],
['Losses', stats.losses],
[
'Average damage',
(stats.damage_dealt / stats.battles).toFixed(0),
],
['Average XP', (stats.xp / stats.battles).toFixed(0)],
['Average shots', (stats.shots / stats.battles).toFixed(2)],
['Average hits', (stats.hits / stats.battles).toFixed(2)],
['Average kills', (stats.frags / stats.battles).toFixed(2)],
['Average spots', (stats.spotted / stats.battles).toFixed(2)],
[
'Damage ratio',
(stats.damage_dealt / stats.damage_received).toFixed(2),
],
[
'Kills to death ratio',
(
stats.frags /
(stats.battles - stats.survived_battles)
).toFixed(2),
],
]}
/>
)}
<PoweredByBlitzStars />
</Wrapper>,
);
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
fullBlitzStarsStats(server, accountInfo[id].nickname),
supportBlitzStars,
);
await interaction.editReply({
files: [image],
components: [actionRow],
});
console.log(`Showing stats for ${accountInfo[id].nickname}`);
},
autocomplete: (interaction) => {
tanksAutocomplete(interaction);
usernameAutocomplete(interaction);
},
} satisfies CommandRegistry;
+2 -9
View File
@@ -30,7 +30,7 @@ import resolveTankName from '../utilities/resolveTankName.js';
export default {
inProduction: true,
inDevelopment: true,
inDevelopment: false,
inPublic: true,
command: new SlashCommandBuilder()
@@ -82,14 +82,7 @@ export default {
return (
<Breakdown.Row
key={tankId}
name={
tankId === '0'
? 'Today'
: resolveTankName({
tank_id: tankId as unknown as number,
name: tankopedia[tankId as unknown as number].name,
})
}
name={tankId === '0' ? 'Today' : resolveTankName(Number(tankId))}
winrate={tankStats.wins / tankStats.battles}
careerWinrate={career.wins / career.battles}
WN8={-Infinity}