mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: moved tankopedia and tankaverages into a runtime cache
This commit is contained in:
1 parent
95b105cb65
commit
00b721f34f
29 files changed
+268
-235
No files matched your search
+1
-24
@@ -1,5 +1,5 @@
|
|||||||
import { build } from 'esbuild';
|
import { build } from 'esbuild';
|
||||||
import { copyFile, mkdir, rm, writeFile } from 'fs/promises';
|
import { copyFile, mkdir, rm } from 'fs/promises';
|
||||||
import { argv } from 'process';
|
import { argv } from 'process';
|
||||||
|
|
||||||
const isProd = !argv.includes('--dev');
|
const isProd = !argv.includes('--dev');
|
||||||
@@ -17,29 +17,6 @@ if (isProd) {
|
|||||||
await mkdir('dist');
|
await mkdir('dist');
|
||||||
console.log('✅ Dist created');
|
console.log('✅ Dist created');
|
||||||
|
|
||||||
console.log('🟡 Caching tankopedia...');
|
|
||||||
fetch('https://www.blitzstars.com/bs-tankopedia.json')
|
|
||||||
.then((response) => response.text())
|
|
||||||
.then((tankopedia) => writeFile('./dist/tankopedia.json', tankopedia))
|
|
||||||
.then(() => console.log('✅ Tankopedia cached'));
|
|
||||||
|
|
||||||
console.log('🟡 Caching tank averages...');
|
|
||||||
fetch('https://www.blitzstars.com/api/tankaverages.json')
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((individualTankAverages) => {
|
|
||||||
const tankAverages = {};
|
|
||||||
|
|
||||||
individualTankAverages.forEach((individualTankAverage) => {
|
|
||||||
tankAverages[individualTankAverage.tank_id] = individualTankAverage;
|
|
||||||
});
|
|
||||||
|
|
||||||
return writeFile(
|
|
||||||
'./dist/tankaverages.json',
|
|
||||||
JSON.stringify(tankAverages),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(() => console.log('✅ Tank averages cached'));
|
|
||||||
|
|
||||||
console.log('🟡 Copying package.json...');
|
console.log('🟡 Copying package.json...');
|
||||||
copyFile('package.dist.json', 'dist/package.json').then(() =>
|
copyFile('package.dist.json', 'dist/package.json').then(() =>
|
||||||
console.log('✅ package.json copied'),
|
console.log('✅ package.json copied'),
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ const executionStart = new Date().getTime();
|
|||||||
|
|
||||||
export const debugCommand: CommandRegistry = {
|
export const debugCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
.setName('debug')
|
.setName('debug')
|
||||||
.setDescription('Debug information about the bot'),
|
.setDescription('Debug information about the bot'),
|
||||||
|
|
||||||
handler(interaction) {
|
async handler(interaction) {
|
||||||
const currentTime = new Date().getTime();
|
const currentTime = new Date().getTime();
|
||||||
const uptime = currentTime - executionStart;
|
const uptime = currentTime - executionStart;
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ export const debugCommand: CommandRegistry = {
|
|||||||
(uptime / 1000) % 60,
|
(uptime / 1000) % 60,
|
||||||
)}s ${Math.floor(uptime % 1000)}ms`,
|
)}s ${Math.floor(uptime % 1000)}ms`,
|
||||||
],
|
],
|
||||||
['Tankopedia cached', `${tankopedia !== undefined}`],
|
['Tankopedia cached', `${(await tankopedia) !== undefined}`],
|
||||||
['Tank averages cached', `${tankAverages !== undefined}`],
|
['Tank averages cached', `${(await tankAverages) !== undefined}`],
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
+45
-29
@@ -15,6 +15,7 @@ import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.j
|
|||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
||||||
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
import { CommandRegistry } from '../events/interactionCreate/index.js';
|
||||||
import { AccountInfo } from '../types/accountInfo.js';
|
import { AccountInfo } from '../types/accountInfo.js';
|
||||||
|
import { PossiblyPromise } from '../types/possiblyPromise.js';
|
||||||
|
|
||||||
export type SkilledClan = 'SKLLD' | 'SMRI';
|
export type SkilledClan = 'SKLLD' | 'SMRI';
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ export const SKILLED_CLANS: Record<SkilledClan, string> = {
|
|||||||
|
|
||||||
export const eligibleCommand: CommandRegistry = {
|
export const eligibleCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: false,
|
inPublic: false,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -67,41 +68,56 @@ export const eligibleCommand: CommandRegistry = {
|
|||||||
const entries = Object.entries(tankStatsOverTime);
|
const entries = Object.entries(tankStatsOverTime);
|
||||||
const stats = sumStats(entries.map(([, stats]) => stats));
|
const stats = sumStats(entries.map(([, stats]) => stats));
|
||||||
const averageTier =
|
const averageTier =
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
const tankId = parseInt(tankIdString);
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankTier = tankopedia[tankId].tier;
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankTier = (await tankopedia)[tankId].tier;
|
||||||
|
|
||||||
return accumulator + tankTier * stats.battles;
|
return (await accumulator) + tankTier * stats.battles;
|
||||||
}, 0) / stats.battles;
|
},
|
||||||
|
0,
|
||||||
|
)) / stats.battles;
|
||||||
const WN8 =
|
const WN8 =
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
const tankId = parseInt(tankIdString);
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
|
const tankId = parseInt(tankIdString);
|
||||||
|
|
||||||
// edge case where new tanks don't have averages
|
// edge case where new tanks don't have averages
|
||||||
if (tankAverages[tankId]) {
|
if ((await tankAverages)[tankId]) {
|
||||||
const tankWN8 = calculateWN8(tankAverages[tankId].all, stats);
|
const tankWN8 = calculateWN8(
|
||||||
|
(await tankAverages)[tankId].all,
|
||||||
|
stats,
|
||||||
|
);
|
||||||
|
|
||||||
if (isNaN(tankWN8)) return accumulator;
|
if (isNaN(tankWN8)) return accumulator;
|
||||||
return accumulator + tankWN8 * stats.battles;
|
return (await accumulator) + tankWN8 * stats.battles;
|
||||||
} else return accumulator;
|
} else return accumulator;
|
||||||
}, 0) / stats.battles;
|
},
|
||||||
|
0,
|
||||||
|
)) / stats.battles;
|
||||||
const tier10Damage =
|
const tier10Damage =
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
const tankId = parseInt(tankIdString);
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
|
const tankId = parseInt(tankIdString);
|
||||||
|
|
||||||
if (tankopedia[tankId]?.tier === 10) {
|
if ((await tankopedia)[tankId]?.tier === 10) {
|
||||||
const tankStats = tankStatsOverTime[tankId];
|
const tankStats = tankStatsOverTime[tankId];
|
||||||
return accumulator + tankStats.damage_dealt;
|
return (await accumulator) + tankStats.damage_dealt;
|
||||||
} else return accumulator;
|
} else return accumulator;
|
||||||
}, 0) /
|
},
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
0,
|
||||||
const tankId = parseInt(tankIdString);
|
)) /
|
||||||
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
|
const tankId = parseInt(tankIdString);
|
||||||
|
|
||||||
if (tankopedia[tankId]?.tier === 10) {
|
if ((await tankopedia)[tankId]?.tier === 10) {
|
||||||
const tankStats = tankStatsOverTime[tankId];
|
const tankStats = tankStatsOverTime[tankId];
|
||||||
return accumulator + tankStats.battles;
|
return (await accumulator) + tankStats.battles;
|
||||||
} else return accumulator;
|
} else return accumulator;
|
||||||
}, 0);
|
},
|
||||||
|
0,
|
||||||
|
));
|
||||||
|
|
||||||
if (stats.battles < 126) {
|
if (stats.battles < 126) {
|
||||||
problems.push(['Battles', `${stats.battles} (${stats.battles - 126})`]);
|
problems.push(['Battles', `${stats.battles} (${stats.battles - 126})`]);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const evolutionCommand: CommandRegistry = {
|
|||||||
const player = await resolvePlayerFromCommand(interaction);
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
const period = resolvePeriodFromCommand(interaction);
|
const period = resolvePeriodFromCommand(interaction);
|
||||||
const tankIdRaw = interaction.options.getString('tank')!;
|
const tankIdRaw = interaction.options.getString('tank')!;
|
||||||
const tankId = commandGroup === 'tank' ? resolveTankId(tankIdRaw) : null;
|
const tankId = commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||||
const start = interaction.options.getInteger('start');
|
const start = interaction.options.getInteger('start');
|
||||||
const end = interaction.options.getInteger('end');
|
const end = interaction.options.getInteger('end');
|
||||||
const { nickname } = (
|
const { nickname } = (
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
|
|
||||||
export const helpCommand: CommandRegistry = {
|
export const helpCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const DEFAULT_THRESHOLD = 7;
|
|||||||
|
|
||||||
export const inactiveCommand: CommandRegistry = {
|
export const inactiveCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const COMP_TANKS = [
|
|||||||
|
|
||||||
export const ownedTanksCommand: CommandRegistry = {
|
export const ownedTanksCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
@@ -77,9 +77,11 @@ export const ownedTanksCommand: CommandRegistry = {
|
|||||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||||
);
|
);
|
||||||
const tankStats = await getTankStats(server, id);
|
const tankStats = await getTankStats(server, id);
|
||||||
const tanks = tankStats
|
const tanks = (
|
||||||
.map((tankData) => tankopedia[tankData.tank_id])
|
await Promise.all(
|
||||||
.filter((tank) => tank?.tier === tier);
|
tankStats.map(async (tankData) => (await tankopedia)[tankData.tank_id]),
|
||||||
|
)
|
||||||
|
).filter((tank) => tank?.tier === tier);
|
||||||
const clanData = await getWargamingResponse<PlayerClanData>(
|
const clanData = await getWargamingResponse<PlayerClanData>(
|
||||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { CommandRegistry } from '../events/interactionCreate/index.js';
|
|||||||
|
|
||||||
export const pingCommand: CommandRegistry<true> = {
|
export const pingCommand: CommandRegistry<true> = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
handlesInteraction: true,
|
handlesInteraction: true,
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type SortBy = 'name' | 'count';
|
|||||||
|
|
||||||
export const playerAchievementsCommand: CommandRegistry = {
|
export const playerAchievementsCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { AccountInfo } from '../types/accountInfo.js';
|
|||||||
|
|
||||||
export const playerInfoCommand: CommandRegistry = {
|
export const playerInfoCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { ClanList } from '../types/clanList.js';
|
|||||||
|
|
||||||
export const searchClansCommand: CommandRegistry = {
|
export const searchClansCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { AccountList } from '../types/accountList.js';
|
|||||||
|
|
||||||
export const searchPlayersCommand: CommandRegistry = {
|
export const searchPlayersCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { CommandRegistry } from '../events/interactionCreate/index.js';
|
|||||||
|
|
||||||
export const searchTanksCommand: CommandRegistry = {
|
export const searchTanksCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export const statsCommand: CommandRegistry = {
|
|||||||
const player = await resolvePlayerFromCommand(interaction);
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
const period = resolvePeriodFromCommand(interaction);
|
const period = resolvePeriodFromCommand(interaction);
|
||||||
const tankIdRaw = interaction.options.getString('tank')!;
|
const tankIdRaw = interaction.options.getString('tank')!;
|
||||||
const tankId = commandGroup === 'tank' ? resolveTankId(tankIdRaw) : null;
|
const tankId =
|
||||||
|
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||||
const start = interaction.options.getInteger('start');
|
const start = interaction.options.getInteger('start');
|
||||||
const end = interaction.options.getInteger('end');
|
const end = interaction.options.getInteger('end');
|
||||||
const path = interactionToURL(interaction, {
|
const path = interactionToURL(interaction, {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { AccountInfo } from '../types/accountInfo.js';
|
|||||||
|
|
||||||
export const statsFullCommand: CommandRegistry = {
|
export const statsFullCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: addStatTypeSubCommandGroups(
|
command: addStatTypeSubCommandGroups(
|
||||||
@@ -35,7 +35,8 @@ export const statsFullCommand: CommandRegistry = {
|
|||||||
const player = await resolvePlayerFromCommand(interaction);
|
const player = await resolvePlayerFromCommand(interaction);
|
||||||
const period = resolvePeriodFromCommand(interaction);
|
const period = resolvePeriodFromCommand(interaction);
|
||||||
const tankIdRaw = interaction.options.getString('tank')!;
|
const tankIdRaw = interaction.options.getString('tank')!;
|
||||||
const tankId = commandGroup === 'tank' ? resolveTankId(tankIdRaw) : null;
|
const tankId =
|
||||||
|
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||||
const start = interaction.options.getInteger('start');
|
const start = interaction.options.getInteger('start');
|
||||||
const end = interaction.options.getInteger('end');
|
const end = interaction.options.getInteger('end');
|
||||||
const path = interactionToURL(interaction, {
|
const path = interactionToURL(interaction, {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { AccountInfo } from '../types/accountInfo.js';
|
|||||||
|
|
||||||
export const todayCommand: CommandRegistry = {
|
export const todayCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { PlayerClanData } from '../types/playerClanData.js';
|
|||||||
|
|
||||||
export const verifyCommand: CommandRegistry = {
|
export const verifyCommand: CommandRegistry = {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { go } from 'fuzzysort';
|
|||||||
import throwError from '../node/throwError.js';
|
import throwError from '../node/throwError.js';
|
||||||
import { TANKS, tankopedia } from './tankopedia.js';
|
import { TANKS, tankopedia } from './tankopedia.js';
|
||||||
|
|
||||||
export default function resolveTankId(tank: string) {
|
export default async function resolveTankId(tank: string) {
|
||||||
const number = parseInt(tank);
|
const number = parseInt(tank);
|
||||||
|
|
||||||
if (Number.isNaN(number)) {
|
if (Number.isNaN(number)) {
|
||||||
@@ -17,7 +17,7 @@ export default function resolveTankId(tank: string) {
|
|||||||
return searchResult[0].obj.tank_id;
|
return searchResult[0].obj.tank_id;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (tankopedia[number]) {
|
if ((await tankopedia)[number]) {
|
||||||
return number;
|
return number;
|
||||||
} else {
|
} else {
|
||||||
throw throwError(
|
throw throwError(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { tankopedia } from './tankopedia.js';
|
import { tankopedia } from './tankopedia.js';
|
||||||
|
|
||||||
export default function resolveTankName(tankId: number) {
|
export default async function resolveTankName(tankId: number) {
|
||||||
const tankopediaName = tankopedia[tankId].name;
|
const tankopediaName = (await tankopedia)[tankId].name;
|
||||||
return tankopediaName ? tankopediaName : `Unknown Tank ${tankId}`;
|
return tankopediaName ? tankopediaName : `Unknown Tank ${tankId}`;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
import { readFileSync } from 'fs';
|
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank';
|
export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank';
|
||||||
|
|
||||||
export interface TankopediaEntry {
|
export interface TankopediaEntry {
|
||||||
@@ -19,11 +16,13 @@ export interface Tankopedia {
|
|||||||
[id: number]: TankopediaEntry;
|
[id: number]: TankopediaEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tankopedia = (
|
console.log('Caching tankopedia...');
|
||||||
JSON.parse(
|
export const tankopedia = fetch('https://www.blitzstars.com/bs-tankopedia.json')
|
||||||
readFileSync(join(__dirname, 'tankopedia.json')) as unknown as string,
|
.then((response) => response.json())
|
||||||
) as { data: Tankopedia }
|
.then((wrapperTankopedia) => {
|
||||||
).data;
|
console.log('Tankopedia cached');
|
||||||
|
return (wrapperTankopedia as { data: Tankopedia }).data;
|
||||||
|
});
|
||||||
const entries = Object.entries(tankopedia);
|
const entries = Object.entries(tankopedia);
|
||||||
export const TANK_IDS = entries.map(([, { tank_id }]) => tank_id as number);
|
export const TANK_IDS = entries.map(([, { tank_id }]) => tank_id as number);
|
||||||
export const TANKS = entries.map(([, entry]) => entry as TankopediaEntry);
|
export const TANKS = entries.map(([, entry]) => entry as TankopediaEntry);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { emptyAllStats } from './getTankStatsDiffed.js';
|
|||||||
import { tankAverages } from './tankAverages.js';
|
import { tankAverages } from './tankAverages.js';
|
||||||
|
|
||||||
console.log('Calculating player average...');
|
console.log('Calculating player average...');
|
||||||
export const playerAverage = Object.entries(tankAverages).reduce(
|
export const playerAverage = Object.entries(await tankAverages).reduce(
|
||||||
(accumulator, [, tankAverage]) => {
|
(accumulator, [, tankAverage]) => {
|
||||||
return sumStats([accumulator, tankAverage.all]);
|
return sumStats([accumulator, tankAverage.all]);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { readFileSync } from 'fs';
|
|
||||||
import { join } from 'path';
|
|
||||||
import { AllStats } from '../../types/accountInfo.js';
|
import { AllStats } from '../../types/accountInfo.js';
|
||||||
|
|
||||||
export interface SpecialStats {
|
export interface SpecialStats {
|
||||||
@@ -82,6 +80,22 @@ export interface IndividualTankAverage {
|
|||||||
|
|
||||||
export type TankAverages = Record<number, IndividualTankAverage>;
|
export type TankAverages = Record<number, IndividualTankAverage>;
|
||||||
|
|
||||||
export const tankAverages = JSON.parse(
|
console.log('Caching tank averages...');
|
||||||
readFileSync(join(__dirname, 'tankaverages.json')) as unknown as string,
|
export const tankAverages = fetch(
|
||||||
) as TankAverages;
|
'https://www.blitzstars.com/api/tankaverages.json',
|
||||||
|
)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((individualTankAverages) => {
|
||||||
|
const partialTankAverages: TankAverages = {};
|
||||||
|
|
||||||
|
(individualTankAverages as IndividualTankAverage[]).forEach(
|
||||||
|
(individualTankAverage) => {
|
||||||
|
partialTankAverages[individualTankAverage.tank_id] =
|
||||||
|
individualTankAverage;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Tank averages cached');
|
||||||
|
|
||||||
|
return partialTankAverages;
|
||||||
|
});
|
||||||
@@ -15,14 +15,16 @@ export default async function autocompleteTanks(
|
|||||||
|
|
||||||
await interaction.respond(
|
await interaction.respond(
|
||||||
focusedOption.value
|
focusedOption.value
|
||||||
? go(focusedOption.value, TANKS, { keys: ['name'], limit: 10 }).map(
|
? await Promise.all(
|
||||||
(item) =>
|
go(focusedOption.value, TANKS, { keys: ['name'], limit: 10 }).map(
|
||||||
({
|
async (item) =>
|
||||||
name: item.obj.name
|
({
|
||||||
? item.obj.name
|
name: item.obj.name
|
||||||
: resolveTankName(item.obj.tank_id),
|
? item.obj.name
|
||||||
value: `${item.obj.tank_id}`,
|
: await resolveTankName(item.obj.tank_id),
|
||||||
} satisfies ApplicationCommandOptionChoiceData<string>),
|
value: `${item.obj.tank_id}`,
|
||||||
|
} satisfies ApplicationCommandOptionChoiceData<string>),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ export default async function evolution<Type extends StatType>(
|
|||||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
||||||
: undefined;
|
: undefined;
|
||||||
} else {
|
} else {
|
||||||
nameDiscriminator = `(${resolveTankName(tankId!)})`;
|
nameDiscriminator = `(${await resolveTankName(tankId!)})`;
|
||||||
image = tankopedia[tankId!].images.normal;
|
image = (await tankopedia)[tankId!].images.normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
let histories: Histories;
|
let histories: Histories;
|
||||||
|
|||||||
+48
-40
@@ -22,6 +22,7 @@ import {
|
|||||||
SupplementaryStats,
|
SupplementaryStats,
|
||||||
} from '../types/accountInfo.js';
|
} from '../types/accountInfo.js';
|
||||||
import { PlayerClanData } from '../types/playerClanData.js';
|
import { PlayerClanData } from '../types/playerClanData.js';
|
||||||
|
import { PossiblyPromise } from '../types/possiblyPromise.js';
|
||||||
|
|
||||||
export type StatType = 'player' | 'tank';
|
export type StatType = 'player' | 'tank';
|
||||||
|
|
||||||
@@ -47,8 +48,8 @@ export default async function stats<Type extends StatType>(
|
|||||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
||||||
: undefined;
|
: undefined;
|
||||||
} else if (type === 'tank') {
|
} else if (type === 'tank') {
|
||||||
nameDiscriminator = `(${resolveTankName(tankId!)})`;
|
nameDiscriminator = `(${await resolveTankName(tankId!)})`;
|
||||||
image = tankopedia[tankId!].images.normal;
|
image = (await tankopedia)[tankId!].images.normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tankStats = await getTankStatsDiffed(server, id, start, end);
|
const tankStats = await getTankStatsDiffed(server, id, start, end);
|
||||||
@@ -59,54 +60,61 @@ export default async function stats<Type extends StatType>(
|
|||||||
if (type === 'player') {
|
if (type === 'player') {
|
||||||
const entries = Object.entries(tankStats);
|
const entries = Object.entries(tankStats);
|
||||||
stats = sumStats(entries.map(([, stats]) => stats));
|
stats = sumStats(entries.map(([, stats]) => stats));
|
||||||
const battlesOfTanksWithAverages = entries.reduce(
|
const battlesOfTanksWithAverages = await entries.reduce<
|
||||||
(accumulator, [tankIdString, stats]) => {
|
PossiblyPromise<number>
|
||||||
const tankId = parseInt(tankIdString);
|
>(async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankAverage = tankAverages[tankId];
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankAverage = (await tankAverages)[tankId];
|
||||||
|
|
||||||
return tankAverage ? accumulator + stats.battles : accumulator;
|
return tankAverage ? (await accumulator) + stats.battles : accumulator;
|
||||||
},
|
}, 0);
|
||||||
0,
|
const battlesOfTanksWithTankopediaEntry = await entries.reduce<
|
||||||
);
|
PossiblyPromise<number>
|
||||||
const battlesOfTanksWithTankopediaEntry = entries.reduce(
|
>(async (accumulator, [tankIdString, stats]) => {
|
||||||
(accumulator, [tankIdString, stats]) => {
|
const tankId = parseInt(tankIdString);
|
||||||
const tankId = parseInt(tankIdString);
|
const tankopediaEntry = (await tankopedia)[tankId];
|
||||||
const tankopediaEntry = tankopedia[tankId];
|
|
||||||
|
|
||||||
return tankopediaEntry ? accumulator + stats.battles : accumulator;
|
return tankopediaEntry
|
||||||
},
|
? (await accumulator) + stats.battles
|
||||||
0,
|
: accumulator;
|
||||||
);
|
}, 0);
|
||||||
|
|
||||||
supplementaryStats = {
|
supplementaryStats = {
|
||||||
WN8:
|
WN8:
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
const tankId = parseInt(tankIdString);
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankAverage = tankAverages[tankId];
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankAverage = (await tankAverages)[tankId];
|
||||||
|
|
||||||
return tankAverage
|
return tankAverage
|
||||||
? accumulator + calculateWN8(tankAverage.all, stats) * stats.battles
|
? (await accumulator) +
|
||||||
: accumulator;
|
calculateWN8(tankAverage.all, stats) * stats.battles
|
||||||
}, 0) / battlesOfTanksWithAverages,
|
: accumulator;
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)) / battlesOfTanksWithAverages,
|
||||||
tier:
|
tier:
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
const tankId = parseInt(tankIdString);
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankopediaEntry = tankopedia[tankId];
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankopediaEntry = (await tankopedia)[tankId];
|
||||||
|
|
||||||
return tankopediaEntry
|
return tankopediaEntry
|
||||||
? accumulator + tankopediaEntry.tier * stats.battles
|
? (await accumulator) + tankopediaEntry.tier * stats.battles
|
||||||
: accumulator;
|
: accumulator;
|
||||||
}, 0) / battlesOfTanksWithTankopediaEntry,
|
},
|
||||||
|
0,
|
||||||
|
)) / battlesOfTanksWithTankopediaEntry,
|
||||||
};
|
};
|
||||||
tierWeights = entries.reduce<TierWeightsRecord>(
|
tierWeights = await entries.reduce<PossiblyPromise<TierWeightsRecord>>(
|
||||||
(accumulator, [tankIdString, stats]) => {
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankId = parseInt(tankIdString);
|
const tankId = parseInt(tankIdString);
|
||||||
const tier = tankopedia[tankId].tier as Tier;
|
const tier = (await tankopedia)[tankId].tier as Tier;
|
||||||
|
|
||||||
if (accumulator[tier]) {
|
if ((await accumulator)[tier]) {
|
||||||
accumulator[tier]! += stats.battles;
|
(await accumulator)[tier]! += stats.battles;
|
||||||
} else {
|
} else {
|
||||||
accumulator[tier] = stats.battles;
|
(await accumulator)[tier] = stats.battles;
|
||||||
}
|
}
|
||||||
|
|
||||||
return accumulator;
|
return accumulator;
|
||||||
@@ -118,9 +126,9 @@ export default async function stats<Type extends StatType>(
|
|||||||
|
|
||||||
supplementaryStats = {
|
supplementaryStats = {
|
||||||
WN8: stats
|
WN8: stats
|
||||||
? calculateWN8(tankAverages[tankId!].all, tankStats[tankId!])
|
? calculateWN8((await tankAverages)[tankId!].all, tankStats[tankId!])
|
||||||
: undefined,
|
: undefined,
|
||||||
tier: tankopedia[tankId!].tier,
|
tier: (await tankopedia)[tankId!].tier,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+48
-40
@@ -21,6 +21,7 @@ import {
|
|||||||
SupplementaryStats,
|
SupplementaryStats,
|
||||||
} from '../types/accountInfo.js';
|
} from '../types/accountInfo.js';
|
||||||
import { PlayerClanData } from '../types/playerClanData.js';
|
import { PlayerClanData } from '../types/playerClanData.js';
|
||||||
|
import { PossiblyPromise } from '../types/possiblyPromise.js';
|
||||||
|
|
||||||
export type StatType = 'player' | 'tank';
|
export type StatType = 'player' | 'tank';
|
||||||
|
|
||||||
@@ -45,8 +46,8 @@ export default async function statsfull<Type extends StatType>(
|
|||||||
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
|
||||||
: undefined;
|
: undefined;
|
||||||
} else if (type === 'tank') {
|
} else if (type === 'tank') {
|
||||||
nameDiscriminator = `(${resolveTankName(tankId!)})`;
|
nameDiscriminator = `(${await resolveTankName(tankId!)})`;
|
||||||
image = tankopedia[tankId!].images.normal;
|
image = (await tankopedia)[tankId!].images.normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tankStats = await getTankStatsDiffed(server, id, start, end);
|
const tankStats = await getTankStatsDiffed(server, id, start, end);
|
||||||
@@ -57,54 +58,61 @@ export default async function statsfull<Type extends StatType>(
|
|||||||
if (type === 'player') {
|
if (type === 'player') {
|
||||||
const entries = Object.entries(tankStats);
|
const entries = Object.entries(tankStats);
|
||||||
stats = sumStats(entries.map(([, stats]) => stats));
|
stats = sumStats(entries.map(([, stats]) => stats));
|
||||||
const battlesOfTanksWithAverages = entries.reduce(
|
const battlesOfTanksWithAverages = await entries.reduce<
|
||||||
(accumulator, [tankIdString, stats]) => {
|
PossiblyPromise<number>
|
||||||
const tankId = parseInt(tankIdString);
|
>(async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankAverage = tankAverages[tankId];
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankAverage = (await tankAverages)[tankId];
|
||||||
|
|
||||||
return tankAverage ? accumulator + stats.battles : accumulator;
|
return tankAverage ? (await accumulator) + stats.battles : accumulator;
|
||||||
},
|
}, 0);
|
||||||
0,
|
const battlesOfTanksWithTankopediaEntry = await entries.reduce<
|
||||||
);
|
PossiblyPromise<number>
|
||||||
const battlesOfTanksWithTankopediaEntry = entries.reduce(
|
>(async (accumulator, [tankIdString, stats]) => {
|
||||||
(accumulator, [tankIdString, stats]) => {
|
const tankId = parseInt(tankIdString);
|
||||||
const tankId = parseInt(tankIdString);
|
const tankopediaEntry = (await tankopedia)[tankId];
|
||||||
const tankopediaEntry = tankopedia[tankId];
|
|
||||||
|
|
||||||
return tankopediaEntry ? accumulator + stats.battles : accumulator;
|
return tankopediaEntry
|
||||||
},
|
? (await accumulator) + stats.battles
|
||||||
0,
|
: accumulator;
|
||||||
);
|
}, 0);
|
||||||
|
|
||||||
supplementaryStats = {
|
supplementaryStats = {
|
||||||
WN8:
|
WN8:
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
const tankId = parseInt(tankIdString);
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankAverage = tankAverages[tankId];
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankAverage = (await tankAverages)[tankId];
|
||||||
|
|
||||||
return tankAverage
|
return tankAverage
|
||||||
? accumulator + calculateWN8(tankAverage.all, stats) * stats.battles
|
? (await accumulator) +
|
||||||
: accumulator;
|
calculateWN8(tankAverage.all, stats) * stats.battles
|
||||||
}, 0) / battlesOfTanksWithAverages,
|
: accumulator;
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)) / battlesOfTanksWithAverages,
|
||||||
tier:
|
tier:
|
||||||
entries.reduce((accumulator, [tankIdString, stats]) => {
|
(await entries.reduce<PossiblyPromise<number>>(
|
||||||
const tankId = parseInt(tankIdString);
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankopediaEntry = tankopedia[tankId];
|
const tankId = parseInt(tankIdString);
|
||||||
|
const tankopediaEntry = (await tankopedia)[tankId];
|
||||||
|
|
||||||
return tankopediaEntry
|
return tankopediaEntry
|
||||||
? accumulator + tankopediaEntry.tier * stats.battles
|
? (await accumulator) + tankopediaEntry.tier * stats.battles
|
||||||
: accumulator;
|
: accumulator;
|
||||||
}, 0) / battlesOfTanksWithTankopediaEntry,
|
},
|
||||||
|
0,
|
||||||
|
)) / battlesOfTanksWithTankopediaEntry,
|
||||||
};
|
};
|
||||||
tierWeights = entries.reduce<TierWeightsRecord>(
|
tierWeights = await entries.reduce<PossiblyPromise<TierWeightsRecord>>(
|
||||||
(accumulator, [tankIdString, stats]) => {
|
async (accumulator, [tankIdString, stats]) => {
|
||||||
const tankId = parseInt(tankIdString);
|
const tankId = parseInt(tankIdString);
|
||||||
const tier = tankopedia[tankId].tier as Tier;
|
const tier = (await tankopedia)[tankId].tier as Tier;
|
||||||
|
|
||||||
if (accumulator[tier]) {
|
if ((await accumulator)[tier]) {
|
||||||
accumulator[tier]! += stats.battles;
|
(await accumulator)[tier]! += stats.battles;
|
||||||
} else {
|
} else {
|
||||||
accumulator[tier] = stats.battles;
|
(await accumulator)[tier] = stats.battles;
|
||||||
}
|
}
|
||||||
|
|
||||||
return accumulator;
|
return accumulator;
|
||||||
@@ -115,8 +123,8 @@ export default async function statsfull<Type extends StatType>(
|
|||||||
stats = tankStats[tankId!];
|
stats = tankStats[tankId!];
|
||||||
|
|
||||||
supplementaryStats = {
|
supplementaryStats = {
|
||||||
WN8: calculateWN8(tankAverages[tankId!].all, tankStats[tankId!]),
|
WN8: calculateWN8((await tankAverages)[tankId!].all, tankStats[tankId!]),
|
||||||
tier: tankopedia[tankId!].tier,
|
tier: (await tankopedia)[tankId!].tier,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+55
-51
@@ -18,6 +18,7 @@ import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand.js';
|
|||||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments.js';
|
||||||
import { AccountInfo, AllStats } from '../types/accountInfo.js';
|
import { AccountInfo, AllStats } from '../types/accountInfo.js';
|
||||||
import { PlayerClanData } from '../types/playerClanData.js';
|
import { PlayerClanData } from '../types/playerClanData.js';
|
||||||
|
import { PossiblyPromise } from '../types/possiblyPromise.js';
|
||||||
|
|
||||||
export default async function today({ server, id }: ResolvedPlayer) {
|
export default async function today({ server, id }: ResolvedPlayer) {
|
||||||
const tankStatsOverTime = await getTankStatsDiffed(
|
const tankStatsOverTime = await getTankStatsDiffed(
|
||||||
@@ -52,33 +53,31 @@ export default async function today({ server, id }: ResolvedPlayer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tankStatsOverTimeEntries = Object.entries(tankStatsOverTime);
|
const tankStatsOverTimeEntries = Object.entries(tankStatsOverTime);
|
||||||
const todayWN8s = tankStatsOverTimeEntries.reduce<Record<number, number>>(
|
const todayWN8s = await tankStatsOverTimeEntries.reduce<
|
||||||
(accumulator, [tankIdString, tankStats]) => {
|
PossiblyPromise<Record<number, number>>
|
||||||
const tankId = Number(tankIdString);
|
>(async (accumulator, [tankIdString, tankStats]) => {
|
||||||
|
const tankId = Number(tankIdString);
|
||||||
|
|
||||||
return tankId === 0 || tankAverages[tankId] === undefined
|
return tankId === 0 || (await tankAverages)[tankId] === undefined
|
||||||
? accumulator
|
? accumulator
|
||||||
: {
|
: {
|
||||||
...accumulator,
|
...accumulator,
|
||||||
[tankId]: calculateWN8(tankAverages[tankId].all, tankStats),
|
[tankId]: calculateWN8((await tankAverages)[tankId].all, tankStats),
|
||||||
};
|
};
|
||||||
},
|
}, {});
|
||||||
{},
|
const careerWN8s = await careerTankStatsRaw.reduce<
|
||||||
);
|
PossiblyPromise<Record<number, number>>
|
||||||
const careerWN8s = careerTankStatsRaw.reduce<Record<number, number>>(
|
>(async (accumulator, { tank_id }) => {
|
||||||
(accumulator, { tank_id }) => {
|
return tank_id === 0 || (await tankAverages)[tank_id] === undefined
|
||||||
return tank_id === 0 || tankAverages[tank_id] === undefined
|
? accumulator
|
||||||
? accumulator
|
: {
|
||||||
: {
|
...accumulator,
|
||||||
...accumulator,
|
[tank_id]: calculateWN8(
|
||||||
[tank_id]: calculateWN8(
|
(await tankAverages)[tank_id].all,
|
||||||
tankAverages[tank_id].all,
|
careerStats[tank_id],
|
||||||
careerStats[tank_id],
|
),
|
||||||
),
|
};
|
||||||
};
|
}, {});
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const todayWN8sEntries = Object.entries(todayWN8s);
|
const todayWN8sEntries = Object.entries(todayWN8s);
|
||||||
const careerWN8sEntries = Object.entries(careerWN8s);
|
const careerWN8sEntries = Object.entries(careerWN8s);
|
||||||
|
|
||||||
@@ -109,32 +108,37 @@ export default async function today({ server, id }: ResolvedPlayer) {
|
|||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
const rows = tankStatsOverTimeEntries.map(([tankIdString, tankStats]) => {
|
const rows = await Promise.all(
|
||||||
const tankId = Number(tankIdString);
|
tankStatsOverTimeEntries.map(async ([tankIdString, tankStats]) => {
|
||||||
const career = careerStats[tankId];
|
const tankId = Number(tankIdString);
|
||||||
|
const career = careerStats[tankId];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Breakdown.Row
|
<Breakdown.Row
|
||||||
key={tankId}
|
key={tankId}
|
||||||
name={tankId === 0 ? 'Total' : resolveTankName(Number(tankIdString))}
|
name={
|
||||||
winrate={tankStats.wins / tankStats.battles}
|
tankId === 0 ? 'Total' : await resolveTankName(Number(tankIdString))
|
||||||
careerWinrate={career.wins / career.battles}
|
}
|
||||||
WN8={isNaN(todayWN8s[tankId]) ? undefined : todayWN8s[tankId]}
|
winrate={tankStats.wins / tankStats.battles}
|
||||||
careerWN8={isNaN(careerWN8s[tankId]) ? undefined : careerWN8s[tankId]}
|
careerWinrate={career.wins / career.battles}
|
||||||
damage={tankStats.damage_dealt / tankStats.battles}
|
WN8={isNaN(todayWN8s[tankId]) ? undefined : todayWN8s[tankId]}
|
||||||
careerDamage={career.damage_dealt / career.battles}
|
careerWN8={isNaN(careerWN8s[tankId]) ? undefined : careerWN8s[tankId]}
|
||||||
survival={tankStats.survived_battles / tankStats.battles}
|
damage={tankStats.damage_dealt / tankStats.battles}
|
||||||
careerSurvival={career.survived_battles / career.battles}
|
careerDamage={career.damage_dealt / career.battles}
|
||||||
battles={tankStats.battles}
|
survival={tankStats.survived_battles / tankStats.battles}
|
||||||
careerBattles={career.battles}
|
careerSurvival={career.survived_battles / career.battles}
|
||||||
icon={
|
battles={tankStats.battles}
|
||||||
tankId === 0
|
careerBattles={career.battles}
|
||||||
? undefined
|
icon={
|
||||||
: tankopedia[tankIdString as unknown as number].images.normal
|
tankId === 0
|
||||||
}
|
? undefined
|
||||||
/>
|
: (await tankopedia)[tankIdString as unknown as number].images
|
||||||
);
|
.normal
|
||||||
});
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { RouteRegistry } from '../server.js';
|
|||||||
export const statsRoute: RouteRegistry = {
|
export const statsRoute: RouteRegistry = {
|
||||||
route: '/stats/:type/:period',
|
route: '/stats/:type/:period',
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
|
|
||||||
async handler(req) {
|
async handler(req) {
|
||||||
const type = req.params.type as StatType;
|
const type = req.params.type as StatType;
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export type PossiblyPromise<Type> = Promise<Type> | Type;
|
||||||
Reference in new issue
Block a user