diff --git a/.vscode/settings.json b/.vscode/settings.json
index b89dc3f62..45f0cf251 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -13,9 +13,11 @@
"Everytime",
"fullstats",
"fuzzysort",
+ "immer",
"kranvagn",
"Leaderboard",
"leaderboards",
+ "MURICA",
"Oopsy",
"outdir",
"outfile",
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 3a60f46c9..08880bb3e 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -13,7 +13,7 @@ export default defineConfig({
themeConfig: {
nav: [
{ text: 'Home', link: '/' },
- { text: 'Guide', link: '/guide/about' },
+ { text: 'Guide', link: '/guide/introduction' },
],
sidebar: [
diff --git a/docs/changelogs/1.15.1.md b/docs/changelogs/1.15.1.md
new file mode 100644
index 000000000..15772a353
--- /dev/null
+++ b/docs/changelogs/1.15.1.md
@@ -0,0 +1,24 @@
+# Blitzkrieg 1.15.0
+
+'MURICA!
+
+## Changes
+
+- "Neighbor" is now spelled in the American way
+- Updated documentation website with the latest information
+
+## Fixes
+
+- Fixed the guide link on the documentation website
+
+## Technical features
+
+- Added capability to fetch patiently while also parsing the JSON
+
+## Technical changes
+
+- Removed neighbor relative position caching
+
+## Technical fixes
+
+- `/ratings` now uses the non-caching APIs
diff --git a/docs/guide/install.md b/docs/guide/install.md
deleted file mode 100644
index 91e0a3ec2..000000000
--- a/docs/guide/install.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Installing Blitzkrieg
-
-It's easy as 1, 2, 3!
-
-## Step 1
-
-Join a server that already has Blitzkrieg or [join our official Discord server](https://discord.gg/nDt7AjGJQH).
-
-## Step 2
-
-View the bot's profile and click "Add to Server".
-
-
-
-## Step 3
-
-Review the permissions the bot is asking for and add it to your server!
diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md
index 80e2f87a3..733ba8c6b 100644
--- a/docs/guide/introduction.md
+++ b/docs/guide/introduction.md
@@ -1,6 +1,10 @@
# Introduction
-Blitzkrieg is a multipurpose bot for anything and everything World of Tanks Blitz.
+🎉 Services for everything and anything World of Tanks Blitz
+
+## Bot
+
+Blitzkrieg is available as a Discord bot and can be [installed very easily](/guide/invite).
- Track statistics
- Find inactive players in a clan
@@ -8,3 +12,11 @@ Blitzkrieg is a multipurpose bot for anything and everything World of Tanks Blit
- Create embeds for streams
- Track a player/tanks statistical evolution
- ...and a variety of other similar things
+
+## Website
+
+Provides a bunch of very helpful tools; https://blitz-krieg.vercel.app/
+
+- Session tracker
+- Ratings leaderboard
+- Way more to come
diff --git a/docs/index.md b/docs/index.md
index bcb10e1f0..22e3f283d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -12,10 +12,10 @@ hero:
actions:
- theme: brand
text: Add to Discord Server
- link: guide/install
+ link: /guide/invite
- theme: alt
text: Guide
- link: /guide/about
+ link: /guide/introduction
features:
- title: Clean and Efficient UI
diff --git a/scripts/buildRatingsLeaderboard.ts b/scripts/buildRatingsLeaderboard.ts
index a2b2fe43f..564bffb66 100644
--- a/scripts/buildRatingsLeaderboard.ts
+++ b/scripts/buildRatingsLeaderboard.ts
@@ -6,7 +6,7 @@ import {
RatingsNeighbors,
RatingsPlayer,
} from '../src/commands/ratings';
-import { patientFetch } from '../src/core/blitzkrieg/patientFetch';
+import { patientFetchJSON } from '../src/core/blitzkrieg/patientFetchJSON';
import commitMultipleFiles from './commitMultipleFiles.js';
/*
@@ -29,11 +29,9 @@ const server = argv.find((arg) => arg.startsWith('--region='))?.split('=')[1];
if (!server) throw new Error('Region parameter not specified');
const NEIGHBORS = 1024;
-const PLAYERS = await patientFetch(
+const PLAYERS = await patientFetchJSON(
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
-)
- .then((response) => response.json() as Promise)
- .then((data) => (data.detail === undefined ? data.count : undefined));
+).then((data) => (data.detail === undefined ? data.count : undefined));
if (PLAYERS === undefined) {
throw new Error('No current ratings season running');
@@ -44,9 +42,9 @@ const leaderboard: BlitzkriegRatingsLeaderboard = [];
let registered = 0;
async function branchFromPlayer(id: number) {
- const { neighbors } = await patientFetch(
+ const { neighbors } = await patientFetchJSON(
`https://${server}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${NEIGHBORS}`,
- ).then((response) => response.json() as Promise);
+ );
const firstPlayer = neighbors[0];
const lastPlayer = neighbors[neighbors.length - 1];
@@ -75,9 +73,9 @@ async function branchFromPlayer(id: number) {
}
async function branchFromLeague(id: number) {
- const { result } = await patientFetch(
+ const { result } = await patientFetchJSON<{ result: RatingsPlayer[] }>(
`https://${server}.wotblitz.com/en/api/rating-leaderboards/league/${id}/top/`,
- ).then((response) => response.json() as Promise<{ result: RatingsPlayer[] }>);
+ );
const firstPlayer = result[0];
const lastPlayer = result[result.length - 1];
@@ -113,11 +111,8 @@ const leaderboardJSON = JSON.stringify(leaderboard);
if (publish) {
const octokit = new Octokit({ auth: env.GH_TOKEN });
- const info = await patientFetch(
+ const info = await patientFetchJSON(
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
- ).then(
- (response) =>
- response.json() as Promise,
);
const normalizedServer = server === 'na' ? 'com' : server;
diff --git a/src/app/tools/ratings/page.tsx b/src/app/tools/ratings/page.tsx
index 3d3661c09..fe70ff97f 100644
--- a/src/app/tools/ratings/page.tsx
+++ b/src/app/tools/ratings/page.tsx
@@ -137,15 +137,12 @@ export default function Page() {
await Promise.all(
range(0, 5).map(async (league: number) => {
const data = await getRatingsLeague(region, league);
- leaguePositionCache.current[region][league] =
- data.result[0].number - 1;
+ leaguePositionCache.current[region][league] = data[0].number - 1;
return data;
}),
)
- )
- .map(({ result }) => result)
- .flat();
+ ).flat();
useLeaderboardCache.setState(
produce((draft: LeaderboardCache) => {
@@ -308,13 +305,14 @@ export default function Page() {
if (seedingPlayerNeighborEntry === undefined) return null;
- const seedingPlayerPosition = parseInt(seedingPlayerNeighborEntry[0]);
+ // const seedingPlayerPosition = parseInt(seedingPlayerNeighborEntry[0]);
const { neighbors } = await getRatingsNeighbors(region, id, size, true);
useLeaderboardCache.setState(
produce((draft: LeaderboardCache) => {
neighbors.forEach((neighbor, neighborIndex) => {
- const position = neighborIndex - radius + seedingPlayerPosition;
+ // const position = neighborIndex - radius + seedingPlayerPosition;
+ const position = neighbor.number - 1;
if (!(position in draft[region][season])) {
draft[region][season][position] = neighbor.spa_id;
diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx
index 7abe31c82..d1067e78a 100644
--- a/src/commands/ratings.tsx
+++ b/src/commands/ratings.tsx
@@ -15,6 +15,8 @@ import { REGION_NAMES_SHORT, 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 regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain';
import { getArchivedLatestSeasonNumber } from '../core/blitzkrieg/getArchivedLatestSeasonNumber';
import getArchivedRatingsInfo from '../core/blitzkrieg/getArchivedRatingsInfo';
@@ -123,14 +125,14 @@ function addSubcommands(
.addSubcommand((option) =>
addOptions(
option
- .setName('neighbours')
- .setDescription('Your position and neighbours'),
+ .setName('neighbors')
+ .setDescription('Your position and neighbors'),
)
.addStringOption(addUsernameChoices)
.addIntegerOption((option) =>
option
.setName('limit')
- .setDescription('How many neighbours to display (default: 10)')
+ .setDescription('How many neighbors to display (default: 10)')
.setRequired(false)
.setMinValue(5)
.setMaxValue(30),
@@ -228,7 +230,7 @@ export const ratingsCommand = new Promise(
| 'current';
const subcommand = interaction.options.getSubcommand(true) as
| 'league'
- | 'neighbours';
+ | 'neighbors';
const limit = interaction.options.getInteger('limit') ?? 10;
const seasonOption = interaction.options.getString('season');
const season = seasonOption ? parseInt(seasonOption) : undefined;
@@ -257,11 +259,8 @@ export const ratingsCommand = new Promise(
const regionSubdomain = regionToRegionSubdomain(region);
const result =
subcommandGroup === 'current'
- ? await fetch(
- `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`,
- )
- .then((response) => response.json() as Promise)
- .then(async ({ result }) =>
+ ? await getRatingsLeague(region, leagueIndex).then(
+ async (result) =>
result.slice(0, limit).map(
(player) =>
({
@@ -272,7 +271,7 @@ export const ratingsCommand = new Promise(
nickname: player.nickname,
}) satisfies SimplifiedPlayer,
),
- )
+ )
: await getArchivedRatingsLeaderboard(region, season!).then(
async (players) => {
const firstPlayerIndex =
@@ -338,29 +337,24 @@ export const ratingsCommand = new Promise(
const accountInfo = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
- const regionSubdomain = regionToRegionSubdomain(region);
const neighbors =
subcommandGroup === 'current'
- ? await fetch(
- `https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${Math.round(
- limit / 2,
- )}`,
+ ? 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,
+ ),
)
- .then(
- (response) => response.json() as Promise,
- )
- .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.findIndex(
@@ -414,7 +408,7 @@ export const ratingsCommand = new Promise(
titleName = neighbors
? accountInfo.nickname
: `No Ongoing Season - ${REGION_NAMES_SHORT[region]}`;
- titleDescription = 'Ratings neighbours';
+ titleDescription = 'Ratings neighbors';
playersBefore = neighbors ? neighbors[0].position - 1 : 0;
playersAfter =
regionRatingsInfo.count - neighbors[neighbors.length - 1].position;
diff --git a/src/core/blitz/getRatingsLeague.ts b/src/core/blitz/getRatingsLeague.ts
index 49445335e..6779cc78b 100644
--- a/src/core/blitz/getRatingsLeague.ts
+++ b/src/core/blitz/getRatingsLeague.ts
@@ -1,4 +1,4 @@
-import { LeagueTop } from '../../commands/ratings';
+import { LeagueTop, RatingsPlayer } from '../../commands/ratings';
import { Region } from '../../constants/regions';
import { context } from '../blitzkrieg/context';
import { patientFetch } from '../blitzkrieg/patientFetch';
@@ -9,7 +9,10 @@ export async function getRatingsLeague(
league: number,
usePatientFetcher = false,
) {
- const fetcher = usePatientFetcher ? patientFetch : fetch;
+ const fetcher = (url: string) =>
+ usePatientFetcher
+ ? patientFetch(url, undefined, { cache: 'no-store' })
+ : fetch(url, { cache: 'no-store' });
const response = await fetcher(
context === 'website'
? `/api/${region}/ratings/current/league/${league}`
@@ -17,7 +20,9 @@ export async function getRatingsLeague(
region,
)}.wotblitz.com/en/api/rating-leaderboards/league/${league}/top/`,
);
- const json = (await response.json()) as LeagueTop;
+ const json = await response.json();
- return json;
+ return context === 'website'
+ ? (json as RatingsPlayer[])
+ : (json as LeagueTop).result;
}
diff --git a/src/core/blitz/getRatingsNeighbors.ts b/src/core/blitz/getRatingsNeighbors.ts
index 045cf9a01..2cd33aab9 100644
--- a/src/core/blitz/getRatingsNeighbors.ts
+++ b/src/core/blitz/getRatingsNeighbors.ts
@@ -10,9 +10,10 @@ export async function getRatingsNeighbors(
count: number,
usePatientFetcher = false,
) {
- const fetcher = usePatientFetcher
- ? patientFetch
- : (url: string) => fetch(url, { cache: 'no-store' });
+ const fetcher = (url: string) =>
+ usePatientFetcher
+ ? patientFetch(url, undefined, { cache: 'no-store' })
+ : fetch(url, { cache: 'no-store' });
const response = await fetcher(
withCORSProxy(
`https://${regionToRegionSubdomain(
diff --git a/src/core/blitzkrieg/patientFetchJSON.ts b/src/core/blitzkrieg/patientFetchJSON.ts
new file mode 100644
index 000000000..c96b4dc83
--- /dev/null
+++ b/src/core/blitzkrieg/patientFetchJSON.ts
@@ -0,0 +1,23 @@
+export async function patientFetchJSON(
+ url: string,
+ timeout = 1000,
+ options?: Partial<{ cache: 'no-store' }>,
+) {
+ let patientJSON: Type;
+
+ while (patientJSON! === undefined) {
+ try {
+ patientJSON = (await fetch(url, options).then(({ json }) =>
+ json(),
+ )) as Type;
+ } catch (error) {
+ console.warn(
+ `Failed to fetch ${url} as JSON; retrying in ${timeout}ms...`,
+ error,
+ );
+ await new Promise((resolve) => setTimeout(resolve, timeout));
+ }
+ }
+
+ return patientJSON;
+}