feat: made /ratings data-driven

This commit is contained in:
TresAbhi committed 2023-08-09 00:47:15 +00:00
1 parent d511cfa4cc
commit f7eadb08a9
1 file changed
+133 -145
+133 -145
View File
@@ -1,4 +1,4 @@
import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js'; import { SlashCommandBuilder } from 'discord.js';
import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard'; import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard';
import { octokit } from '../bot'; import { octokit } from '../bot';
import * as Leaderboard from '../components/Leaderboard'; import * as Leaderboard from '../components/Leaderboard';
@@ -7,7 +7,7 @@ import Wrapper from '../components/Wrapper';
import { Region } from '../constants/regions'; import { Region } from '../constants/regions';
import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain'; import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain';
import addRegionChoices from '../core/discord/addRegionChoices'; import addRegionChoices from '../core/discord/addRegionChoices';
import { CommandRegistry } from '../events/interactionCreate'; import { CommandRegistryRaw } from '../events/interactionCreate';
export interface RatingsPlayer { export interface RatingsPlayer {
spa_id: number; spa_id: number;
@@ -102,157 +102,145 @@ const ratingsInfo = fetch(
return info; return info;
}); });
function optionalParameters(option: SlashCommandSubcommandBuilder) {
return option
.addStringOption(addRegionChoices)
.addIntegerOption((option) =>
option
.setName('limit')
.setDescription('How many rows to display (default: 10)')
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
);
}
const LEAGUE_INDEXES = ['diamond', 'platinum', 'gold', 'silver', 'bronze'];
export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' }; export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' };
export const ratingsCommand: CommandRegistry = { const LEAGUE_SUBCOMMAND_TO_INDEX: Record<string, number> = {};
inProduction: true,
inDevelopment: true,
inPublic: true,
command: new SlashCommandBuilder() export const ratingsCommand = new Promise<CommandRegistryRaw>(
.setName('ratings') async (resolve) => {
.setDescription('Ratings battles statistics') const awaitedRatingsInfo = await ratingsInfo;
.addSubcommandGroup((option) =>
option
.setName('league')
.setDescription('Top league players')
.addSubcommand((option) =>
optionalParameters(
option
.setName('diamond')
.setDescription('Top Diamond League players'),
),
)
.addSubcommand((option) =>
optionalParameters(
option
.setName('platinum')
.setDescription('Top Platinum League players'),
),
)
.addSubcommand((option) =>
optionalParameters(
option.setName('gold').setDescription('Top Gold League players'),
),
)
.addSubcommand((option) =>
optionalParameters(
option
.setName('silver')
.setDescription('Top Silver League players'),
),
)
.addSubcommand((option) =>
optionalParameters(
option
.setName('bronze')
.setDescription('Top Bronze League players'),
),
),
),
async handler(interaction) {
const subcommandGroup = interaction.options.getSubcommandGroup(true);
if (subcommandGroup === 'league') { const command = new SlashCommandBuilder()
const limit = interaction.options.getInteger('limit') ?? 10; .setName('ratings')
const subcommand = interaction.options.getSubcommand(true); .setDescription('Ratings battles statistics')
const leagueIndex = LEAGUE_INDEXES.indexOf(subcommand); .addSubcommandGroup((option) => {
const region = interaction.options.getString('region') as Region; const subcommandGroup = option
const regionSubdomain = regionToRegionSubdomain(region); .setName('league')
const result = await fetch( .setDescription('Top league players');
`https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`,
)
.then((response) => response.json() as Promise<LeagueTop>)
.then(({ result }) => result.slice(0, limit));
const awaitedInfo = await ratingsInfo;
const leagueInfo = awaitedInfo.leagues[leagueIndex];
const midnightLeaderboard = await octokit.repos
.getContent({
...DATABASE_REPO,
path: `${region}/ratings/${awaitedInfo.current_season}/midnight.json`,
})
.then(({ data }) => {
if (!Array.isArray(data) && data.type === 'file') {
const content = Buffer.from(data.content, 'base64').toString();
const jsonContent = JSON.parse(
content,
) as BlitzkriegRatingsLeaderboard;
return jsonContent; awaitedRatingsInfo.leagues.forEach((league) => {
} subcommandGroup.addSubcommand((option) => {
}) const subcommandName = league.title.split(' ')[0].toLowerCase();
.catch(() => console.warn('No midnight leaderboard found')); LEAGUE_SUBCOMMAND_TO_INDEX[subcommandName] = league.index;
const items = await Promise.all(
result.map(async (player) => { return option
const reward = awaitedInfo.rewards.find( .setName(subcommandName)
(reward) => .setDescription(`Top ${league.title} players`)
player.number >= reward.from_position && .addStringOption(addRegionChoices)
player.number <= reward.to_position, .addIntegerOption((option) =>
option
.setName('limit')
.setDescription('How many rows to display (default: 10)')
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
);
});
});
return subcommandGroup;
});
resolve({
inProduction: true,
inDevelopment: true,
inPublic: true,
command,
async handler(interaction) {
const subcommandGroup = interaction.options.getSubcommandGroup(true);
if (subcommandGroup === 'league') {
const limit = interaction.options.getInteger('limit') ?? 10;
const subcommand = interaction.options.getSubcommand(true);
const leagueIndex = LEAGUE_SUBCOMMAND_TO_INDEX[subcommand];
const region = interaction.options.getString('region') as Region;
const regionSubdomain = regionToRegionSubdomain(region);
const result = await fetch(
`https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`,
)
.then((response) => response.json() as Promise<LeagueTop>)
.then(({ result }) => result.slice(0, limit));
const awaitedInfo = await ratingsInfo;
const leagueInfo = awaitedInfo.leagues[leagueIndex];
const midnightLeaderboard = await octokit.repos
.getContent({
...DATABASE_REPO,
path: `${region}/ratings/${awaitedInfo.current_season}/midnight.json`,
})
.then(({ data }) => {
if (!Array.isArray(data) && data.type === 'file') {
const content = Buffer.from(data.content, 'base64').toString();
const jsonContent = JSON.parse(
content,
) as BlitzkriegRatingsLeaderboard;
return jsonContent;
}
})
.catch(() => console.warn('No midnight leaderboard found'));
const items = await Promise.all(
result.map(async (player) => {
const reward = awaitedInfo.rewards.find(
(reward) =>
player.number >= reward.from_position &&
player.number <= reward.to_position,
);
const midnightIndex = midnightLeaderboard?.findIndex(
(item) => item.id === player.spa_id,
);
const midnightScore =
midnightIndex === undefined
? player.score
: midnightLeaderboard![midnightIndex].score;
const midnightPosition =
midnightIndex === undefined ? player.number : midnightIndex + 1;
return (
<Leaderboard.Item
nickname={player.nickname}
score={player.score}
position={player.number}
clan={player.clan_tag}
reward={reward}
deltaScore={player.score - midnightScore}
deltaPosition={midnightPosition - player.number}
key={player.spa_id}
/>
);
}),
); );
const midnightIndex = midnightLeaderboard?.findIndex(
(item) => item.id === player.spa_id, const playersBefore = result[0].number - 1;
); const playersAfter =
const midnightScore = awaitedInfo.count - result[result.length - 1].number;
midnightIndex === undefined
? player.score
: midnightLeaderboard![midnightIndex].score;
const midnightPosition =
midnightIndex === undefined ? player.number : midnightIndex + 1;
return ( return (
<Leaderboard.Item <Wrapper>
nickname={player.nickname} <TitleBar
score={player.score} name={leagueInfo.title}
position={player.number} image={
clan={player.clan_tag} leagueInfo.big_icon.startsWith('http')
reward={reward} ? leagueInfo.big_icon
deltaScore={player.score - midnightScore} : `https:${leagueInfo.big_icon}`
deltaPosition={midnightPosition - player.number} }
key={player.spa_id} description={`Top ${limit} players • ${new Date().toDateString()}`}
/> />
<Leaderboard.Root>
{playersBefore > 0 && (
<Leaderboard.Gap number={playersBefore} />
)}
{items}
{playersAfter > 0 && <Leaderboard.Gap number={playersAfter} />}
</Leaderboard.Root>
</Wrapper>
); );
}), }
);
const playersBefore = result[0].number - 1; return [];
const playersAfter = awaitedInfo.count - result[result.length - 1].number; },
});
return (
<Wrapper>
<TitleBar
name={leagueInfo.title}
image={
leagueInfo.big_icon.startsWith('http')
? leagueInfo.big_icon
: `https:${leagueInfo.big_icon}`
}
description={`Top ${limit} players • ${new Date().toDateString()}`}
/>
<Leaderboard.Root>
{playersBefore > 0 && <Leaderboard.Gap number={playersBefore} />}
{items}
{playersAfter > 0 && <Leaderboard.Gap number={playersAfter} />}
</Leaderboard.Root>
</Wrapper>
);
}
return [];
}, },
}; );