diff --git a/.github/workflows/cd-ratings-asia.yml b/.github/workflows/cd-rating-asia.yml similarity index 87% rename from .github/workflows/cd-ratings-asia.yml rename to .github/workflows/cd-rating-asia.yml index 5aaedffaf..a25ce9887 100644 --- a/.github/workflows/cd-ratings-asia.yml +++ b/.github/workflows/cd-rating-asia.yml @@ -1,4 +1,4 @@ -name: CD Ratings Asia +name: CD Rating Asia on: schedule: @@ -31,4 +31,4 @@ jobs: run: yarn - name: Generate leaderboard - run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingsleaderboard --region=asia --target=midnight --production + run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingleaderboard --region=asia --target=midnight --production diff --git a/.github/workflows/cd-ratings-eu.yml b/.github/workflows/cd-rating-eu.yml similarity index 87% rename from .github/workflows/cd-ratings-eu.yml rename to .github/workflows/cd-rating-eu.yml index 63efa1d89..42092a58c 100644 --- a/.github/workflows/cd-ratings-eu.yml +++ b/.github/workflows/cd-rating-eu.yml @@ -1,4 +1,4 @@ -name: CD Ratings Europe +name: CD Rating Europe on: schedule: @@ -31,4 +31,4 @@ jobs: run: yarn - name: Generate leaderboard - run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingsleaderboard --region=eu --target=midnight --production + run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingleaderboard --region=eu --target=midnight --production diff --git a/.github/workflows/cd-ratings-latest.yml b/.github/workflows/cd-rating-latest.yml similarity index 80% rename from .github/workflows/cd-ratings-latest.yml rename to .github/workflows/cd-rating-latest.yml index 91bd8f8dd..57245a61f 100644 --- a/.github/workflows/cd-ratings-latest.yml +++ b/.github/workflows/cd-rating-latest.yml @@ -1,4 +1,4 @@ -name: CD Ratings North America +name: CD Rating North America on: schedule: @@ -31,13 +31,13 @@ jobs: run: yarn - name: Generate NA leaderboard - run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingsleaderboard --region=na --target=latest --production + run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingleaderboard --region=na --target=latest --production if: always() - name: Generate EU leaderboard - run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingsleaderboard --region=eu --target=latest --production + run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingleaderboard --region=eu --target=latest --production if: always() - name: Generate Asia leaderboard - run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingsleaderboard --region=asia --target=latest --production + run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingleaderboard --region=asia --target=latest --production if: always() diff --git a/.github/workflows/cd-ratings-na.yml b/.github/workflows/cd-rating-na.yml similarity index 86% rename from .github/workflows/cd-ratings-na.yml rename to .github/workflows/cd-rating-na.yml index 18c771840..d554998c4 100644 --- a/.github/workflows/cd-ratings-na.yml +++ b/.github/workflows/cd-rating-na.yml @@ -1,4 +1,4 @@ -name: CD Ratings North America +name: CD Rating North America on: schedule: @@ -31,4 +31,4 @@ jobs: run: yarn - name: Generate leaderboard - run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingsleaderboard --region=na --target=midnight --production + run: GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} DISCORD_BLITZ_DB=${{ secrets.DISCORD_BLITZ_DB }} yarn publish:ratingleaderboard --region=na --target=midnight --production diff --git a/.vscode/settings.json b/.vscode/settings.json index 55ef783ec..549fa2304 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -100,8 +100,7 @@ "POSITIVEZ", "Promisable", "Pyogenics", - "ratingsleaderboard", - "ratingsplot", + "ratingleaderboard", "raycaster", "researchable", "Resvg", diff --git a/docs/guide/bkrl.md b/docs/guide/bkrl.md index b5a109be7..e66db7018 100644 --- a/docs/guide/bkrl.md +++ b/docs/guide/bkrl.md @@ -1,9 +1,9 @@ -# Blitzkrieg Ratings Leaderboard +# Blitzkrieg Rating Leaderboard -A straightforward way of storing the entire World of Tanks Blitz ratings leaderboard to disk. +A straightforward way of storing the entire World of Tanks Blitz rating leaderboard to disk. ```cpp -primary BlitzkriegRatingsLeaderboard { +primary BlitzkriegRatingLeaderboard { utf8"BKRL" magic; Header header; primary Body
body; diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md index 733ba8c6b..100e4f3dc 100644 --- a/docs/guide/introduction.md +++ b/docs/guide/introduction.md @@ -18,5 +18,5 @@ Blitzkrieg is available as a Discord bot and can be [installed very easily](/gui Provides a bunch of very helpful tools; https://blitz-krieg.vercel.app/ - Session tracker -- Ratings leaderboard +- Rating leaderboard - Way more to come diff --git a/package.json b/package.json index 27dc9d625..71a4b2aa2 100644 --- a/package.json +++ b/package.json @@ -8,17 +8,16 @@ "build:bot": "vite-node --script scripts/build", "build:website": "next build", "build:docs": "vitepress build docs", - "build:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard", + "build:ratingleaderboard": "vite-node --script scripts/buildRatingLeaderboard", "build:assets": "vite-node --max_old_space_size=16384 --script scripts/buildAssets/index", "build:test": "vite-node --script scripts/buildTest", - "build:ratingsplot": "vite-node --script scripts/buildRatingsPlot", "dev:bot": "yarn build:bot --dev && yarn start:bot", "dev:docs": "vitepress dev docs", "dev:website": "next dev", "start:bot": "node --enable-source-maps dist/bot/index.cjs", "start:website": "next start", "host:docs": "vitepress preview docs", - "publish:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard --publish", + "publish:ratingleaderboard": "vite-node --script scripts/buildRatingLeaderboard --publish", "setup:patches": "vite-node --script scripts/applyPatches", "lint": "npx tsc", "lint:website": "next lint", diff --git a/public/assets/banners/ratings.png b/public/assets/banners/rating.png similarity index 100% rename from public/assets/banners/ratings.png rename to public/assets/banners/rating.png diff --git a/public/assets/banners/ratings.webp b/public/assets/banners/rating.webp similarity index 100% rename from public/assets/banners/ratings.webp rename to public/assets/banners/rating.webp diff --git a/scripts/buildRatingsLeaderboard.ts b/scripts/buildRatingLeaderboard.ts similarity index 88% rename from scripts/buildRatingsLeaderboard.ts rename to scripts/buildRatingLeaderboard.ts index 010a3c87e..710fee84d 100644 --- a/scripts/buildRatingsLeaderboard.ts +++ b/scripts/buildRatingLeaderboard.ts @@ -1,10 +1,10 @@ import { chunk } from 'lodash'; import { argv } from 'process'; import { - RatingsInfo, - RatingsNeighbors, - RatingsPlayer, -} from '../src/commands/ratings'; + RatingInfo, + RatingNeighbors, + RatingPlayer, +} from '../src/commands/ratingLeaderboard'; import { RegionSubdomain } from '../src/constants/regions'; import { getAccountInfo } from '../src/core/blitz/getAccountInfo'; import regionSubdomainToRegion from '../src/core/blitz/regionSubdomainToRegion'; @@ -40,20 +40,20 @@ if (!regionSubdomain) throw new Error('Region parameter not specified'); const region = regionSubdomainToRegion(regionSubdomain); const NEIGHBORS = 2 ** 8; -const PLAYERS = await patientFetchJSON( +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 ratings season running'); + throw new Error('No current rating season running'); } -const players: Record = {}; +const players: Record = {}; const leaderboard: BkrlMinimalEntry[] = []; let registered = 0; async function branchFromPlayer(id: number) { - const { neighbors } = await patientFetchJSON( + const { neighbors } = await patientFetchJSON( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${NEIGHBORS}`, ); @@ -84,7 +84,7 @@ async function branchFromPlayer(id: number) { } async function branchFromLeague(id: number) { - const { result } = await patientFetchJSON<{ result: RatingsPlayer[] }>( + const { result } = await patientFetchJSON<{ result: RatingPlayer[] }>( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${id}/top/`, ); @@ -164,7 +164,7 @@ for (const leaderboardChunk of chunk(leaderboard, 100)) { chunkIndex++; } -const info = await patientFetchJSON( +const info = await patientFetchJSON( `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/season/`, ); const normalizedServer = regionSubdomain === 'na' ? 'com' : regionSubdomain; @@ -180,19 +180,19 @@ leaderboardWriteStream.bkrl({ const changes: FileChange[] = [ { - path: `regions/${normalizedServer}/ratings/${info.current_season}/info.cdon.lz4`, + path: `regions/${normalizedServer}/rating/${info.current_season}/info.cdon.lz4`, content: superCompress(infoContent), encoding: 'base64', }, { - path: `regions/${normalizedServer}/ratings/${info.current_season}/${target}.bkrl`, + path: `regions/${normalizedServer}/rating/${info.current_season}/${target}.bkrl`, content: Buffer.from(leaderboardWriteStream.uint8Array).toString('base64'), encoding: 'base64', }, ]; commitAssets( - `ratings leaderboard ${regionSubdomain} ${target}`, + `rating leaderboard ${regionSubdomain} ${target}`, changes, production, ); diff --git a/scripts/buildRatingsPlot.ts b/scripts/buildRatingsPlot.ts deleted file mode 100644 index aac576f4d..000000000 --- a/scripts/buildRatingsPlot.ts +++ /dev/null @@ -1,48 +0,0 @@ -// what the hell is this script??? - -import { writeFile } from 'fs/promises'; -import { Region } from '../src/constants/regions'; -import { getRatingsNeighbors } from '../src/core/blitz/getRatingsNeighbors'; - -const REGION: Region = 'com'; -const PLAYER = 1041988373; -const ITERATIONS = 2 ** 4; -const MAX_NEIGHBORS = 2 ** 7; -const data: [number, number][] = []; -let finished = 0; - -for (let index = 0; index < ITERATIONS; index++) { - const neighbors = Math.round((Math.random() * MAX_NEIGHBORS) / 2) * 2; - const start = Date.now(); - - console.log(`🟡 Iteration ${index} with ${neighbors} neighbors`); - - try { - await getRatingsNeighbors(REGION, PLAYER, neighbors); - - finished++; - console.log( - `🟢 (${Math.round( - 100 * (finished / ITERATIONS), - )}%) Iteration ${index} with ${neighbors} neighbors`, - ); - - const end = Date.now(); - const time = end - start; - - data.push([neighbors, time]); - } catch (error) { - console.log( - `🔴 Iteration ${index} with ${neighbors} neighbors\n\n${error}`, - ); - } -} - -console.log('🟢 Done, sorting'); -data.sort(([neighborsA], [neighborsB]) => neighborsA - neighborsB); - -console.log('🟢 Writing'); -writeFile( - 'dist/ratingsPlot.csv', - data.map(([neighbors, time]) => `${neighbors},${time}`).join('\n'), -); diff --git a/src/app/api/[region]/ratings/current/info/route.ts b/src/app/api/[region]/rating/current/info/route.ts similarity index 59% rename from src/app/api/[region]/ratings/current/info/route.ts rename to src/app/api/[region]/rating/current/info/route.ts index f7245897c..e464ca6b5 100644 --- a/src/app/api/[region]/ratings/current/info/route.ts +++ b/src/app/api/[region]/rating/current/info/route.ts @@ -1,10 +1,10 @@ import { NextResponse } from 'next/server'; import { Region } from '../../../../../../constants/regions'; -import getRatingsInfo from '../../../../../../core/blitz/getRatingsInfo'; +import getRatingInfo from '../../../../../../core/blitz/getRatingInfo'; export async function GET( request: Request, { params }: { params: { region: Region } }, ) { - return NextResponse.json(await getRatingsInfo(params.region)); + return NextResponse.json(await getRatingInfo(params.region)); } diff --git a/src/app/api/[region]/ratings/current/league/[league]/route.ts b/src/app/api/[region]/rating/current/league/[league]/route.ts similarity index 63% rename from src/app/api/[region]/ratings/current/league/[league]/route.ts rename to src/app/api/[region]/rating/current/league/[league]/route.ts index 9871416e4..e75eb6e09 100644 --- a/src/app/api/[region]/ratings/current/league/[league]/route.ts +++ b/src/app/api/[region]/rating/current/league/[league]/route.ts @@ -1,12 +1,12 @@ import { NextResponse } from 'next/server'; import { Region } from '../../../../../../../constants/regions'; -import { getRatingsLeague } from '../../../../../../../core/blitz/getRatingsLeague'; +import { getRatingLeague } from '../../../../../../../core/blitz/getRatingLeague'; export async function GET( request: Request, { params }: { params: { region: Region; league: number } }, ) { return NextResponse.json( - await getRatingsLeague(params.region, params.league, true), + await getRatingLeague(params.region, params.league, true), ); } diff --git a/src/app/api/[region]/ratings/current/search/[username]/route.ts b/src/app/api/[region]/rating/current/search/[username]/route.ts similarity index 59% rename from src/app/api/[region]/ratings/current/search/[username]/route.ts rename to src/app/api/[region]/rating/current/search/[username]/route.ts index 0f5e51b53..edf3df3dc 100644 --- a/src/app/api/[region]/ratings/current/search/[username]/route.ts +++ b/src/app/api/[region]/rating/current/search/[username]/route.ts @@ -1,12 +1,12 @@ import { NextResponse } from 'next/server'; import { Region } from '../../../../../../../constants/regions'; -import { searchCurrentRatingsPlayers } from '../../../../../../../core/blitz/searchCurrentRatingsPlayers'; +import { searchCurrentRatingPlayers } from '../../../../../../../core/blitz/searchCurrentRatingPlayers'; export async function GET( request: Request, { params }: { params: { region: Region; username: string } }, ) { return NextResponse.json( - await searchCurrentRatingsPlayers(params.region, params.username), + await searchCurrentRatingPlayers(params.region, params.username), ); } diff --git a/src/app/tools/ratings/page.tsx b/src/app/tools/ratings/page.tsx index 555bb9f8d..c910ad0f6 100644 --- a/src/app/tools/ratings/page.tsx +++ b/src/app/tools/ratings/page.tsx @@ -15,11 +15,11 @@ import { produce } from 'immer'; import { debounce, range } from 'lodash'; import { ChangeEvent, useEffect, useRef, useState } from 'react'; import { create } from 'zustand'; -import { RatingsInfo, RatingsPlayer } from '../../../commands/ratings'; +import { RatingInfo, RatingPlayer } from '../../../commands/ratingLeaderboard'; import PageWrapper from '../../../components/PageWrapper'; import { Skeleton } from '../../../components/Skeleton'; import { LEAGUES } from '../../../constants/leagues'; -import { FIRST_ARCHIVED_RATINGS_SEASON } from '../../../constants/ratings'; +import { FIRST_ARCHIVED_RATING_SEASON } from '../../../constants/rating'; import { REGIONS, Region, @@ -28,17 +28,17 @@ import { import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID'; import fetchBlitz from '../../../core/blitz/fetchBlitz'; import { getClanAccountInfo } from '../../../core/blitz/getClanAccountInfo'; -import getRatingsInfo from '../../../core/blitz/getRatingsInfo'; -import { getRatingsLeague } from '../../../core/blitz/getRatingsLeague'; -import { getRatingsNeighbors } from '../../../core/blitz/getRatingsNeighbors'; -import { searchCurrentRatingsPlayers } from '../../../core/blitz/searchCurrentRatingsPlayers'; +import getRatingInfo from '../../../core/blitz/getRatingInfo'; +import { getRatingLeague } from '../../../core/blitz/getRatingLeague'; +import { getRatingNeighbors } from '../../../core/blitz/getRatingNeighbors'; +import { searchCurrentRatingPlayers } from '../../../core/blitz/searchCurrentRatingPlayers'; import { AccountList, AccountListItem, } from '../../../core/blitz/searchPlayersAcrossRegions'; import { getArchivedLatestSeasonNumber } from '../../../core/blitzkrieg/getArchivedLatestSeasonNumber'; -import getArchivedRatingsInfo from '../../../core/blitzkrieg/getArchivedRatingsInfo'; -import { getArchivedRatingsLeaderboard } from '../../../core/blitzkrieg/getArchivedRatingsLeaderboard'; +import getArchivedRatingInfo from '../../../core/blitzkrieg/getArchivedRatingInfo'; +import { getArchivedRatingLeaderboard } from '../../../core/blitzkrieg/getArchivedRatingLeaderboard'; import { theme } from '../../../stitches.config'; import { PageTurner } from './components/PageTurner'; @@ -51,7 +51,7 @@ interface NameCacheEntry { } type NameCache = Record>; type LeaderboardCache = Record>>; -type InfoCache = Record>; +type InfoCache = Record>; type ScoreCache = Record>>; const useNameCache = create(() => ({ @@ -93,7 +93,7 @@ export default function Page() { { type: 'id'; id: number } | { type: 'position'; position: number } | null >(null); - const ratingsInfo = useInfoCache()[region][season]; + const ratingInfo = useInfoCache()[region][season]; const [latestArchivedSeasonNumber, setLatestArchivedSeasonNumber] = useState< number | null >(null); @@ -111,7 +111,7 @@ export default function Page() { }); const pages = Math.ceil( - (ratingsInfo && ratingsInfo.detail === undefined ? ratingsInfo?.count : 1) / + (ratingInfo && ratingInfo.detail === undefined ? ratingInfo?.count : 1) / ROWS_PER_PAGE, ); @@ -122,8 +122,8 @@ export default function Page() { (async () => { const newInfo = season === 0 - ? await getRatingsInfo(region) - : await getArchivedRatingsInfo(region, season); + ? await getRatingInfo(region) + : await getArchivedRatingInfo(region, season); useInfoCache.setState( produce((draft: InfoCache) => { draft[region][season] = newInfo; @@ -140,7 +140,7 @@ export default function Page() { const results = ( await Promise.all( range(0, 5).map(async (league: number) => { - const data = await getRatingsLeague(region, league); + const data = await getRatingLeague(region, league); leaguePositionCache.current[region][league] = data[0].number - 1; return data; @@ -178,12 +178,12 @@ export default function Page() { ); })(); } else { - getArchivedRatingsLeaderboard(region, season).then((results) => { + getArchivedRatingLeaderboard(region, season).then((results) => { useLeaderboardCache.setState( produce((draft: LeaderboardCache) => { if (!(season in draft[region])) draft[region][season] = {}; - results.forEach((result, index) => { + results.entries.forEach((result, index) => { draft[region][season][index] = result.id; }); }), @@ -192,7 +192,7 @@ export default function Page() { produce((draft: ScoreCache) => { if (!(season in draft[region])) draft[region][season] = {}; - results.forEach((result) => { + results.entries.forEach((result) => { draft[region][season][result.id] = result.score; }); }), @@ -213,7 +213,7 @@ export default function Page() { }, [leaderboard[region]?.[season] && 0 in leaderboard[region][season]]); // neighboring page caching for all seasons useEffect(() => { - if (!ratingsInfo || ratingsInfo?.detail) return; + if (!ratingInfo || ratingInfo?.detail) return; if (season === 0) { const middleIndex = page * ROWS_PER_PAGE + ROWS_PER_PAGE / 2; @@ -236,7 +236,7 @@ export default function Page() { const ids = range( Math.max(0, (page - 1) * ROWS_PER_PAGE), Math.min( - ratingsInfo.count - 1, + ratingInfo.count - 1, (page + 1) * ROWS_PER_PAGE + ROWS_PER_PAGE, ), ) @@ -265,7 +265,7 @@ export default function Page() { page, region, season, - ratingsInfo?.detail, + ratingInfo?.detail, leaderboard[region][season] !== undefined && 0 in leaderboard[region][season], ]); @@ -279,7 +279,7 @@ export default function Page() { async function cacheNeighbors( id: number, size: number, - ): Promise { + ): Promise { const radius = Math.round(size / 2); const targetPosition = Object.entries(leaderboard[region][season]).find( ([, player]) => id === player, @@ -303,7 +303,7 @@ export default function Page() { } } - const { neighbors } = await getRatingsNeighbors(region, id, size, true); + const { neighbors } = await getRatingNeighbors(region, id, size, true); useLeaderboardCache.setState( produce((draft: LeaderboardCache) => { @@ -341,11 +341,11 @@ export default function Page() { return neighbors; } async function jumpToPosition(position: number) { - if (!ratingsInfo || ratingsInfo?.detail) return; + if (!ratingInfo || ratingInfo?.detail) return; const targetPosition = Math.min( - ratingsInfo.count - 1, - Math.max(0, Math.min(position, ratingsInfo.count - 1)), + ratingInfo.count - 1, + Math.max(0, Math.min(position, ratingInfo.count - 1)), ); if (season === 0) { @@ -378,7 +378,7 @@ export default function Page() { const loadingDirection = Math.sign(targetPosition - closestPosition); let seedingPlayer = closestPlayerId as number; let targetPositionAcquired = false; - let neighbors: RatingsPlayer[] | null; + let neighbors: RatingPlayer[] | null; setLoadingProgress([0, closestPositionDistance]); @@ -423,7 +423,7 @@ export default function Page() { } } async function jumpToScore() { - if (!ratingsInfo || ratingsInfo?.detail) return; + if (!ratingInfo || ratingInfo?.detail) return; const targetScore = scoreInput.current!.valueAsNumber; @@ -522,7 +522,7 @@ export default function Page() { } else { let playerIndex = -1; - for (let index = ratingsInfo.count; index >= 0; index--) { + for (let index = ratingInfo.count; index >= 0; index--) { if (!(index in leaderboard[region][season])) continue; if (scores[leaderboard[region][season][index]] >= targetScore) { @@ -545,7 +545,7 @@ export default function Page() { } const totalPlayers = - !ratingsInfo?.detail && ratingsInfo?.count ? ratingsInfo.count : undefined; + !ratingInfo?.detail && ratingInfo?.count ? ratingInfo.count : undefined; return ( @@ -573,7 +573,7 @@ export default function Page() { - {ratingsInfo?.detail === undefined && ( + {ratingInfo?.detail === undefined && ( Current season )} @@ -582,7 +582,7 @@ export default function Page() { {latestArchivedSeasonNumber && range( - FIRST_ARCHIVED_RATINGS_SEASON, + FIRST_ARCHIVED_RATING_SEASON, latestArchivedSeasonNumber + 1, ) .map((season) => ( @@ -681,8 +681,8 @@ export default function Page() { - {ratingsInfo?.detail === undefined && - ratingsInfo?.leagues.map(({ index, title }) => ( + {ratingInfo?.detail === undefined && + ratingInfo?.leagues.map(({ index, title }) => ( {title} @@ -715,8 +715,8 @@ export default function Page() { } } else { if ( - !ratingsInfo || - ratingsInfo.detail || + !ratingInfo || + ratingInfo.detail || !leaderboard ) return; @@ -727,7 +727,7 @@ export default function Page() { for ( let index = 0; - index < ratingsInfo.count; + index < ratingInfo.count; index++ ) { if ( @@ -779,7 +779,7 @@ export default function Page() { setSearchLoading(true); const accountList = - await searchCurrentRatingsPlayers( + await searchCurrentRatingPlayers( region, trimmedSearch, ); @@ -885,12 +885,12 @@ export default function Page() { id: player.account_id, }); } else { - if (!ratingsInfo || ratingsInfo.detail) return; + if (!ratingInfo || ratingInfo.detail) return; let playerIndex = -1; for ( let index = 0; - index < ratingsInfo.count; + index < ratingInfo.count; index++ ) { if ( @@ -952,7 +952,7 @@ export default function Page() { { const id = leaderboard[region][season]?.[index]; @@ -994,8 +994,8 @@ export default function Page() { : highlightedPlayer.position === index : false; const reward = - !ratingsInfo?.detail && ratingsInfo?.rewards - ? ratingsInfo.rewards.find( + !ratingInfo?.detail && ratingInfo?.rewards + ? ratingInfo.rewards.find( (reward) => reward.from_position <= position && reward.to_position >= position, @@ -1091,7 +1091,7 @@ export default function Page() { ( + async (resolve) => { + const latestArchivedSeasonNumber = await getArchivedLatestSeasonNumber(); + const onGoingSeason = await isOnGoingRatingSeason(); + const seasonNumbers = range( + FIRST_ARCHIVED_RATING_SEASON, + latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1, + ); + + function addOptions(option: SlashCommandSubcommandBuilder) { + const { t, translate } = translator(Locale.EnglishUS); + + return option + .addIntegerOption((option) => + option + .setName(t`bot.commands.rating_leaderboard.options.limit`) + .setNameLocalizations( + localizationObject( + 'bot.commands.rating_leaderboard.options.limit', + ), + ) + .setDescription( + translate( + 'bot.commands.rating_leaderboard.options.limit.description', + [`${DEFAULT_LIMIT}`], + ), + ) + .setDescriptionLocalizations( + localizationObject( + 'bot.commands.rating_leaderboard.options.limit.description', + [`${DEFAULT_LIMIT}`], + ), + ) + .setRequired(false) + .setMinValue(5) + .setMaxValue(30), + ) + .addStringOption((option) => + option + .setName(t`bot.commands.rating_leaderboard.options.season`) + .setNameLocalizations( + localizationObject( + 'bot.commands.rating_leaderboard.options.season', + ), + ) + .setDescription( + translate( + 'bot.commands.rating_leaderboard.options.season.description', + ), + ) + .setDescriptionLocalizations( + localizationObject( + 'bot.commands.rating_leaderboard.options.season.description', + ), + ) + .setRequired(false) + .addChoices( + ...seasonNumbers + .map((number, index) => { + const current = + index === seasonNumbers.length - 1 && onGoingSeason; + + return { + name: current + ? translate( + 'bot.commands.rating_leaderboard.options.season.choices.current', + [`${number}`], + ) + : `${number}`, + value: `${number}`, + name_localizations: current + ? localizationObject( + 'bot.commands.rating_leaderboard.options.season.choices.current', + [`${number}`], + ) + : undefined, + } satisfies APIApplicationCommandOptionChoice; + }) + .reverse(), + ), + ); + } + + resolve({ + inProduction: true, + inPublic: true, + + command: createLocalizedCommand('rating-leaderboard', [ + { + subcommand: 'neighbors', + modify(option: SlashCommandSubcommandBuilder) { + option.addStringOption(addUsernameChoices); + addOptions(option); + }, + }, + { + subcommand: 'league', + modify(option: SlashCommandSubcommandBuilder) { + option + .addStringOption((option) => { + const { t, translate } = translator(Locale.EnglishUS); + + return option + .setName(t`bot.commands.rating_leaderboard.options.league`) + .setNameLocalizations( + localizationObject( + 'bot.commands.rating_leaderboard.options.league', + ), + ) + .setDescription( + translate( + 'bot.commands.rating_leaderboard.options.league.description', + ), + ) + .setDescriptionLocalizations( + localizationObject( + 'bot.commands.rating_leaderboard.options.league.description', + ), + ) + .addChoices( + ...LEAGUES.map( + ({ name }, value) => + ({ + name: translate(`common.leagues.${name}`), + value: `${value}`, + name_localizations: localizationObject( + `common.leagues.${name}`, + ), + }) satisfies APIApplicationCommandOptionChoice, + ), + ) + .setRequired(true); + }) + .addStringOption(addRegionChoices); + addOptions(option); + }, + }, + ]), + + // BIG TODO: title seems to be in english + + async handler(interaction) { + const { t, translate } = translator(interaction.locale); + const subcommand = interaction.options.getSubcommand(true) as + | 'league' + | 'neighbors'; + const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT; + const seasonOption = interaction.options.getString('season'); + const season = seasonOption + ? parseInt(seasonOption) + : onGoingSeason + ? latestArchivedSeasonNumber + 1 + : latestArchivedSeasonNumber; + const isCurrentSeason = season === latestArchivedSeasonNumber + 1; + let titleName: string; + let titleImage: string | undefined; + let titleDescription: string; + let playersAfter: number; + let players: SimplifiedPlayer[] | undefined; + let midnightLeaderboard: BkrlDiscriminatedEntries | null; + let playerId: number | undefined; + let regionRatingInfo: RatingInfo; + + if (subcommand === 'league') { + const leagueIndex = parseInt( + interaction.options.getString('league')!, + ); + const region = interaction.options.getString('region') as Region; + regionRatingInfo = await (isCurrentSeason + ? getRatingInfo(region) + : getArchivedRatingInfo(region, season!)); + + if (regionRatingInfo.detail !== undefined) + return t`bot.commands.rating_leaderboard.body.no_ongoing_season`; + + const result = isCurrentSeason + ? await getRatingLeague(region, leagueIndex).then(async (result) => + 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, + ), + ) + : await getArchivedRatingLeaderboard(region, season!).then( + async (players) => { + const firstPlayerIndex = + leagueIndex === 0 + ? 0 + : players.entries.findIndex( + (player) => + player.score < LEAGUES[leagueIndex - 1].minScore, + ); + const lastPlayerIndex = firstPlayerIndex + limit - 1; + const trimmed = players.entries.slice( + firstPlayerIndex, + lastPlayerIndex + 1, + ); + const clanData = await getClanAccountInfo( + region, + trimmed.map(({ id }) => id), + ['clan'], + ); + + return trimmed.map( + (player, index) => + ({ + id: player.id, + score: player.score, + position: firstPlayerIndex + index + 1, + clan: clanData[player.id]?.clan?.tag, + nickname: + clanData[index]?.account_name ?? + translate( + 'bot.commands.rating_leaderboard.body.deleted_player', + [`${player.id}`], + ), + }) satisfies SimplifiedPlayer, + ); + }, + ); + const leagueInfo = regionRatingInfo.leagues[leagueIndex]; + midnightLeaderboard = isCurrentSeason + ? await getArchivedRatingMidnightLeaderboard( + region, + regionRatingInfo.current_season, + ) + : await getArchivedRatingMidnightLeaderboard(region, season!); + + players = result; + playersAfter = + regionRatingInfo.count - result[result.length - 1].position; + titleName = `${ + leagueInfo + ? translate(`common.leagues.${LEAGUES[leagueInfo.index].name}`) + : '' + } - ${translate(`common.regions.short.${region}`)}`; + titleImage = leagueInfo.big_icon.startsWith('http') + ? leagueInfo.big_icon + : `https:${leagueInfo.big_icon}`; + titleDescription = translate( + 'bot.commands.rating_leaderboard.body.top_players', + [`${limit}`], + ); + } else { + const { region, id } = await resolvePlayerFromCommand(interaction); + regionRatingInfo = isCurrentSeason + ? await getRatingInfo(region) + : await getArchivedRatingInfo(region, season!); + + if (regionRatingInfo.detail !== undefined) + return t`bot.commands.rating_leaderboard.body.no_ongoing_season`; + + const accountInfo = await getAccountInfo(region, id); + const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan; + const neighbors = isCurrentSeason + ? await getRatingNeighbors(region, id, Math.round(limit / 2)).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, + ), + ) + : await getArchivedRatingLeaderboard(region, season!).then( + async (players) => { + const playerIndex = players.entries.findIndex( + (player) => player.id === id, + ); + + if (playerIndex === -1) { + throw new UserError( + translate( + 'bot.commands.rating_leaderboard.body.no_participation', + [markdownEscape(accountInfo.nickname), `${season}`], + ), + ); + } + + const halfRange = Math.round(limit / 2); + const trimmed = players.entries.slice( + playerIndex - halfRange, + playerIndex + halfRange + 1, + ); + const clanData = await getClanAccountInfo( + region, + trimmed.map((player) => player.id), + ['clan'], + ); + + return trimmed.map((player, index) => ({ + id: player.id, + score: player.score, + position: playerIndex + index + 1, + clan: clanData[index]?.clan?.tag, + nickname: clanData[index] + ? clanData[index]!.account_name + : `Deleted ${player.id}`, + })); + }, + ); + midnightLeaderboard = isCurrentSeason + ? await getArchivedRatingMidnightLeaderboard( + region, + regionRatingInfo.current_season, + ) + : await getArchivedRatingMidnightLeaderboard(region, season!); + + players = neighbors; + titleImage = clan + ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` + : undefined; + titleName = neighbors ? accountInfo.nickname : ''; + titleDescription = t`bot.commands.rating_leaderboard.body.neighbors`; + playersAfter = + regionRatingInfo.count - neighbors[neighbors.length - 1].position; + playerId = id; + } + + const items = players.map((player) => { + const midnightIndex = midnightLeaderboard?.entries.findIndex( + (item) => item.id === player.id, + ); + const midnightScore = + midnightIndex === undefined + ? player.score + : midnightLeaderboard!.entries[midnightIndex].score; + const midnightPosition = + midnightIndex === undefined ? player.position : midnightIndex + 1; + + return ( + + ); + }); + + return ( + + + + {items && ( + + {items} + {playersAfter > 0 && ( + + )} + + )} + + ); + }, + + autocomplete: autocompleteUsername, + }); + }, +); diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx deleted file mode 100644 index a3f1942c0..000000000 --- a/src/commands/ratings.tsx +++ /dev/null @@ -1,495 +0,0 @@ -import { - APIApplicationCommandOptionChoice, - Locale, - SlashCommandSubcommandBuilder, -} from 'discord.js'; -import { range } from 'lodash'; -import markdownEscape from 'markdown-escape'; -import CommandWrapper from '../components/CommandWrapper'; -import * as Leaderboard from '../components/Leaderboard'; -import TitleBar from '../components/TitleBar'; -import { LEAGUES } from '../constants/leagues'; -import { FIRST_ARCHIVED_RATINGS_SEASON } from '../constants/ratings'; -import { Region } from '../constants/regions'; -import { getAccountInfo } from '../core/blitz/getAccountInfo'; -import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; -import getRatingsInfo from '../core/blitz/getRatingsInfo'; -import { getRatingsLeague } from '../core/blitz/getRatingsLeague'; -import { getRatingsNeighbors } from '../core/blitz/getRatingsNeighbors'; -import { isOnGoingRatingsSeason } from '../core/blitz/isOnGoingRatingsSeason'; -import { getArchivedLatestSeasonNumber } from '../core/blitzkrieg/getArchivedLatestSeasonNumber'; -import getArchivedRatingsInfo from '../core/blitzkrieg/getArchivedRatingsInfo'; -import { getArchivedRatingsLeaderboard } from '../core/blitzkrieg/getArchivedRatingsLeaderboard'; -import getArchivedRatingsMidnightLeaderboard from '../core/blitzkrieg/getArchivedRatingsMidnightLeaderboard'; -import addRegionChoices from '../core/discord/addRegionChoices'; -import addUsernameChoices from '../core/discord/addUsernameChoices'; -import autocompleteUsername from '../core/discord/autocompleteUsername'; -import { createLocalizedCommand } from '../core/discord/createLocalizedCommand'; -import { localizationObject } from '../core/discord/localizationObject'; -import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; -import { translator } from '../core/localization/translator'; -import { BkrlDiscriminatedEntries } from '../core/streams/bkrl'; -import { CommandRegistry } from '../events/interactionCreate'; -import { UserError } from '../hooks/userError'; - -export interface RatingsPlayer { - spa_id: number; - mmr: number; - season_number: number; - calibrationBattlesLeft: number; - number: number; - percentile: number; - skip: boolean; - updated_at: string; - score: number; - nickname: string; - clan_tag: string; -} - -export interface LeagueTop { - result: RatingsPlayer[]; -} - -export type RatingsReward = { - from_position: number; - to_position: number; - count: number; -} & ( - | { - type: 'vehicle'; - - vehicle: { - id: number; - name: string; - nation: string; - subnation: string; - use_subnation_flag: boolean; - type_slug: string; - level: number; - roman_level: string; - user_string: string; - image_url: string; - preview_url: string; - is_premium: boolean; - is_collectible: boolean; - }; - } - | { - type: 'stuff'; - - stuff: { - name: string; - count: number; - title: string; - image_url: string; - type: string; - - sizes: {}; - }; - } -); - -export interface RatingsLeague { - title: string; - small_icon: string; - big_icon: string; - background: string; - index: number; - percentile: number; -} - -export type RatingsInfo = - | { - detail: undefined; - - title: string; - 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 { - neighbors: RatingsPlayer[]; -} - -interface SimplifiedPlayer { - id: number; - score: number; - position: number; - clan: string | undefined; - nickname: string; -} - -const DEFAULT_LIMIT = 10; - -export const ratingsCommand = new Promise(async (resolve) => { - const latestArchivedSeasonNumber = await getArchivedLatestSeasonNumber(); - const onGoingSeason = await isOnGoingRatingsSeason(); - const seasonNumbers = range( - FIRST_ARCHIVED_RATINGS_SEASON, - latestArchivedSeasonNumber + (onGoingSeason ? 1 : 0) + 1, - ); - - function addOptions(option: SlashCommandSubcommandBuilder) { - const { t, translate } = translator(Locale.EnglishUS); - - return option - .addIntegerOption((option) => - option - .setName(t`bot.commands.ratings.options.limit`) - .setNameLocalizations( - localizationObject('bot.commands.ratings.options.limit'), - ) - .setDescription( - translate('bot.commands.ratings.options.limit.description', [ - `${DEFAULT_LIMIT}`, - ]), - ) - .setDescriptionLocalizations( - localizationObject( - 'bot.commands.ratings.options.limit.description', - [`${DEFAULT_LIMIT}`], - ), - ) - .setRequired(false) - .setMinValue(5) - .setMaxValue(30), - ) - .addStringOption((option) => - option - .setName(t`bot.commands.ratings.options.season`) - .setNameLocalizations( - localizationObject('bot.commands.ratings.options.season'), - ) - .setDescription( - translate('bot.commands.ratings.options.season.description'), - ) - .setDescriptionLocalizations( - localizationObject( - 'bot.commands.ratings.options.season.description', - ), - ) - .setRequired(false) - .addChoices( - ...seasonNumbers - .map((number, index) => { - const current = - index === seasonNumbers.length - 1 && onGoingSeason; - - return { - name: current - ? translate( - 'bot.commands.ratings.options.season.choices.current', - [`${number}`], - ) - : `${number}`, - value: `${number}`, - name_localizations: current - ? localizationObject( - 'bot.commands.ratings.options.season.choices.current', - [`${number}`], - ) - : undefined, - } satisfies APIApplicationCommandOptionChoice; - }) - .reverse(), - ), - ); - } - - resolve({ - inProduction: true, - inPublic: true, - - command: createLocalizedCommand('ratings', [ - { - subcommand: 'neighbors', - modify(option: SlashCommandSubcommandBuilder) { - option.addStringOption(addUsernameChoices); - addOptions(option); - }, - }, - { - subcommand: 'league', - modify(option: SlashCommandSubcommandBuilder) { - option - .addStringOption((option) => { - const { t, translate } = translator(Locale.EnglishUS); - - return option - .setName(t`bot.commands.ratings.options.league`) - .setNameLocalizations( - localizationObject('bot.commands.ratings.options.league'), - ) - .setDescription( - translate('bot.commands.ratings.options.league.description'), - ) - .setDescriptionLocalizations( - localizationObject( - 'bot.commands.ratings.options.league.description', - ), - ) - .addChoices( - ...LEAGUES.map( - ({ name }, value) => - ({ - name: translate(`common.leagues.${name}`), - value: `${value}`, - name_localizations: localizationObject( - `common.leagues.${name}`, - ), - }) satisfies APIApplicationCommandOptionChoice, - ), - ) - .setRequired(true); - }) - .addStringOption(addRegionChoices); - addOptions(option); - }, - }, - ]), - - // BIG TODO: title seems to be in english - - async handler(interaction) { - const { t, translate } = translator(interaction.locale); - const subcommand = interaction.options.getSubcommand(true) as - | 'league' - | 'neighbors'; - const limit = interaction.options.getInteger('limit') ?? DEFAULT_LIMIT; - const seasonOption = interaction.options.getString('season'); - const season = seasonOption - ? parseInt(seasonOption) - : onGoingSeason - ? latestArchivedSeasonNumber + 1 - : latestArchivedSeasonNumber; - const isCurrentSeason = season === latestArchivedSeasonNumber + 1; - let titleName: string; - let titleImage: string | undefined; - let titleDescription: string; - let playersAfter: number; - let players: SimplifiedPlayer[] | undefined; - let midnightLeaderboard: BkrlDiscriminatedEntries | null; - let playerId: number | undefined; - let regionRatingsInfo: RatingsInfo; - - if (subcommand === 'league') { - const leagueIndex = parseInt(interaction.options.getString('league')!); - const region = interaction.options.getString('region') as Region; - regionRatingsInfo = await (isCurrentSeason - ? getRatingsInfo(region) - : getArchivedRatingsInfo(region, season!)); - - if (regionRatingsInfo.detail !== undefined) - return t`bot.commands.ratings.body.no_ongoing_season`; - - const result = isCurrentSeason - ? await getRatingsLeague(region, leagueIndex).then(async (result) => - 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, - ), - ) - : await getArchivedRatingsLeaderboard(region, season!).then( - async (players) => { - const firstPlayerIndex = - leagueIndex === 0 - ? 0 - : players.entries.findIndex( - (player) => - player.score < LEAGUES[leagueIndex - 1].minScore, - ); - const lastPlayerIndex = firstPlayerIndex + limit - 1; - const trimmed = players.entries.slice( - firstPlayerIndex, - lastPlayerIndex + 1, - ); - const clanData = await getClanAccountInfo( - region, - trimmed.map(({ id }) => id), - ['clan'], - ); - - return trimmed.map( - (player, index) => - ({ - id: player.id, - score: player.score, - position: firstPlayerIndex + index + 1, - clan: clanData[player.id]?.clan?.tag, - nickname: - clanData[index]?.account_name ?? - translate('bot.commands.ratings.body.deleted_player', [ - `${player.id}`, - ]), - }) satisfies SimplifiedPlayer, - ); - }, - ); - const leagueInfo = regionRatingsInfo.leagues[leagueIndex]; - midnightLeaderboard = isCurrentSeason - ? await getArchivedRatingsMidnightLeaderboard( - region, - regionRatingsInfo.current_season, - ) - : await getArchivedRatingsMidnightLeaderboard(region, season!); - - players = result; - playersAfter = - regionRatingsInfo.count - result[result.length - 1].position; - titleName = `${ - leagueInfo - ? translate(`common.leagues.${LEAGUES[leagueInfo.index].name}`) - : '' - } - ${translate(`common.regions.short.${region}`)}`; - titleImage = leagueInfo.big_icon.startsWith('http') - ? leagueInfo.big_icon - : `https:${leagueInfo.big_icon}`; - titleDescription = translate('bot.commands.ratings.body.top_players', [ - `${limit}`, - ]); - } else { - const { region, id } = await resolvePlayerFromCommand(interaction); - regionRatingsInfo = isCurrentSeason - ? await getRatingsInfo(region) - : await getArchivedRatingsInfo(region, season!); - - if (regionRatingsInfo.detail !== undefined) - return t`bot.commands.ratings.body.no_ongoing_season`; - - const accountInfo = await getAccountInfo(region, id); - const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan; - const neighbors = isCurrentSeason - ? await getRatingsNeighbors(region, id, Math.round(limit / 2)).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, - ), - ) - : await getArchivedRatingsLeaderboard(region, season!).then( - async (players) => { - const playerIndex = players.entries.findIndex( - (player) => player.id === id, - ); - - if (playerIndex === -1) { - throw new UserError( - translate('bot.commands.ratings.body.no_participation', [ - markdownEscape(accountInfo.nickname), - `${season}`, - ]), - ); - } - - const halfRange = Math.round(limit / 2); - const trimmed = players.entries.slice( - playerIndex - halfRange, - playerIndex + halfRange + 1, - ); - const clanData = await getClanAccountInfo( - region, - trimmed.map((player) => player.id), - ['clan'], - ); - - return trimmed.map((player, index) => ({ - id: player.id, - score: player.score, - position: playerIndex + index + 1, - clan: clanData[index]?.clan?.tag, - nickname: clanData[index] - ? clanData[index]!.account_name - : `Deleted ${player.id}`, - })); - }, - ); - midnightLeaderboard = isCurrentSeason - ? await getArchivedRatingsMidnightLeaderboard( - region, - regionRatingsInfo.current_season, - ) - : await getArchivedRatingsMidnightLeaderboard(region, season!); - - players = neighbors; - titleImage = clan - ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` - : undefined; - titleName = neighbors ? accountInfo.nickname : ''; - titleDescription = t`bot.commands.ratings.body.neighbors`; - playersAfter = - regionRatingsInfo.count - neighbors[neighbors.length - 1].position; - playerId = id; - } - - const items = players.map((player) => { - const midnightIndex = midnightLeaderboard?.entries.findIndex( - (item) => item.id === player.id, - ); - const midnightScore = - midnightIndex === undefined - ? player.score - : midnightLeaderboard!.entries[midnightIndex].score; - const midnightPosition = - midnightIndex === undefined ? player.position : midnightIndex + 1; - - return ( - - ); - }); - - return ( - - - - {items && ( - - {items} - {playersAfter > 0 && ( - - )} - - )} - - ); - }, - - autocomplete: autocompleteUsername, - }); -}); diff --git a/src/components/Leaderboard/components/Item.tsx b/src/components/Leaderboard/components/Item.tsx index d0767b348..08e701e7d 100644 --- a/src/components/Leaderboard/components/Item.tsx +++ b/src/components/Leaderboard/components/Item.tsx @@ -154,6 +154,7 @@ export function Item({ } /> )} + {score !== undefined && ( > = {}; +const cache: Partial> = {}; -export default async function getRatingsInfo(region: Region) { - if (ratingsInfoCache[region]) return ratingsInfoCache[region]!; +export default async function getRatingInfo(region: Region) { + if (cache[region]) return cache[region]!; const response = await patientFetch( context === 'website' - ? `/api/${region}/ratings/current/info` + ? `/api/${region}/rating/current/info` : `https://${regionToRegionSubdomain( region, )}.wotblitz.com/en/api/rating-leaderboards/season/`, undefined, { cache: 'no-store' }, ); - const data = (await response.json()) as RatingsInfo; + const data = (await response.json()) as RatingInfo; - ratingsInfoCache[region] = data; + cache[region] = data; return data; } diff --git a/src/core/blitz/getRatingsLeague.ts b/src/core/blitz/getRatingLeague.ts similarity index 78% rename from src/core/blitz/getRatingsLeague.ts rename to src/core/blitz/getRatingLeague.ts index 6779cc78b..d3ecd7ea5 100644 --- a/src/core/blitz/getRatingsLeague.ts +++ b/src/core/blitz/getRatingLeague.ts @@ -1,10 +1,10 @@ -import { LeagueTop, RatingsPlayer } from '../../commands/ratings'; +import { LeagueTop, RatingPlayer } from '../../commands/ratingLeaderboard'; import { Region } from '../../constants/regions'; import { context } from '../blitzkrieg/context'; import { patientFetch } from '../blitzkrieg/patientFetch'; import regionToRegionSubdomain from './regionToRegionSubdomain'; -export async function getRatingsLeague( +export async function getRatingLeague( region: Region, league: number, usePatientFetcher = false, @@ -15,7 +15,7 @@ export async function getRatingsLeague( : fetch(url, { cache: 'no-store' }); const response = await fetcher( context === 'website' - ? `/api/${region}/ratings/current/league/${league}` + ? `/api/${region}/rating/current/league/${league}` : `https://${regionToRegionSubdomain( region, )}.wotblitz.com/en/api/rating-leaderboards/league/${league}/top/`, @@ -23,6 +23,6 @@ export async function getRatingsLeague( const json = await response.json(); return context === 'website' - ? (json as RatingsPlayer[]) + ? (json as RatingPlayer[]) : (json as LeagueTop).result; } diff --git a/src/core/blitz/getRatingsNeighbors.ts b/src/core/blitz/getRatingNeighbors.ts similarity index 80% rename from src/core/blitz/getRatingsNeighbors.ts rename to src/core/blitz/getRatingNeighbors.ts index 2cd33aab9..1f747820c 100644 --- a/src/core/blitz/getRatingsNeighbors.ts +++ b/src/core/blitz/getRatingNeighbors.ts @@ -1,10 +1,10 @@ -import { RatingsNeighbors } from '../../commands/ratings'; +import { RatingNeighbors } from '../../commands/ratingLeaderboard'; import { Region } from '../../constants/regions'; import { patientFetch } from '../blitzkrieg/patientFetch'; import { withCORSProxy } from '../blitzkrieg/withCORSProxy'; import regionToRegionSubdomain from './regionToRegionSubdomain'; -export async function getRatingsNeighbors( +export async function getRatingNeighbors( region: Region, id: number, count: number, @@ -21,7 +21,7 @@ export async function getRatingsNeighbors( )}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${count}`, ), ); - const json = (await response.json()) as RatingsNeighbors; + const json = (await response.json()) as RatingNeighbors; return json; } diff --git a/src/core/blitz/isOnGoingRatingSeason.ts b/src/core/blitz/isOnGoingRatingSeason.ts new file mode 100644 index 000000000..14efb9b42 --- /dev/null +++ b/src/core/blitz/isOnGoingRatingSeason.ts @@ -0,0 +1,6 @@ +import getRatingInfo from './getRatingInfo'; + +export async function isOnGoingRatingSeason() { + const ratingInfo = await getRatingInfo('com'); + return ratingInfo.detail === undefined; +} diff --git a/src/core/blitz/isOnGoingRatingsSeason.ts b/src/core/blitz/isOnGoingRatingsSeason.ts deleted file mode 100644 index ef6f1e2d5..000000000 --- a/src/core/blitz/isOnGoingRatingsSeason.ts +++ /dev/null @@ -1,6 +0,0 @@ -import getRatingsInfo from './getRatingsInfo'; - -export async function isOnGoingRatingsSeason() { - const ratingsInfo = await getRatingsInfo('com'); - return ratingsInfo.detail === undefined; -} diff --git a/src/core/blitz/searchCurrentRatingsPlayers.ts b/src/core/blitz/searchCurrentRatingPlayers.ts similarity index 74% rename from src/core/blitz/searchCurrentRatingsPlayers.ts rename to src/core/blitz/searchCurrentRatingPlayers.ts index 4be3a943c..55b3c7d83 100644 --- a/src/core/blitz/searchCurrentRatingsPlayers.ts +++ b/src/core/blitz/searchCurrentRatingPlayers.ts @@ -2,7 +2,7 @@ import { Region } from '../../constants/regions'; import { context } from '../blitzkrieg/context'; import regionToRegionSubdomain from './regionToRegionSubdomain'; -type CurrentRatingsPlayerSearch = Record< +type CurrentRatingPlayerSearch = Record< number, { spa_id: number; @@ -24,20 +24,17 @@ type CurrentRatingsPlayerSearch = Record< ) >; -export async function searchCurrentRatingsPlayers( - region: Region, - name: string, -) { +export async function searchCurrentRatingPlayers(region: Region, name: string) { const encodedSearch = encodeURIComponent(name); const response = await fetch( context === 'website' - ? `/api/${region}/ratings/current/search/${encodedSearch}` + ? `/api/${region}/rating/current/search/${encodedSearch}` : `https://${regionToRegionSubdomain( region, )}.wotblitz.com/en/api/rating-leaderboards/search/?prefix=${encodedSearch}`, { cache: 'no-store' }, ); - const accountList = (await response.json()) as CurrentRatingsPlayerSearch; + const accountList = (await response.json()) as CurrentRatingPlayerSearch; return accountList; } diff --git a/src/core/blitzkrieg/getArchivedLatestSeasonNumber.ts b/src/core/blitzkrieg/getArchivedLatestSeasonNumber.ts index 3e7cca876..22216d380 100644 --- a/src/core/blitzkrieg/getArchivedLatestSeasonNumber.ts +++ b/src/core/blitzkrieg/getArchivedLatestSeasonNumber.ts @@ -1,4 +1,4 @@ -import getRatingsInfo from '../blitz/getRatingsInfo'; +import getRatingInfo from '../blitz/getRatingInfo'; interface GitHubTrees { tree: { path: string; url: string }[]; @@ -11,9 +11,9 @@ export async function getArchivedLatestSeasonNumber() { return cachedLatestArchivedSeasonNumber; } - const ratingsInfo = await getRatingsInfo('com'); + const ratingInfo = await getRatingInfo('com'); - if (ratingsInfo.detail) { + if (ratingInfo.detail) { const regionsURL = ( (await fetch( 'https://api.github.com/repos/tresabhi/blitzkrieg-assets/git/trees/main', @@ -24,14 +24,14 @@ export async function getArchivedLatestSeasonNumber() { response.json(), )) as GitHubTrees ).tree.find(({ path }) => path === 'com')!.url; - const ratingsURL = ( + const ratingURL = ( (await fetch(comURL).then((response) => response.json())) as { tree: [{ url: string }]; } ).tree[0].url; const latestSeason = Math.max( ...( - (await fetch(ratingsURL).then((response) => response.json())) as { + (await fetch(ratingURL).then((response) => response.json())) as { tree: { path: string }[]; } ).tree.map(({ path }) => Number(path)), @@ -39,7 +39,7 @@ export async function getArchivedLatestSeasonNumber() { cachedLatestArchivedSeasonNumber = latestSeason; } else { - cachedLatestArchivedSeasonNumber = ratingsInfo.current_season - 1; + cachedLatestArchivedSeasonNumber = ratingInfo.current_season - 1; } return cachedLatestArchivedSeasonNumber; diff --git a/src/core/blitzkrieg/getArchivedRatingsInfo.ts b/src/core/blitzkrieg/getArchivedRatingInfo.ts similarity index 68% rename from src/core/blitzkrieg/getArchivedRatingsInfo.ts rename to src/core/blitzkrieg/getArchivedRatingInfo.ts index ab25d4308..cfe85020e 100644 --- a/src/core/blitzkrieg/getArchivedRatingsInfo.ts +++ b/src/core/blitzkrieg/getArchivedRatingInfo.ts @@ -1,4 +1,4 @@ -import { RatingsInfo } from '../../commands/ratings'; +import { RatingInfo } from '../../commands/ratingLeaderboard'; import { Region } from '../../constants/regions'; import { asset } from './asset'; import { superDecompress } from './superDecompress'; @@ -7,7 +7,7 @@ const cache: Record< Region, Record< number, - RatingsInfo & { + RatingInfo & { detail: undefined; } > @@ -17,17 +17,17 @@ const cache: Record< asia: {}, }; -export default async function getArchivedRatingsInfo( +export default async function getArchivedRatingInfo( region: Region, season: number, ) { if (!cache[region][season]) { const info = await fetch( - asset(`regions/${region}/ratings/${season}/info.cdon.lz4`), + asset(`regions/${region}/rating/${season}/info.cdon.lz4`), ) .then((response) => response.arrayBuffer()) .then((buffer) => - superDecompress( + superDecompress( new Uint8Array(buffer), ), ); diff --git a/src/core/blitzkrieg/getArchivedRatingsLeaderboard.ts b/src/core/blitzkrieg/getArchivedRatingLeaderboard.ts similarity index 82% rename from src/core/blitzkrieg/getArchivedRatingsLeaderboard.ts rename to src/core/blitzkrieg/getArchivedRatingLeaderboard.ts index 58e096afa..be9a5278f 100644 --- a/src/core/blitzkrieg/getArchivedRatingsLeaderboard.ts +++ b/src/core/blitzkrieg/getArchivedRatingLeaderboard.ts @@ -8,13 +8,13 @@ const cache: Record> = { com: {}, }; -export async function getArchivedRatingsLeaderboard( +export async function getArchivedRatingLeaderboard( region: Region, season: number, ) { if (!cache[region][season]) { const info = await fetch( - asset(`regions/${region}/ratings/${season}/latest.bkrl`), + asset(`regions/${region}/rating/${season}/latest.bkrl`), ) .then((response) => response.arrayBuffer()) .then((buffer) => new BkrlReadStream(buffer).bkrl()); diff --git a/src/core/blitzkrieg/getArchivedRatingsMidnightLeaderboard.ts b/src/core/blitzkrieg/getArchivedRatingMidnightLeaderboard.ts similarity index 82% rename from src/core/blitzkrieg/getArchivedRatingsMidnightLeaderboard.ts rename to src/core/blitzkrieg/getArchivedRatingMidnightLeaderboard.ts index 0a611eb65..2413c7396 100644 --- a/src/core/blitzkrieg/getArchivedRatingsMidnightLeaderboard.ts +++ b/src/core/blitzkrieg/getArchivedRatingMidnightLeaderboard.ts @@ -8,13 +8,13 @@ const cache: Record> = { asia: {}, }; -export default async function getArchivedRatingsMidnightLeaderboard( +export default async function getArchivedRatingMidnightLeaderboard( region: Region, season: number, ) { if (!cache[region][season]) { const info = await fetch( - asset(`regions/${region}/ratings/${season}/midnight.bkrl`), + asset(`regions/${region}/rating/${season}/midnight.bkrl`), ) .then((response) => response.status === 404 ? null : response.arrayBuffer(), diff --git a/src/core/blitzstars/getStatsInPeriod/index.ts b/src/core/blitzstars/getStatsInPeriod/index.ts index 650bb40ac..38b9afd6b 100644 --- a/src/core/blitzstars/getStatsInPeriod/index.ts +++ b/src/core/blitzstars/getStatsInPeriod/index.ts @@ -69,7 +69,7 @@ export default async function getStatsInPeriod( return get(latest.all) - get(previous.all); } - // check if there was a change in battles as games in ratings do update last_battle_time + // check if there was a change in battles as games in rating do update last_battle_time if (d((a) => a.battles) > 0) { diff[tankId] = { battles: d((a) => a.battles), diff --git a/src/events/interactionCreate/index.ts b/src/events/interactionCreate/index.ts index 4b22d1fb4..b0f2a58a8 100644 --- a/src/events/interactionCreate/index.ts +++ b/src/events/interactionCreate/index.ts @@ -25,7 +25,7 @@ import { ownedTanksCommand } from '../../commands/ownedTanks'; import { permissionsCommand } from '../../commands/permissions'; import { pingCommand } from '../../commands/ping'; import { playerInfoCommand } from '../../commands/playerInfo'; -import { ratingsCommand } from '../../commands/ratings'; +import { ratingLeaderboardCommand } from '../../commands/ratingLeaderboard'; import { researchCommand } from '../../commands/research'; import { searchClansCommand } from '../../commands/searchClans'; import { searchPlayersCommand } from '../../commands/searchPlayers'; @@ -93,7 +93,7 @@ export const COMMANDS_RAW: Promise[] = [ pingCommand, evolutionCommand, statsCommand, - ratingsCommand, + ratingLeaderboardCommand, todayCommand, researchCommand, testCommand, diff --git a/src/lang/en-US.json b/src/lang/en-US.json index 0f7ef52ac..a2fdaae3c 100644 --- a/src/lang/en-US.json +++ b/src/lang/en-US.json @@ -414,9 +414,9 @@ } }, - "ratings": { - "$": "ratings", - "description": "Ratings battles statistics", + "rating_leaderboard": { + "$": "rating-leaderboard", + "description": "Rating battles statistics", "subcommands": { "neighbors": { @@ -453,10 +453,10 @@ "body": { "top_players": "Top %s players", - "no_participation": "# %s didn't play ratings in season %s\n\nThis player did not participate in this season or did not get past calibration.", - "no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running ratings season.", + "no_participation": "# %s didn't play rating in season %s\n\nThis player did not participate in this season or did not get past calibration.", + "no_ongoing_season": "# No ongoing season\n\nThere isn't a currently running rating season.", "deleted_player": "Deleted %s", - "neighbors": "Ratings neighbors", + "neighbors": "Rating neighbors", "more": "+ %s more" } },