feat: core chunk 1

This commit is contained in:
TrèsAbhi committed 2023-10-12 13:00:33 -05:00
1 parent 7447ccc487
commit e96538bae0
31 files changed
+463 -466

No files matched your search

+36
View File
@@ -0,0 +1,36 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import getWargamingResponse from '../../core/blitz/getWargamingResponse';
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 getWargamingResponse<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 getWargamingResponse from '../../core/blitz/getWargamingResponse';
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 getWargamingResponse<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 getWargamingResponse from '../../core/blitz/getWargamingResponse';
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 getWargamingResponse<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 getWargamingResponse from '../../core/blitz/getWargamingResponse';
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 getWargamingResponse<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;
}
}
+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(',');
}
+38
View File
@@ -0,0 +1,38 @@
import { Region } from '../../constants/regions';
import { AllStats } from '../blitz/getAccountInfo';
export interface IndividualPlayerStats {
clan: { clan_id: number; name: string; tag: string };
statistics: {
all: AllStats;
wn8: number;
wn7: number;
average_tier: number;
};
last_battle_time: number;
nickname: string;
account_id: number;
achievements: {
account_id: number;
[key: string]: number;
};
region: Region;
}
export type PlayerStats = IndividualPlayerStats[];
export async function getPlayerStats<
Ids extends number | number[],
ReturnType = Ids extends number ? PlayerStats : PlayerStats[],
>(region: Region, ids: Ids) {
const stats = await Promise.all(
((typeof ids === 'number' ? [ids] : ids) as number[]).map((id) =>
fetch(`https://www.blitzstars.com/api/playerstats/${id}`).then(
(response) => response.json() as Promise<PlayerStats>,
),
),
);
if (typeof ids === 'number') return stats[0] as ReturnType;
return stats as ReturnType;
}
@@ -1,7 +1,10 @@
import { AllStats } from '../../types/accountInfo';
import { AllStats } from '../blitz/getAccountInfo';
/**
* Formula from https://web.archive.org/web/20190105064817/http://wiki.wnefficiency.net/pages/WN8
*
* @param expected what all players on average perform like
* @param average what the player in question performs like
*/
export default function calculateWN8(expected: AllStats, average: AllStats) {
const avgDmg = average.damage_dealt / average.battles;
@@ -4,10 +4,7 @@ import {
emptyIndividualTankStats,
} from '../../types/tanksStats';
export function diffNormalizedTankStats(
a: NormalizedTankStats,
b: NormalizedTankStats,
) {
export function deltaTankStats(a: NormalizedTankStats, b: NormalizedTankStats) {
const bTanks = Object.keys(b);
const changedTanks = bTanks.filter((id) => {
return (
@@ -1,11 +1,9 @@
// BIG TODO: reorganize code folder
import { TreeTypeString } from '../../components/Tanks';
import { DiffedTankStats } from '../blitzstars/getDiffedTankStats';
import { tankAverages } from '../blitzstars/tankAverages';
import sumStats from '../../core/blitz/sumStats';
import { tankopedia } from '../../core/blitz/tankopedia';
import { DiffedTankStats } from '../../core/blitzstars/getDiffedTankStats';
import { tankAverages } from '../../core/blitzstars/tankAverages';
import calculateWN8 from './calculateWN8';
import sumStats from './sumStats';
import { tankopedia } from './tankopedia';
export interface StatFilters {
nation?: string;
+3 -3
View File
@@ -2,11 +2,11 @@
import { ContextMenu } from '@radix-ui/themes';
import { useEffect, useState } from 'react';
import calculateWN8 from '../../../_core/statistics/calculateWN8';
import { deltaTankStats } from '../../../_core/statistics/deltaTankStats';
import * as Breakdown from '../../../components/Breakdown';
import { TreeTypeEnum } from '../../../components/Tanks';
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
import calculateWN8 from '../../../core/blitz/calculateWN8';
import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats';
import getWN8Percentile from '../../../core/blitz/getWN8Percentile';
import getWargamingResponse from '../../../core/blitz/getWargamingResponse';
import { TankopediaEntry, tankopedia } from '../../../core/blitz/tankopedia';
@@ -111,7 +111,7 @@ export default function SessionPage() {
list: (
Object.values(
diffNormalizedTankStats(session.tankStats, career),
deltaTankStats(session.tankStats, career),
) as IndividualTankStats[]
).map((stats) => {
return {
+87 -91
View File
@@ -1,97 +1,93 @@
import { SlashCommandBuilder, escapeMarkdown } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import { PlayerHistoriesRaw } from '../core/blitzstars/getPlayerHistories';
import getTimeDaysAgo from '../core/blitzstars/getTimeDaysAgo';
import addClanChoices from '../core/discord/addClanChoices';
import autocompleteClan from '../core/discord/autocompleteClan';
import embedWarning from '../core/discord/embedWarning';
import resolveClanFromCommand from '../core/discord/resolveClanFromCommand';
import { CommandRegistry } from '../events/interactionCreate';
import { AccountAchievements } from '../types/accountAchievements';
import { AccountInfo } from '../types/accountInfo';
import { ClanInfo } from '../types/clanInfo';
// _____ _____ _ _ _ _ _____ ____ __ __ ______ ____ _____ _ __
// |_ _| / ____| | | | /\ | | | | / ____/ __ \| \/ | ____| | _ \ /\ / ____| |/ /
// | | | (___ | |__| | / \ | | | | | | | | | | \ / | |__ | |_) | / \ | | | ' /
// | | \___ \| __ | / /\ \ | | | | | | | | | | |\/| | __| | _ < / /\ \| | | <
// _| |_ ____) | | | |/ ____ \| |____| |____ | |___| |__| | | | | |____ | |_) / ____ \ |____| . \
// |_____| |_____/|_| |_/_/ \_\______|______| \_____\____/|_| |_|______| |____/_/ \_\_____|_|\_\
export const clanEventCommand: CommandRegistry = {
inProduction: false,
inPublic: true,
// import { SlashCommandBuilder, escapeMarkdown } from 'discord.js';
// import markdownEscape from 'markdown-escape';
// import { getAccountAchievements } from '../_core/blitz/getAccountAchievements';
// import { getAccountInfo } from '../_core/blitz/getAccountInfo';
// import { getClanInfo } from '../_core/blitz/getClanInfo';
// import { PlayerHistoriesRaw } from '../core/blitzstars/getPlayerHistories';
// import getTimeDaysAgo from '../core/blitzstars/getTimeDaysAgo';
// import addClanChoices from '../core/discord/addClanChoices';
// import autocompleteClan from '../core/discord/autocompleteClan';
// import embedWarning from '../core/discord/embedWarning';
// import resolveClanFromCommand from '../core/discord/resolveClanFromCommand';
// import { CommandRegistry } from '../events/interactionCreate';
command: new SlashCommandBuilder()
.setName('clan-event')
.setDescription('Lists all inactive players')
.addStringOption(addClanChoices),
// export const clanEventCommand: CommandRegistry = {
// inProduction: false,
// inPublic: true,
async handler(interaction) {
const { region, id } = await resolveClanFromCommand(interaction);
const clan = (
await getWargamingResponse<ClanInfo>(
`https://api.wotblitz.${region}/wotb/clans/info/?application_id=${WARGAMING_APPLICATION_ID}&clan_id=${id}`,
)
)[id];
const memberIds = clan.members_ids.join(',');
const [players, achievements, previousJointVictories] = await Promise.all([
getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${memberIds}`,
),
getWargamingResponse<AccountAchievements>(
`https://api.wotblitz.${region}/wotb/account/achievements/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${memberIds}`,
),
Promise.all(
clan.members_ids.map((id) =>
fetch(`https://www.blitzstars.com/api/playerstats/${id}`)
.then((response) => response.json() as Promise<PlayerHistoriesRaw>)
.then((data) => ({
id,
jointVictory: data[0]?.achievements.jointVictoryCount as
| number
| undefined,
})),
),
).then((data) =>
data.reduce<Record<number, number | undefined>>(
(accumulator, value) => ({
...accumulator,
[value.id]: value.jointVictory,
}),
{},
),
),
]);
const jointVictories = clan.members_ids.map((id) => ({
id,
joinVictory:
players[id].last_battle_time > getTimeDaysAgo(region, 1)
? previousJointVictories[id] === undefined
? 0
: achievements[id].max_series.jointVictory -
previousJointVictories[id]!
: 0,
}));
// command: new SlashCommandBuilder()
// .setName('clan-event')
// .setDescription('Lists all inactive players')
// .addStringOption(addClanChoices),
return [
`# ${escapeMarkdown(clan.name)} [${escapeMarkdown(
clan.tag,
)}]'s platoon wins today: ${(
jointVictories.reduce(
(accumulator, { joinVictory }) => accumulator + joinVictory,
0,
) / 2
).toFixed(0)}\n${jointVictories
.filter(({ joinVictory }) => joinVictory)
.sort((a, b) => b.joinVictory - a.joinVictory)
.map(
({ id, joinVictory }) =>
`- ${markdownEscape(players[id].nickname)}: ${joinVictory}`,
)
.join('\n')}`,
// async handler(interaction) {
// const { region, id } = await resolveClanFromCommand(interaction);
// const clan = await getClanInfo(region, id);
// const [players, achievements, previousJointVictories] = await Promise.all([
// getAccountInfo(region, clan.members_ids),
// getAccountAchievements(region, clan.members_ids),
// Promise.all(
// clan.members_ids.map((id) =>
// fetch(`https://www.blitzstars.com/api/playerstats/${id}`)
// .then((response) => response.json() as Promise<PlayerHistoriesRaw>)
// .then((data) => ({
// id,
// jointVictory: data[0]?.achievements.jointVictoryCount as
// | number
// | undefined,
// })),
// ),
// ).then((data) =>
// data.reduce<Record<number, number | undefined>>(
// (accumulator, value) => ({
// ...accumulator,
// [value.id]: value.jointVictory,
// }),
// {},
// ),
// ),
// ]);
// const jointVictories = clan.members_ids.map((id) => ({
// id,
// joinVictory:
// players[id].last_battle_time > getTimeDaysAgo(region, 1)
// ? previousJointVictories[id] === undefined
// ? 0
// : achievements[id].max_series.jointVictory -
// previousJointVictories[id]!
// : 0,
// }));
embedWarning(
'This is an approximation!',
"Wargaming provides very little information about platoons publicly. Caveats:\n- Players in two different clans platooning artificially inflates the total count\n- Game-mode battles aren't counted since Wargaming doesn't publicly share that data\n- This command is also subject to Blitzkrieg's reset times (use `/about timezones` for more info)",
),
];
},
// return [
// `# ${escapeMarkdown(clan.name)} [${escapeMarkdown(
// clan.tag,
// )}]'s platoon wins today: ${(
// jointVictories.reduce(
// (accumulator, { joinVictory }) => accumulator + joinVictory,
// 0,
// ) / 2
// ).toFixed(0)}\n${jointVictories
// .filter(({ joinVictory }) => joinVictory)
// .sort((a, b) => b.joinVictory - a.joinVictory)
// .map(
// ({ id, joinVictory }) =>
// `- ${markdownEscape(players[id].nickname)}: ${joinVictory}`,
// )
// .join('\n')}`,
autocomplete: autocompleteClan,
};
// embedWarning(
// 'This is an approximation!',
// "Wargaming provides very little information about platoons publicly. Caveats:\n- Players in two different clans platooning artificially inflates the total count\n- Game-mode battles aren't counted since Wargaming doesn't publicly share that data\n- This command is also subject to Blitzkrieg's reset times (use `/about timezones` for more info)",
// ),
// ];
// },
// autocomplete: autocompleteClan,
// };
+8 -12
View File
@@ -1,7 +1,6 @@
import { SlashCommandBuilder } from 'discord.js';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import calculateWN8 from '../core/blitz/calculateWN8';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import calculateWN8 from '../_core/statistics/calculateWN8';
import sumStats from '../core/blitz/sumStats';
import { tankopedia } from '../core/blitz/tankopedia';
import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats';
@@ -14,7 +13,6 @@ import embedPositive from '../core/discord/embedPositive';
import markdownTable, { TableInput } from '../core/discord/markdownTable';
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../events/interactionCreate';
import { AccountInfo } from '../types/accountInfo';
import { PossiblyPromise } from '../types/possiblyPromise';
export type SkilledClan = 'SKLLD' | 'SMRI';
@@ -48,20 +46,18 @@ export const eligibleCommand: CommandRegistry = {
async handler(interaction) {
const clan = interaction.options.getString('clan') as SkilledClan;
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}`,
);
const { id, region: region } = await resolvePlayerFromCommand(interaction);
const accountInfo = await getAccountInfo(region, id);
const problems: TableInput = [];
const title = `${accountInfo[id].nickname}'s eligibility for ${SKILLED_CLANS[clan]}`;
const title = `${accountInfo.nickname}'s eligibility for ${SKILLED_CLANS[clan]}`;
let isEligible = false;
let body = '';
if (clan === 'SKLLD') {
const { diff: diffed } = await getDiffedTankStats(
server,
region,
id,
getTimeDaysAgo(server, 30),
getTimeDaysAgo(region, 30),
getPeriodNow(),
);
const entries = Object.entries(diffed);
@@ -171,7 +167,7 @@ export const eligibleCommand: CommandRegistry = {
problems,
)}`;
} else if (clan === 'SMRI') {
const stats = accountInfo[id].statistics.all;
const stats = accountInfo.statistics.all;
if (stats.battles < 1500) {
problems.push([
+2 -9
View File
@@ -2,11 +2,11 @@ import {
SlashCommandBuilder,
SlashCommandSubcommandGroupBuilder,
} from 'discord.js';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import * as Graph from '../components/Graph';
import NoData, { NoDataType } from '../components/NoData';
import TitleBar from '../components/TitleBar';
import Wrapper from '../components/Wrapper';
import { getAccountInfo } from '../core/blitz/getAccountInfo';
import { getClanLogo } from '../core/blitz/getClanLogo';
import { getPlayerClanInfo } from '../core/blitz/getPlayerClanInfo';
import resolveTankId from '../core/blitz/resolveTankId';
@@ -75,11 +75,7 @@ async function render(
return (
<Wrapper>
<TitleBar
name={accountInfo[id].nickname}
image={logo}
description={name}
/>
<TitleBar name={accountInfo.nickname} image={logo} description={name} />
{plot.length > 0 && (
<Graph.Root
@@ -156,9 +152,6 @@ export const evolutionCommand = new Promise<CommandRegistryRaw>(
const tankIdRaw = interaction.options.getString('tank')!;
const tankId =
commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null;
const { nickname } = (await getAccountInfo(player.region, player.id))[
player.id
];
const path = interactionToURL(interaction, {
...player,
...getCustomPeriodParams(interaction),
+3 -3
View File
@@ -1,11 +1,11 @@
import { SlashCommandBuilder } from 'discord.js';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import { StatFilters, filterStats } from '../_core/statistics/filterStats';
import GenericAllStats from '../components/GenericAllStats';
import NoData, { NoDataType } from '../components/NoData';
import TierWeights from '../components/TierWeights';
import TitleBar from '../components/TitleBar';
import Wrapper from '../components/Wrapper';
import { StatFilters, filterStats } from '../core/blitz/filterStats';
import { getAccountInfo } from '../core/blitz/getAccountInfo';
import { getClanLogo } from '../core/blitz/getClanLogo';
import { getPlayerClanInfo } from '../core/blitz/getPlayerClanInfo';
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
@@ -36,7 +36,7 @@ async function render(
{ start, end, name }: ResolvedPeriod,
filters: StatFilters,
) {
const nickname = (await getAccountInfo(region, id))[id].nickname;
const { nickname } = await getAccountInfo(region, id);
const clan = (await getPlayerClanInfo(region, id))[id]?.clan;
const clanImage = clan ? getClanLogo(clan.emblem_set_id) : undefined;
const diffedTankStats = await getDiffedTankStats(region, id, start, end);
+13 -23
View File
@@ -1,16 +1,14 @@
import { SlashCommandBuilder } from 'discord.js';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import { getClanInfo } from '../_core/blitz/getClanInfo';
import GenericStats from '../components/GenericStats';
import NoData, { NoDataType } from '../components/NoData';
import TitleBar from '../components/TitleBar';
import Wrapper from '../components/Wrapper';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import addClanChoices from '../core/discord/addClanChoices';
import autocompleteClan from '../core/discord/autocompleteClan';
import resolveClanFromCommand from '../core/discord/resolveClanFromCommand';
import { CommandRegistry } from '../events/interactionCreate';
import { AccountInfo } from '../types/accountInfo';
import { ClanInfo } from '../types/clanInfo';
const DEFAULT_THRESHOLD = 7;
@@ -36,24 +34,16 @@ export const inactiveCommand: CommandRegistry = {
const threshold =
interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD;
const time = new Date().getTime() / 1000;
const clanData = (
await getWargamingResponse<ClanInfo>(
`https://api.wotblitz.${region}/wotb/clans/info/?application_id=${WARGAMING_APPLICATION_ID}&clan_id=${id}`,
const clanInfo = await getClanInfo(region, id);
const accountInfo = await getAccountInfo(region, clanInfo.members_ids);
const inactive = accountInfo
.map(
(account) =>
[
account.nickname,
(time - account.last_battle_time) / 60 / 60 / 24,
] as [string, number],
)
)[id];
const memberIds = clanData.members_ids;
const accounts = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${memberIds.join(
',',
)}`,
);
const inactive = memberIds
.map((memberId) => {
const member = accounts[memberId];
const inactiveDays = (time - member.last_battle_time) / 60 / 60 / 24;
return [member.nickname, inactiveDays] as [string, number];
})
.filter(([, inactiveDays]) => inactiveDays >= threshold)
.sort((a, b) => b[1] - a[1])
.map(
@@ -64,8 +54,8 @@ export const inactiveCommand: CommandRegistry = {
return (
<Wrapper>
<TitleBar
name={clanData.name}
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData.emblem_set_id}.png`}
name={clanInfo.name}
image={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanInfo.emblem_set_id}.png`}
description={`Inactive for ${threshold}+ Days • ${new Date().toDateString()}`}
/>
+33 -80
View File
@@ -1,18 +1,22 @@
import { teal } from '@radix-ui/colors';
import { AttachmentBuilder, SlashCommandBuilder } from 'discord.js';
import {
AttachmentBuilder,
GuildMemberRoleManager,
SlashCommandBuilder,
} from 'discord.js';
import markdownEscape from 'markdown-escape';
import discord from '../../discord.json' assert { type: 'json' };
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import { getClanAccountInfo } from '../_core/blitz/getClanAccountInfo';
import { Glow } from '../components/AllStatsOverview/components/WN8Display/components/Glow';
import Wrapper from '../components/Wrapper';
import { REGION_NAMES_SHORT, Region } from '../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import { linkBlitzAndDiscord } from '../core/cockroackdb/discordBlitz';
import addUsernameChoices from '../core/discord/addUsernameChoices';
import autocompleteUsername from '../core/discord/autocompleteUsername';
import embedNegative from '../core/discord/embedNegative';
import { CommandRegistry } from '../events/interactionCreate';
import { theme } from '../stitches.config';
import { AccountInfo } from '../types/accountInfo';
import { PlayerClanInfo } from '../types/playerClanData';
const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
@@ -26,86 +30,33 @@ export const verifyCommand: CommandRegistry = {
.addStringOption((option) => addUsernameChoices(option).setRequired(true)),
async handler(interaction) {
// const blitzAccount = await resolvePlayerFromCommand(interaction);
// 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}`,
// );
// const clanData = await getWargamingResponse<PlayerClanData>(
// `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
// );
// const clan = clanData[id]?.clan;
// const clanTag = clan ? ` [${clan!.tag}]` : '';
// const member =
// interaction.member &&
// interaction.guild?.members.cache.get(interaction.member?.user.id);
// if (!interaction.guild?.members.me?.permissions.has('ManageNicknames')) {
// return embedNegative(
// `${markdownEscape(interaction.user.username)} failed to verify`,
// "I don't have the permission to change your nickname.",
// );
// }
// if (!interaction.member || !member) {
// return embedNegative(
// `${interaction.user.username} is not in this server`,
// "I couldn't find this person in this server. Did they leave?",
// );
// }
// try {
// await member.setNickname(`${accountInfo[id].nickname}${clanTag}`);
// if (interaction.guildId === discord.sklld_guild_id) {
// if (!interaction.guild?.members.me?.permissions.has('ManageRoles')) {
// return embedNegative(
// `${markdownEscape(interaction.user.username)} failed to verify`,
// "I don't have the permission to change your manage roles.",
// );
// }
// await member.roles.remove(discord.sklld_verify_role);
// await member.roles.add(discord.sklld_peasant_role);
// }
// return embedPositive(
// `${interaction.user.username} is verified`,
// `The user is now verified as ${markdownEscape(
// accountInfo[id].nickname,
// )}${markdownEscape(clanTag)}`,
// );
// } catch (error) {
// return embedNegative(
// `${interaction.user.username} failed to verify`,
// "I can't change your nickname because you have higher permissions than me. Try setting your nickname to your Blitz username manually.",
// );
// }
const username = interaction.options.getString('username', true);
if (serverAndIdPattern.test(username)) {
const [region, idString] = username.split('/');
const id = parseInt(idString);
const discordId = parseInt(interaction.user.id);
// const account = (
// await getWargamingResponse<AccountInfo>(
// `https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
// )
// )[id];
// const clan = await getWargamingResponse<PlayerClanData>(
// `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
// );
const [account, clan] = await Promise.all([
getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
),
getWargamingResponse<PlayerClanInfo>(
`https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
),
]);
const accountInfo = await getAccountInfo(region as Region, id);
const clanAccountInfo = await getClanAccountInfo(region as Region, id);
await linkBlitzAndDiscord(discordId, region as Region, id);
if (interaction.guildId === discord.sklld_guild_id) {
if (!interaction.guild?.members.me?.permissions.has('ManageRoles')) {
return embedNegative(
`${markdownEscape(interaction.user.username)} failed to verify`,
"I don't have the permission to change your manage roles.",
);
}
await (interaction.member!.roles as GuildMemberRoleManager).remove(
discord.sklld_verify_role,
);
await (interaction.member!.roles as GuildMemberRoleManager).add(
discord.sklld_peasant_role,
);
}
return (
<Wrapper fat>
<div
@@ -205,7 +156,7 @@ export const verifyCommand: CommandRegistry = {
justifyContent: 'center',
}}
>
{clan[id]?.clan ? (
{clanAccountInfo?.clan ? (
<img
style={{
width: 64,
@@ -213,7 +164,7 @@ export const verifyCommand: CommandRegistry = {
borderRadius: 32,
objectFit: 'cover',
}}
src={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan[id]?.clan?.emblem_set_id}.png`}
src={`https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanAccountInfo?.clan?.emblem_set_id}.png`}
/>
) : null}
<div
@@ -234,7 +185,7 @@ export const verifyCommand: CommandRegistry = {
whiteSpace: 'nowrap',
}}
>
{account[id].nickname}
{accountInfo.nickname}
</span>
<span
style={{
@@ -242,7 +193,9 @@ export const verifyCommand: CommandRegistry = {
color: theme.colors.textLowContrast,
}}
>
{clan[id]?.clan ? `[${clan[id]?.clan?.tag}] - ` : ''}
{clanAccountInfo?.clan
? `[${clanAccountInfo?.clan?.tag}] - `
: ''}
{REGION_NAMES_SHORT[region as Region]}
</span>
</div>
+8 -34
View File
@@ -1,12 +1,13 @@
import { SlashCommandBuilder } from 'discord.js';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import { getClanAccountInfo } from '../_core/blitz/getClanAccountInfo';
import NoData, { NoDataType } from '../components/NoData';
import * as Tanks from '../components/Tanks';
import TitleBar from '../components/TitleBar';
import Wrapper from '../components/Wrapper';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import { getClanLogo } from '../core/blitz/getClanLogo';
import getTankStats from '../core/blitz/getTankStats';
import getTreeType from '../core/blitz/getTreeType';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import resolveTankName from '../core/blitz/resolveTankName';
import {
TIER_ROMAN_NUMERALS,
@@ -20,29 +21,6 @@ import addUsernameChoices from '../core/discord/addUsernameChoices';
import autocompleteUsername from '../core/discord/autocompleteUsername';
import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
import { CommandRegistry } from '../events/interactionCreate';
import { AccountInfo } from '../types/accountInfo';
import { PlayerClanInfo } from '../types/playerClanData';
const COMP_TANKS = [
// light tanks
24321, // t-100 lt
20257, // sheridan
3649, // bat chat
19537, // vickers
// heavy tanks
6145, // is-4
7169, // is-7
10785, // t110e5
12161, // strvk
4481, // kranvagn
22817, // m6 yoh
6225, // fv215b
5425, // wz 113
7297, // 60tp
58641, // vk 72
6753, // type 71
];
export const ownedTanksCommand: CommandRegistry = {
inProduction: true,
@@ -58,9 +36,7 @@ export const ownedTanksCommand: CommandRegistry = {
const tier = Number(interaction.options.getString('tier'));
const account = await resolvePlayerFromCommand(interaction);
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}`,
);
const accountInfo = await getAccountInfo(server, id);
const tankStats = await getTankStats(server, id);
const filteredTanks = (
await Promise.all(
@@ -70,9 +46,7 @@ export const ownedTanksCommand: CommandRegistry = {
})),
)
).filter((tank) => tank.tankopedia?.tier === tier);
const clanData = await getWargamingResponse<PlayerClanInfo>(
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
);
const clanAccountInfo = await getClanAccountInfo(server, id);
const groupedTanks: Record<string, TankopediaEntry[]> = {};
const nations: string[] = [];
@@ -90,10 +64,10 @@ export const ownedTanksCommand: CommandRegistry = {
return (
<Wrapper>
<TitleBar
name={accountInfo[id].nickname}
name={accountInfo.nickname}
image={
clanData[id]?.clan
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clanData[id]?.clan?.emblem_set_id}.png`
clanAccountInfo?.clan
? getClanLogo(clanAccountInfo.clan.emblem_set_id)
: undefined
}
description={`Tier ${TIER_ROMAN_NUMERALS[tier as Tier]} tanks`}
+7 -14
View File
@@ -1,16 +1,14 @@
import { SlashCommandBuilder } from 'discord.js';
import { startCase } from 'lodash';
import markdownEscape from 'markdown-escape';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import { getAccountAchievements } from '../_core/blitz/getAccountAchievements';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import addUsernameChoices from '../core/discord/addUsernameChoices';
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 { CommandRegistry } from '../events/interactionCreate';
import { AccountAchievements } from '../types/accountAchievements';
import { AccountInfo } from '../types/accountInfo';
type SortBy = 'name' | 'count';
@@ -35,22 +33,17 @@ export const playerAchievementsCommand: CommandRegistry = {
async handler(interaction) {
const sortBy = (interaction.options.getString('sort') ?? 'name') as SortBy;
const account = await resolvePlayerFromCommand(interaction);
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}`,
);
const accountsAchievements =
await getWargamingResponse<AccountAchievements>(
`https://api.wotblitz.${server}/wotb/account/achievements/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
);
const accountAchievements = accountsAchievements[id];
const { id, region: region } = account;
const accountInfo = await getAccountInfo(region, id);
const accountsAchievements = await getAccountAchievements(region, id);
const accountAchievements = accountsAchievements;
const compound = {
...accountAchievements.achievements,
...accountAchievements.max_series,
};
return embedInfo(
`${markdownEscape(accounts[id].nickname)}'s information`,
`${markdownEscape(accountInfo.nickname)}'s information`,
markdownTable(
Object.keys(compound)
+3 -8
View File
@@ -1,14 +1,12 @@
import { SlashCommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import addUsernameChoices from '../core/discord/addUsernameChoices';
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 { CommandRegistry } from '../events/interactionCreate';
import { AccountInfo } from '../types/accountInfo';
export const playerInfoCommand: CommandRegistry = {
inProduction: true,
@@ -21,11 +19,8 @@ export const playerInfoCommand: CommandRegistry = {
async handler(interaction) {
const account = await resolvePlayerFromCommand(interaction);
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}`,
);
const accountInfo = accounts[id];
const { id, region: region } = account;
const accountInfo = await getAccountInfo(region, id);
return embedInfo(
`${markdownEscape(accountInfo.nickname)}'s information`,
+13 -20
View File
@@ -5,14 +5,14 @@ import {
SlashCommandSubcommandGroupBuilder,
} from 'discord.js';
import { BlitzkriegRatingsLeaderboard } from '../../scripts/buildRatingsLeaderboard';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import { getClanAccountInfo } from '../_core/blitz/getClanAccountInfo';
import * as Leaderboard from '../components/Leaderboard';
import TitleBar from '../components/TitleBar';
import Wrapper from '../components/Wrapper';
import { REGION_NAMES_SHORT, Region } from '../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID';
import getArchivedRatingsInfo from '../core/blitz/getArchivedRatingsInfo';
import getRatingsInfo from '../core/blitz/getRatingsInfo';
import getWargamingResponse from '../core/blitz/getWargamingResponse';
import regionToRegionSubdomain from '../core/blitz/regionToRegionSubdomain';
import getMidnightLeaderboard, {
DATABASE_REPO,
@@ -25,8 +25,6 @@ import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand';
import { octokit } from '../core/github/octokit';
import throwError from '../core/node/throwError';
import { CommandRegistryRaw } from '../events/interactionCreate';
import { AccountInfo } from '../types/accountInfo';
import { PlayerClanInfo } from '../types/playerClanData';
export interface RatingsPlayer {
spa_id: number;
@@ -312,9 +310,9 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
firstPlayerIndex,
lastPlayerIndex + 1,
);
const ids = trimmed.map((player) => player.id).join(',');
const clanData = await getWargamingResponse<PlayerClanInfo>(
`https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${ids}&extra=clan`,
const clanData = await getClanAccountInfo(
region,
trimmed.map(({ id }) => id),
);
return trimmed.map(
@@ -373,14 +371,8 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
if (regionRatingsInfo.detail !== undefined) return noOngoingSeason;
const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
);
const clan = (
await getWargamingResponse<PlayerClanInfo>(
`https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`,
)
)[id]?.clan;
const accountInfo = await getAccountInfo(region, id);
const clan = (await getClanAccountInfo(region, id, ['clan']))?.clan;
const regionSubdomain = regionToRegionSubdomain(region);
const neighbors =
subcommandGroup === 'current'
@@ -426,7 +418,7 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
if (playerIndex === -1) {
throw throwError(
`${accountInfo[id].nickname} didn't player ratings in season ${season}`,
`${accountInfo.nickname} didn't player ratings in season ${season}`,
'This player did not participate in this season or did not get past calibration.',
);
}
@@ -436,9 +428,10 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
playerIndex - halfRange,
playerIndex + halfRange + 1,
);
const ids = trimmed.map((player) => player.id).join(',');
const clanData = await getWargamingResponse<PlayerClanInfo>(
`https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${ids}&extra=clan`,
const clanData = await getClanAccountInfo(
region,
trimmed.map((player) => player.id),
['clan'],
);
return trimmed.map((player, index) => ({
@@ -464,7 +457,7 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png`
: undefined;
titleName = neighbors
? accountInfo[id].nickname
? accountInfo.nickname
: `No Ongoing Season - ${REGION_NAMES_SHORT[region]}`;
titleDescription = 'Ratings neighbours';
playersBefore = neighbors ? neighbors[0].position - 1 : 0;
+2 -2
View File
@@ -1,10 +1,10 @@
import { SlashCommandBuilder } from 'discord.js';
import { getAccountInfo } from '../_core/blitz/getAccountInfo';
import { StatFilters, filterStats } from '../_core/statistics/filterStats';
import AllStatsOverview from '../components/AllStatsOverview';
import NoData, { NoDataType } from '../components/NoData';
import TitleBar from '../components/TitleBar';
import Wrapper from '../components/Wrapper';
import { StatFilters, filterStats } from '../core/blitz/filterStats';
import { getAccountInfo } from '../core/blitz/getAccountInfo';
import { getClanLogo } from '../core/blitz/getClanLogo';
import { getPlayerClanInfo } from '../core/blitz/getPlayerClanInfo';
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
-11
View File
@@ -1,11 +0,0 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import { AccountInfo } from '../../types/accountInfo';
import getWargamingResponse from './getWargamingResponse';
// BIG TODO: generalize all get wargaming calls
export function getAccountInfo(region: Region, id: number) {
return getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
);
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { StatFilters } from '../../_core/statistics/filterStats';
import { TREE_TYPE_NAMES } from '../../components/Tanks';
import { StatFilters } from '../blitz/filterStats';
import resolveTankName from '../blitz/resolveTankName';
import { TIER_ROMAN_NUMERALS, Tier, tankopediaInfo } from '../blitz/tankopedia';
+1 -21
View File
@@ -1,6 +1,6 @@
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
import { AccountInfo, AllStats } from '../../types/accountInfo';
import { AccountInfo } from '../../types/accountInfo';
import {
GetHistoriesOptions,
Histories,
@@ -10,26 +10,6 @@ import {
import getWargamingResponse from '../blitz/getWargamingResponse';
import { emptyAllStats } from './getDiffedTankStats';
export interface PlayerHistoryRaw {
clan: { clan_id: number; name: string; tag: string };
statistics: {
all: AllStats;
wn8: number;
wn7: number;
average_tier: number;
};
last_battle_time: number;
nickname: string;
account_id: number;
achievements: {
account_id: number;
[key: string]: number;
};
region: Region;
}
export type PlayerHistoriesRaw = PlayerHistoryRaw[];
export default async function getPlayerHistories(
server: Region,
id: number,
+1 -1
View File
@@ -1,6 +1,6 @@
import { ButtonInteraction } from 'discord.js';
import { StatFilters } from '../../_core/statistics/filterStats';
import { TreeTypeString } from '../../components/Tanks';
import { StatFilters } from '../blitz/filterStats';
export function getFiltersFromButton(interaction: ButtonInteraction) {
const url = new URL(`https://example.com/${interaction.customId}`);
+1 -1
View File
@@ -1,6 +1,6 @@
import { ChatInputCommandInteraction } from 'discord.js';
import { StatFilters } from '../../_core/statistics/filterStats';
import { TreeTypeString } from '../../components/Tanks';
import { StatFilters } from '../blitz/filterStats';
export function getFiltersFromCommand(
interaction: ChatInputCommandInteraction,
-10
View File
@@ -1,10 +0,0 @@
export interface AccountAchievements {
[id: number]: {
achievements: {
[achievement: string]: number;
};
max_series: {
[achievement: string]: number;
};
};
}
-63
View File
@@ -1,63 +0,0 @@
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;
}
export interface AccountInfo {
[accountId: number]: {
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;
};
};
}
-30
View File
@@ -1,30 +0,0 @@
export interface ClanInfo {
[clanId: number]: {
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;
};
}
-18
View File
@@ -1,18 +0,0 @@
export type PlayerClanInfo = {
[accountId: number]: {
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;
};