feat: swap core and _core

This commit is contained in:
TrèsAbhi committed 2023-10-14 11:06:03 -05:00
1 parent 07e3660388
commit b827f7f730
113 files changed
+254 -247

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
import throwError from '../../LEGACY_core/node/throwError';
export type BlitzResponse<Data extends object> =
| {
status: 'error';
error: { field: string; message: string; code: 402; value: null };
}
| {
status: 'ok';
data: Data;
};
export default async function fetchBlitz<Data extends object>(url: string) {
const data = (await fetch(url).then((response) =>
response.json(),
)) as BlitzResponse<Data>;
if (data.status === 'ok') {
return data.data;
} else {
throw throwError(
`Wargaming response error status:"${data.status}"`,
`Message: "${data.error.message}"\nURL: "${url}"`,
);
}
}
+36
View File
@@ -0,0 +1,36 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import fetchBlitz from './fetchWargaming';
import { normalizeIds } from './normalizeIds';
interface IndividualAccountAchievements {
achievements: {
[achievement: string]: number;
};
max_series: {
[achievement: string]: number;
};
}
export interface AccountAchievements {
[id: number]: IndividualAccountAchievements;
}
export async function getAccountAchievements<
Ids extends number | number[],
ReturnType = Ids extends number
? IndividualAccountAchievements
: IndividualAccountAchievements[],
>(region: Region, ids: Ids) {
const object = await fetchBlitz<AccountAchievements>(
`https://api.wotblitz.${region}/wotb/account/achievements/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${normalizeIds(
ids,
)}`,
);
if (typeof ids === 'number') {
return object[ids as number] as ReturnType;
} else {
return ids.map((id) => object[id]) as ReturnType;
}
}
+89
View File
@@ -0,0 +1,89 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import fetchBlitz from './fetchWargaming';
import { normalizeIds } from './normalizeIds';
export interface AllStats {
spotted: number;
hits: number;
frags: number;
max_xp: number;
wins: number;
losses: number;
capture_points: number;
battles: number;
damage_dealt: number;
damage_received: number;
max_frags: number;
shots: number;
frags8p: number;
xp: number;
win_and_survived: number;
survived_battles: number;
dropped_capture_points: number;
}
export interface SupplementaryStats {
// if undefined, WN8 is not available for tanks without averages
WN8?: number;
tier?: number;
}
export interface ClanStats {
spotted: number;
max_frags_tank_id: number;
hits: number;
frags: number;
max_xp: number;
max_xp_tank_id: number;
wins: number;
losses: number;
capture_points: number;
battles: number;
damage_dealt: number;
damage_received: number;
max_frags: number;
shots: number;
frags8p: number;
xp: number;
win_and_survived: number;
survived_battles: number;
dropped_capture_points: number;
}
interface IndividualAccountInfo {
account_id: number;
created_at: number;
updated_at: number;
private: null;
last_battle_time: number;
nickname: string;
statistics: {
all: AllStats;
clan: ClanStats;
frags: null | number;
};
}
export interface AccountInfo {
[accountId: number]: IndividualAccountInfo;
}
export async function getAccountInfo<
Ids extends number | number[],
ReturnType = Ids extends number
? IndividualAccountInfo
: IndividualAccountInfo[],
>(region: Region, ids: Ids) {
const object = await fetchBlitz<AccountInfo>(
`https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${normalizeIds(
ids,
)}`,
);
if (typeof ids === 'number') {
return object[ids as number] as ReturnType;
} else {
return ids.map((id) => object[id]) as ReturnType;
}
}
+45
View File
@@ -0,0 +1,45 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import fetchBlitz from './fetchWargaming';
import { normalizeExtra } from './normalizeExtra';
import { normalizeIds } from './normalizeIds';
export type IndividualClanAccountInfo = {
account_id: number;
joined_at: number;
clan_id: number;
role: string;
account_name: string;
clan?: {
members_count: number;
name: string;
created_at: number;
tag: string;
clan_id: number;
emblem_set_id: number;
};
} | null;
export interface ClanAccountInfo {
[accountId: number]: IndividualClanAccountInfo;
}
export async function getClanAccountInfo<
Ids extends number | number[],
ReturnType = Ids extends number
? IndividualClanAccountInfo
: IndividualClanAccountInfo[],
>(region: Region, ids: Ids, extra?: string[]) {
const object = await fetchBlitz<ClanAccountInfo>(
`https://api.wotblitz.${region}/wotb/clans/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${normalizeIds(
ids,
)}${normalizeExtra(extra)}`,
);
if (typeof ids === 'number') {
return object[ids as number] as ReturnType;
} else {
return ids.map((id) => object[id]) as ReturnType;
}
}
+54
View File
@@ -0,0 +1,54 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import fetchBlitz from './fetchWargaming';
import { normalizeIds } from './normalizeIds';
export interface IndividualClanInfo {
recruiting_options: {
vehicles_level: number;
wins_ratio: number;
average_battles_per_day: number;
battles: number;
average_damage: number;
};
members_count: number;
name: string;
creator_name: string;
clan_id: number;
created_at: number;
updated_at: number;
leader_name: string;
members_ids: number[];
recruiting_policy: string;
tag: string;
is_clan_disbanded: boolean;
old_name: string;
emblem_set_id: number;
creator_id: number;
motto: string;
renamed_at: number;
old_tag: null;
leader_id: number;
description: string;
}
export interface ClanInfo {
[clanId: number]: IndividualClanInfo;
}
export async function getClanInfo<
Ids extends number | number[],
ReturnType = Ids extends number ? IndividualClanInfo : IndividualClanInfo[],
>(region: Region, ids: Ids) {
const object = await fetchBlitz<ClanInfo>(
`https://api.wotblitz.${region}/wotb/clans/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${normalizeIds(
ids,
)}`,
);
if (typeof ids === 'number') {
return object[ids as number] as ReturnType;
} else {
return ids.map((id) => object[id]) as ReturnType;
}
}
+13
View File
@@ -0,0 +1,13 @@
import { RatingsInfo } from '../../commands/ratings';
import { Region } from '../../constants/regions';
import regionToRegionSubdomain from '../../LEGACY_core/blitz/regionToRegionSubdomain';
export default async function getRatingsInfo(region: Region) {
const response = await fetch(
`https://${regionToRegionSubdomain(
region,
)}.wotblitz.com/en/api/rating-leaderboards/season/`,
);
return (await response.json()) as Promise<RatingsInfo>;
}
+20
View File
@@ -0,0 +1,20 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import throwError from '../../LEGACY_core/node/throwError';
import { TanksStats } from '../../types/tanksStats';
import fetchBlitz from './fetchWargaming';
export default async function getTankStats(region: Region, id: number) {
const tankStats = await fetchBlitz<TanksStats>(
`https://api.wotblitz.${region}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
);
if (tankStats[id] === null) {
throw throwError(
'No tank stats available',
"This player doesn't have any stats for tanks available. This may not be the player you are looking for.",
);
}
return tankStats[id];
}
+11
View File
@@ -0,0 +1,11 @@
import { TreeTypeEnum } from '../../components/Tanks';
import { tankopedia } from '../../LEGACY_core/blitz/tankopedia';
export default async function getTreeType(id: number) {
const entry = (await tankopedia)[id];
if (entry?.is_collectible) return TreeTypeEnum.Collector;
if (entry?.is_premium) return TreeTypeEnum.Premium;
return TreeTypeEnum.TechTree;
}
-21
View File
@@ -1,21 +0,0 @@
import { Percentile } from '../../constants/percentiles';
export const WN8_PERCENTILES: [number, Percentile][] = [
[0, Percentile.VeryBad],
[300, Percentile.Bad],
[450, Percentile.BelowAverage],
[650, Percentile.Average],
[900, Percentile.AboveAverage],
[1200, Percentile.Good],
[1600, Percentile.VeryGood],
[2000, Percentile.Great],
[2450, Percentile.Unicum],
[2900, Percentile.SuperUnicum],
];
export default function getWN8Percentile(WN8: number) {
const lastIndex = WN8_PERCENTILES.findLastIndex(
([WN8Listing]) => WN8Listing <= WN8,
);
return WN8_PERCENTILES[lastIndex === -1 ? 0 : lastIndex][1];
}
-51
View File
@@ -1,51 +0,0 @@
import fetchBlitz from '../../_core/blitz/fetchWargaming';
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import { Clan, ClanList } from '../../types/clanList';
export type ClanListWithRegion = (Clan & {
region: 'com' | 'eu' | 'asia';
})[];
export default async function listClans(search: string, limit = 9) {
const trimmed = search.trim();
if (trimmed.length < 3 && trimmed.length > 100) return [];
const normalizedLimit = Math.round(limit / 3);
return (
await Promise.all([
fetchBlitz<ClanList>(
`https://api.wotblitz.com/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
).then(
(value) =>
value &&
value.map((account) => ({
...account,
region: 'com' as Region,
})),
),
fetchBlitz<ClanList>(
`https://api.wotblitz.eu/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
).then(
(value) =>
value &&
value.map((account) => ({
...account,
region: 'eu' as Region,
})),
),
fetchBlitz<ClanList>(
`https://api.wotblitz.asia/wotb/clans/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${search}&limit=${normalizedLimit}`,
).then(
(value) =>
value &&
value.map((account) => ({
...account,
region: 'asia' as Region,
})),
),
])
)
.filter(Boolean)
.flat();
}
-62
View File
@@ -1,62 +0,0 @@
import fetchBlitz from '../../_core/blitz/fetchWargaming';
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
interface Account {
nickname: string;
account_id: number;
}
export type AccountList = Account[];
export type AccountListWithServer = (Account & {
region: Region;
})[];
export const usernamePattern = /^[a-zA-Z0-9_]{3,24}$/;
export const usernamePatternWithoutPosition = /[a-zA-Z0-9_]{3,24}/;
export default async function listPlayers(search: string, limit = 9) {
const trimmedSearch = search.trim();
const normalizedLimit = Math.round(limit / 3);
const encodedSearch = encodeURIComponent(trimmedSearch);
if (usernamePattern.test(trimmedSearch)) {
return (
await Promise.all([
fetchBlitz<AccountList>(
`https://api.wotblitz.com/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
).then(
(value) =>
value &&
value.map((account) => ({
...account,
region: 'com' as Region,
})),
),
fetchBlitz<AccountList>(
`https://api.wotblitz.eu/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
).then(
(value) =>
value &&
value.map((account) => ({
...account,
region: 'eu' as Region,
})),
),
fetchBlitz<AccountList>(
`https://api.wotblitz.asia/wotb/account/list/?application_id=${WARGAMING_APPLICATION_ID}&search=${encodedSearch}&limit=${normalizedLimit}`,
).then(
(value) =>
value &&
value.map((account) => ({
...account,
region: 'asia' as Region,
})),
),
])
)
.filter(Boolean)
.flat() as AccountListWithServer;
} else {
return [] as AccountListWithServer;
}
}
+3
View File
@@ -0,0 +1,3 @@
export function normalizeExtra(extra?: string[]) {
return extra ? `&extra=${extra.join(',')}` : '';
}
+3
View File
@@ -0,0 +1,3 @@
export function normalizeIds(ids: number | number[]): string {
return typeof ids === 'number' ? ids.toString() : ids.join(',');
}
-32
View File
@@ -1,32 +0,0 @@
/**
* Thanks, August!
* https://stackoverflow.com/a/41358305/12294756
*/
const ROMAN = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1,
};
const ROMAN_KEYS = Object.keys(ROMAN) as (keyof typeof ROMAN)[];
export default function numberToRomanNumeral(value: number) {
let string = '';
ROMAN_KEYS.forEach((index) => {
const part = Math.floor(value / ROMAN[index]);
value -= part * ROMAN[index];
string += index.repeat(part);
});
return string;
}
@@ -1,7 +0,0 @@
import { Region, RegionSubdomain } from '../../constants/regions';
export default function regionToRegionSubdomain(
regionDomain: Region,
): RegionSubdomain {
return regionDomain === 'com' ? 'na' : regionDomain;
}
-29
View File
@@ -1,29 +0,0 @@
import { go } from 'fuzzysort';
import throwError from '../node/throwError';
import { TANKS, tankopedia } from './tankopedia';
export default async function resolveTankId(tank: string) {
const number = parseInt(tank);
if (Number.isNaN(number)) {
const searchResult = go(tank, await TANKS, { keys: ['name'], limit: 1 });
if (searchResult.length === 0) {
throw throwError(
'Tank not found',
`Could not find tank by the name "${tank}".`,
);
} else {
return searchResult[0].obj.tank_id;
}
} else {
if ((await tankopedia)[number]) {
return number;
} else {
throw throwError(
'Tank not found',
`Could not find tank by the ID "${number}".`,
);
}
}
}
-6
View File
@@ -1,6 +0,0 @@
import { tankopedia } from './tankopedia';
export default async function resolveTankName(tankId: number) {
const tankopediaName = (await tankopedia)[tankId]?.name;
return tankopediaName ? tankopediaName : `Unknown Tank ${tankId}`;
}
-33
View File
@@ -1,33 +0,0 @@
import { AllStats } from '../../_core/blitz/getAccountInfo';
import { emptyAllStats } from '../blitzstars/getDiffedTankStats';
export default function sumStats(allStatsCollection: AllStats[]) {
return allStatsCollection.reduce((accumulated, current) => {
function sum(value: (allStats: AllStats) => number) {
return value(accumulated) + value(current);
}
function max(value: (allStats: AllStats) => number) {
return Math.max(value(accumulated), value(current));
}
return {
battles: sum((a) => a.battles),
wins: sum((a) => a.wins),
capture_points: sum((a) => a.capture_points),
damage_dealt: sum((a) => a.damage_dealt),
damage_received: sum((a) => a.damage_received),
dropped_capture_points: sum((a) => a.dropped_capture_points),
frags: sum((a) => a.frags),
frags8p: sum((a) => a.frags8p),
hits: sum((a) => a.hits),
losses: sum((a) => a.losses),
max_frags: max((a) => a.max_frags),
max_xp: max((a) => a.max_xp),
shots: sum((a) => a.shots),
spotted: sum((a) => a.spotted),
survived_battles: sum((a) => a.survived_battles),
win_and_survived: sum((a) => a.win_and_survived),
xp: sum((a) => a.xp),
} satisfies AllStats;
}, emptyAllStats);
}
-113
View File
@@ -1,113 +0,0 @@
import { deburr } from 'lodash';
import fetchBlitz from '../../_core/blitz/fetchWargaming';
import { TreeTypeString } from '../../components/Tanks';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import { context } from '../blitzkrieg/context';
export interface TankopediaEntry {
name: string;
nation: string;
is_premium: boolean;
is_collectible: boolean;
tier: Tier;
cost: { price_credit: number; price_gold: number };
images: { preview: string; normal: string };
tank_id: number;
type: string;
description: string;
}
type asd = TreeTypeString;
export interface Tankopedia {
[id: number]: TankopediaEntry | undefined;
}
console.log('Caching tankopedia...');
export const tankopedia = fetch(
context === 'bot'
? 'https://www.blitzstars.com/bs-tankopedia.json'
: '/api/tankopedia',
)
.then(async (response) => response.json())
.then((wrapperTankopedia) => {
console.log('Tankopedia cached');
return context === 'bot'
? (wrapperTankopedia as { data: Tankopedia }).data
: (wrapperTankopedia as Tankopedia);
});
const entries = new Promise<TankopediaEntry[]>(async (resolve) => {
resolve(Object.entries(await tankopedia).map(([, entry]) => entry));
});
export const TANKS = new Promise<TankopediaEntry[]>(async (resolve) => {
resolve((await entries).map((entry) => entry));
});
export const TANK_NAMES = new Promise<string[]>(async (resolve) => {
resolve((await entries).map(({ name }) => name));
});
export const TANK_NAMES_DIACRITICS = TANK_NAMES.then((tankNames) =>
Promise.all(
tankNames.map(async (tankName, index) => {
const id = (await TANKS)[index].tank_id;
const name = tankName ?? `Unknown Tank ${id}`;
const diacriticless = deburr(name);
return {
original: name,
diacriticless,
combined: `${name}${diacriticless}`,
id,
};
}),
),
);
export interface TankopediaInfo {
achievement_sections: Record<string, { name: string; order: number }>;
tanks_updated_at: number;
languages: Record<string, string>;
vehicle_types: Record<string, string>;
vehicle_nations: Record<string, string>;
game_version: string;
}
// this is blocking because info is needed for command creation
console.log('Caching tankopedia info...');
export const tankopediaInfo = fetchBlitz<TankopediaInfo>(
`https://api.wotblitz.com/wotb/encyclopedia/info/?application_id=${WARGAMING_APPLICATION_ID}`,
);
console.log('Cached tankopedia info');
export const TANK_ICONS: Record<string, string> = {
'AT-SPG': 'https://i.imgur.com/BIHSEH0.png',
lightTank: 'https://i.imgur.com/CSNha5V.png',
mediumTank: 'https://i.imgur.com/wvf3ltm.png',
heavyTank: 'https://i.imgur.com/ECeqlZa.png',
};
export const TANK_ICONS_PREMIUM: Record<string, string> = {
'AT-SPG': 'https://i.imgur.com/TCu3EdR.png',
lightTank: 'https://i.imgur.com/zdkpTRb.png',
mediumTank: 'https://i.imgur.com/3z7eHX6.png',
heavyTank: 'https://i.imgur.com/P3vbmyA.png',
};
export const TANK_ICONS_COLLECTOR: Record<string, string> = {
'AT-SPG': 'https://i.imgur.com/WTjeirB.png',
lightTank: 'https://i.imgur.com/EwhtKkU.png',
mediumTank: 'https://i.imgur.com/u8YDMBh.png',
heavyTank: 'https://i.imgur.com/8xRf3nc.png',
};
export type Tier = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
export const TIER_ROMAN_NUMERALS: Record<Tier, string> = {
1: 'I',
2: 'II',
3: 'III',
4: 'IV',
5: 'V',
6: 'VI',
7: 'VII',
8: 'VIII',
9: 'IX',
10: 'X',
};