Compare commits

...
4 Commits
Author SHA1 Message Date
TresAbhi f5bab8a698 faet: clan-event 2023-09-07 01:24:12 +00:00
TrèsAbhi 331040bbdf Merge pull request #26 from tresabhi/1.12.3 2023-09-03 13:21:00 -05:00
TresAbhi 70e1d4b30d feat: changelogs 2023-09-03 18:20:19 +00:00
TresAbhi 03dbd797fb feat: no ongoing season proof 2023-09-03 18:15:10 +00:00
9 changed files with 519 additions and 117 deletions

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
# Blitzkrieg 1.12.3
3-day downtime fixed!
## Changes
- Split `/ratings` into `/ratings current` and `/ratings archive`
- `current` now properly handles when there is no ongoing ratings season
- `archive` displays older archived seasons
## Fixes
- The bot is now no ratings seasons tolerant
+7
View File
@@ -0,0 +1,7 @@
# Blitzkrieg 1.12.4
Added very badly-coded temporary `/clan-event` command (without linting)
## Changes
- Added `/clan-event` command
+107
View File
@@ -0,0 +1,107 @@
import { SlashCommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import autocompleteClan from '../core/discord/autocompleteClan';
import embedInfo from '../core/discord/embedInfo';
import { secrets } from '../core/node/secrets';
import { CommandRegistry } from '../events/interactionCreate';
const DEFAULT_THRESHOLD = 7;
export const clanEventCommand: CommandRegistry = {
inProduction: true,
inDevelopment: true,
inPublic: true,
command: new SlashCommandBuilder()
.setName('clan-event')
.setDescription('Lists all inactive players')
.addStringOption((option) =>
option
.setName('clan')
.setDescription('The clan name or tag you are checking')
.setRequired(true),
),
async handler(interaction) {
const REGION = 'com';
const CLAN_SEARCH = interaction.options.getString('clan', true);
const {
clan_id,
name: clan_name,
tag: clan_tag,
} = await fetch(
`https://api.wotblitz.${REGION}/wotb/clans/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${CLAN_SEARCH}`,
)
.then((response) => response.json())
.then(({ data }) => data[0]);
const memebers = await fetch(
`https://api.wotblitz.${REGION}/wotb/clans/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&clan_id=${clan_id}`,
)
.then((response) => response.json())
.then(({ data }) => data[clan_id].members_ids);
const stats = await Promise.all(
memebers.map(async (id, index) => {
await new Promise((resolve) => setTimeout(resolve, index * 200));
const [yesterdayPlatoonWins, name, todayPlatoonWins] =
await Promise.all([
fetch(`https://www.blitzstars.com/api/playerstats/${id}`)
.then((response) => response.json())
.then((data) => data[0]?.achievements.jointVictoryCount),
fetch(
`https://api.wotblitz.${REGION}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
)
.then((response) => response.json())
.then(({ data }) => data[id].nickname),
fetch(
`https://api.wotblitz.${REGION}/wotb/account/achievements/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
)
.then((response) => response.json())
.then(({ data }) => data[id].max_series.jointVictory),
]);
return {
id,
name,
yesterdayPlatoonWins,
todayPlatoonWins,
};
}),
);
return embedInfo(
`Estimation of ${clan_name} [${clan_tag}]'s total platoon wins today: ${(
stats.reduce(
(accumulator, player) =>
accumulator +
(player.yesterdayPlatoonWins === undefined
? 0
: player.todayPlatoonWins - player.yesterdayPlatoonWins),
0,
) / 2
).toFixed(0)}`,
stats
.sort(
(playerA, playerB) =>
(playerB.yesterdayPlatoonWins === undefined
? 0
: playerB.todayPlatoonWins - playerB.yesterdayPlatoonWins) -
(playerA.yesterdayPlatoonWins === undefined
? 0
: playerA.todayPlatoonWins - playerA.yesterdayPlatoonWins),
)
.map(
(player) =>
`${markdownEscape(player.name)}: ${
player.yesterdayPlatoonWins === undefined
? 0
: player.todayPlatoonWins - player.yesterdayPlatoonWins
}`,
)
.join('\n'),
);
},
autocomplete: autocompleteClan,
};
+358 -112
View File
@@ -1,21 +1,28 @@
import { import {
APIApplicationCommandOptionChoice, APIApplicationCommandOptionChoice,
SlashCommandBuilder, SlashCommandBuilder,
SlashCommandSubcommandBuilder,
SlashCommandSubcommandGroupBuilder,
} from 'discord.js'; } from 'discord.js';
import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard'; import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard';
import * as Leaderboard from '../components/Leaderboard'; import * as Leaderboard from '../components/Leaderboard';
import TitleBar from '../components/TitleBar'; import TitleBar from '../components/TitleBar';
import Wrapper from '../components/Wrapper'; import Wrapper from '../components/Wrapper';
import { REGION_NAMES_SHORT, Region } from '../constants/regions'; import { REGION_NAMES_SHORT, Region } from '../constants/regions';
import getArchivedRatingsInfo from '../core/blitz/getArchivedRatingsInfo';
import getRatingsInfo from '../core/blitz/getRatingsInfo'; import getRatingsInfo from '../core/blitz/getRatingsInfo';
import getWargamingResponse from '../core/blitz/getWargamingResponse'; import getWargamingResponse from '../core/blitz/getWargamingResponse';
import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain'; import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain';
import getMidnightLeaderboard from '../core/database/getMidnightLeaderboard'; import getMidnightLeaderboard, {
DATABASE_REPO,
} from '../core/database/getMidnightLeaderboard';
import addRegionChoices from '../core/discord/addRegionChoices'; import addRegionChoices from '../core/discord/addRegionChoices';
import addUsernameChoices from '../core/discord/addUsernameChoices'; import addUsernameChoices from '../core/discord/addUsernameChoices';
import autocompleteUsername from '../core/discord/autocompleteUsername'; import autocompleteUsername from '../core/discord/autocompleteUsername';
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
import { octokit } from '../core/express/octokit';
import { secrets } from '../core/node/secrets'; import { secrets } from '../core/node/secrets';
import throwError from '../core/node/throwError';
import { CommandRegistryRaw } from '../events/interactionCreate'; import { CommandRegistryRaw } from '../events/interactionCreate';
import { AccountInfo } from '../types/accountInfo'; import { AccountInfo } from '../types/accountInfo';
import { PlayerClanData } from '../types/playerClanData'; import { PlayerClanData } from '../types/playerClanData';
@@ -86,78 +93,127 @@ export interface RatingsLeague {
percentile: number; percentile: number;
} }
export interface RatingsInfo { export type RatingsInfo =
title: string; | {
icon: string | null; detail: undefined;
start_at: string;
finish_at: string;
current_season: number;
updated_at: string;
count: number;
rewards: RatingsReward[]; title: string;
leagues: RatingsLeague[]; icon: string | null;
} start_at: string;
finish_at: string;
current_season: number;
updated_at: string;
count: number;
rewards: RatingsReward[];
leagues: RatingsLeague[];
}
| { detail: { error: string } };
export interface RatingsNeighbors { export interface RatingsNeighbors {
neighbors: RatingsPlayer[]; neighbors: RatingsPlayer[];
} }
console.log('Caching ratings info...'); const LEAGUES = [
const ratingsInfo = getRatingsInfo('com').then((info) => { { name: 'Diamond', minScore: 5000 },
console.log('Cached ratings info'); { name: 'Platinum', minScore: 4000 },
return info; { name: 'Gold', minScore: 3000 },
}); { name: 'Silver', minScore: 2000 },
{ name: 'Bronze', minScore: 0 },
];
function addSubcommands(
option: SlashCommandSubcommandGroupBuilder,
addOptions = (option: SlashCommandSubcommandBuilder) => option,
) {
return option
.addSubcommand((option) =>
addOptions(
option
.setName('neighbours')
.setDescription('Your position and neighbours'),
)
.addStringOption(addUsernameChoices)
.addIntegerOption((option) =>
option
.setName('limit')
.setDescription('How many neighbours to display (default: 10)')
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
),
)
.addSubcommand((option) =>
addOptions(option.setName('league').setDescription('Top league players'))
.addStringOption((option) =>
option
.setName('league')
.setDescription('The league to display')
.addChoices(
...LEAGUES.map(
({ name }, value) =>
({
name,
value: `${value}`,
}) satisfies APIApplicationCommandOptionChoice<string>,
),
)
.setRequired(true),
)
.addStringOption(addRegionChoices)
.addIntegerOption((option) =>
option
.setName('limit')
.setDescription('How many rows to display (default: 10)')
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
),
);
}
export const ratingsCommand = new Promise<CommandRegistryRaw>( export const ratingsCommand = new Promise<CommandRegistryRaw>(
async (resolve) => { async (resolve) => {
const awaitedRatingsInfo = await ratingsInfo; const seasonChoices = await octokit.repos
.getContent({
...DATABASE_REPO,
path: 'com/ratings',
})
.then(({ data }) => {
if (!Array.isArray(data)) {
throw throwError('Archived ratings data is malformed');
}
return data.map(
(folder) =>
({
name: `Season ${folder.name}`,
value: folder.name,
}) satisfies APIApplicationCommandOptionChoice<string>,
);
});
const command = new SlashCommandBuilder() const command = new SlashCommandBuilder()
.setName('ratings') .setName('ratings')
.setDescription('Ratings battles statistics') .setDescription('Ratings battles statistics')
.addSubcommand((option) => .addSubcommandGroup((option) =>
option addSubcommands(
.setName('league') option.setName('archive').setDescription('Any previous season'),
.setDescription('Top league players') (option) =>
.addStringOption((option) => option.addStringOption((option) =>
option option
.setName('league') .setName('season')
.setDescription('The league to display') .setDescription('The season number')
.addChoices( .setChoices(...seasonChoices)
...awaitedRatingsInfo.leagues.map( .setRequired(true),
(league) => ),
({ ),
name: league.title.split(' ')[0].toLowerCase(),
value: `${league.index}`,
}) satisfies APIApplicationCommandOptionChoice<string>,
),
)
.setRequired(true),
)
.addStringOption(addRegionChoices)
.addIntegerOption((option) =>
option
.setName('limit')
.setDescription('How many rows to display (default: 10)')
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
),
) )
.addSubcommand((option) => .addSubcommandGroup((option) =>
option addSubcommands(
.setName('neighbours') option
.setDescription('Your position and neighbours') .setName('current')
.addStringOption(addUsernameChoices) .setDescription('The current running Ratings season'),
.addIntegerOption((option) => ),
option
.setName('limit')
.setDescription('How many neighbours to display (default: 10)')
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
),
); );
resolve({ resolve({
@@ -168,15 +224,30 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
command, command,
async handler(interaction) { async handler(interaction) {
const subcommand = interaction.options.getSubcommand(true); interface SimplifiedPlayer {
id: number;
score: number;
position: number;
clan: string | undefined;
nickname: string;
}
const subcommandGroup = interaction.options.getSubcommandGroup(true) as
| 'archive'
| 'current';
const subcommand = interaction.options.getSubcommand(true) as
| 'league'
| 'neighbours';
const limit = interaction.options.getInteger('limit') ?? 10; const limit = interaction.options.getInteger('limit') ?? 10;
const seasonOption = interaction.options.getString('season');
const season = seasonOption ? parseInt(seasonOption) : undefined;
let titleName: string; let titleName: string;
let titleImage: string | undefined; let titleImage: string | undefined;
let titleNameDiscriminator: string | undefined; let titleNameDiscriminator: string | undefined;
let titleDescription: string; let titleDescription: string;
let playersBefore: number; let playersBefore: number;
let playersAfter: number; let playersAfter: number;
let players: RatingsPlayer[]; let players: SimplifiedPlayer[] | undefined;
let midnightLeaderboard: BlitzkriegRatingsLeaderboard | undefined; let midnightLeaderboard: BlitzkriegRatingsLeaderboard | undefined;
let playerId: number | undefined; let playerId: number | undefined;
let regionRatingsInfo: RatingsInfo; let regionRatingsInfo: RatingsInfo;
@@ -186,31 +257,120 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
interaction.options.getString('league')!, interaction.options.getString('league')!,
); );
const region = interaction.options.getString('region') as Region; const region = interaction.options.getString('region') as Region;
regionRatingsInfo = await getRatingsInfo(region); regionRatingsInfo = await (subcommandGroup === 'current'
? getRatingsInfo(region)
: getArchivedRatingsInfo(season!, region));
const regionSubdomain = regionToRegionSubdomain(region); const regionSubdomain = regionToRegionSubdomain(region);
const result = await fetch( const result =
`https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`, subcommandGroup === 'current'
) ? regionRatingsInfo.detail === undefined
.then((response) => response.json() as Promise<LeagueTop>) ? await fetch(
.then(({ result }) => result.slice(0, limit)); `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`,
const leagueInfo = regionRatingsInfo.leagues[leagueIndex]; )
midnightLeaderboard = await getMidnightLeaderboard( .then((response) => response.json() as Promise<LeagueTop>)
region, .then(async ({ result }) =>
regionRatingsInfo.current_season, result.slice(0, limit).map(
); (player) =>
({
id: player.spa_id,
score: player.score,
position: player.number,
clan: player.clan_tag,
nickname: player.nickname,
}) satisfies SimplifiedPlayer,
),
)
: undefined
: await octokit.repos
.getContent({
...DATABASE_REPO,
path: `${region}/ratings/${season}/latest.json`,
})
.then(async ({ data }) => {
if (Array.isArray(data) || data.type !== 'file') {
throw throwError('Archived ratings data is malformed');
}
const players = (await fetch(data.download_url!).then(
(response) => response.json(),
)) as BlitzkriegRatingsLeaderboard;
const firstPlayerIndex =
leagueIndex === 0
? 0
: players.findIndex(
(player) =>
player.score < LEAGUES[leagueIndex - 1].minScore,
);
const lastPlayerIndex = firstPlayerIndex + limit - 1;
const trimmed = players.slice(
firstPlayerIndex,
lastPlayerIndex + 1,
);
const ids = trimmed.map((player) => player.id).join(',');
const clanData = await getWargamingResponse<PlayerClanData>(
`https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${ids}&extra=clan`,
);
return trimmed.map(
(player, index) =>
({
id: player.id,
score: player.score,
position: firstPlayerIndex + index + 1,
clan: clanData[player.id]!.clan?.tag,
nickname: clanData[player.id]!.account_name,
}) satisfies SimplifiedPlayer,
);
});
const leagueInfo =
regionRatingsInfo.detail === undefined
? regionRatingsInfo.leagues[leagueIndex]
: undefined;
midnightLeaderboard =
subcommandGroup === 'current'
? regionRatingsInfo.detail === undefined
? await getMidnightLeaderboard(
region,
regionRatingsInfo.current_season,
)
: undefined
: await getMidnightLeaderboard(region, season!);
players = result; players = result;
playersBefore = result[0].number - 1; playersBefore = result ? result[0].position - 1 : 0;
playersAfter = playersAfter =
regionRatingsInfo.count - result[result.length - 1].number; regionRatingsInfo.detail === undefined && result !== undefined
titleName = `${leagueInfo.title} - ${REGION_NAMES_SHORT[region]}`; ? regionRatingsInfo.count - result[result.length - 1].position
titleImage = leagueInfo.big_icon.startsWith('http') : 0;
? leagueInfo.big_icon titleName = `${
: `https:${leagueInfo.big_icon}`; leagueInfo ? leagueInfo.title : 'No Ongoing Season'
} - ${REGION_NAMES_SHORT[region]}`;
titleImage = leagueInfo
? leagueInfo.big_icon.startsWith('http')
? leagueInfo.big_icon
: `https:${leagueInfo.big_icon}`
: undefined;
titleDescription = `Top ${limit} players`; titleDescription = `Top ${limit} players`;
} else { } else {
const { region, id } = await resolvePlayerFromCommand(interaction); const { region, id } = await resolvePlayerFromCommand(interaction);
regionRatingsInfo = await getRatingsInfo(region); regionRatingsInfo =
subcommandGroup === 'current'
? await getRatingsInfo(region)
: await octokit.repos
.getContent({
...DATABASE_REPO,
path: `${region}/ratings/${season}/info.json`,
})
.then(async ({ data }) => {
if (Array.isArray(data) || data.type !== 'file') {
throw throwError('Archived ratings info is malformed');
}
const response = fetch(data.download_url!);
return (await response.then((response) =>
response.json(),
)) as RatingsInfo;
});
const accountInfo = await getWargamingResponse<AccountInfo>( const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`, `https://api.wotblitz.${region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
); );
@@ -220,57 +380,139 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
) )
)[id]?.clan; )[id]?.clan;
const regionSubdomain = regionToRegionSubdomain(region); const regionSubdomain = regionToRegionSubdomain(region);
const { neighbors } = await fetch( const neighbors =
`https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${Math.round( subcommandGroup === 'current'
limit / 2, ? regionRatingsInfo.detail === undefined
)}`, ? await fetch(
).then((response) => response.json() as Promise<RatingsNeighbors>); `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${Math.round(
midnightLeaderboard = await getMidnightLeaderboard( limit / 2,
region, )}`,
regionRatingsInfo.current_season, )
); .then(
(response) =>
response.json() as Promise<RatingsNeighbors>,
)
.then((players) =>
players.neighbors.map(
(player) =>
({
id: player.spa_id,
position: player.number,
score: player.score,
clan: player.clan_tag,
nickname: player.nickname,
}) satisfies SimplifiedPlayer,
),
)
: undefined
: await octokit.repos
.getContent({
...DATABASE_REPO,
path: `${region}/ratings/${season}/latest.json`,
})
.then(async ({ data }) => {
if (Array.isArray(data) || data.type !== 'file') {
throw throwError(
'Archived ratings latest data is malformed',
);
}
if (clan) titleNameDiscriminator = `[${clan.tag}]`; const response = fetch(data.download_url!);
const players = (await response.then((response) =>
response.json(),
)) as BlitzkriegRatingsLeaderboard;
const playerIndex = players.findIndex(
(player) => player.id === id,
);
if (playerIndex === -1) {
throw throwError(
`${accountInfo[id].nickname} didn't player ratings in season ${season}`,
'This player did not participate in this season or did not get past calibration.',
);
}
const halfRange = Math.round(limit / 2);
const trimmed = players.slice(
playerIndex - halfRange,
playerIndex + halfRange + 1,
);
const ids = trimmed.map((player) => player.id).join(',');
const clanData = await getWargamingResponse<PlayerClanData>(
`https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${ids}&extra=clan`,
);
return trimmed.map((player, index) => ({
id: player.id,
score: player.score,
position: playerIndex + index + 1,
clan: clanData[player.id]!.clan?.tag,
nickname: clanData[player.id]!.account_name,
}));
});
midnightLeaderboard =
subcommandGroup === 'current'
? regionRatingsInfo.detail === undefined
? await getMidnightLeaderboard(
region,
regionRatingsInfo.current_season,
)
: undefined
: await getMidnightLeaderboard(region, season!);
if (clan && neighbors) titleNameDiscriminator = `[${clan.tag}]`;
players = neighbors; players = neighbors;
titleImage = clan titleImage = neighbors
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` ? clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
: undefined
: undefined; : undefined;
titleName = accountInfo[id].nickname; titleName = neighbors
? accountInfo[id].nickname
: `No Ongoing Season - ${REGION_NAMES_SHORT[region]}`;
titleDescription = 'Ratings neighbours'; titleDescription = 'Ratings neighbours';
playersBefore = neighbors[0].number - 1; playersBefore = neighbors ? neighbors[0].position - 1 : 0;
playersAfter = playersAfter =
regionRatingsInfo.count - neighbors[neighbors.length - 1].number; regionRatingsInfo.detail === undefined
? neighbors
? regionRatingsInfo.count -
neighbors[neighbors.length - 1].position
: 0
: 0;
playerId = id; playerId = id;
} }
const items = players.map((player) => { const items = players?.map((player) => {
const reward = regionRatingsInfo.rewards.find( const reward =
(reward) => regionRatingsInfo.detail === undefined
player.number >= reward.from_position && ? regionRatingsInfo.rewards.find(
player.number <= reward.to_position, (reward) =>
); player.position >= reward.from_position &&
player.position <= reward.to_position,
)
: undefined;
const midnightIndex = midnightLeaderboard?.findIndex( const midnightIndex = midnightLeaderboard?.findIndex(
(item) => item.id === player.spa_id, (item) => item.id === player.id,
); );
const midnightScore = const midnightScore =
midnightIndex === undefined midnightIndex === undefined
? player.score ? player.score
: midnightLeaderboard![midnightIndex].score; : midnightLeaderboard![midnightIndex].score;
const midnightPosition = const midnightPosition =
midnightIndex === undefined ? player.number : midnightIndex + 1; midnightIndex === undefined ? player.position : midnightIndex + 1;
return ( return (
<Leaderboard.Item <Leaderboard.Item
nickname={player.nickname} nickname={player.nickname}
score={player.score} score={player.score}
position={player.number} position={player.position}
clan={player.clan_tag} clan={player.clan}
reward={reward} reward={reward}
deltaScore={player.score - midnightScore} deltaScore={player.score - midnightScore}
deltaPosition={midnightPosition - player.number} deltaPosition={midnightPosition - player.position}
key={player.spa_id} key={player.id}
highlight={player.spa_id === playerId} highlight={player.id === playerId}
/> />
); );
}); });
@@ -284,11 +526,15 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
description={`${titleDescription} • ${new Date().toDateString()}`} description={`${titleDescription} • ${new Date().toDateString()}`}
/> />
<Leaderboard.Root> {items && (
{playersBefore > 0 && <Leaderboard.Gap number={playersBefore} />} <Leaderboard.Root>
{items} {playersBefore > 0 && (
{playersAfter > 0 && <Leaderboard.Gap number={playersAfter} />} <Leaderboard.Gap number={playersBefore} />
</Leaderboard.Root> )}
{items}
{playersAfter > 0 && <Leaderboard.Gap number={playersAfter} />}
</Leaderboard.Root>
)}
</Wrapper> </Wrapper>
); );
}, },
+25
View File
@@ -0,0 +1,25 @@
import { RatingsInfo } from '../../commands/ratings';
import { Region } from '../../constants/regions';
import { DATABASE_REPO } from '../database/getMidnightLeaderboard';
import { octokit } from '../express/octokit';
import throwError from '../node/throwError';
export default async function getArchivedRatingsInfo(
season: number,
region: Region,
) {
const { data } = await octokit.repos.getContent({
...DATABASE_REPO,
path: `${region}/ratings/${season}/info.json`,
});
if (Array.isArray(data) || data.type !== 'file')
throw throwError('Archived ratings info is malformed');
const content = Buffer.from(data.content, 'base64').toString();
const jsonContent = JSON.parse(content) as RatingsInfo & {
detail: undefined;
};
return jsonContent;
}
+1
View File
@@ -8,5 +8,6 @@ export default async function getRatingsInfo(region: Region) {
region, region,
)}.wotblitz.com/en/api/rating-leaderboards/season/`, )}.wotblitz.com/en/api/rating-leaderboards/season/`,
); );
return (await response.json()) as Promise<RatingsInfo>; return (await response.json()) as Promise<RatingsInfo>;
} }
+5 -4
View File
@@ -15,12 +15,13 @@ export default async function getMidnightLeaderboard(
}); });
if (!Array.isArray(data) && data.type === 'file') { if (!Array.isArray(data) && data.type === 'file') {
const content = Buffer.from(data.content, 'base64').toString(); const response = await fetch(data.download_url!);
const jsonContent = JSON.parse(content) as BlitzkriegRatingsLeaderboard;
return jsonContent; return (await response.json()) as BlitzkriegRatingsLeaderboard;
} }
} catch (error) { } catch (error) {
console.warn('No midnight leaderboard found'); console.warn(
`No midnight leaderboard found for season ${season}, region ${region}`,
);
} }
} }
+2
View File
@@ -13,6 +13,7 @@ import {
SlashCommandSubcommandsOnlyBuilder, SlashCommandSubcommandsOnlyBuilder,
} from 'discord.js'; } from 'discord.js';
import discord from '../../../discord.json' assert { type: 'json' }; import discord from '../../../discord.json' assert { type: 'json' };
import { clanEventCommand } from '../../commands/clanEvent';
import { debugCommand } from '../../commands/debug'; import { debugCommand } from '../../commands/debug';
import { eligibleCommand } from '../../commands/eligible'; import { eligibleCommand } from '../../commands/eligible';
import { evolutionCommand } from '../../commands/evolution'; import { evolutionCommand } from '../../commands/evolution';
@@ -99,6 +100,7 @@ export const COMMANDS_RAW: CommandRegistry[] = [
evolutionCommand, evolutionCommand,
statsCommand, statsCommand,
ratingsCommand, ratingsCommand,
clanEventCommand,
]; ];
export const commands = Promise.all(COMMANDS_RAW).then((rawCommands) => export const commands = Promise.all(COMMANDS_RAW).then((rawCommands) =>
+1 -1
View File
@@ -11,5 +11,5 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": ["src/**/*.ts", "src/components/Tanks/components/Row.tsx"] "include": ["src/**/*.ts", "src/components/Tanks/components/Row.tsx", "src/commands/clanEvent.ts"]
} }