feat: mostly complete rating command

This commit is contained in:
TrèsAbhi committed 2024-04-24 16:22:48 -05:00
1 parent 983b96fd99
commit 1f30d1d2c3
11 files changed
+384 -32

No files matched your search

+2 -2
View File
@@ -11,7 +11,7 @@ import { getRatingNeighbors } from '../core/blitz/getRatingNeighbors';
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
import { emblemURL } from '../core/blitzkit/emblemURL';
import getArchivedRatingMidnightLeaderboard from '../core/blitzkit/getArchivedRatingMidnightLeaderboard';
import { webpToPng } from '../core/blitzkit/iconPng';
import { iconPng } from '../core/blitzkit/iconPng';
import addUsernameChoices from '../core/discord/addUsernameChoices';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
@@ -295,7 +295,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
height: 64,
objectFit: 'cover',
}}
src={await webpToPng(
src={await iconPng(
reward.type === 'vehicle'
? reward.vehicle.preview_image_url
: reward.stuff.image_url,
+340 -9
View File
@@ -1,7 +1,17 @@
import { clamp } from 'lodash';
import CommandWrapper from '../components/CommandWrapper';
import TitleBar from '../components/TitleBar';
import { iconPng } from '../core/blitzkit/iconPng';
import { RenderConfiguration } from '../core/blitzkit/renderConfiguration';
import { tankDefinitions } from '../core/blitzkit/tankDefinitions';
import { TIER_ROMAN_NUMERALS } from '../core/blitzkit/tankDefinitions/constants';
import { tankIcon } from '../core/blitzkit/tankIcon';
import buttonLink from '../core/discord/buttonLink';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
type WotInspectorReplayResponse =
type WotInspectorReplaySubmission =
| { status: 'error'; data: unknown; error: { message: string } }
| {
status: 'ok';
@@ -83,6 +93,259 @@ type WotInspectorReplayResponse =
error: {};
};
interface WotInspectorReplay {
id: string;
map_id: number;
battle_duration: number;
title: null;
player_name: string;
protagonist: number;
vehicle_descr: number;
mastery_badge: number;
exp_base: number;
enemies_spotted: number;
enemies_destroyed: number;
damage_assisted: number;
damage_made: number;
details_url: string;
download_url: string;
game_version: WotInspectorReplayGameVersion;
arena_unique_id: string;
download_count: number;
data_version: number;
private: boolean;
private_clan: boolean;
battle_start_time: Date;
upload_time: Date;
allies: number[];
enemies: number[];
protagonist_clan: number;
protagonist_team: number;
battle_result: number;
credits_base: number;
tags: number[];
battle_type: number;
room_type: number;
last_accessed_time: Date;
winner_team: number;
finish_reason: number;
players_data: WotInspectorReplayPlayerData[];
exp_total: number;
credits_total: number;
repair_cost: number;
exp_free: number;
exp_free_base: number;
exp_penalty: number;
credits_penalty: number;
credits_contribution_in: number;
credits_contribution_out: number;
camouflage_id: number;
}
export interface WotInspectorReplayGameVersion {
name: string;
package: string;
}
export interface WotInspectorReplayPlayerData {
team: number;
name: string;
entity_id: number;
dbid: number;
clanid: number | null;
clan_tag: null | string;
hitpoints_left: number;
credits: number;
exp: number;
shots_made: number;
shots_hit: number;
shots_splash: number;
shots_pen: number;
damage_made: number;
damage_received: number;
damage_assisted: number;
damage_assisted_track: number;
hits_received: number;
hits_bounced: number;
hits_splash: number;
hits_pen: number;
enemies_spotted: number;
enemies_damaged: number;
enemies_destroyed: number;
time_alive: number;
distance_travelled: number;
killed_by: number;
base_capture_points: number;
base_defend_points: number;
exp_for_damage: number;
exp_for_assist: number;
exp_team_bonus: number;
wp_points_earned: number;
wp_points_stolen: number;
hero_bonus_credits: number;
hero_bonus_exp: number;
death_reason: number;
achievements: any[];
vehicle_descr: number;
turret_id: number;
gun_id: number;
chassis_id: number;
squad_index: number;
damage_blocked: number;
}
function stat(value: string | number, flex = 2) {
return (
<div
style={{
flex,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<span style={{ fontSize: 16 }}>{value}</span>
</div>
);
}
async function playerListing(
player: WotInspectorReplayPlayerData,
protagonistTeam: boolean,
recordingPlayer = false,
) {
const awaitedTankDefinitions = await tankDefinitions;
const tank = awaitedTankDefinitions[player.vehicle_descr];
const blockAccent = recordingPlayer ? '_amber' : '';
const healthAccent = protagonistTeam
? recordingPlayer
? '_amber'
: '_green'
: '_red';
// our turret ids are always off by 1 or 2 lol so imma just grab the closest
// TODO: 💀
const turret = tank.turrets.sort(
(a, b) =>
Math.abs(a.id - player.turret_id) - Math.abs(b.id - player.turret_id),
)[0];
const totalHealth = turret.health + tank.health;
const healthLeft = clamp(player.hitpoints_left / totalHealth, 0, 1);
return (
<div
key={player.entity_id}
style={{
backgroundColor: theme.colors[`appBackground2${blockAccent}`],
padding: 8,
display: 'flex',
flexDirection: 'column',
gap: 8,
}}
>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
}}
>
<div
style={{
display: 'flex',
gap: 8,
flex: 1,
overflow: 'hidden',
alignItems: 'center',
}}
>
<img
src={await iconPng(tankIcon(tank.id))}
style={{
width: 32,
height: 32,
objectFit: 'contain',
}}
/>
<div
style={{
display: 'flex',
flexDirection: 'column',
// align
}}
>
<div
style={{
display: 'flex',
gap: 4,
alignItems: 'center',
}}
>
<span
style={{
color: theme.colors[`textLowContrast${blockAccent}`],
fontSize: 12,
}}
>
{TIER_ROMAN_NUMERALS[tank.tier]}
</span>
<span
style={{
color: theme.colors[`textHighContrast${blockAccent}`],
fontSize: 16,
}}
>
{tank.name}
</span>
</div>
<span
style={{
fontSize: 16,
color: theme.colors[`textLowContrast${blockAccent}`],
}}
>
{player.name}
{player.clan_tag ? ` [${player.clan_tag}]` : ''}
</span>
</div>
</div>
<div
style={{
display: 'flex',
flex: 1,
}}
>
{stat(player.damage_made.toLocaleString())}
{stat((2830).toLocaleString())}
{stat(player.exp.toLocaleString())}
{stat(player.enemies_destroyed, 1)}
</div>
</div>
<div
style={{
height: 4,
borderRadius: 2,
backgroundColor: theme.colors[`componentInteractive${healthAccent}`],
display: 'flex',
}}
>
<div
style={{
width: `${healthLeft * 100}%`,
height: '100%',
borderRadius: 2,
backgroundColor: theme.colors[`solidBackground${healthAccent}`],
}}
/>
</div>
</div>
);
}
export const replayCommand = new Promise<CommandRegistry>((resolve) => {
resolve({
inProduction: true,
@@ -93,17 +356,85 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
async handler(interaction) {
const file = interaction.options.getAttachment('file')!;
const url = `https://wotinspector.com/api/replay/upload/?url=${encodeURIComponent(
file.url,
)}`;
const response = await fetch(url);
const replay = (await response.json()) as WotInspectorReplayResponse;
// const file = {
// url: 'https://cdn.discordapp.com/ephemeral-attachments/1232434891652861972/1232781496016834581/20240328_1530__TresAbhi_M60_1157057448155406708.wotbreplay?ex=662ab4bc&is=6629633c&hm=c784cd10e80232350ddaf291fa987b879c4d0a8083b78298c9eee4fa0d507f6c&',
// };
const submissionData = (await fetch(
`https://wotinspector.com/api/replay/upload/?url=${encodeURIComponent(
file.url,
)}`,
).then((response) => response.json())) as WotInspectorReplaySubmission;
if (replay.status === 'error') {
return `# Uh oh! WoTInspoec`
if (submissionData.status === 'error') {
return `# Uh oh! WoT Inspector didn't like that.\n\nThere was an error processing your replay. Get help on [the official Discord server](https://discord.gg/nDt7AjGJQH).`;
}
return await response.text();
const replayId = submissionData.data.download_url.split('/').at(-1)!;
const replayData = (await fetch(
`https://api.wotinspector.com/v2/blitz/replays/${replayId}/`,
).then((response) => response.json())) as WotInspectorReplay;
const protagonistTeamId = replayData.players_data.find(
(player) => player.dbid === replayData.protagonist,
)!.team;
const antagonistTeamId = 3 - protagonistTeamId;
const players = replayData.players_data.sort(
(a, b) => b.damage_made - a.damage_made,
);
return [
new RenderConfiguration({ width: 960 }),
<CommandWrapper>
<TitleBar
title={replayData.player_name}
description={`uwu <3 xoxo hugs and kicks`}
/>
<div
style={{
display: 'flex',
gap: 8,
}}
>
<div
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
gap: 4,
}}
>
{await Promise.all(
players
.filter((player) => player.team === protagonistTeamId)
.map((player) =>
playerListing(
player,
true,
player.dbid === replayData.protagonist,
),
),
)}
</div>
<div
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
gap: 4,
}}
>
{await Promise.all(
players
.filter((player) => player.team === antagonistTeamId)
.map((player) => playerListing(player, false)),
)}
</div>
</div>
</CommandWrapper>,
buttonLink(submissionData.data.view_url, 'WoT Inspector'),
buttonLink(submissionData.data.download_url, 'Download'),
];
},
});
});
+8 -10
View File
@@ -9,7 +9,7 @@ import resolveTankId from '../core/blitz/resolveTankId';
import { asset } from '../core/blitzkit/asset';
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine';
import { emblemURL } from '../core/blitzkit/emblemURL';
import { webpToPng } from '../core/blitzkit/iconPng';
import { iconPng } from '../core/blitzkit/iconPng';
import { resolveAncestry } from '../core/blitzkit/resolveAncestry';
import { tankDefinitions } from '../core/blitzkit/tankDefinitions';
import {
@@ -280,7 +280,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
src={await webpToPng(asset('icons/currencies/xp.webp'))}
src={await iconPng(asset('icons/currencies/xp.webp'))}
width={16}
height={16}
/>
@@ -305,7 +305,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
src={await webpToPng(asset('icons/currencies/silver.webp'))}
src={await iconPng(asset('icons/currencies/silver.webp'))}
width={16}
height={16}
/>
@@ -330,7 +330,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
src={await webpToPng(asset('icons/currencies/silver.webp'))}
src={await iconPng(asset('icons/currencies/silver.webp'))}
width={16}
height={16}
/>
@@ -447,9 +447,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
src={await webpToPng(
asset('icons/currencies/xp.webp'),
)}
src={await iconPng(asset('icons/currencies/xp.webp'))}
width={16}
height={16}
/>
@@ -476,7 +474,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
src={await webpToPng(
src={await iconPng(
asset('icons/currencies/free-xp.webp'),
)}
width={16}
@@ -503,7 +501,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
src={await webpToPng(
src={await iconPng(
asset('icons/currencies/silver.webp'),
)}
width={16}
@@ -531,7 +529,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}}
>
<img
src={await webpToPng(
src={await iconPng(
asset('icons/currencies/silver.webp'),
)}
width={16}