mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: player evolution
This commit is contained in:
1 parent
e49bd6aa68
commit
91da65196d
10 files changed
+193
-71
No files matched your search
+2
-1
@@ -7,4 +7,5 @@ package-lock.json
|
||||
dist/
|
||||
yarn-error.log
|
||||
dist.zip
|
||||
commands.txt
|
||||
commands.txt
|
||||
/test.*
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"main": "src/index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "yarn build:raw --dev && yarn start",
|
||||
"dev": "yarn build:raw --dev && yarn start --dev",
|
||||
"start": "node --enable-source-maps --trace-warnings dist/index.cjs",
|
||||
"zip": "zip -r dist.zip dist",
|
||||
"build:raw": "node scripts/build",
|
||||
|
||||
+23
-17
@@ -12,6 +12,7 @@ import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
|
||||
import resolveTankId from '../core/blitz/resolveTankId.js';
|
||||
import resolveTankName from '../core/blitz/resolveTankName.js';
|
||||
import { tankopedia } from '../core/blitz/tankopedia.js';
|
||||
import getPlayerHistories from '../core/blitzstars/getPlayerHistories.js';
|
||||
import getTankHistories from '../core/blitzstars/getTankHistories.js';
|
||||
import { supportBlitzStars } from '../core/interaction/supportBlitzStars.js';
|
||||
import {
|
||||
@@ -23,6 +24,7 @@ import getPeriodDataFromSubcommand from '../core/options/getPeriodDataFromSubcom
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/process/args.js';
|
||||
import { CommandRegistry } from '../events/interactionCreate.js';
|
||||
import { AccountInfo } from '../types/accountInfo.js';
|
||||
import { Histories } from '../types/histories.js';
|
||||
import { PlayerClanData } from '../types/playerClanData.js';
|
||||
|
||||
export default {
|
||||
@@ -61,36 +63,40 @@ export default {
|
||||
image = tankopedia[tankId].images.normal;
|
||||
}
|
||||
|
||||
let histories: Histories;
|
||||
|
||||
const { evolutionName, start, end } =
|
||||
getPeriodDataFromSubcommand(interaction);
|
||||
|
||||
let careerWinrate: Graph.Plot;
|
||||
let careerBattles: Graph.Plot;
|
||||
|
||||
if (commandGroup === 'player') {
|
||||
histories = await getPlayerHistories(server, id, {
|
||||
start,
|
||||
end,
|
||||
includeLatestHistories: true,
|
||||
includePreviousHistories: true,
|
||||
});
|
||||
} else {
|
||||
const tankId = resolveTankId(tankIdRaw);
|
||||
const histories = await getTankHistories(server, id, {
|
||||
histories = await getTankHistories(server, id, {
|
||||
tankId,
|
||||
start,
|
||||
end,
|
||||
includeLatestHistories: true,
|
||||
includePreviousHistories: true,
|
||||
});
|
||||
|
||||
careerWinrate = histories.map(
|
||||
(history) =>
|
||||
[
|
||||
history.last_battle_time,
|
||||
(history.all.wins / history.all.battles) * 100,
|
||||
] as Graph.PlotItem,
|
||||
);
|
||||
careerBattles = histories.map(
|
||||
(history) =>
|
||||
[history.last_battle_time, history.all.battles] as Graph.PlotItem,
|
||||
);
|
||||
}
|
||||
|
||||
const careerWinrate = histories.map(
|
||||
(history) =>
|
||||
[
|
||||
history.last_battle_time,
|
||||
(history.all.wins / history.all.battles) * 100,
|
||||
] as Graph.PlotItem,
|
||||
);
|
||||
const careerBattles = histories.map(
|
||||
(history) =>
|
||||
[history.last_battle_time, history.all.battles] as Graph.PlotItem,
|
||||
);
|
||||
const winrateYs = careerWinrate.map(([, y]) => y);
|
||||
const maxWinrate = Math.max(...winrateYs);
|
||||
const minWinrate = Math.min(...winrateYs);
|
||||
@@ -121,7 +127,7 @@ export default {
|
||||
interaction.options.getSubcommand() as Period
|
||||
]
|
||||
}
|
||||
xMaxLabel="Today"
|
||||
xMaxLabel="Latest recorded battle"
|
||||
leftVerticalMargin={{
|
||||
min: minWinrate,
|
||||
max: maxWinrate,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { theme } from '../../../stitches.config.js';
|
||||
import { Stat } from '../index.js';
|
||||
|
||||
import '../../../core/blitz/getWN8Percentile.js';
|
||||
import PercentileIndicator from '../../PercentileIndicator.js';
|
||||
import { Stat } from '../index.js';
|
||||
|
||||
export interface GenericStatsRowProps {
|
||||
stat: Stat;
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { BlitzServer } from '../../constants/servers.js';
|
||||
import { AccountInfo, AllStats } from '../../types/accountInfo.js';
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
Histories,
|
||||
History,
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories.js';
|
||||
import getWargamingResponse from '../blitz/getWargamingResponse.js';
|
||||
import { WARGAMING_APPLICATION_ID } from '../process/args.js';
|
||||
import { emptyAllStats } from './getTankStatsOverTime.js';
|
||||
|
||||
export interface PlayerHistoryRaw {
|
||||
clan: { clan_id: number; name: string; tag: string };
|
||||
statistics: {
|
||||
all: AllStats;
|
||||
wn8: number;
|
||||
wn7: number;
|
||||
average_tier: number;
|
||||
};
|
||||
last_battle_time: number;
|
||||
nickname: string;
|
||||
account_id: number;
|
||||
achievements: {
|
||||
account_id: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
region: BlitzServer;
|
||||
}
|
||||
|
||||
export type PlayerHistoriesRaw = PlayerHistoryRaw[];
|
||||
|
||||
export default async function getPlayerHistories(
|
||||
server: BlitzServer,
|
||||
id: number,
|
||||
options?: Partial<GetHistoriesOptions>,
|
||||
) {
|
||||
const mergedOptions = { ...getHistoriesDefaultOptions, ...options };
|
||||
const response = await fetch(
|
||||
`https://www.blitzstars.com/api/playerstats/${id}`,
|
||||
);
|
||||
const playerHistories = ((await response.json()) as PlayerHistoriesRaw).map(
|
||||
(history) =>
|
||||
({
|
||||
all: history.statistics.all,
|
||||
last_battle_time: history.last_battle_time,
|
||||
} satisfies History),
|
||||
);
|
||||
let lastNonCompliantIndex = -1;
|
||||
let hasCompliedOnce = false;
|
||||
const filtered = playerHistories.filter(({ last_battle_time }, index) => {
|
||||
const complies =
|
||||
last_battle_time >= mergedOptions.start &&
|
||||
last_battle_time <= mergedOptions.end;
|
||||
|
||||
if (complies && !hasCompliedOnce) hasCompliedOnce = true;
|
||||
if (!hasCompliedOnce) lastNonCompliantIndex = index;
|
||||
|
||||
return complies;
|
||||
});
|
||||
|
||||
const latestNodes: Histories = [];
|
||||
|
||||
if (mergedOptions.includeLatestHistories) {
|
||||
const {
|
||||
last_battle_time,
|
||||
statistics: { all },
|
||||
} = (
|
||||
await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
)
|
||||
)[id];
|
||||
|
||||
latestNodes.push({ all, last_battle_time });
|
||||
}
|
||||
|
||||
const previousNodes: Histories = [];
|
||||
|
||||
if (mergedOptions.includePreviousHistories) {
|
||||
if (lastNonCompliantIndex !== -1) {
|
||||
previousNodes.push(
|
||||
playerHistories[lastNonCompliantIndex] ??
|
||||
({
|
||||
all: emptyAllStats,
|
||||
last_battle_time: -Infinity,
|
||||
} satisfies History),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [...latestNodes, ...filtered, ...previousNodes] satisfies Histories;
|
||||
}
|
||||
@@ -1,41 +1,34 @@
|
||||
import { BlitzServer } from '../../constants/servers.js';
|
||||
import { AllStats } from '../../types/accountInfo.js';
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
Histories,
|
||||
History,
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories.js';
|
||||
import getTankStats from '../blitz/getTankStats.js';
|
||||
import { emptyAllStats } from './getTankStatsOverTime.js';
|
||||
|
||||
export interface TankHistory {
|
||||
all: AllStats;
|
||||
last_battle_time: number;
|
||||
mark_of_mastery: number;
|
||||
battle_life_time: number;
|
||||
account_id: number;
|
||||
export interface TankHistory extends History {
|
||||
tank_id: number;
|
||||
}
|
||||
|
||||
export const emptyTankHistory: TankHistory = {
|
||||
all: emptyAllStats,
|
||||
last_battle_time: -Infinity,
|
||||
mark_of_mastery: 0,
|
||||
battle_life_time: 0,
|
||||
account_id: 0,
|
||||
tank_id: 0,
|
||||
};
|
||||
|
||||
export type TankHistories = TankHistory[];
|
||||
|
||||
export interface GetTankHistoriesOptions {
|
||||
start: number;
|
||||
end: number;
|
||||
includeLatestHistories: boolean;
|
||||
includePreviousHistories: boolean;
|
||||
export interface TankHistoryRaw extends TankHistory {
|
||||
mark_of_mastery: number;
|
||||
battle_life_time: number;
|
||||
account_id: number;
|
||||
}
|
||||
|
||||
export type TankHistoriesRaw = TankHistoryRaw[];
|
||||
|
||||
export interface GetTankHistoriesOptions extends GetHistoriesOptions {
|
||||
tankId?: number;
|
||||
}
|
||||
|
||||
export const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
||||
start: -Infinity,
|
||||
end: Infinity,
|
||||
includeLatestHistories: false,
|
||||
includePreviousHistories: false,
|
||||
...getHistoriesDefaultOptions,
|
||||
tankId: undefined,
|
||||
};
|
||||
|
||||
export default async function getTankHistories(
|
||||
@@ -47,7 +40,16 @@ export default async function getTankHistories(
|
||||
const tankHistoriesResponse = await fetch(
|
||||
`https://www.blitzstars.com/api/tankhistories/for/${id}/`,
|
||||
);
|
||||
const tankHistories = (await tankHistoriesResponse.json()) as TankHistories;
|
||||
const tankHistories = (
|
||||
(await tankHistoriesResponse.json()) as TankHistoriesRaw
|
||||
).map(
|
||||
(history) =>
|
||||
({
|
||||
all: history.all,
|
||||
last_battle_time: history.last_battle_time,
|
||||
tank_id: history.tank_id,
|
||||
} satisfies TankHistory),
|
||||
);
|
||||
const compliantTanks: number[] = [];
|
||||
const lastBattles: Record<number, number> = {};
|
||||
const lastNonCompliantIndex: Record<number, number> = {};
|
||||
@@ -96,15 +98,16 @@ export default async function getTankHistories(
|
||||
compliantTanks.forEach((tankId) => {
|
||||
if (tankId in lastNonCompliantIndex) {
|
||||
previousNodes.push(
|
||||
tankHistories[lastNonCompliantIndex[tankId]] ?? {
|
||||
...emptyTankHistory,
|
||||
account_id: id,
|
||||
tank_id: tankId,
|
||||
},
|
||||
tankHistories[lastNonCompliantIndex[tankId]] ??
|
||||
({
|
||||
all: emptyAllStats,
|
||||
last_battle_time: -Infinity,
|
||||
tank_id: tankId,
|
||||
} satisfies TankHistory),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return [...previousNodes, ...filtered, ...latestNodes];
|
||||
return [...latestNodes, ...filtered, ...previousNodes] satisfies Histories;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BlitzServer } from '../../constants/servers.js';
|
||||
import { AllStats } from '../../types/accountInfo.js';
|
||||
import getTankHistories, {
|
||||
TankHistories,
|
||||
TankHistory,
|
||||
TankHistoriesRaw,
|
||||
TankHistoryRaw,
|
||||
} from './getTankHistories.js';
|
||||
|
||||
export const emptyAllStats: AllStats = {
|
||||
@@ -25,7 +25,7 @@ export const emptyAllStats: AllStats = {
|
||||
wins: 0,
|
||||
};
|
||||
|
||||
export const emptyTankHistoryNode: TankHistory = {
|
||||
export const emptyTankHistoryNode: TankHistoryRaw = {
|
||||
account_id: 0,
|
||||
battle_life_time: 0,
|
||||
last_battle_time: 0,
|
||||
@@ -45,7 +45,7 @@ export default async function getTankStatsOverTime(
|
||||
});
|
||||
|
||||
// sort them even though most won't be used
|
||||
const tankSortedHistory: Record<number, TankHistories> = {};
|
||||
const tankSortedHistory: Record<number, TankHistoriesRaw> = {};
|
||||
const tankIds: number[] = [];
|
||||
history.forEach((node) => {
|
||||
if (!tankSortedHistory[node.tank_id]) {
|
||||
|
||||
@@ -11,7 +11,7 @@ export const RELATIVE_PERIOD_NAMES: Record<Period, string> = {
|
||||
'30': '30 days ago',
|
||||
'60': '60 days ago',
|
||||
'90': '90 days ago',
|
||||
career: 'Start of career',
|
||||
career: 'Extent of data',
|
||||
custom: 'Custom period',
|
||||
};
|
||||
|
||||
|
||||
@@ -124,20 +124,18 @@ try {
|
||||
});
|
||||
|
||||
console.log(`Refreshing ${guildCommands.length} guild command(s).`);
|
||||
Promise.all([
|
||||
rest.put(
|
||||
Routes.applicationGuildCommands(getClientId(), discord.sklld_guild_id),
|
||||
{ body: guildCommands },
|
||||
),
|
||||
rest.put(
|
||||
rest
|
||||
.put(
|
||||
Routes.applicationGuildCommands(getClientId(), discord.tres_guild_id),
|
||||
{ body: guildCommands },
|
||||
),
|
||||
]).then((guildData) => {
|
||||
console.log(
|
||||
`Successfully refreshed ${guildData.flat().length / 2} guild command(s).`,
|
||||
);
|
||||
});
|
||||
)
|
||||
.then((guildData) => {
|
||||
console.log(
|
||||
`Successfully refreshed ${
|
||||
(guildData as unknown[]).length
|
||||
} guild command(s).`,
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// a cleaner simplification of Blitz Stars' histories because it's a mess there
|
||||
|
||||
import { AllStats } from './accountInfo.js';
|
||||
|
||||
export interface History {
|
||||
all: AllStats;
|
||||
last_battle_time: number;
|
||||
}
|
||||
|
||||
export type Histories = History[];
|
||||
|
||||
export interface GetHistoriesOptions {
|
||||
start: number;
|
||||
end: number;
|
||||
includeLatestHistories: boolean;
|
||||
includePreviousHistories: boolean;
|
||||
}
|
||||
|
||||
export const getHistoriesDefaultOptions: GetHistoriesOptions = {
|
||||
start: -Infinity,
|
||||
end: Infinity,
|
||||
includeLatestHistories: false,
|
||||
includePreviousHistories: false,
|
||||
};
|
||||
Reference in new issue
Block a user