import { getAccountInfo, patientFetchJSON, RatingInfo, RatingLeaderboard, RatingLeaderboardEntryV1, RatingLeaderboardEntryV2, RatingNeighbors, RatingPlayer, Region, REGIONS, regionToRegionSubdomain, } from '@blitzkit/core'; import { chunk } from 'lodash'; import { argv } from 'process'; import { commitAssets } from './core/github/commitAssets'; import { FileChange } from './core/github/commitMultipleFiles'; const region = argv .find((arg) => arg.startsWith('--region=')) ?.split('=')[1] as Region | undefined; if (region === undefined || !REGIONS.includes(region)) { throw new Error('No valid region specified'); } console.log(`Building rating leaderboard for ${region}`); const regionSubdomain = regionToRegionSubdomain(region); const NEIGHBORS = 2 ** 8; const PLAYERS = await patientFetchJSON( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/season/`, ).then((data) => (data.detail === undefined ? data.count : undefined)); if (PLAYERS === undefined) { throw new Error('No current rating season running'); } const players: Record = {}; const entriesV1: RatingLeaderboardEntryV1[] = []; let registered = 0; async function branchFromPlayer(id: number) { const { neighbors } = await patientFetchJSON( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${NEIGHBORS}`, ); const firstPlayer = neighbors[0]; const lastPlayer = neighbors[neighbors.length - 1]; const branches: Promise[] = []; if (!players[firstPlayer.number]) branches.push(branchFromPlayer(firstPlayer.spa_id)); if (!players[lastPlayer.number]) branches.push(branchFromPlayer(lastPlayer.spa_id)); neighbors.forEach((neighbor) => { if (players[neighbor.number]) return; players[neighbor.number] = neighbor; registered++; }); console.log( `Registered ${registered} / ${PLAYERS} (${( (registered / PLAYERS!) * 100 ).toFixed(2)}%)`, ); await Promise.all(branches); } async function branchFromLeague(id: number) { const { result } = await patientFetchJSON<{ result: RatingPlayer[] }>( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${id}/top/`, ); const firstPlayer = result[0]; const lastPlayer = result.at(-1)!; await Promise.all([ branchFromPlayer(firstPlayer.spa_id), branchFromPlayer(lastPlayer.spa_id), ]); } console.log('Branching from leagues...'); await Promise.all([ branchFromLeague(0), branchFromLeague(1), branchFromLeague(2), branchFromLeague(3), branchFromLeague(4), ]); console.log('Converting to an array...'); for (let index = 0; index < PLAYERS; index++) { const listing = players[index + 1]; if (listing) { entriesV1[index] = { id: listing.spa_id, score: listing.score }; } else { console.warn(`Position ${index + 1} had no player`); entriesV1[index] = { id: 0, score: 0 }; } } console.log('Requesting further stats...'); const entries: RatingLeaderboardEntryV2[] = []; let chunkIndex = 0; for (const leaderboardChunk of chunk(entriesV1, 100)) { console.log( `Chunk ${chunkIndex + 1} of ${Math.ceil(entriesV1.length / 100)}`, ); const stats = await getAccountInfo( region, leaderboardChunk.map(({ id }) => id), ['statistics.rating'], ); stats.forEach((stat, index) => { const trueIndex = chunkIndex * 100 + index; if ( leaderboardChunk[index].id === 0 || stat?.statistics.rating === undefined ) { entries[trueIndex] = { battles: 0, damage: 0, id: 0, kills: 0, score: 0, survived: 0, wins: 0, }; } else { entries[trueIndex] = { id: leaderboardChunk[index].id, score: leaderboardChunk[index].score, battles: stat.statistics.rating.battles, damage: stat.statistics.rating.damage_dealt, kills: stat.statistics.rating.frags, survived: stat.statistics.rating.survived_battles, wins: stat.statistics.rating.wins, }; } }); chunkIndex++; } const info = await patientFetchJSON( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/season/`, ); const normalizedServer = regionSubdomain === 'na' ? 'com' : regionSubdomain; const content = RatingLeaderboard.encode({ version: { $case: 'v2', value: { entries } }, }).finish(); const changes: FileChange[] = [ { path: `regions/${normalizedServer}/rating/${info.current_season}.pb`, content: Buffer.from(content), }, ]; commitAssets(`rating leaderboard ${regionSubdomain}`, changes);