mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: updated /tankstats
This commit is contained in:
1 parent
50369e9225
commit
831300cf69
13 files changed
+300
-272
No files matched your search
@@ -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
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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}
|
||||
|
||||
+37
-11
@@ -17,29 +17,55 @@ export default function TitleBar({
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
{image && <img style={{ width: 64, height: 64 }} src={image} />}
|
||||
<div style={{ display: 'flex', gap: 8, flex: 1 }}>
|
||||
{image && (
|
||||
<img
|
||||
style={{ width: 64, height: 64, objectFit: 'contain' }}
|
||||
src={image}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<span
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'column', gap: 8, flex: 1 }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 32,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 900,
|
||||
display: 'flex',
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
<span
|
||||
style={{
|
||||
fontSize: 32,
|
||||
color: theme.colors.textHighContrast,
|
||||
fontWeight: 900,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
{nameDiscriminator && (
|
||||
<span style={{ color: theme.colors.textLowContrast }}>
|
||||
<span
|
||||
style={{
|
||||
flex: 1,
|
||||
fontSize: 32,
|
||||
color: theme.colors.textLowContrast,
|
||||
fontWeight: 900,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{nameDiscriminator}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span style={{ color: theme.colors.textLowContrast, fontSize: 16 }}>
|
||||
{description}
|
||||
|
||||
@@ -18,7 +18,9 @@ export default async function tanksAutocomplete(
|
||||
? go(focusedOption.value, tanks, { keys: ['name'], limit: 10 }).map(
|
||||
(item) =>
|
||||
({
|
||||
name: item.obj.name ? item.obj.name : resolveTankName(item.obj),
|
||||
name: item.obj.name
|
||||
? item.obj.name
|
||||
: resolveTankName(item.obj.tank_id),
|
||||
value: `${item.obj.tank_id}`,
|
||||
} satisfies ApplicationCommandOptionChoiceData<string>),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { go } from 'fuzzysort';
|
||||
import { tankopedia, tanks } from '../blitzstars/tankopedia.js';
|
||||
import errorEmbed from '../interaction/errorEmbed.js';
|
||||
|
||||
export default async function resolveTankId(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
tank: string,
|
||||
) {
|
||||
const number = Number(tank);
|
||||
|
||||
if (Number.isNaN(number)) {
|
||||
const searchResult = go(tank, tanks, { keys: ['name'], limit: 1 });
|
||||
|
||||
if (searchResult.length === 0) {
|
||||
await interaction.editReply({
|
||||
embeds: [
|
||||
errorEmbed(
|
||||
'Tank not found',
|
||||
`Could not find tank by the name "${tank}".`,
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
throw new Error(`Tank not found by name "${tank}"`);
|
||||
} else {
|
||||
return searchResult[0].obj.tank_id;
|
||||
}
|
||||
} else {
|
||||
if (tankopedia[number]) {
|
||||
return number;
|
||||
} else {
|
||||
await interaction.editReply({
|
||||
embeds: [
|
||||
errorEmbed(
|
||||
'Tank not found',
|
||||
`Could not find tank by the ID "${number}".`,
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
throw new Error(`Tank not found by ID "${number}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function getPeriodNow() {
|
||||
return new Date().getTime() / 1000;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { StatPeriod } from '../options/addStatPeriodChoices.js';
|
||||
|
||||
export default function getPeriodicStart(period: StatPeriod) {
|
||||
if (period === 'career') {
|
||||
return -Infinity;
|
||||
} else {
|
||||
const daysAgo = period === 'today' ? 0 : Number(period) - 1;
|
||||
const periodStart = new Date();
|
||||
|
||||
periodStart.setDate(periodStart.getDate() - daysAgo);
|
||||
periodStart.setHours(5, 0, 0, 0);
|
||||
|
||||
return periodStart.getTime() / 1000;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
export interface Period {
|
||||
statsId: number;
|
||||
account_id: number;
|
||||
tank_id: number;
|
||||
recordDate: string;
|
||||
last_battle_time: number;
|
||||
fromDate: string;
|
||||
prior_battle_time: number;
|
||||
battles: number;
|
||||
wins: number;
|
||||
losses: number;
|
||||
spotted: number;
|
||||
shots: number;
|
||||
hits: number;
|
||||
xp: number;
|
||||
battle_life_time: number;
|
||||
frags: number;
|
||||
max_frags: number;
|
||||
damage_dealt: number;
|
||||
damage_received: number;
|
||||
survived_battles: number;
|
||||
win_and_survived: number;
|
||||
capture_points: number;
|
||||
dropped_capture_points: number;
|
||||
killStealerValue: number;
|
||||
isRating: boolean;
|
||||
wn8: number;
|
||||
region: string;
|
||||
}
|
||||
|
||||
export default async function getPeriodicStats(id: number, period: number) {
|
||||
const response = await fetch(
|
||||
`https://blitztankstats.com/api/Stats/period/${id}/${period}`,
|
||||
);
|
||||
return (await response.json()) as Period[];
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { SlashCommandStringOption } from 'discord.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 function addStatPeriodChoices(option: SlashCommandStringOption) {
|
||||
return option
|
||||
.setName('period')
|
||||
.setDescription('The period to get stats for')
|
||||
.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);
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { tankopedia } from '../core/blitzstars/tankopedia.js';
|
||||
|
||||
export default function resolveTankName(tank: {
|
||||
tank_id: number;
|
||||
name?: string;
|
||||
}) {
|
||||
return tank.name ? markdownEscape(tank.name) : `Unknown Tank ${tank.tank_id}`;
|
||||
export default function resolveTankName(tankId: number) {
|
||||
const tankopediaName = tankopedia[tankId].name;
|
||||
return tankopediaName ? tankopediaName : `Unknown Tank ${tankId}`;
|
||||
}
|
||||
Reference in new issue
Block a user