mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: mostly complete rating command
This commit is contained in:
1 parent
983b96fd99
commit
1f30d1d2c3
11 files changed
+384
-32
No files matched your search
@@ -11,7 +11,7 @@ import { getRatingNeighbors } from '../core/blitz/getRatingNeighbors';
|
|||||||
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
|
import { normalizeLeagueIcon } from '../core/blitz/normalizeLeagueIcon';
|
||||||
import { emblemURL } from '../core/blitzkit/emblemURL';
|
import { emblemURL } from '../core/blitzkit/emblemURL';
|
||||||
import getArchivedRatingMidnightLeaderboard from '../core/blitzkit/getArchivedRatingMidnightLeaderboard';
|
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 addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||||
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||||
@@ -295,7 +295,7 @@ export const ratingCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
height: 64,
|
height: 64,
|
||||||
objectFit: 'cover',
|
objectFit: 'cover',
|
||||||
}}
|
}}
|
||||||
src={await webpToPng(
|
src={await iconPng(
|
||||||
reward.type === 'vehicle'
|
reward.type === 'vehicle'
|
||||||
? reward.vehicle.preview_image_url
|
? reward.vehicle.preview_image_url
|
||||||
: reward.stuff.image_url,
|
: reward.stuff.image_url,
|
||||||
|
|||||||
+340
-9
@@ -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 { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
|
||||||
import { CommandRegistry } from '../events/interactionCreate';
|
import { CommandRegistry } from '../events/interactionCreate';
|
||||||
|
import { theme } from '../stitches.config';
|
||||||
|
|
||||||
type WotInspectorReplayResponse =
|
type WotInspectorReplaySubmission =
|
||||||
| { status: 'error'; data: unknown; error: { message: string } }
|
| { status: 'error'; data: unknown; error: { message: string } }
|
||||||
| {
|
| {
|
||||||
status: 'ok';
|
status: 'ok';
|
||||||
@@ -83,6 +93,259 @@ type WotInspectorReplayResponse =
|
|||||||
error: {};
|
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) => {
|
export const replayCommand = new Promise<CommandRegistry>((resolve) => {
|
||||||
resolve({
|
resolve({
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
@@ -93,17 +356,85 @@ export const replayCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
async handler(interaction) {
|
async handler(interaction) {
|
||||||
const file = interaction.options.getAttachment('file')!;
|
const file = interaction.options.getAttachment('file')!;
|
||||||
const url = `https://wotinspector.com/api/replay/upload/?url=${encodeURIComponent(
|
// const file = {
|
||||||
file.url,
|
// url: 'https://cdn.discordapp.com/ephemeral-attachments/1232434891652861972/1232781496016834581/20240328_1530__TresAbhi_M60_1157057448155406708.wotbreplay?ex=662ab4bc&is=6629633c&hm=c784cd10e80232350ddaf291fa987b879c4d0a8083b78298c9eee4fa0d507f6c&',
|
||||||
)}`;
|
// };
|
||||||
const response = await fetch(url);
|
const submissionData = (await fetch(
|
||||||
const replay = (await response.json()) as WotInspectorReplayResponse;
|
`https://wotinspector.com/api/replay/upload/?url=${encodeURIComponent(
|
||||||
|
file.url,
|
||||||
|
)}`,
|
||||||
|
).then((response) => response.json())) as WotInspectorReplaySubmission;
|
||||||
|
|
||||||
if (replay.status === 'error') {
|
if (submissionData.status === 'error') {
|
||||||
return `# Uh oh! WoTInspoec`
|
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'),
|
||||||
|
];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -9,7 +9,7 @@ import resolveTankId from '../core/blitz/resolveTankId';
|
|||||||
import { asset } from '../core/blitzkit/asset';
|
import { asset } from '../core/blitzkit/asset';
|
||||||
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine';
|
import { buildTechTreeLine } from '../core/blitzkit/buildTechTreeLine';
|
||||||
import { emblemURL } from '../core/blitzkit/emblemURL';
|
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 { resolveAncestry } from '../core/blitzkit/resolveAncestry';
|
||||||
import { tankDefinitions } from '../core/blitzkit/tankDefinitions';
|
import { tankDefinitions } from '../core/blitzkit/tankDefinitions';
|
||||||
import {
|
import {
|
||||||
@@ -280,7 +280,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={await webpToPng(asset('icons/currencies/xp.webp'))}
|
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -305,7 +305,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={await webpToPng(asset('icons/currencies/silver.webp'))}
|
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -330,7 +330,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={await webpToPng(asset('icons/currencies/silver.webp'))}
|
src={await iconPng(asset('icons/currencies/silver.webp'))}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -447,9 +447,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={await webpToPng(
|
src={await iconPng(asset('icons/currencies/xp.webp'))}
|
||||||
asset('icons/currencies/xp.webp'),
|
|
||||||
)}
|
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
/>
|
/>
|
||||||
@@ -476,7 +474,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={await webpToPng(
|
src={await iconPng(
|
||||||
asset('icons/currencies/free-xp.webp'),
|
asset('icons/currencies/free-xp.webp'),
|
||||||
)}
|
)}
|
||||||
width={16}
|
width={16}
|
||||||
@@ -503,7 +501,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={await webpToPng(
|
src={await iconPng(
|
||||||
asset('icons/currencies/silver.webp'),
|
asset('icons/currencies/silver.webp'),
|
||||||
)}
|
)}
|
||||||
width={16}
|
width={16}
|
||||||
@@ -531,7 +529,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={await webpToPng(
|
src={await iconPng(
|
||||||
asset('icons/currencies/silver.webp'),
|
asset('icons/currencies/silver.webp'),
|
||||||
)}
|
)}
|
||||||
width={16}
|
width={16}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function CommandWrapper({ children, fat }: CommandWrapperProps) {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: `16px ${fat ? 0 : 16}px`,
|
padding: `16px ${fat ? 0 : 16}px`,
|
||||||
gap: 16,
|
gap: 16,
|
||||||
width: 480,
|
width: '100%',
|
||||||
color: theme.colors.textHighContrast,
|
color: theme.colors.textHighContrast,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
background: theme.colors.appBackground1,
|
background: theme.colors.appBackground1,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import sharp from 'sharp';
|
|||||||
|
|
||||||
const cache: Record<string, Buffer> = {};
|
const cache: Record<string, Buffer> = {};
|
||||||
|
|
||||||
export async function webpToPng(url: string) {
|
export async function iconPng(url: string) {
|
||||||
if (!url.endsWith('.webp')) return url;
|
if (!url.endsWith('.webp')) return url;
|
||||||
|
|
||||||
if (!cache[url]) {
|
if (!cache[url]) {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import jsxToSvg from './jsxToSvg';
|
import jsxToSvg from './jsxToSvg';
|
||||||
|
import { RenderConfiguration } from './renderConfiguration';
|
||||||
import { svgToPngThreaded } from './svgToPngThreaded';
|
import { svgToPngThreaded } from './svgToPngThreaded';
|
||||||
|
|
||||||
export default async function jsxToPngThreaded(jsx: JSX.Element) {
|
export default async function jsxToPngThreaded(
|
||||||
const svg = await jsxToSvg(jsx);
|
jsx: JSX.Element,
|
||||||
|
renderConfiguration: RenderConfiguration,
|
||||||
|
) {
|
||||||
|
const svg = await jsxToSvg(jsx, renderConfiguration);
|
||||||
const png = await svgToPngThreaded(svg);
|
const png = await svgToPngThreaded(svg);
|
||||||
|
|
||||||
return png;
|
return png;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import satori from 'satori';
|
import satori from 'satori';
|
||||||
|
import { RenderConfiguration } from '../renderConfiguration';
|
||||||
import robotoBlack from './Roboto-Black.ttf';
|
import robotoBlack from './Roboto-Black.ttf';
|
||||||
import robotoBold from './Roboto-Bold.ttf';
|
import robotoBold from './Roboto-Bold.ttf';
|
||||||
import roboto from './Roboto.ttf';
|
import roboto from './Roboto.ttf';
|
||||||
@@ -10,9 +11,12 @@ const fonts = [
|
|||||||
{ file: robotoBlack, weight: 900 as const },
|
{ file: robotoBlack, weight: 900 as const },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default async function jsxToSvg(jsx: JSX.Element) {
|
export default async function jsxToSvg(
|
||||||
|
jsx: JSX.Element,
|
||||||
|
renderConfiguration: RenderConfiguration,
|
||||||
|
) {
|
||||||
return await satori(jsx, {
|
return await satori(jsx, {
|
||||||
width: 480,
|
width: renderConfiguration.width,
|
||||||
fonts: fonts.map(({ file, weight }) => ({
|
fonts: fonts.map(({ file, weight }) => ({
|
||||||
weight,
|
weight,
|
||||||
name: 'Roboto',
|
name: 'Roboto',
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export interface RenderConfiguration {
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RenderConfiguration implements RenderConfiguration {
|
||||||
|
width = 480;
|
||||||
|
|
||||||
|
constructor(configuration?: Partial<RenderConfiguration>) {
|
||||||
|
Object.assign(this, configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { webpToPng } from './iconPng';
|
import { iconPng } from './iconPng';
|
||||||
import { tankIcon } from './tankIcon';
|
import { tankIcon } from './tankIcon';
|
||||||
|
|
||||||
export function tankIconPng(id: number) {
|
export function tankIconPng(id: number) {
|
||||||
return webpToPng(tankIcon(id));
|
return iconPng(tankIcon(id));
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { InteractionReturnable } from '../../events/interactionCreate';
|
import { InteractionReturnable } from '../../events/interactionCreate';
|
||||||
import jsxToPngThreaded from '../blitzkit/jsxToPngThreaded';
|
import jsxToPngThreaded from '../blitzkit/jsxToPngThreaded';
|
||||||
|
import { RenderConfiguration } from '../blitzkit/renderConfiguration';
|
||||||
|
|
||||||
export default async function normalizeInteractionReturnable(
|
export default async function normalizeInteractionReturnable(
|
||||||
returnable: InteractionReturnable,
|
returnable: InteractionReturnable,
|
||||||
@@ -17,11 +18,11 @@ export default async function normalizeInteractionReturnable(
|
|||||||
const reply: InteractionEditReplyOptions &
|
const reply: InteractionEditReplyOptions &
|
||||||
InteractionReplyOptions &
|
InteractionReplyOptions &
|
||||||
MessageEditOptions = {};
|
MessageEditOptions = {};
|
||||||
|
|
||||||
const awaitedReturnable = await returnable;
|
const awaitedReturnable = await returnable;
|
||||||
const normalizedReturnable = Array.isArray(awaitedReturnable)
|
const normalizedReturnable = Array.isArray(awaitedReturnable)
|
||||||
? awaitedReturnable
|
? awaitedReturnable
|
||||||
: [awaitedReturnable];
|
: [awaitedReturnable];
|
||||||
|
let renderConfiguration = new RenderConfiguration();
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
normalizedReturnable.map(async (item, index) => {
|
normalizedReturnable.map(async (item, index) => {
|
||||||
@@ -39,10 +40,12 @@ export default async function normalizeInteractionReturnable(
|
|||||||
} else if (item instanceof AttachmentBuilder) {
|
} else if (item instanceof AttachmentBuilder) {
|
||||||
if (!reply.files) reply.files = [];
|
if (!reply.files) reply.files = [];
|
||||||
reply.files.push(item);
|
reply.files.push(item);
|
||||||
|
} else if (item instanceof RenderConfiguration) {
|
||||||
|
renderConfiguration = item;
|
||||||
} else if (item === null) {
|
} else if (item === null) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const image = await jsxToPngThreaded(item);
|
const image = await jsxToPngThreaded(item, renderConfiguration);
|
||||||
images.push([index, image]);
|
images.push([index, image]);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { statsCommand } from '../../commands/stats';
|
|||||||
import { todayCommand } from '../../commands/today';
|
import { todayCommand } from '../../commands/today';
|
||||||
import getClientId from '../../core/blitzkit/getClientId';
|
import getClientId from '../../core/blitzkit/getClientId';
|
||||||
import isDev from '../../core/blitzkit/isDev';
|
import isDev from '../../core/blitzkit/isDev';
|
||||||
|
import { RenderConfiguration } from '../../core/blitzkit/renderConfiguration';
|
||||||
import { secrets } from '../../core/blitzkit/secrets';
|
import { secrets } from '../../core/blitzkit/secrets';
|
||||||
import handleAutocomplete from './handlers/autocomplete';
|
import handleAutocomplete from './handlers/autocomplete';
|
||||||
import handleButton from './handlers/button';
|
import handleButton from './handlers/button';
|
||||||
@@ -45,6 +46,7 @@ export type InteractionRawReturnable =
|
|||||||
| EmbedBuilder
|
| EmbedBuilder
|
||||||
| ButtonBuilder
|
| ButtonBuilder
|
||||||
| AttachmentBuilder
|
| AttachmentBuilder
|
||||||
|
| RenderConfiguration
|
||||||
| JSX.Element
|
| JSX.Element
|
||||||
| null;
|
| null;
|
||||||
export type InteractionIterableReturnable =
|
export type InteractionIterableReturnable =
|
||||||
|
|||||||
Reference in new issue
Block a user