mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c2861f956 | ||
|
|
894b841769 | ||
|
|
62627c96a3 | ||
|
|
c173ece882 | ||
|
|
eca7cbbbeb | ||
|
|
c72bfa9f71 | ||
|
|
b4ae39a509 | ||
|
|
631c051895 | ||
|
|
9a2e11a2a4 | ||
|
|
aa6011b18d | ||
|
|
9419aec116 | ||
|
|
638402589f | ||
|
|
6d6a6bf738 |
No files matched your search
@@ -1,8 +1,8 @@
|
||||
name: CD North America Ratings Latest
|
||||
name: CD Asia Ratings Latest
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
host:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm install --location=global yarn
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Use node_modules cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
name: CD North America Ratings Leaderboards
|
||||
name: CD Asia Ratings Leaderboards
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 17 * * *'
|
||||
- cron: "0 17 * * *"
|
||||
|
||||
jobs:
|
||||
host:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm install --location=global yarn
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Use node_modules cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
name: CD North America Ratings Latest
|
||||
name: CD Europe Ratings Latest
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
host:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm install --location=global yarn
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Use node_modules cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
name: CD North America Ratings Leaderboards
|
||||
name: CD Europe Ratings Leaderboards
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 23 * * *'
|
||||
- cron: "0 23 * * *"
|
||||
|
||||
jobs:
|
||||
host:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm install --location=global yarn
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Use node_modules cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
Vendored
+2
@@ -28,6 +28,7 @@
|
||||
"statsfull",
|
||||
"Stringifying",
|
||||
"strvk",
|
||||
"subnation",
|
||||
"tankaverages",
|
||||
"tankopedia",
|
||||
"tankstats",
|
||||
@@ -37,6 +38,7 @@
|
||||
"vickers",
|
||||
"vitepress",
|
||||
"wargaming",
|
||||
"webp",
|
||||
"winrate"
|
||||
],
|
||||
"deno.enable": false,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Blitzkrieg 1.11.0
|
||||
|
||||
I am going on vacation so I thought I'd publish this a bit prematurely without all the planned features.
|
||||
|
||||
## Changes
|
||||
|
||||
- Added `/ratings`
|
||||
- This command currently only has the `leagues` subcommand group
|
||||
- Displays the top players in a league
|
||||
- Also shows the changes in their positions and scores since last region midnight
|
||||
- Use `/help timezones` to know when Blitzkrieg records the leaderboard
|
||||
- Blitzkrieg now internally generates and keeps a permanent record of all leaderboards updated every `UTC+0` midnight
|
||||
- This approach will allow us to capture the absolute latest state of the leaderboard before it is wiped for the next season
|
||||
|
||||
## Fixed
|
||||
|
||||
- Removed region "Europe" in `/verify` example
|
||||
|
||||
## Technical Changes
|
||||
|
||||
- Add scripts were upgraded to TypeScript
|
||||
- Secrets now only throw errors if they're used when not provided
|
||||
+8
-6
@@ -5,24 +5,25 @@
|
||||
"main": "src/server.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build:bot": "node scripts/build --build=bot",
|
||||
"build:server": "node scripts/build --build=server",
|
||||
"build:bot": "vite-node --script scripts/build --build=bot",
|
||||
"build:server": "vite-node --script scripts/build --build=server",
|
||||
"build:docs": "vitepress build docs",
|
||||
"build:ratingsleaderboard": "node scripts/generateRatingsLeaderboard",
|
||||
"build": "node scripts/build --build=all",
|
||||
"build:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard",
|
||||
"build": "vite-node --script scripts/build --build=all",
|
||||
"dev:bot": "yarn build:bot --dev && yarn start:bot",
|
||||
"dev:server": "yarn build:server --dev && yarn start:server",
|
||||
"dev:docs": "vitepress dev docs",
|
||||
"start:bot": "node --enable-source-maps --trace-warnings dist/bot.cjs",
|
||||
"start:server": "node --enable-source-maps --trace-warnings dist/server.cjs",
|
||||
"host:docs": "vitepress preview docs",
|
||||
"publish:ratingsleaderboard": "node scripts/generateRatingsLeaderboard --publish",
|
||||
"publish:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard --publish",
|
||||
"zip": "zip -r dist.zip dist",
|
||||
"lint": "npx tsc",
|
||||
"format": "prettier --write \"**/*.@(json|js|jsx|css|md|ts|tsx|html|xml|yml|gltf)\""
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/rest": "^20.0.1",
|
||||
"@radix-ui/colors": "^0.1.8",
|
||||
"@resvg/resvg-js": "^2.4.1",
|
||||
"bepaint": "^0.1.0-beta.3",
|
||||
@@ -30,15 +31,16 @@
|
||||
"esbuild": "^0.18.10",
|
||||
"escape-html": "^1.0.3",
|
||||
"express": "^4.18.2",
|
||||
"false": "^0.0.4",
|
||||
"fuzzysort": "^2.0.4",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-escape": "^2.0.0",
|
||||
"react": "^18.2.0",
|
||||
"satori": "^0.9.1",
|
||||
"vite-node": "^0.33.0",
|
||||
"vitepress-plugin-auto-sidebar": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/rest": "^20.0.1",
|
||||
"@total-typescript/ts-reset": "^0.4.2",
|
||||
"@types/escape-html": "^1.0.2",
|
||||
"@types/express": "^4.17.17",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { build } from 'esbuild';
|
||||
import { BuildOptions, build } from 'esbuild';
|
||||
import { copyFile, mkdir, rm } from 'fs/promises';
|
||||
import { argv } from 'process';
|
||||
|
||||
@@ -23,7 +23,7 @@ if (isProd) {
|
||||
);
|
||||
}
|
||||
|
||||
const commonOptions = {
|
||||
const commonOptions: BuildOptions = {
|
||||
platform: 'node',
|
||||
loader: {
|
||||
'.node': 'copy',
|
||||
@@ -46,9 +46,7 @@ if (buildBot) {
|
||||
|
||||
entryPoints: ['src/bot.ts'],
|
||||
outfile: 'dist/bot.cjs',
|
||||
}).then(() => {
|
||||
console.log('Bot built');
|
||||
});
|
||||
}).then(() => console.log('Bot built'));
|
||||
}
|
||||
|
||||
if (buildServer) {
|
||||
@@ -1,6 +1,11 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { rm, writeFile } from 'fs/promises';
|
||||
import { argv, env } from 'process';
|
||||
import {
|
||||
RatingsInfo,
|
||||
RatingsNeighbors,
|
||||
RatingsPlayer,
|
||||
} from '../src/commands/ratings';
|
||||
import commitMultipleFiles from './commitMultipleFiles.js';
|
||||
|
||||
/*
|
||||
@@ -8,11 +13,13 @@ import commitMultipleFiles from './commitMultipleFiles.js';
|
||||
* Central European Time (UTC+1): use 0 23 * * *.
|
||||
* Central Asia Standard Time (UTC+7): use 0 17 * * *.
|
||||
*
|
||||
* :region/ratings/:season/info.json: season info like rewards, start & end time, etc.
|
||||
* :region/ratings/:season/latest.json: the absolute latest leaderboard recorded at UTC+0 midnight
|
||||
* :region/ratings/:season/leaderboards/:time.json: collection of all recorded leaderboards at local midnights
|
||||
* na/ratings/49/info.json: season info like rewards, start & end time, etc.
|
||||
* na/ratings/49/latest.json: the absolute latest leaderboard recorded at UTC+0 midnight
|
||||
* na/ratings/49/midnight.json: last midnight's leaderboard relative to the region
|
||||
*/
|
||||
|
||||
export type BlitzkriegRatingsLeaderboard = { id: number; score: number }[];
|
||||
|
||||
const publish = argv.includes('--publish');
|
||||
const latest = argv.includes('--latest');
|
||||
const server = argv.find((arg) => arg.startsWith('--region='))?.split('=')[1];
|
||||
@@ -23,22 +30,22 @@ const NEIGHBORS = 1000;
|
||||
const PLAYERS = await fetch(
|
||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||
)
|
||||
.then((response) => response.json())
|
||||
.then((response) => response.json() as Promise<RatingsInfo>)
|
||||
.then((data) => data.count);
|
||||
|
||||
const players = {};
|
||||
const leaderboard = [];
|
||||
const players: Record<number, RatingsPlayer> = {};
|
||||
const leaderboard: BlitzkriegRatingsLeaderboard = [];
|
||||
let registered = 0;
|
||||
|
||||
async function branchFromPlayer(id) {
|
||||
async function branchFromPlayer(id: number) {
|
||||
const { neighbors } = await fetch(
|
||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/user/${id}/?neighbors=${NEIGHBORS}`,
|
||||
).then((response) => response.json());
|
||||
).then((response) => response.json() as Promise<RatingsNeighbors>);
|
||||
|
||||
const firstPlayer = neighbors[0];
|
||||
const lastPlayer = neighbors[neighbors.length - 1];
|
||||
|
||||
const branches = [];
|
||||
const branches: Promise<void>[] = [];
|
||||
if (!players[firstPlayer.number])
|
||||
branches.push(branchFromPlayer(firstPlayer.spa_id));
|
||||
if (!players[lastPlayer.number])
|
||||
@@ -61,10 +68,10 @@ async function branchFromPlayer(id) {
|
||||
await Promise.all(branches);
|
||||
}
|
||||
|
||||
async function branchFromLeague(id) {
|
||||
async function branchFromLeague(id: number) {
|
||||
const { result } = await fetch(
|
||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/league/${id}/top/`,
|
||||
).then((response) => response.json());
|
||||
).then((response) => response.json() as Promise<{ result: RatingsPlayer[] }>);
|
||||
|
||||
const firstPlayer = result[0];
|
||||
const lastPlayer = result[result.length - 1];
|
||||
@@ -75,7 +82,7 @@ async function branchFromLeague(id) {
|
||||
]);
|
||||
}
|
||||
|
||||
console.log('Branching form leagues...');
|
||||
console.log('Branching from leagues...');
|
||||
await Promise.all([
|
||||
branchFromLeague(0),
|
||||
branchFromLeague(1),
|
||||
@@ -86,8 +93,13 @@ await Promise.all([
|
||||
|
||||
console.log('Converting to an array...');
|
||||
for (let index = 0; index < PLAYERS; index++) {
|
||||
leaderboard[index] = players[index + 1];
|
||||
if (!leaderboard[index]) console.warn(`${index} had no player`);
|
||||
const listing = players[index + 1];
|
||||
if (!listing) console.warn(`Position ${index + 1} had no player`);
|
||||
|
||||
leaderboard[index] = {
|
||||
id: listing.spa_id,
|
||||
score: listing.score,
|
||||
};
|
||||
}
|
||||
|
||||
const leaderboardJSON = JSON.stringify(leaderboard);
|
||||
@@ -97,14 +109,13 @@ if (publish) {
|
||||
|
||||
const info = await fetch(
|
||||
`https://${server}.wotblitz.com/en/api/rating-leaderboards/season/`,
|
||||
).then((response) => response.json());
|
||||
const time = Math.round(Date.now() / 1000);
|
||||
).then((response) => response.json() as Promise<RatingsInfo>);
|
||||
const normalizedServer = server === 'na' ? 'com' : server;
|
||||
|
||||
const infoPath = `${normalizedServer}/ratings/${info.current_season}/info.json`;
|
||||
const leaderboardPath = `${normalizedServer}/ratings/${info.current_season}/${
|
||||
latest ? 'latest.json' : `leaderboards/${time}.json`
|
||||
}`;
|
||||
latest ? 'latest' : 'midnight'
|
||||
}.json`;
|
||||
const infoJSON = JSON.stringify(info);
|
||||
|
||||
console.log(`Publishing to season ${info.current_season}...`);
|
||||
@@ -113,7 +124,7 @@ if (publish) {
|
||||
'tresabhi',
|
||||
'blitzkrieg-db',
|
||||
'main',
|
||||
`${info.current_season}/${time}`,
|
||||
new Date().toString(),
|
||||
[
|
||||
{ path: infoPath, content: infoJSON },
|
||||
{ path: leaderboardPath, content: leaderboardJSON },
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
|
||||
export default async function commitMultipleFiles(
|
||||
octokit,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
message,
|
||||
changes,
|
||||
octokit: Octokit,
|
||||
owner: string,
|
||||
repo: string,
|
||||
branch: string,
|
||||
message: string,
|
||||
changes: { path: string; content: string }[],
|
||||
) {
|
||||
// Get the latest commit SHA on the specified branch
|
||||
const { data: refData } = await octokit.git.getRef({
|
||||
@@ -31,12 +33,13 @@ export default async function commitMultipleFiles(
|
||||
content: change.content,
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
return {
|
||||
sha: blobData.sha,
|
||||
path: change.path,
|
||||
mode: '100644',
|
||||
type: 'blob',
|
||||
};
|
||||
} as const;
|
||||
}),
|
||||
);
|
||||
|
||||
+5
-2
@@ -1,5 +1,6 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import { DISCORD_TOKEN } from './core/node/arguments';
|
||||
import { secrets } from './core/node/secrets';
|
||||
import { registerErrorHandlers } from './events/error';
|
||||
import guildMemberAdd from './events/guildMemberAdd';
|
||||
import interactionCreate from './events/interactionCreate';
|
||||
@@ -12,5 +13,7 @@ export const client = new Client({
|
||||
.on('guildMemberAdd', guildMemberAdd)
|
||||
.on('interactionCreate', interactionCreate);
|
||||
|
||||
export const octokit = new Octokit({ auth: secrets.GH_TOKEN });
|
||||
|
||||
registerErrorHandlers();
|
||||
client.login(DISCORD_TOKEN);
|
||||
client.login(secrets.DISCORD_TOKEN);
|
||||
@@ -12,7 +12,7 @@ import embedNegative from '../core/discord/embedNegative';
|
||||
import embedPositive from '../core/discord/embedPositive';
|
||||
import markdownTable, { TableInput } from '../core/discord/markdownTable';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
import { PossiblyPromise } from '../types/possiblyPromise';
|
||||
@@ -49,9 +49,9 @@ export const eligibleCommand: CommandRegistry = {
|
||||
|
||||
async handler(interaction) {
|
||||
const clan = interaction.options.getString('clan') as SkilledClan;
|
||||
const { id, server } = await resolvePlayerFromCommand(interaction);
|
||||
const { id, region: server } = await resolvePlayerFromCommand(interaction);
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const problems: TableInput = [];
|
||||
const title = `${accountInfo[id].nickname}'s eligibility for ${SKILLED_CLANS[clan]}`;
|
||||
|
||||
@@ -12,7 +12,7 @@ import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton';
|
||||
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand';
|
||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import evolution from '../renderers/evolution';
|
||||
import { StatType } from '../renderers/fullStats';
|
||||
@@ -34,7 +34,7 @@ export const evolutionCommand: CommandRegistry = {
|
||||
true,
|
||||
) as StatType;
|
||||
const player = await resolvePlayerFromCommand(interaction);
|
||||
const period = resolvePeriodFromCommand(player.server, interaction);
|
||||
const period = resolvePeriodFromCommand(player.region, interaction);
|
||||
const tankIdRaw = interaction.options.getString('tank')!;
|
||||
const tankId =
|
||||
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||
@@ -42,7 +42,7 @@ export const evolutionCommand: CommandRegistry = {
|
||||
const end = interaction.options.getInteger('end');
|
||||
const { nickname } = (
|
||||
await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${player.server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
`https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
)
|
||||
)[player.id];
|
||||
const path = interactionToURL(interaction, {
|
||||
@@ -57,7 +57,7 @@ export const evolutionCommand: CommandRegistry = {
|
||||
primaryButton(path, 'Refresh'),
|
||||
// linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
||||
linkButton(
|
||||
`https://www.blitzstars.com/player/${player.server}/${nickname}${
|
||||
`https://www.blitzstars.com/player/${player.region}/${nickname}${
|
||||
commandGroup === 'tank' ? `/tank/${tankId!}` : ''
|
||||
}`,
|
||||
'BlitzStars',
|
||||
@@ -75,7 +75,7 @@ export const evolutionCommand: CommandRegistry = {
|
||||
const path = url.pathname.split('/').filter(Boolean);
|
||||
const commandGroup = path[1] as StatType;
|
||||
const player = await resolvePlayerFromButton(interaction);
|
||||
const period = resolvePeriodFromButton(player.server, interaction);
|
||||
const period = resolvePeriodFromButton(player.region, interaction);
|
||||
|
||||
return await evolution(
|
||||
commandGroup,
|
||||
|
||||
@@ -12,7 +12,7 @@ import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton';
|
||||
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand';
|
||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import fullStats, { StatType } from '../renderers/fullStats';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
@@ -33,7 +33,7 @@ export const fullStatsCommand: CommandRegistry = {
|
||||
true,
|
||||
) as StatType;
|
||||
const player = await resolvePlayerFromCommand(interaction);
|
||||
const period = resolvePeriodFromCommand(player.server, interaction);
|
||||
const period = resolvePeriodFromCommand(player.region, interaction);
|
||||
const tankIdRaw = interaction.options.getString('tank')!;
|
||||
const tankId =
|
||||
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||
@@ -47,7 +47,7 @@ export const fullStatsCommand: CommandRegistry = {
|
||||
});
|
||||
const { nickname } = (
|
||||
await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${player.server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
`https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
)
|
||||
)[player.id];
|
||||
|
||||
@@ -56,7 +56,7 @@ export const fullStatsCommand: CommandRegistry = {
|
||||
primaryButton(path, 'Refresh'),
|
||||
// linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
||||
linkButton(
|
||||
`https://www.blitzstars.com/player/${player.server}/${nickname}`,
|
||||
`https://www.blitzstars.com/player/${player.region}/${nickname}`,
|
||||
'BlitzStars',
|
||||
),
|
||||
];
|
||||
@@ -72,7 +72,7 @@ export const fullStatsCommand: CommandRegistry = {
|
||||
const path = url.pathname.split('/').filter(Boolean);
|
||||
const commandGroup = path[1] as StatType;
|
||||
const player = await resolvePlayerFromButton(interaction);
|
||||
const period = resolvePeriodFromButton(player.server, interaction);
|
||||
const period = resolvePeriodFromButton(player.region, interaction);
|
||||
|
||||
return await fullStats(
|
||||
commandGroup,
|
||||
|
||||
@@ -4,12 +4,12 @@ import NoData, { NoDataType } from '../components/NoData';
|
||||
import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import Wrapper from '../components/Wrapper';
|
||||
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||
import { REGION_NAMES } from '../constants/regions';
|
||||
import getBlitzClan from '../core/blitz/getBlitzClan';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
import addClanChoices from '../core/discord/addClanChoices';
|
||||
import autocompleteClan from '../core/discord/autocompleteClan';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
import { ClanInfo } from '../types/clanInfo';
|
||||
@@ -42,14 +42,14 @@ export const inactiveCommand: CommandRegistry = {
|
||||
const time = new Date().getTime() / 1000;
|
||||
const clanData = (
|
||||
await getWargamingResponse<ClanInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/info/?application_id=${WARGAMING_APPLICATION_ID}&clan_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&clan_id=${id}`,
|
||||
)
|
||||
)[id];
|
||||
const memberIds = clanData.members_ids;
|
||||
const accounts = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${memberIds.join(
|
||||
',',
|
||||
)}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${
|
||||
secrets.WARGAMING_APPLICATION_ID
|
||||
}&account_id=${memberIds.join(',')}`,
|
||||
);
|
||||
const inactive = memberIds
|
||||
.map((memberId) => {
|
||||
@@ -72,7 +72,7 @@ export const inactiveCommand: CommandRegistry = {
|
||||
nameDiscriminator="(Inactivity)"
|
||||
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData.emblem_set_id}.png`}
|
||||
description={`${threshold}+ Days • ${new Date().toDateString()} • ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
REGION_NAMES[server]
|
||||
}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||
import * as Tanks from '../components/Tanks';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import Wrapper from '../components/Wrapper';
|
||||
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||
import { REGION_NAMES } from '../constants/regions';
|
||||
import getTankStats from '../core/blitz/getTankStats';
|
||||
import getTreeType from '../core/blitz/getTreeType';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||
import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
import { PlayerClanData } from '../types/playerClanData';
|
||||
@@ -74,9 +74,9 @@ export const ownedTanksCommand: CommandRegistry = {
|
||||
async handler(interaction) {
|
||||
const tier = Number(interaction.options.getString('tier'));
|
||||
const account = await resolvePlayerFromCommand(interaction);
|
||||
const { id, server } = account;
|
||||
const { id, region: server } = account;
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const tankStats = await getTankStats(server, id);
|
||||
const tanks = (
|
||||
@@ -88,7 +88,7 @@ export const ownedTanksCommand: CommandRegistry = {
|
||||
)
|
||||
).filter((tank) => tank?.tier === tier);
|
||||
const clanData = await getWargamingResponse<PlayerClanData>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
);
|
||||
const leftColumnSize = Math.ceil(tanks.length / 2);
|
||||
const leftColumn = tanks.slice(0, leftColumnSize);
|
||||
@@ -105,7 +105,7 @@ export const ownedTanksCommand: CommandRegistry = {
|
||||
: undefined
|
||||
}
|
||||
description={`Owned tanks • ${new Date().toDateString()} • ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
REGION_NAMES[server]
|
||||
}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||
import embedInfo from '../core/discord/embedInfo';
|
||||
import markdownTable, { TableInputEntry } from '../core/discord/markdownTable';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountAchievements } from '../types/accountAchievements';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
@@ -36,13 +36,13 @@ export const playerAchievementsCommand: CommandRegistry = {
|
||||
async handler(interaction) {
|
||||
const sortBy = (interaction.options.getString('sort') ?? 'name') as SortBy;
|
||||
const account = await resolvePlayerFromCommand(interaction);
|
||||
const { id, server } = account;
|
||||
const { id, region: server } = account;
|
||||
const accounts = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const accountsAchievements =
|
||||
await getWargamingResponse<AccountAchievements>(
|
||||
`https://api.wotblitz.${server}/wotb/account/achievements/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/achievements/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const accountAchievements = accountsAchievements[id];
|
||||
const compound = {
|
||||
|
||||
@@ -6,7 +6,7 @@ import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||
import embedInfo from '../core/discord/embedInfo';
|
||||
import markdownTable from '../core/discord/markdownTable';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
|
||||
@@ -22,9 +22,9 @@ export const playerInfoCommand: CommandRegistry = {
|
||||
|
||||
async handler(interaction) {
|
||||
const account = await resolvePlayerFromCommand(interaction);
|
||||
const { id, server } = account;
|
||||
const { id, region: server } = account;
|
||||
const accounts = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const accountInfo = accounts[id];
|
||||
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js';
|
||||
import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard';
|
||||
import { octokit } from '../bot';
|
||||
import * as Leaderboard from '../components/Leaderboard';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import Wrapper from '../components/Wrapper';
|
||||
import { REGION_NAMES, Region } from '../constants/regions';
|
||||
import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain';
|
||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
|
||||
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 interface RatingsInfo {
|
||||
title: string;
|
||||
icon: string | null;
|
||||
start_at: string;
|
||||
finish_at: string;
|
||||
current_season: number;
|
||||
updated_at: string;
|
||||
count: number;
|
||||
|
||||
rewards: RatingsReward[];
|
||||
leagues: RatingsLeague[];
|
||||
}
|
||||
|
||||
export interface RatingsNeighbors {
|
||||
neighbors: RatingsPlayer[];
|
||||
}
|
||||
|
||||
console.log('Caching ratings info...');
|
||||
const ratingsInfo = fetch(
|
||||
'https://na.wotblitz.com/en/api/rating-leaderboards/season/',
|
||||
)
|
||||
.then((response) => response.json() as Promise<RatingsInfo>)
|
||||
.then((info) => {
|
||||
console.log('Cached ratings info');
|
||||
return info;
|
||||
});
|
||||
|
||||
function optionalParameters(option: SlashCommandSubcommandBuilder) {
|
||||
return option
|
||||
.addStringOption(addRegionChoices)
|
||||
.addIntegerOption((option) =>
|
||||
option
|
||||
.setName('limit')
|
||||
.setDescription('How many rows to display (default: 10)')
|
||||
.setRequired(false)
|
||||
.setMinValue(5)
|
||||
.setMaxValue(30),
|
||||
);
|
||||
}
|
||||
|
||||
const LEAGUE_INDEXES = ['diamond', 'platinum', 'gold', 'silver', 'bronze'];
|
||||
|
||||
export const DATABASE_REPO = { owner: 'tresabhi', repo: 'blitzkrieg-db' };
|
||||
|
||||
export const ratingsCommand: CommandRegistry = {
|
||||
inProduction: true,
|
||||
inDevelopment: true,
|
||||
inPublic: true,
|
||||
inPreview: true,
|
||||
|
||||
command: new SlashCommandBuilder()
|
||||
.setName('ratings')
|
||||
.setDescription('Ratings battles statistics')
|
||||
.addSubcommandGroup((option) =>
|
||||
option
|
||||
.setName('league')
|
||||
.setDescription('Top league players')
|
||||
.addSubcommand((option) =>
|
||||
optionalParameters(
|
||||
option
|
||||
.setName('diamond')
|
||||
.setDescription('Top Diamond League players'),
|
||||
),
|
||||
)
|
||||
.addSubcommand((option) =>
|
||||
optionalParameters(
|
||||
option
|
||||
.setName('platinum')
|
||||
.setDescription('Top Platinum League players'),
|
||||
),
|
||||
)
|
||||
.addSubcommand((option) =>
|
||||
optionalParameters(
|
||||
option.setName('gold').setDescription('Top Gold League players'),
|
||||
),
|
||||
)
|
||||
.addSubcommand((option) =>
|
||||
optionalParameters(
|
||||
option
|
||||
.setName('silver')
|
||||
.setDescription('Top Silver League players'),
|
||||
),
|
||||
)
|
||||
.addSubcommand((option) =>
|
||||
optionalParameters(
|
||||
option
|
||||
.setName('bronze')
|
||||
.setDescription('Top Bronze League players'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
async handler(interaction) {
|
||||
const subcommandGroup = interaction.options.getSubcommandGroup(true);
|
||||
|
||||
if (subcommandGroup === 'league') {
|
||||
const limit = interaction.options.getInteger('limit') ?? 10;
|
||||
const subcommand = interaction.options.getSubcommand(true);
|
||||
const leagueIndex = LEAGUE_INDEXES.indexOf(subcommand);
|
||||
const region = interaction.options.getString('region') as Region;
|
||||
const regionSubdomain = regionToRegionSubdomain(region);
|
||||
const result = await fetch(
|
||||
`https://${regionSubdomain}.wotblitz.com/en/api/rating-leaderboards/league/${leagueIndex}/top/`,
|
||||
)
|
||||
.then((response) => response.json() as Promise<LeagueTop>)
|
||||
.then(({ result }) => result.slice(0, limit));
|
||||
const awaitedInfo = await ratingsInfo;
|
||||
const leagueInfo = awaitedInfo.leagues[leagueIndex];
|
||||
const midnightLeaderboard = await octokit.repos
|
||||
.getContent({
|
||||
...DATABASE_REPO,
|
||||
path: `${region}/ratings/${awaitedInfo.current_season}/midnight.json`,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (!Array.isArray(data) && data.type === 'file') {
|
||||
const content = Buffer.from(data.content, 'base64').toString();
|
||||
const jsonContent = JSON.parse(
|
||||
content,
|
||||
) as BlitzkriegRatingsLeaderboard;
|
||||
|
||||
return jsonContent;
|
||||
}
|
||||
})
|
||||
.catch(() => console.warn('No midnight leaderboard found'));
|
||||
const items = await Promise.all(
|
||||
result.map(async (player) => {
|
||||
const reward = awaitedInfo.rewards.find(
|
||||
(reward) =>
|
||||
player.number >= reward.from_position &&
|
||||
player.number <= reward.to_position,
|
||||
);
|
||||
const midnightIndex = midnightLeaderboard?.findIndex(
|
||||
(item) => item.id === player.spa_id,
|
||||
);
|
||||
const midnightScore =
|
||||
midnightIndex === undefined
|
||||
? player.score
|
||||
: midnightLeaderboard![midnightIndex].score;
|
||||
const midnightPosition =
|
||||
midnightIndex === undefined ? player.number : midnightIndex + 1;
|
||||
|
||||
return (
|
||||
<Leaderboard.Item
|
||||
nickname={player.nickname}
|
||||
score={player.score}
|
||||
position={player.number}
|
||||
clan={player.clan_tag}
|
||||
reward={reward}
|
||||
deltaScore={player.score - midnightScore}
|
||||
deltaPosition={midnightPosition - player.number}
|
||||
key={player.spa_id}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
const playersBefore = result[0].number - 1;
|
||||
const playersAfter = awaitedInfo.count - result[result.length - 1].number;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<TitleBar
|
||||
name={leagueInfo.title}
|
||||
image={
|
||||
leagueInfo.big_icon.startsWith('http')
|
||||
? leagueInfo.big_icon
|
||||
: `https:${leagueInfo.big_icon}`
|
||||
}
|
||||
description={`Top ${limit} players • ${new Date().toDateString()} • ${
|
||||
REGION_NAMES[region]
|
||||
}`}
|
||||
/>
|
||||
|
||||
<Leaderboard.Root>
|
||||
{playersBefore > 0 && <Leaderboard.Gap number={playersBefore} />}
|
||||
{items}
|
||||
{playersAfter > 0 && <Leaderboard.Gap number={playersAfter} />}
|
||||
</Leaderboard.Root>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
};
|
||||
@@ -1,11 +1,11 @@
|
||||
import { SlashCommandBuilder } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { REGION_DOMAIN_NAMES, RegionDomain } from '../constants/regions';
|
||||
import { REGION_NAMES, Region } from '../constants/regions';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
import addClanChoices from '../core/discord/addClanChoices';
|
||||
import addServerChoices from '../core/discord/addServerChoices';
|
||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||
import embedInfo from '../core/discord/embedInfo';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { ClanList } from '../types/clanList';
|
||||
|
||||
@@ -17,7 +17,7 @@ export const searchClansCommand: CommandRegistry = {
|
||||
command: new SlashCommandBuilder()
|
||||
.setName('searchclans')
|
||||
.setDescription('Search clans')
|
||||
.addStringOption(addServerChoices)
|
||||
.addStringOption(addRegionChoices)
|
||||
.addStringOption((option) => addClanChoices(option).setAutocomplete(false))
|
||||
.addIntegerOption((option) =>
|
||||
option
|
||||
@@ -28,17 +28,15 @@ export const searchClansCommand: CommandRegistry = {
|
||||
),
|
||||
|
||||
async handler(interaction) {
|
||||
const server = interaction.options.getString('server') as RegionDomain;
|
||||
const server = interaction.options.getString('region') as Region;
|
||||
const clan = interaction.options.getString('clan')!;
|
||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
||||
const clanList = await getWargamingResponse<ClanList>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${clan}&limit=${limit}`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${clan}&limit=${limit}`,
|
||||
);
|
||||
|
||||
return embedInfo(
|
||||
`Clan search for "${markdownEscape(clan)}" in ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
}`,
|
||||
`Clan search for "${markdownEscape(clan)}" in ${REGION_NAMES[server]}`,
|
||||
clanList.length === 0
|
||||
? 'No clans found.'
|
||||
: `\`\`\`\n${clanList
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { SlashCommandBuilder } from 'discord.js';
|
||||
import markdownEscape from 'markdown-escape';
|
||||
import { REGION_DOMAIN_NAMES, RegionDomain } from '../constants/regions';
|
||||
import { REGION_NAMES, Region } from '../constants/regions';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
import { usernamePattern } from '../core/blitz/listPlayers';
|
||||
import addServerChoices from '../core/discord/addServerChoices';
|
||||
import addRegionChoices from '../core/discord/addRegionChoices';
|
||||
import addUsernameChoices from '../core/discord/addUsernameChoices';
|
||||
import embedInfo from '../core/discord/embedInfo';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountList } from '../types/accountList';
|
||||
|
||||
@@ -18,7 +18,7 @@ export const searchPlayersCommand: CommandRegistry = {
|
||||
command: new SlashCommandBuilder()
|
||||
.setName('searchplayers')
|
||||
.setDescription('Search players in a Blitz server')
|
||||
.addStringOption(addServerChoices)
|
||||
.addStringOption(addRegionChoices)
|
||||
.addStringOption((option) =>
|
||||
addUsernameChoices(option).setAutocomplete(false).setRequired(true),
|
||||
)
|
||||
@@ -31,22 +31,20 @@ export const searchPlayersCommand: CommandRegistry = {
|
||||
),
|
||||
|
||||
async handler(interaction) {
|
||||
const server = interaction.options.getString('server') as RegionDomain;
|
||||
const server = interaction.options.getString('region') as Region;
|
||||
const name = interaction.options.getString('username')!;
|
||||
const limit = interaction.options.getInteger('limit') ?? 25;
|
||||
const trimmedSearch = name.trim();
|
||||
const players = usernamePattern.test(trimmedSearch)
|
||||
? await getWargamingResponse<AccountList>(
|
||||
`https://api.wotblitz.${server}/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodeURIComponent(
|
||||
trimmedSearch,
|
||||
)}&limit=${limit}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/list/?application_id=${
|
||||
secrets.WARGAMING_APPLICATION_ID
|
||||
}&search=${encodeURIComponent(trimmedSearch)}&limit=${limit}`,
|
||||
)
|
||||
: [];
|
||||
|
||||
return embedInfo(
|
||||
`Player search for "${markdownEscape(name)}" in ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
}`,
|
||||
`Player search for "${markdownEscape(name)}" in ${REGION_NAMES[server]}`,
|
||||
`\`\`\`${
|
||||
players.length === 0
|
||||
? 'No players found.'
|
||||
|
||||
@@ -12,7 +12,7 @@ import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton';
|
||||
import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand';
|
||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { StatType } from '../renderers/fullStats';
|
||||
import stats from '../renderers/stats';
|
||||
@@ -26,7 +26,7 @@ export const statsCommand: CommandRegistry = {
|
||||
command: addStatTypeSubCommandGroups(
|
||||
new SlashCommandBuilder()
|
||||
.setName('stats')
|
||||
.setDescription('In-game statistics'),
|
||||
.setDescription('Regular battles statistics'),
|
||||
),
|
||||
|
||||
async handler(interaction) {
|
||||
@@ -34,7 +34,7 @@ export const statsCommand: CommandRegistry = {
|
||||
true,
|
||||
) as StatType;
|
||||
const player = await resolvePlayerFromCommand(interaction);
|
||||
const period = resolvePeriodFromCommand(player.server, interaction);
|
||||
const period = resolvePeriodFromCommand(player.region, interaction);
|
||||
const tankIdRaw = interaction.options.getString('tank')!;
|
||||
const tankId =
|
||||
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
|
||||
@@ -48,7 +48,7 @@ export const statsCommand: CommandRegistry = {
|
||||
});
|
||||
const { nickname } = (
|
||||
await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${player.server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
`https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
)
|
||||
)[player.id];
|
||||
|
||||
@@ -57,7 +57,7 @@ export const statsCommand: CommandRegistry = {
|
||||
primaryButton(path, 'Refresh'),
|
||||
linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
||||
linkButton(
|
||||
`https://www.blitzstars.com/player/${player.server}/${nickname}`,
|
||||
`https://www.blitzstars.com/player/${player.region}/${nickname}`,
|
||||
'BlitzStars',
|
||||
),
|
||||
];
|
||||
@@ -73,7 +73,7 @@ export const statsCommand: CommandRegistry = {
|
||||
const path = url.pathname.split('/').filter(Boolean);
|
||||
const commandGroup = path[1] as StatType;
|
||||
const player = await resolvePlayerFromButton(interaction);
|
||||
const period = resolvePeriodFromButton(player.server, interaction);
|
||||
const period = resolvePeriodFromButton(player.region, interaction);
|
||||
|
||||
return await stats(
|
||||
commandGroup,
|
||||
|
||||
@@ -8,7 +8,7 @@ import linkButton from '../core/discord/linkButton';
|
||||
import primaryButton from '../core/discord/primaryButton';
|
||||
import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import today from '../renderers/today';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
@@ -37,7 +37,7 @@ export const todayCommand: CommandRegistry = {
|
||||
const limit = interaction.options.getInteger('limit') ?? Infinity;
|
||||
const { nickname } = (
|
||||
await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${player.server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
`https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${player.id}`,
|
||||
)
|
||||
)[player.id];
|
||||
const path = interactionToURL(interaction, player);
|
||||
@@ -47,7 +47,7 @@ export const todayCommand: CommandRegistry = {
|
||||
primaryButton(path, 'Refresh'),
|
||||
linkButton(`${CYCLIC_API}/${path}`, 'Embed'),
|
||||
linkButton(
|
||||
`https://www.blitzstars.com/player/${player.server}/${nickname}`,
|
||||
`https://www.blitzstars.com/player/${player.region}/${nickname}`,
|
||||
'BlitzStars',
|
||||
),
|
||||
];
|
||||
|
||||
@@ -7,7 +7,7 @@ import autocompleteUsername from '../core/discord/autocompleteUsername';
|
||||
import embedNegative from '../core/discord/embedNegative';
|
||||
import embedPositive from '../core/discord/embedPositive';
|
||||
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { CommandRegistry } from '../events/interactionCreate';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
import { PlayerClanData } from '../types/playerClanData';
|
||||
@@ -24,12 +24,12 @@ export const verifyCommand: CommandRegistry = {
|
||||
|
||||
async handler(interaction) {
|
||||
const blitzAccount = await resolvePlayerFromCommand(interaction);
|
||||
const { id, server } = blitzAccount;
|
||||
const { id, region: server } = blitzAccount;
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const clanData = await getWargamingResponse<PlayerClanData>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
);
|
||||
const clan = clanData[id]?.clan;
|
||||
const clanTag = clan ? ` [${clan!.tag}]` : '';
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { theme } from '../../../stitches.config';
|
||||
|
||||
export interface GapProps {
|
||||
number: number;
|
||||
}
|
||||
|
||||
export function Gap({ number }: GapProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
padding: 8,
|
||||
backgroundColor: theme.colors.componentNonInteractive,
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: theme.colors.textLowContrast,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
+ {number.toLocaleString()} more
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
import { RatingsReward } from '../../../commands/ratings';
|
||||
import { theme } from '../../../stitches.config';
|
||||
|
||||
export interface ItemProps {
|
||||
position: number;
|
||||
deltaPosition?: number;
|
||||
score: number;
|
||||
deltaScore?: number;
|
||||
reward?: RatingsReward;
|
||||
nickname: string;
|
||||
clan?: string;
|
||||
}
|
||||
|
||||
function normalizeImage(url: string) {
|
||||
return url.endsWith('.webp') ? 'https://i.imgur.com/ZniCzbO.png' : url;
|
||||
}
|
||||
|
||||
export function Item({
|
||||
position,
|
||||
deltaPosition,
|
||||
score,
|
||||
deltaScore,
|
||||
reward,
|
||||
nickname,
|
||||
clan,
|
||||
}: ItemProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
padding: 8,
|
||||
backgroundColor: theme.colors.componentInteractive,
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: theme.colors.textLowContrast,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{position.toLocaleString()}.
|
||||
</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: theme.colors.textHighContrast,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{nickname}
|
||||
</span>
|
||||
{clan && (
|
||||
<span
|
||||
style={{
|
||||
color: theme.colors.textLowContrast,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
[{clan}]
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{deltaPosition !== undefined && deltaPosition !== 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
}}
|
||||
src={
|
||||
deltaPosition > 0
|
||||
? 'https://i.imgur.com/qbjiXa1.png'
|
||||
: 'https://i.imgur.com/3uyNhun.png'
|
||||
}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
color: theme.colors.textLowContrast,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{Math.abs(deltaPosition).toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
{reward && (
|
||||
<img
|
||||
style={{ width: 16, height: 16, objectFit: 'cover' }}
|
||||
src={normalizeImage(
|
||||
reward.type === 'vehicle'
|
||||
? reward.vehicle.image_url
|
||||
: reward.stuff.image_url,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{reward && reward.count > 1 && (
|
||||
<span
|
||||
style={{
|
||||
color: theme.colors.textLowContrast,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
x {reward.count.toLocaleString()}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{deltaScore !== 0 && deltaScore !== undefined && (
|
||||
<span style={{ color: theme.colors.textLowContrast, fontSize: 16 }}>
|
||||
{Math.abs(deltaScore).toLocaleString()}
|
||||
</span>
|
||||
)}
|
||||
{deltaScore !== 0 && deltaScore !== undefined && (
|
||||
<img
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
}}
|
||||
src={
|
||||
deltaScore > 0
|
||||
? 'https://i.imgur.com/qbjiXa1.png'
|
||||
: 'https://i.imgur.com/3uyNhun.png'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<span style={{ color: theme.colors.textHighContrast, fontSize: 16 }}>
|
||||
{score.toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface RootProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function Root({ children }: RootProps) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './components/Gap';
|
||||
export * from './components/Item';
|
||||
export * from './components/Root';
|
||||
@@ -1,7 +1,7 @@
|
||||
export type RegionDomain = 'com' | 'eu' | 'asia';
|
||||
export type Region = 'com' | 'eu' | 'asia';
|
||||
export type RegionSubdomain = 'na' | 'eu' | 'asia';
|
||||
|
||||
export const REGION_DOMAIN_NAMES: Record<RegionDomain, string> = {
|
||||
export const REGION_NAMES: Record<Region, string> = {
|
||||
com: 'North America',
|
||||
eu: 'Europe',
|
||||
asia: 'Asia',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { serverAndIdPattern } from '../discord/resolvePlayerFromCommand';
|
||||
import throwError from '../node/throwError';
|
||||
import listClans from './listClans';
|
||||
@@ -6,7 +6,7 @@ import listClans from './listClans';
|
||||
export default async function getBlitzClan(clan: string) {
|
||||
if (serverAndIdPattern.test(clan)) {
|
||||
const [server, accountId] = clan.split('/');
|
||||
return { server: server as RegionDomain, id: Number(accountId) };
|
||||
return { server: server as Region, id: Number(accountId) };
|
||||
} else {
|
||||
const accounts = await listClans(clan);
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { REGION_DOMAIN_NAMES, RegionDomain } from '../../constants/regions';
|
||||
import { REGION_NAMES, Region } from '../../constants/regions';
|
||||
import { TanksStats } from '../../types/tanksStats';
|
||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||
import { secrets } from '../node/secrets';
|
||||
import throwError from '../node/throwError';
|
||||
import getWargamingResponse from './getWargamingResponse';
|
||||
|
||||
export default async function getTankStats(server: RegionDomain, id: number) {
|
||||
export default async function getTankStats(server: Region, id: number) {
|
||||
const tankStats = await getWargamingResponse<TanksStats>(
|
||||
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
|
||||
if (tankStats[id] === null) {
|
||||
throw throwError(
|
||||
'No tank stats available',
|
||||
`Wargaming says there is no tank stats for this account. This account may not have any battles/tanks or exist in the ${REGION_DOMAIN_NAMES[server]} server.`,
|
||||
`Wargaming says there is no tank stats for this account. This account may not have any battles/tanks or exist in the ${REGION_NAMES[server]} server.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { Clan, ClanList } from '../../types/clanList';
|
||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||
import { secrets } from '../node/secrets';
|
||||
import getWargamingResponse from './getWargamingResponse';
|
||||
|
||||
export type ClanListWithServer = (Clan & {
|
||||
@@ -15,33 +15,33 @@ export default async function listClans(search: string, limit = 9) {
|
||||
return (
|
||||
await Promise.all([
|
||||
getWargamingResponse<ClanList>(
|
||||
`https://api.wotblitz.com/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||
`https://api.wotblitz.com/wotb/clans/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||
).then(
|
||||
(value) =>
|
||||
value &&
|
||||
value.map((account) => ({
|
||||
...account,
|
||||
server: 'com' as RegionDomain,
|
||||
server: 'com' as Region,
|
||||
})),
|
||||
),
|
||||
getWargamingResponse<ClanList>(
|
||||
`https://api.wotblitz.eu/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||
`https://api.wotblitz.eu/wotb/clans/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||
).then(
|
||||
(value) =>
|
||||
value &&
|
||||
value.map((account) => ({
|
||||
...account,
|
||||
server: 'eu' as RegionDomain,
|
||||
server: 'eu' as Region,
|
||||
})),
|
||||
),
|
||||
getWargamingResponse<ClanList>(
|
||||
`https://api.wotblitz.asia/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||
`https://api.wotblitz.asia/wotb/clans/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
|
||||
).then(
|
||||
(value) =>
|
||||
value &&
|
||||
value.map((account) => ({
|
||||
...account,
|
||||
server: 'asia' as RegionDomain,
|
||||
server: 'asia' as Region,
|
||||
})),
|
||||
),
|
||||
])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { Account, AccountList } from '../../types/accountList';
|
||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||
import { secrets } from '../node/secrets';
|
||||
import getWargamingResponse from './getWargamingResponse';
|
||||
|
||||
export type AccountListWithServer = (Account & {
|
||||
@@ -19,33 +19,33 @@ export default async function listPlayers(search: string, limit = 9) {
|
||||
return (
|
||||
await Promise.all([
|
||||
getWargamingResponse<AccountList>(
|
||||
`https://api.wotblitz.com/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
|
||||
`https://api.wotblitz.com/wotb/account/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
|
||||
).then(
|
||||
(value) =>
|
||||
value &&
|
||||
value.map((account) => ({
|
||||
...account,
|
||||
server: 'com' as RegionDomain,
|
||||
server: 'com' as Region,
|
||||
})),
|
||||
),
|
||||
getWargamingResponse<AccountList>(
|
||||
`https://api.wotblitz.eu/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
|
||||
`https://api.wotblitz.eu/wotb/account/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
|
||||
).then(
|
||||
(value) =>
|
||||
value &&
|
||||
value.map((account) => ({
|
||||
...account,
|
||||
server: 'eu' as RegionDomain,
|
||||
server: 'eu' as Region,
|
||||
})),
|
||||
),
|
||||
getWargamingResponse<AccountList>(
|
||||
`https://api.wotblitz.asia/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
|
||||
`https://api.wotblitz.asia/wotb/account/list/?application_id=${secrets.WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
|
||||
).then(
|
||||
(value) =>
|
||||
value &&
|
||||
value.map((account) => ({
|
||||
...account,
|
||||
server: 'asia' as RegionDomain,
|
||||
server: 'asia' as Region,
|
||||
})),
|
||||
),
|
||||
])
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Region, RegionSubdomain } from '../../constants/regions';
|
||||
|
||||
export default function regionToRegionSubdomain(
|
||||
regionDomain: Region,
|
||||
): RegionSubdomain {
|
||||
return regionDomain === 'com' ? 'na' : regionDomain;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { AllStats } from '../../types/accountInfo';
|
||||
import getTankHistories, {
|
||||
TankHistories,
|
||||
@@ -35,7 +35,7 @@ export const emptyTankHistoryNode: TankHistoryRaw = {
|
||||
};
|
||||
|
||||
export default async function getDiffedTankStats(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
id: number,
|
||||
start: number,
|
||||
end: number,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { Period } from '../discord/addPeriodSubCommands';
|
||||
import getTimeDaysAgo from './getTimeDaysAgo';
|
||||
|
||||
export default function getPeriodStart(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
period: Exclude<Period, 'custom'>,
|
||||
) {
|
||||
if (period === 'career') {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { AccountInfo, AllStats } from '../../types/accountInfo';
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getHistoriesDefaultOptions,
|
||||
} from '../../types/histories';
|
||||
import getWargamingResponse from '../blitz/getWargamingResponse';
|
||||
import { WARGAMING_APPLICATION_ID } from '../node/arguments';
|
||||
import { secrets } from '../node/secrets';
|
||||
import { emptyAllStats } from './getDiffedTankStats';
|
||||
|
||||
export interface PlayerHistoryRaw {
|
||||
@@ -25,13 +25,13 @@ export interface PlayerHistoryRaw {
|
||||
account_id: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
region: RegionDomain;
|
||||
region: Region;
|
||||
}
|
||||
|
||||
export type PlayerHistoriesRaw = PlayerHistoryRaw[];
|
||||
|
||||
export default async function getPlayerHistories(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
id: number,
|
||||
options?: Partial<GetHistoriesOptions>,
|
||||
) {
|
||||
@@ -67,7 +67,7 @@ export default async function getPlayerHistories(
|
||||
statistics: { all },
|
||||
} = (
|
||||
await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
)
|
||||
)[id];
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import {
|
||||
GetHistoriesOptions,
|
||||
Histories,
|
||||
@@ -32,7 +32,7 @@ export const getTankHistoriesDefaultOptions: GetTankHistoriesOptions = {
|
||||
};
|
||||
|
||||
export default async function getTankHistories(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
id: number,
|
||||
options?: Partial<GetTankHistoriesOptions>,
|
||||
) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
|
||||
export const TIME_ZONE_MAPPINGS: Record<RegionDomain, number> = {
|
||||
export const TIME_ZONE_MAPPINGS: Record<Region, number> = {
|
||||
com: -5, // Central North American Time
|
||||
eu: +1, // Central European Time
|
||||
asia: +7, // Central Asia Standard Time
|
||||
};
|
||||
|
||||
export default function getTimeDaysAgo(server: RegionDomain, daysAgo: number) {
|
||||
export default function getTimeDaysAgo(server: Region, daysAgo: number) {
|
||||
const now = new Date();
|
||||
if (daysAgo === 0) now.getTime() / 1000;
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { SlashCommandStringOption } from 'discord.js';
|
||||
import { REGION_NAMES } from '../../constants/regions';
|
||||
|
||||
export default function addRegionChoices(option: SlashCommandStringOption) {
|
||||
return option
|
||||
.setName('region')
|
||||
.setDescription('The Blitz region you are in')
|
||||
.addChoices(
|
||||
{ name: REGION_NAMES.com, value: 'com' },
|
||||
{ name: REGION_NAMES.eu, value: 'eu' },
|
||||
{ name: REGION_NAMES.asia, value: 'asia' },
|
||||
)
|
||||
.setRequired(true);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { SlashCommandStringOption } from 'discord.js';
|
||||
import { REGION_DOMAIN_NAMES } from '../../constants/regions';
|
||||
|
||||
export default function addServerChoices(option: SlashCommandStringOption) {
|
||||
return option
|
||||
.setName('server')
|
||||
.setDescription('The Blitz server you are in')
|
||||
.addChoices(
|
||||
{ name: REGION_DOMAIN_NAMES.com, value: 'com' },
|
||||
{ name: REGION_DOMAIN_NAMES.eu, value: 'eu' },
|
||||
{ name: REGION_DOMAIN_NAMES.asia, value: 'asia' },
|
||||
)
|
||||
.setRequired(true);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
AutocompleteInteraction,
|
||||
CacheType,
|
||||
} from 'discord.js';
|
||||
import { REGION_DOMAIN_NAMES } from '../../constants/regions';
|
||||
import { REGION_NAMES } from '../../constants/regions';
|
||||
import listClans from '../blitz/listClans';
|
||||
|
||||
export default async function autocompleteClan(
|
||||
@@ -18,7 +18,7 @@ export default async function autocompleteClan(
|
||||
? players.map(
|
||||
(player) =>
|
||||
({
|
||||
name: `${player.name} (${REGION_DOMAIN_NAMES[player.server]})`,
|
||||
name: `${player.name} (${REGION_NAMES[player.server]})`,
|
||||
value: `${player.server}/${player.clan_id}`,
|
||||
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
AutocompleteInteraction,
|
||||
CacheType,
|
||||
} from 'discord.js';
|
||||
import { REGION_DOMAIN_NAMES } from '../../constants/regions';
|
||||
import { REGION_NAMES } from '../../constants/regions';
|
||||
import listPlayers from '../blitz/listPlayers';
|
||||
|
||||
export default async function autocompleteUsername(
|
||||
@@ -19,9 +19,7 @@ export default async function autocompleteUsername(
|
||||
? players.map(
|
||||
(player) =>
|
||||
({
|
||||
name: `${player.nickname} (${
|
||||
REGION_DOMAIN_NAMES[player.server]
|
||||
})`,
|
||||
name: `${player.nickname} (${REGION_NAMES[player.server]})`,
|
||||
value: `${player.server}/${player.account_id}`,
|
||||
}) satisfies ApplicationCommandOptionChoiceData<string>,
|
||||
)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ButtonInteraction, CacheType } from 'discord.js';
|
||||
import { CYCLIC_API } from '../../constants/cyclic';
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import resolvePeriodFromURL from '../express/resolvePeriodFromURL';
|
||||
|
||||
export default function resolvePeriodFromButton(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
interaction: ButtonInteraction<CacheType>,
|
||||
) {
|
||||
return resolvePeriodFromURL(server, `${CYCLIC_API}/${interaction.customId}`);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import getPeriodNow from '../blitzstars/getPeriodNow';
|
||||
import getPeriodStart from '../blitzstars/getPeriodStart';
|
||||
import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo';
|
||||
@@ -31,7 +31,7 @@ export interface ResolvedPeriod {
|
||||
}
|
||||
|
||||
export default function resolvePeriodFromCommand(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
let statsName: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import listPlayers, {
|
||||
usernamePatternWithoutPosition,
|
||||
} from '../blitz/listPlayers';
|
||||
@@ -8,7 +8,7 @@ import throwError from '../node/throwError';
|
||||
export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
|
||||
|
||||
export interface ResolvedPlayer {
|
||||
server: RegionDomain;
|
||||
region: Region;
|
||||
id: number;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export default async function resolvePlayerFromCommand(
|
||||
if (serverAndIdPattern.test(username)) {
|
||||
const [server, accountId] = username.split('/');
|
||||
return {
|
||||
server: server as RegionDomain,
|
||||
region: server as Region,
|
||||
id: Number(accountId),
|
||||
} satisfies ResolvedPlayer;
|
||||
} else {
|
||||
@@ -39,7 +39,7 @@ export default async function resolvePlayerFromCommand(
|
||||
|
||||
if (accounts[0]) {
|
||||
return {
|
||||
server: accounts[0].server,
|
||||
region: accounts[0].server,
|
||||
id: accounts[0].account_id,
|
||||
} satisfies ResolvedPlayer;
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Request } from 'express';
|
||||
import { CYCLIC_API } from '../../constants/cyclic';
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import resolvePeriodFromURL from './resolvePeriodFromURL';
|
||||
|
||||
export default function resolvePeriodFromRequest(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
request: Request,
|
||||
) {
|
||||
return resolvePeriodFromURL(server, `${CYCLIC_API}${request.path}`);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import getPeriodNow from '../blitzstars/getPeriodNow';
|
||||
import getPeriodStart from '../blitzstars/getPeriodStart';
|
||||
import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo';
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '../discord/resolvePeriodFromCommand';
|
||||
|
||||
export default function resolvePeriodFromURL(
|
||||
server: RegionDomain,
|
||||
server: Region,
|
||||
urlString: string,
|
||||
) {
|
||||
let statsName: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RegionDomain } from '../../constants/regions';
|
||||
import { Region } from '../../constants/regions';
|
||||
import { ResolvedPlayer } from '../discord/resolvePlayerFromCommand';
|
||||
|
||||
export default function resolvePlayerFromURL(urlString: string) {
|
||||
@@ -6,6 +6,6 @@ export default function resolvePlayerFromURL(urlString: string) {
|
||||
|
||||
return {
|
||||
id: parseInt(url.searchParams.get('id')!),
|
||||
server: url.searchParams.get('server') as RegionDomain,
|
||||
region: url.searchParams.get('region') as Region,
|
||||
} satisfies ResolvedPlayer;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export const DISCORD_TOKEN = process.env.DISCORD_TOKEN!;
|
||||
export const WARGAMING_APPLICATION_ID = process.env.WARGAMING_APPLICATION_ID!;
|
||||
|
||||
if (!DISCORD_TOKEN) {
|
||||
throw new Error('No Discord token provided');
|
||||
}
|
||||
if (!WARGAMING_APPLICATION_ID) {
|
||||
throw new Error('No Wargaming application id provided}');
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export const SECRET_KEYS = [
|
||||
'DISCORD_TOKEN',
|
||||
'WARGAMING_APPLICATION_ID',
|
||||
'GH_TOKEN',
|
||||
] as const;
|
||||
|
||||
export type SECRET = typeof SECRET_KEYS extends readonly (infer T)[]
|
||||
? T
|
||||
: never;
|
||||
|
||||
export const secrets = SECRET_KEYS.reduce<Partial<Record<SECRET, string>>>(
|
||||
(accumulator, key) => ({
|
||||
...accumulator,
|
||||
|
||||
get [key](): string {
|
||||
if (!(key in process.env)) throw new Error(`Key ${key} not provided`);
|
||||
return process.env[key]!;
|
||||
},
|
||||
}),
|
||||
{},
|
||||
) as Record<SECRET, string>;
|
||||
@@ -15,7 +15,7 @@ export default async function (member: GuildMember) {
|
||||
.setColor(ACCENT_COLOR)
|
||||
.setTitle(`Welcome ${member.user.username}`)
|
||||
.setDescription(
|
||||
`Welcome to the Skilled server, **${member.user.username}**! To continue, please use the \`/verify\` command.\n\nExample: \`/verify Europe Sabina244\``,
|
||||
`Welcome to the Skilled server, **${member.user.username}**! To continue, please use the \`/verify\` command.\n\nExample: \`/verify Sabina244\``,
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ChatInputCommandInteraction,
|
||||
} from 'discord.js';
|
||||
import { commands } from '..';
|
||||
import discord from '../../../../discord.json' assert { type: 'json' };
|
||||
import embedNegative from '../../../core/discord/embedNegative';
|
||||
import embedWarning from '../../../core/discord/embedWarning';
|
||||
import normalizeInteractionReturnable from '../../../core/discord/normalizeInteractionReturnable';
|
||||
@@ -21,6 +22,19 @@ export default async function handleChatInputCommand(
|
||||
await interaction.deferReply();
|
||||
|
||||
try {
|
||||
if (registry.inPreview && interaction.guildId !== discord.tres_guild_id) {
|
||||
interaction.editReply({
|
||||
embeds: [
|
||||
embedWarning(
|
||||
`\`/${registry.command.name}\` is in Public Preview`,
|
||||
'[Join the official Discord server](https://discord.gg/nDt7AjGJQH) to gain early access to this command before it is released to the public.',
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const returnable = await registry.handler(interaction);
|
||||
|
||||
if (registry.handlesInteraction) return;
|
||||
|
||||
@@ -23,15 +23,16 @@ import { ownedTanksCommand } from '../../commands/ownedTanks';
|
||||
import { pingCommand } from '../../commands/ping';
|
||||
import { playerAchievementsCommand } from '../../commands/playerAchievements';
|
||||
import { playerInfoCommand } from '../../commands/playerInfo';
|
||||
import { ratingsCommand } from '../../commands/ratings';
|
||||
import { searchClansCommand } from '../../commands/searchClans';
|
||||
import { searchPlayersCommand } from '../../commands/searchPlayers';
|
||||
import { searchTanksCommand } from '../../commands/searchTanks';
|
||||
import { statsCommand } from '../../commands/stats';
|
||||
import { todayCommand } from '../../commands/today';
|
||||
import { verifyCommand } from '../../commands/verify';
|
||||
import { DISCORD_TOKEN } from '../../core/node/arguments';
|
||||
import getClientId from '../../core/node/getClientId';
|
||||
import isDev from '../../core/node/isDev';
|
||||
import { secrets } from '../../core/node/secrets';
|
||||
import handleAutocomplete from './handlers/autocomplete';
|
||||
import handleButton from './handlers/button';
|
||||
import handleChatInputCommand from './handlers/chatInputCommand';
|
||||
@@ -56,6 +57,7 @@ export interface Registry {
|
||||
export interface CommandRegistry<HandlesInteraction extends boolean = boolean>
|
||||
extends Registry {
|
||||
inPublic: boolean;
|
||||
inPreview?: boolean;
|
||||
handlesInteraction?: HandlesInteraction;
|
||||
command:
|
||||
| SlashCommandBuilder
|
||||
@@ -69,7 +71,7 @@ export interface CommandRegistry<HandlesInteraction extends boolean = boolean>
|
||||
button?: (interaction: ButtonInteraction<CacheType>) => InteractionReturnable;
|
||||
}
|
||||
|
||||
const rest = new REST().setToken(DISCORD_TOKEN);
|
||||
const rest = new REST().setToken(secrets.DISCORD_TOKEN);
|
||||
|
||||
export const COMMANDS_RAW: CommandRegistry[] = [
|
||||
debugCommand,
|
||||
@@ -88,6 +90,7 @@ export const COMMANDS_RAW: CommandRegistry[] = [
|
||||
pingCommand,
|
||||
evolutionCommand,
|
||||
statsCommand,
|
||||
ratingsCommand,
|
||||
];
|
||||
export const commands: Record<string, CommandRegistry> = COMMANDS_RAW.reduce(
|
||||
(accumulator, registry) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import NoData, { NoDataType } from '../components/NoData';
|
||||
import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import Wrapper from '../components/Wrapper';
|
||||
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||
import { REGION_NAMES } from '../constants/regions';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
import resolveTankName from '../core/blitz/resolveTankName';
|
||||
import { tankopedia } from '../core/blitz/tankopedia';
|
||||
@@ -11,7 +11,7 @@ import getPlayerHistories from '../core/blitzstars/getPlayerHistories';
|
||||
import getTankHistories from '../core/blitzstars/getTankHistories';
|
||||
import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand';
|
||||
import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { AccountInfo } from '../types/accountInfo';
|
||||
import { Histories } from '../types/histories';
|
||||
import { PlayerClanData } from '../types/playerClanData';
|
||||
@@ -20,7 +20,7 @@ import { StatType } from './fullStats';
|
||||
export default async function evolution<Type extends StatType>(
|
||||
type: Type,
|
||||
{ start, end, evolutionName }: ResolvedPeriod,
|
||||
{ server, id }: ResolvedPlayer,
|
||||
{ region: server, id }: ResolvedPlayer,
|
||||
tankId: Type extends 'tank' ? number : null,
|
||||
) {
|
||||
let nameDiscriminator: string | undefined;
|
||||
@@ -29,7 +29,7 @@ export default async function evolution<Type extends StatType>(
|
||||
if (type === 'player') {
|
||||
const clan = (
|
||||
await getWargamingResponse<PlayerClanData>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
)
|
||||
)[id]?.clan;
|
||||
|
||||
@@ -79,7 +79,7 @@ export default async function evolution<Type extends StatType>(
|
||||
const minX = Math.min(...xs);
|
||||
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -89,7 +89,7 @@ export default async function evolution<Type extends StatType>(
|
||||
nameDiscriminator={nameDiscriminator}
|
||||
image={image}
|
||||
description={`${evolutionName} • ${new Date().toDateString()} • ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
REGION_NAMES[server]
|
||||
}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||
import TierWeights, { TierWeightsRecord } from '../components/TierWeights';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import Wrapper from '../components/Wrapper';
|
||||
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||
import { REGION_NAMES } from '../constants/regions';
|
||||
import calculateWN8 from '../core/blitz/calculateWN8';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
import resolveTankName from '../core/blitz/resolveTankName';
|
||||
@@ -14,7 +14,7 @@ import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats';
|
||||
import { tankAverages } from '../core/blitzstars/tankAverages';
|
||||
import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand';
|
||||
import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import {
|
||||
AccountInfo,
|
||||
AllStats,
|
||||
@@ -28,7 +28,7 @@ export type StatType = 'player' | 'tank';
|
||||
export default async function fullStats<Type extends StatType>(
|
||||
type: Type,
|
||||
{ start, end, statsName }: ResolvedPeriod,
|
||||
{ server, id }: ResolvedPlayer,
|
||||
{ region: server, id }: ResolvedPlayer,
|
||||
tankId: Type extends 'tank' ? number : null,
|
||||
) {
|
||||
let nameDiscriminator: string | undefined;
|
||||
@@ -37,7 +37,7 @@ export default async function fullStats<Type extends StatType>(
|
||||
if (type === 'player') {
|
||||
const clan = (
|
||||
await getWargamingResponse<PlayerClanData>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
)
|
||||
)[id]?.clan;
|
||||
|
||||
@@ -133,7 +133,7 @@ export default async function fullStats<Type extends StatType>(
|
||||
}
|
||||
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -143,7 +143,7 @@ export default async function fullStats<Type extends StatType>(
|
||||
nameDiscriminator={nameDiscriminator}
|
||||
image={image}
|
||||
description={`${statsName} • ${new Date().toDateString()} • ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
REGION_NAMES[server]
|
||||
}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||
import { TierWeightsRecord } from '../components/TierWeights';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import Wrapper from '../components/Wrapper';
|
||||
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||
import { REGION_NAMES } from '../constants/regions';
|
||||
import calculateWN8 from '../core/blitz/calculateWN8';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
import resolveTankName from '../core/blitz/resolveTankName';
|
||||
@@ -14,7 +14,7 @@ import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats';
|
||||
import { tankAverages } from '../core/blitzstars/tankAverages';
|
||||
import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand';
|
||||
import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { theme } from '../stitches.config';
|
||||
import {
|
||||
AccountInfo,
|
||||
@@ -29,7 +29,7 @@ export type StatType = 'player' | 'tank';
|
||||
export default async function stats<Type extends StatType>(
|
||||
type: Type,
|
||||
{ start, end, statsName }: ResolvedPeriod,
|
||||
{ server, id }: ResolvedPlayer,
|
||||
{ region: server, id }: ResolvedPlayer,
|
||||
tankId: Type extends 'tank' ? number : null,
|
||||
naked = false,
|
||||
) {
|
||||
@@ -39,7 +39,7 @@ export default async function stats<Type extends StatType>(
|
||||
if (type === 'player') {
|
||||
const clan = (
|
||||
await getWargamingResponse<PlayerClanData>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
)
|
||||
)[id]?.clan;
|
||||
|
||||
@@ -137,7 +137,7 @@ export default async function stats<Type extends StatType>(
|
||||
}
|
||||
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const overview = (
|
||||
<AllStatsOverview stats={stats} supplementaryStats={supplementaryStats} />
|
||||
@@ -164,7 +164,7 @@ export default async function stats<Type extends StatType>(
|
||||
nameDiscriminator={nameDiscriminator}
|
||||
image={image}
|
||||
description={`${statsName} • ${new Date().toDateString()} • ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
REGION_NAMES[server]
|
||||
}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import NoData, { NoDataType } from '../components/NoData';
|
||||
import PoweredBy, { PoweredByType } from '../components/PoweredBy';
|
||||
import TitleBar from '../components/TitleBar';
|
||||
import Wrapper from '../components/Wrapper';
|
||||
import { REGION_DOMAIN_NAMES } from '../constants/regions';
|
||||
import { REGION_NAMES } from '../constants/regions';
|
||||
import calculateWN8 from '../core/blitz/calculateWN8';
|
||||
import getTankStats from '../core/blitz/getTankStats';
|
||||
import getWargamingResponse from '../core/blitz/getWargamingResponse';
|
||||
@@ -15,13 +15,13 @@ import getPeriodNow from '../core/blitzstars/getPeriodNow';
|
||||
import getTimeDaysAgo from '../core/blitzstars/getTimeDaysAgo';
|
||||
import { tankAverages } from '../core/blitzstars/tankAverages';
|
||||
import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand';
|
||||
import { WARGAMING_APPLICATION_ID } from '../core/node/arguments';
|
||||
import { secrets } from '../core/node/secrets';
|
||||
import { AccountInfo, AllStats } from '../types/accountInfo';
|
||||
import { PlayerClanData } from '../types/playerClanData';
|
||||
import { PossiblyPromise } from '../types/possiblyPromise';
|
||||
|
||||
export default async function today(
|
||||
{ server, id }: ResolvedPlayer,
|
||||
{ region: server, id }: ResolvedPlayer,
|
||||
limit = Infinity,
|
||||
naked?: boolean,
|
||||
) {
|
||||
@@ -32,10 +32,10 @@ export default async function today(
|
||||
getPeriodNow(),
|
||||
);
|
||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||
);
|
||||
const clanData = await getWargamingResponse<PlayerClanData>(
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${secrets.WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
|
||||
);
|
||||
const careerTankStatsRaw = await getTankStats(server, id);
|
||||
const careerStats: Record<number, AllStats> = {
|
||||
@@ -162,7 +162,7 @@ export default async function today(
|
||||
: undefined
|
||||
}
|
||||
description={`Today's breakdown • ${new Date().toDateString()} • ${
|
||||
REGION_DOMAIN_NAMES[server]
|
||||
REGION_NAMES[server]
|
||||
}`}
|
||||
/>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export const statsRoute: RouteRegistry = {
|
||||
async handler(req) {
|
||||
const type = req.params.type as StatType;
|
||||
const player = resolvePlayerFromRequest(req);
|
||||
const period = resolvePeriodFromRequest(player.server, req);
|
||||
const period = resolvePeriodFromRequest(player.region, req);
|
||||
|
||||
return await stats(
|
||||
type,
|
||||
|
||||
+1
-4
@@ -11,8 +11,5 @@
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/components/AllStatsOverview/components/WN8SurroundingStat.tsx"
|
||||
]
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -933,6 +933,11 @@ accepts@~1.3.8:
|
||||
mime-types "~2.1.34"
|
||||
negotiator "0.6.3"
|
||||
|
||||
acorn@^8.9.0:
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
|
||||
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
|
||||
|
||||
algoliasearch@^4.0.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.18.0.tgz#1183ad0384a5b2c14f381c3a361da611acc8edb3"
|
||||
@@ -1013,6 +1018,11 @@ [email protected]:
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
|
||||
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
|
||||
|
||||
cac@^6.7.14:
|
||||
version "6.7.14"
|
||||
resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
|
||||
integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
|
||||
|
||||
call-bind@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
@@ -1089,6 +1099,13 @@ [email protected]:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
[email protected]:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
@@ -1252,6 +1269,11 @@ express@^4.18.2:
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
"false@^0.0.4":
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/false/-/false-0.0.4.tgz#8f55de5af5c03f50bf68aed57ed80e3d2176a517"
|
||||
integrity sha512-CPAG3ud9Cz8NvLKlsTLr/F1aoJL6tPyhNTzaDFzS5CX6id21hVuS5sBK8wlTLuvFklpvH1idvPbW8Lbri3G7qA==
|
||||
|
||||
fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
@@ -1475,11 +1497,26 @@ minisearch@^6.1.0:
|
||||
resolved "https://registry.yarnpkg.com/minisearch/-/minisearch-6.1.0.tgz#6e74e743dbd0e88fa5ca52fef2391e0aa7055252"
|
||||
integrity sha512-PNxA/X8pWk+TiqPbsoIYH0GQ5Di7m6326/lwU/S4mlo4wGQddIcf/V//1f9TB0V4j59b57b+HZxt8h3iMROGvg==
|
||||
|
||||
mlly@^1.2.0, mlly@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.4.0.tgz#830c10d63f1f97bd8785377b24dc2a15d972832b"
|
||||
integrity sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==
|
||||
dependencies:
|
||||
acorn "^8.9.0"
|
||||
pathe "^1.1.1"
|
||||
pkg-types "^1.0.3"
|
||||
ufo "^1.1.2"
|
||||
|
||||
[email protected]:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
|
||||
|
||||
[email protected]:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
[email protected]:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
@@ -1537,6 +1574,11 @@ [email protected]:
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
||||
|
||||
pathe@^1.1.0, pathe@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
|
||||
integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
|
||||
|
||||
peek-readable@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec"
|
||||
@@ -1547,6 +1589,15 @@ picocolors@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||
|
||||
pkg-types@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"
|
||||
integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==
|
||||
dependencies:
|
||||
jsonc-parser "^3.2.0"
|
||||
mlly "^1.2.0"
|
||||
pathe "^1.1.0"
|
||||
|
||||
postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
@@ -1561,6 +1612,15 @@ postcss@^8.1.10, postcss@^8.4.23:
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
postcss@^8.4.25:
|
||||
version "8.4.26"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.26.tgz#1bc62ab19f8e1e5463d98cf74af39702a00a9e94"
|
||||
integrity sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==
|
||||
dependencies:
|
||||
nanoid "^3.3.6"
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
preact@^10.0.0:
|
||||
version "10.15.1"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-10.15.1.tgz#a1de60c9fc0c79a522d969c65dcaddc5d994eede"
|
||||
@@ -1631,6 +1691,13 @@ rollup@^3.21.0:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rollup@^3.25.2:
|
||||
version "3.26.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.26.2.tgz#2e76a37606cb523fc9fef43e6f59c93f86d95e7c"
|
||||
integrity sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
[email protected], safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
@@ -1790,6 +1857,11 @@ typescript@^5.0.4:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
|
||||
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
|
||||
|
||||
ufo@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76"
|
||||
integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==
|
||||
|
||||
undici@^5.22.0:
|
||||
version "5.22.1"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b"
|
||||
@@ -1830,6 +1902,29 @@ vary@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
||||
|
||||
vite-node@^0.33.0:
|
||||
version "0.33.0"
|
||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.33.0.tgz#c6a3a527e0b8090da7436241bc875760ae0eef28"
|
||||
integrity sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw==
|
||||
dependencies:
|
||||
cac "^6.7.14"
|
||||
debug "^4.3.4"
|
||||
mlly "^1.4.0"
|
||||
pathe "^1.1.1"
|
||||
picocolors "^1.0.0"
|
||||
vite "^3.0.0 || ^4.0.0"
|
||||
|
||||
"vite@^3.0.0 || ^4.0.0":
|
||||
version "4.4.4"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.4.tgz#b76e6049c0e080cb54e735ad2d18287753752118"
|
||||
integrity sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==
|
||||
dependencies:
|
||||
esbuild "^0.18.10"
|
||||
postcss "^8.4.25"
|
||||
rollup "^3.25.2"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
vite@^4.3.9:
|
||||
version "4.3.9"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d"
|
||||
|
||||
Reference in new issue
Block a user