feat: dailystats

This commit is contained in:
TresAbhi committed 2023-05-25 18:20:51 +00:00
1 parent f729aee3de
commit e37a7947b4
18 files changed
+637 -153

No files matched your search

-3
View File
@@ -43,8 +43,5 @@ jobs:
- name: Install dependencies
run: yarn
- name: Install playwright
run: npx playwright install
- name: Host
run: yarn run start discord-token=${{ secrets.DISCORD_TOKEN }} wargaming-application-id=${{ secrets.WARGAMING_APPLICATION_ID }}
+1
View File
@@ -1,2 +1,3 @@
tasks:
- init: yarn
- command: npx playwright install && npx playwright install-deps
+1
View File
@@ -5,6 +5,7 @@
"main": "index.js",
"type": "module",
"scripts": {
"install": "playwright install && playwright install-deps",
"start": "vite-node src/index.ts --script",
"lint": "tsc"
},
+81 -78
View File
@@ -1,7 +1,8 @@
import { SlashCommandBuilder } from 'discord.js';
import escapeHTML from 'escape-html';
import GenericStats from '../components/GenericStats/index.js';
import Screenshot from '../components/Screenshot.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';
@@ -12,8 +13,8 @@ import getBlitzStarsAccount from '../core/blitzstars/getBlitzStarsAccount.js';
import cmdName from '../core/interaction/cmdName.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { args } from '../core/process/args.js';
import screenshotHTML from '../core/ui/screenshotHTML.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { browser } from '../index.js';
import { AccountInfo, AllStats } from '../types/accountInfo.js';
import { PlayerClanData } from '../types/playerClanData.js';
import { BlitzStartsComputedPeriodicStatistics } from '../types/statistics.js';
@@ -29,7 +30,7 @@ const periodNames: Record<Period, string> = {
export default {
inProduction: true,
inDevelopment: true,
inDevelopment: false,
inPublic: true,
command: new SlashCommandBuilder()
@@ -72,8 +73,8 @@ export default {
const a2 = accountInfo[id].statistics;
const battles = a2.all.battles - a1.all.battles;
function pb(value: (allStats: AllStats) => number) {
return (value(a2.all) - value(a1.all)) / battles;
function max(value: (allStats: AllStats) => number) {
return Math.max(value(a2.all), value(a1.all));
}
function diff(value: (allStats: AllStats) => number) {
return value(a2.all) - value(a1.all);
@@ -82,18 +83,16 @@ export default {
stats = {
all: {
battles,
capture_points: pb((a) => a.capture_points),
damage_dealt: pb((a) => a.damage_dealt),
damage_received: pb((a) => a.damage_received),
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: pb((a) => a.frags),
frags8p: pb((a) => a.frags8p),
frags: diff((a) => a.frags),
frags8p: diff((a) => a.frags8p),
hits: diff((a) => a.hits),
losses: diff((a) => a.losses),
max_frags: pb((a) => a.max_frags),
max_frags_tank_id: pb((a) => a.max_frags_tank_id),
max_xp: pb((a) => a.max_xp),
max_xp_tank_id: pb((a) => a.max_xp_tank_id),
max_frags: max((a) => a.max_frags),
max_xp: max((a) => a.max_xp),
shots: diff((a) => a.shots),
spotted: diff((a) => a.spotted),
survived_battles: diff((a) => a.survived_battles),
@@ -113,73 +112,77 @@ export default {
}
if (!stats) return;
const page = await browser.newPage();
await page.setViewportSize({ width: 640, height: 1 });
await page.setContent(
Screenshot(
Wrapper(
TitleBar(
escapeHTML(blitzStarsAccount.nickname),
clanData[id]?.clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData[id]?.clan?.emblem_set_id}.png`
: 'https://i.imgur.com/uq6pKbh.png',
clanData[id]?.clan ? `[${clanData[id]?.clan?.tag}]` : '',
`${periodNames[period]} • ${new Date().toDateString()} • ${
BLITZ_SERVERS[server]
}`,
),
GenericStats([
[
'Winrate',
`${(100 * (stats.all.wins / stats.all.battles)).toFixed(2)}%`,
],
['WN8', stats.wn8.toFixed(0)],
[
'Survival',
`${(
100 *
(stats.all.survived_battles / stats.all.battles)
).toFixed(2)}%`,
],
[
'Accuracy',
`${((stats.all.hits / stats.all.shots) * 100).toFixed(2)}%`,
],
['Battles', stats.all.battles],
['Wins', stats.all.wins],
['Losses', stats.all.losses],
[
'Average damage',
(stats.all.damage_dealt / stats.all.battles).toFixed(0),
],
['Average XP', (stats.all.xp / stats.all.battles).toFixed(0)],
['Average shots', (stats.all.shots / stats.all.battles).toFixed(2)],
['Average hits', (stats.all.hits / stats.all.battles).toFixed(2)],
['Average kills', (stats.all.frags / stats.all.battles).toFixed(2)],
[
'Average spots',
(stats.all.spotted / stats.all.battles).toFixed(2),
],
['Average tier', stats.avg_tier.toFixed(2)],
[
'Damage ratio',
(stats.all.damage_dealt / stats.all.damage_received).toFixed(2),
],
[
'Kills to death ratio',
(
stats.all.frags /
(stats.all.battles - stats.all.survived_battles)
).toFixed(2),
],
]),
const screenshot = await screenshotHTML(
Wrapper(
TitleBar(
escapeHTML(blitzStarsAccount.nickname),
clanData[id]?.clan ? `[${clanData[id]?.clan?.tag}]` : '',
clanData[id]?.clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData[id]?.clan?.emblem_set_id}.png`
: undefined,
`${periodNames[period]} • ${new Date().toDateString()} • ${
BLITZ_SERVERS[server]
}`,
),
stats.all.battles === 0
? NoBattlesInPeriod()
: GenericStats([
[
'Winrate',
`${(100 * (stats.all.wins / stats.all.battles)).toFixed(2)}%`,
],
['WN8', stats.wn8.toFixed(0)],
[
'Survival',
`${(
100 *
(stats.all.survived_battles / stats.all.battles)
).toFixed(2)}%`,
],
[
'Accuracy',
`${((stats.all.hits / stats.all.shots) * 100).toFixed(2)}%`,
],
['Battles', stats.all.battles],
['Wins', stats.all.wins],
['Losses', stats.all.losses],
[
'Average damage',
(stats.all.damage_dealt / stats.all.battles).toFixed(0),
],
['Average XP', (stats.all.xp / stats.all.battles).toFixed(0)],
[
'Average shots',
(stats.all.shots / stats.all.battles).toFixed(2),
],
['Average hits', (stats.all.hits / stats.all.battles).toFixed(2)],
[
'Average kills',
(stats.all.frags / stats.all.battles).toFixed(2),
],
[
'Average spots',
(stats.all.spotted / stats.all.battles).toFixed(2),
],
['Average tier', stats.avg_tier.toFixed(2)],
[
'Damage ratio',
(stats.all.damage_dealt / stats.all.damage_received).toFixed(2),
],
[
'Kills to death ratio',
(
stats.all.frags /
(stats.all.battles - stats.all.survived_battles)
).toFixed(2),
],
]),
PoweredByBlitzStars(),
),
);
const screenshot = await page
.locator('#screenshot')
.screenshot({ omitBackground: true, scale: 'css' });
await page.close();
await interaction.editReply({ files: [screenshot] });
console.log(`Showing stats for ${blitzStarsAccount.nickname}`);
+114
View File
@@ -0,0 +1,114 @@
import { SlashCommandBuilder } from 'discord.js';
import escapeHTML from 'escape-html';
import Breakdown from '../components/Breakdown.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 usernameAutocomplete from '../core/autocomplete/username.js';
import getBlitzAccount from '../core/blitz/getBlitzAccount.js';
import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import getTankStatsOverTime from '../core/blitzstars/getTankStatsOverTime.js';
import last5AM from '../core/blitzstars/last5AM.js';
import { tankopedia } from '../core/blitzstars/tankopedia.js';
import cmdName from '../core/interaction/cmdName.js';
import addUsernameOption from '../core/options/addUsernameOption.js';
import { args } from '../core/process/args.js';
import screenshotHTML from '../core/ui/screenshotHTML.js';
import { CommandRegistry } from '../events/interactionCreate.js';
import { AccountInfo, AllStats } from '../types/accountInfo.js';
import { PlayerClanData } from '../types/playerClanData.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('today'))
.setDescription('A general daily breakdown of your performance')
.addStringOption(addUsernameOption),
async execute(interaction) {
const username = interaction.options.getString('username')!;
const blitzAccount = await getBlitzAccount(interaction, username);
if (!blitzAccount) return;
const { id, server } = blitzAccount;
const tankStatsOverTime = await getTankStatsOverTime(
server,
id,
last5AM().getTime() / 1000,
new Date().getTime() / 1000,
);
if (!tankStatsOverTime) return;
const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
);
if (!accountInfo) return;
const clanData = await getWargamingResponse<PlayerClanData>(
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${id}&extra=clan`,
);
if (!clanData) return;
const careerTankStatsRaw = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
);
if (!careerTankStatsRaw) return;
const careerStats: Record<number, AllStats> = {};
Object.entries(careerTankStatsRaw[id]).forEach(([, tankStats]) => {
careerStats[tankStats.tank_id] = tankStats.all;
});
const rows = Object.entries(tankStatsOverTime).map(
([tankId, tankStats]) => {
const career = careerStats[tankId as unknown as number];
return Breakdown.Row(
resolveTankName({
tank_id: tankId as unknown as number,
name: tankopedia.data[tankId as unknown as number].name,
}),
tankStats.wins / tankStats.battles,
career.wins / career.battles,
-Infinity,
-Infinity,
tankStats.damage_dealt / tankStats.battles,
career.damage_dealt / career.battles,
(tankStats.battles - tankStats.survived_battles) / tankStats.battles,
(career.battles - career.survived_battles) / career.battles,
tankStats.battles,
career.battles,
tankopedia.data[tankId as unknown as number].images.normal,
);
},
);
const screenshot = await screenshotHTML(
Wrapper(
TitleBar(
escapeHTML(accountInfo[id].nickname),
clanData[id]?.clan ? `[${clanData[id]?.clan?.tag}]` : '',
clanData[id]?.clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData[id]?.clan?.emblem_set_id}.png`
: undefined,
`Today's breakdown • ${new Date().toDateString()} • ${
BLITZ_SERVERS[server]
}`,
),
rows.length === 0 ? NoBattlesInPeriod() : Breakdown.Root(...rows),
PoweredByBlitzStars(),
),
);
await interaction.editReply({ files: [screenshot] });
console.log(`Showing daily breakdown for ${accountInfo[id].nickname}`);
},
autocomplete: usernameAutocomplete,
} satisfies CommandRegistry;
+183
View File
@@ -0,0 +1,183 @@
import Children from './Children.js';
const Breakdown = {
Root(...children: string[]) {
return `
<style>
.breakdown {
display: flex;
flex-direction: column;
gap: 16px;
}
</style>
<div class="breakdown">
${Children(children)}
</div>
`;
},
Row(
name: string,
winrate: number,
careerWinrate: number,
wn8: number,
careerWn8: number,
averageDamage: number,
careerAverageDamage: number,
survival: number,
careerSurvival: number,
battles: number,
careerBattles: number,
icon?: string,
) {
return `
<style>
.breakdown-row {
display: flex;
gap: 16px;
}
</style>
<div class="breakdown-row">
${this.RowDiscriminator(name, icon)}
${this.RowStat(
'Winrate',
`${(winrate * 100).toFixed(2)}%`,
`${(careerWinrate * 100).toFixed(2)}%`,
winrate - careerWinrate,
)}
${this.RowStat(
'Damage',
averageDamage.toFixed(2),
careerAverageDamage.toFixed(2),
averageDamage - careerAverageDamage,
)}
${this.RowStat(
'Survival',
`${(survival * 100).toFixed(2)}%`,
`${(careerSurvival * 100).toFixed(2)}%`,
survival - careerSurvival,
)}
${this.RowStat('Battles', battles.toFixed(0), careerBattles.toFixed(0))}
</div>
`;
/*
${this.RowStat(
'WN8',
wn8.toFixed(0),
careerWn8.toFixed(0),
wn8 - careerWn8,
)}
*/
},
RowDiscriminator(name: string, icon?: string) {
return `
<style>
.breakdown-row-discriminator {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
box-sizing: border-box;
width: 0px;
align-items: center;
justify-content: center;
}
.breakdown-row-discriminator-icon {
width: 100%;
flex: 1;
object-fit: cover;
}
.breakdown-row-discriminator-name {
color: #A0A0A0;
font-size: 16px;
text-align: center;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
</style>
<div class="breakdown-row-discriminator">
${
icon
? `<img src="${icon}" class="breakdown-row-discriminator-icon" />`
: ''
}
<span class="breakdown-row-discriminator-name">${name}</span>
</div>
`;
},
RowStat(name: string, value: string, career: string, delta?: number) {
return `
<style>
.breakdown-row-stat {
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
justify-content: center;
flex: 1;
}
.breakdown-row-stat-career {
color: #A0A0A0;
font-size: 12px;
}
.breakdown-row-stat-name {
color: #A0A0A0;
font-size: 16px;
}
.breakdown-row-stat-value {
display: flex;
gap: 4px;
align-items: center;
justify-content: center;
}
.breakdown-row-stat-value-delta {
width: 12px;
height: 12px;
}
.breakdown-row-stat-value-text {
color: white;
font-size: 16px;
font-weight: bold;
}
</style>
<div class="breakdown-row-stat">
<span class="breakdown-row-stat-career">${career}</span>
<div class="breakdown-row-stat-value">
${
delta
? `<img src="${
(delta ?? 0) > 0
? 'https://i.imgur.com/qbjiXa1.png'
: 'https://i.imgur.com/3uyNhun.png'
}" class="breakdown-row-stat-value-delta" />`
: ''
}
<span class="breakdown-row-stat-value-text">${value}</span>
</div>
<span class="breakdown-row-stat-name">${name}</span>
</div>
`;
},
};
export default Breakdown;
+20
View File
@@ -0,0 +1,20 @@
export default function NoBattlesInPeriod() {
return `
<style>
.no-battles-in-period {
display: flex;
align-items: center;
justify-content: center;
}
.no-battles-in-period-text {
font-size: 16px;
color: #A0A0A0;
}
</style>
<div class="no-battles-in-period">
<span class="no-battles-in-period-text">No battles played in this period</span>
</div>
`;
}
+26
View File
@@ -0,0 +1,26 @@
export default function PoweredByBlitzStars() {
return `
<style>
.powered-by-blitz-stars {
display: flex;
gap: 4px;
justify-content: flex-end;
}
.powered-by-blitz-stars-icon {
height: 16px;
width: 16px;
}
.powered-by-blitz-stars-text {
font-size: 16px;
color: #A0A0A0;
}
</style>
<div class="powered-by-blitz-stars">
<img class="powered-by-blitz-stars-icon" src="https://www.blitzstars.com/assets/images/4b4cb591.TankyMcPewpew.png" />
<span class="powered-by-blitz-stars-text">Powered by BlitzStars</span>
</div>
`;
}
@@ -1,6 +1,6 @@
import Children from './Children.js';
export default function Screenshot(...children: string[]) {
export default function Root(...children: string[]) {
return `
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:[email protected]');
@@ -14,12 +14,12 @@ export default function Screenshot(...children: string[]) {
font-family: 'Roboto Flex', sans-serif;
}
#screenshot {
#root {
display: flex;
}
</style>
<div id="screenshot">
<div id="root">
${Children(children)}
</div>
`;
+4 -4
View File
@@ -1,8 +1,8 @@
export default function TitleBar(
name: string,
image: string,
nameDiscriminator = '',
description = '',
nameDiscriminator: string,
image?: string,
description?: string,
) {
return `
<style>
@@ -51,7 +51,7 @@ export default function TitleBar(
<div class="title-bar">
<div class="title-bar-info">
<img class="title-bar-avatar" src="${image}" />
${image ? `<img class="title-bar-avatar" src="${image}" />` : ''}
<div class="title-bar-text">
<span class="title-bar-name">
+3 -1
View File
@@ -12,7 +12,9 @@ export default async function getWargamingResponse<Data extends object>(
if (parsed.status === 'ok') {
return parsed.data;
} else {
console.error(`Wargaming response status ${parsed.status}`);
console.error(
`Wargaming response status ${parsed.status} ${parsed.error.message}`,
);
return null;
}
}
+122
View File
@@ -0,0 +1,122 @@
import fetch from 'node-fetch';
import { BlitzServer } from '../../constants/servers.js';
import { AllStats } from '../../types/accountInfo.js';
import { TanksStats } from '../../types/tanksStats.js';
import getWargamingResponse from '../blitz/getWargamingResponse.js';
import { args } from '../process/args.js';
export interface TankHistoryNode {
all: AllStats;
last_battle_time: number;
mark_of_mastery: number;
battle_life_time: number;
account_id: number;
tank_id: number;
}
export const emptyTankHistoryNode: TankHistoryNode = {
account_id: 0,
battle_life_time: 0,
last_battle_time: 0,
mark_of_mastery: 0,
tank_id: 0,
all: {
battles: 0,
capture_points: 0,
damage_dealt: 0,
damage_received: 0,
dropped_capture_points: 0,
frags: 0,
frags8p: 0,
hits: 0,
losses: 0,
max_frags: 0,
max_xp: 0,
shots: 0,
spotted: 0,
survived_battles: 0,
xp: 0,
win_and_survived: 0,
wins: 0,
},
};
export type TankHistory = TankHistoryNode[];
export default async function getTankStatsOverTime(
server: BlitzServer,
id: number,
start = -Infinity,
end = Infinity,
) {
const latestTankStatsRaw = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
);
if (!latestTankStatsRaw) return;
const tankHistoriesResponse = await fetch(
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
);
const tankHistories = (await tankHistoriesResponse.json()) as TankHistory;
const history = [...tankHistories, ...latestTankStatsRaw[id]];
// sort them even though most won't be used
const tankSortedHistory: Record<number, TankHistory> = {};
const tankIds: number[] = [];
history.forEach((node) => {
if (!tankSortedHistory[node.tank_id]) {
tankSortedHistory[node.tank_id] = [node];
tankIds.push(node.tank_id);
} else {
tankSortedHistory[node.tank_id].push(node);
}
});
// find the ones played within the range
const tankIdsInRange = tankIds.filter((tankId) =>
tankSortedHistory[tankId].some(
(node) => node.last_battle_time > start && node.last_battle_time < end,
),
);
// fetch history node right before "start" and right before "end"
const tanksBeforeAndAfter: Record<number, AllStats> = {};
tankIdsInRange.forEach((tankId) => {
const sortedHistory = tankSortedHistory[tankId];
const previousIndex =
sortedHistory.findIndex((node) => node.last_battle_time > start) - 1;
const latestIndex =
sortedHistory.findIndex((node) => node.last_battle_time > end) - 1;
const previous = sortedHistory[previousIndex] ?? emptyTankHistoryNode;
const latest =
sortedHistory[latestIndex] ?? sortedHistory[sortedHistory.length - 1];
function diff(get: (allStats: AllStats) => number) {
return get(latest.all) - get(previous.all);
}
function max(get: (allStats: AllStats) => number) {
return Math.max(get(latest.all), get(previous.all));
}
tanksBeforeAndAfter[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),
max_frags: max((a) => a.max_frags),
max_xp: max((a) => a.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),
};
});
return tanksBeforeAndAfter;
}
+11
View File
@@ -0,0 +1,11 @@
export default function last5AM() {
const fiveAM = new Date();
fiveAM.setHours(5, 0, 0, 0); // Set the time to 5:00 AM today
if (fiveAM > new Date()) {
// If it's now before 5:00 AM, go back one day
fiveAM.setDate(fiveAM.getDate() - 1);
}
return fiveAM;
}
+14
View File
@@ -0,0 +1,14 @@
import Root from '../../components/Root.js';
import { browser } from '../../index.js';
export default async function screenshotHTML(children: string) {
const page = await browser.newPage();
await page.setViewportSize({ width: 640, height: 1 });
await page.setContent(Root(children));
const screenshot = await page
.locator('#root')
.screenshot({ omitBackground: true, scale: 'css' });
await page.close();
return screenshot;
}
-2
View File
@@ -1,10 +1,8 @@
export interface AllStats {
spotted: number;
max_frags_tank_id: number;
hits: number;
frags: number;
max_xp: number;
max_xp_tank_id: number;
wins: number;
losses: number;
capture_points: number;
+17 -31
View File
@@ -1,33 +1,19 @@
import { AllStats } from './accountInfo.js';
export interface IndividualTankStats {
all: AllStats;
last_battle_time: number;
account_id: number;
max_xp: number;
in_garage_updated: number;
max_frags: number;
frags: number | null;
mark_of_mastery: number;
battle_life_time: number;
in_garage: number | null;
tank_id: number;
}
export type TanksStats = {
[accountId: number]: {
all: {
spotted: number;
hits: number;
frags: number;
max_xp: number;
wins: number;
losses: number;
capture_points: number;
battles: number;
damage_dealt: number;
damage_received: number;
max_frags: number;
shots: number;
frags8p: number;
xp: number;
win_and_survived: number;
survived_battles: number;
dropped_capture_points: number;
};
last_battle_time: number;
account_id: number;
max_xp: number;
in_garage_updated: number;
max_frags: number;
frags: number | null;
mark_of_mastery: number;
battle_life_time: number;
in_garage: number | null;
tank_id: number;
}[];
[accountId: number]: IndividualTankStats[];
};
+6
View File
@@ -1,6 +1,12 @@
export type WargamingResponse<Data extends object> =
| {
status: 'error';
error: {
field: string;
message: string;
code: 402;
value: null;
};
}
| {
status: 'ok';
+31 -31
View File
@@ -123,7 +123,7 @@
"@esbuild/[email protected]":
version "0.17.19"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4"
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz"
integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==
"@esbuild/[email protected]":
@@ -158,7 +158,7 @@
"@playwright/test@^1.34.2":
version "1.34.2"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.34.2.tgz#938ccfb8c162e8ef83dbef3d88ce6851acd469e6"
resolved "https://registry.npmjs.org/@playwright/test/-/test-1.34.2.tgz"
integrity sha512-v/LBnwzD0225q8xEv3t9DmNPX61yvNnEbiA8PoNk1fbkxApJFCWYLPpQbdVWzHaARdZD9g1PYBoOvnffortfKw==
dependencies:
"@types/node" "*"
@@ -191,29 +191,29 @@
"@total-typescript/ts-reset@^0.4.2":
version "0.4.2"
resolved "https://registry.yarnpkg.com/@total-typescript/ts-reset/-/ts-reset-0.4.2.tgz#c564c173ba09973968e1046c93965b7a257878a4"
resolved "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.4.2.tgz"
integrity sha512-vqd7ZUDSrXFVT1n8b2kc3LnklncDQFPvR58yUS1kEP23/nHPAO9l1lMjUfnPrXYYk4Hj54rrLKMW5ipwk7k09A==
"@types/escape-html@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-1.0.2.tgz#072b7b13784fb3cee9c2450c22f36405983f5e3c"
resolved "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.2.tgz"
integrity sha512-gaBLT8pdcexFztLSPRtriHeXY/Kn4907uOCZ4Q3lncFBkheAWOuNt53ypsF8szgxbEJ513UeBzcf4utN0EzEwA==
"@types/lodash.startcase@^4.4.7":
version "4.4.7"
resolved "https://registry.yarnpkg.com/@types/lodash.startcase/-/lodash.startcase-4.4.7.tgz#69857f17be0aa5ce74da4fa386e32e22c012258a"
resolved "https://registry.npmjs.org/@types/lodash.startcase/-/lodash.startcase-4.4.7.tgz"
integrity sha512-6v8FVOcfxdomO1Vammc1Zsah7/4aif/Lx16oQQ0WZmKVGF/Yf5c5m68LqI/ELOhKaKUr8KfnDdKrytdrThoRQw==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.194"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76"
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz"
integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==
"@types/markdown-escape@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@types/markdown-escape/-/markdown-escape-1.1.0.tgz#8bd940d6701880eb7229467542c029bc05a7b22f"
resolved "https://registry.npmjs.org/@types/markdown-escape/-/markdown-escape-1.1.0.tgz"
integrity sha512-bJZh73L6fSt8MmTON/btR8hiKRipoIoPfLO41lxr6l65QXdG1rwUjR8Hu5xU7XYHBwKHiJZ+sTQ8L2TKkQrBpw==
"@types/node@*":
@@ -223,7 +223,7 @@
"@types/node@^20.2.1":
version "20.2.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.1.tgz#de559d4b33be9a808fd43372ccee822c70f39704"
resolved "https://registry.npmjs.org/@types/node/-/node-20.2.1.tgz"
integrity sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==
"@types/ws@^8.5.4":
@@ -235,7 +235,7 @@
acorn@^8.8.2:
version "8.8.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
busboy@^1.6.0:
@@ -247,7 +247,7 @@ busboy@^1.6.0:
cac@^6.7.14:
version "6.7.14"
resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz"
integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
data-uri-to-buffer@^4.0.0:
@@ -257,7 +257,7 @@ data-uri-to-buffer@^4.0.0:
debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
@@ -288,7 +288,7 @@ discord.js@^14.9.0:
esbuild@^0.17.5:
version "0.17.19"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz"
integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==
optionalDependencies:
"@esbuild/android-arm" "0.17.19"
@@ -316,7 +316,7 @@ esbuild@^0.17.5:
escape-html@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
fast-deep-equal@^3.1.3:
@@ -355,7 +355,7 @@ [email protected], fsevents@~2.3.2:
fuzzysort@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-2.0.4.tgz#a21d1ce8947eaf2797dc3b7c28c36db9d1165f84"
resolved "https://registry.npmjs.org/fuzzysort/-/fuzzysort-2.0.4.tgz"
integrity sha512-Api1mJL+Ad7W7vnDZnWq5pGaXJjyencT+iKGia2PlHUcSsSzWwIQ3S1isiMpwpavjYtGd2FzhUIhnnhOULZgDw==
ieee754@^1.2.1:
@@ -370,7 +370,7 @@ inherits@^2.0.3:
jsonc-parser@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
lodash.snakecase@^4.1.1:
@@ -380,7 +380,7 @@ lodash.snakecase@^4.1.1:
lodash.startcase@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8"
resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz"
integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==
lodash@^4.17.21:
@@ -390,12 +390,12 @@ lodash@^4.17.21:
markdown-escape@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/markdown-escape/-/markdown-escape-2.0.0.tgz#44484d30d7845b91575c77fd8898f23a0a6f890c"
resolved "https://registry.npmjs.org/markdown-escape/-/markdown-escape-2.0.0.tgz"
integrity sha512-Trz4v0+XWlwy68LJIyw3bLbsJiC8XAbRCKF9DbEtZjyndKOGVx6n+wNB0VfoRmY2LKboQLeniap3xrb6LGSJ8A==
mlly@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.2.1.tgz#cd50151f5712b651c5c379085157bcdff661133b"
resolved "https://registry.npmjs.org/mlly/-/mlly-1.2.1.tgz"
integrity sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==
dependencies:
acorn "^8.8.2"
@@ -405,12 +405,12 @@ mlly@^1.2.0:
[email protected]:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.3.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz"
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
node-domexception@^1.0.0:
@@ -429,7 +429,7 @@ node-fetch@^3.3.1:
pathe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03"
resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz"
integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==
peek-readable@^5.0.0:
@@ -439,12 +439,12 @@ peek-readable@^5.0.0:
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
pkg-types@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"
resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz"
integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==
dependencies:
jsonc-parser "^3.2.0"
@@ -453,12 +453,12 @@ pkg-types@^1.0.3:
[email protected]:
version "1.34.2"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.34.2.tgz#4989ba082f623f21679724033629b2333b626569"
resolved "https://registry.npmjs.org/playwright-core/-/playwright-core-1.34.2.tgz"
integrity sha512-MrkgGWLANc5qthXduvIY1a/tuafROyTORVd86fwKwgoYrmnBooN/GgeZSBm7ljTLV2FCWNSXV3se7qeScKn83g==
postcss@^8.4.23:
version "8.4.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz"
integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
dependencies:
nanoid "^3.3.6"
@@ -483,7 +483,7 @@ readable-web-to-node-stream@^3.0.2:
rollup@^3.21.0:
version "3.22.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.22.0.tgz#e6671baebdd473154ac7998bbc57faabcd7bba20"
resolved "https://registry.npmjs.org/rollup/-/rollup-3.22.0.tgz"
integrity sha512-imsigcWor5Y/dC0rz2q0bBt9PabcL3TORry2hAa6O6BuMvY71bqHyfReAz5qyAqiQATD1m70qdntqBfBQjVWpQ==
optionalDependencies:
fsevents "~2.3.2"
@@ -495,7 +495,7 @@ safe-buffer@~5.2.0:
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
streamsearch@^1.1.0:
@@ -538,12 +538,12 @@ tslib@^2.5.0:
typescript@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz"
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
ufo@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76"
resolved "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz"
integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==
undici@^5.21.0:
@@ -560,7 +560,7 @@ util-deprecate@^1.0.1:
vite-node@^0.31.1:
version "0.31.1"
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.31.1.tgz#9fea18cbf9552ab262b969068249a8b8e7fb8b38"
resolved "https://registry.npmjs.org/vite-node/-/vite-node-0.31.1.tgz"
integrity sha512-BajE/IsNQ6JyizPzu9zRgHrBwczkAs0erQf/JRpgTIESpKvNj9/Gd0vxX905klLkb0I0SJVCKbdrl5c6FnqYKA==
dependencies:
cac "^6.7.14"
@@ -572,7 +572,7 @@ vite-node@^0.31.1:
"vite@^3.0.0 || ^4.0.0":
version "4.3.8"
resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.8.tgz#70cd6a294ab52d7fb8f37f5bc63d117dd19e9918"
resolved "https://registry.npmjs.org/vite/-/vite-4.3.8.tgz"
integrity sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ==
dependencies:
esbuild "^0.17.5"