diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index 3cebffd36..8404f13cc 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -1,10 +1,21 @@ -import { SlashCommandBuilder } from 'discord.js'; +import { + SlashCommandBuilder, + SlashCommandSubcommandGroupBuilder, +} from 'discord.js'; +import * as Graph from '../components/Graph'; +import NoData, { NoDataType } from '../components/NoData'; +import TitleBar from '../components/TitleBar'; +import Wrapper from '../components/Wrapper'; import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID'; +import { getAccountInfo } from '../core/blitz/getAccountInfo'; +import { getClanLogo } from '../core/blitz/getClanLogo'; +import { getPlayerClanInfo } from '../core/blitz/getPlayerClanInfo'; import getWargamingResponse from '../core/blitz/getWargamingResponse'; import resolveTankId from '../core/blitz/resolveTankId'; import getPlayerHistories from '../core/blitzstars/getPlayerHistories'; import getTankHistories from '../core/blitzstars/getTankHistories'; -import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; +import addPeriodSubCommands from '../core/discord/addPeriodSubCommands'; +import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteTanks from '../core/discord/autocompleteTanks'; import autocompleteUsername from '../core/discord/autocompleteUsername'; import interactionToURL from '../core/discord/interactionToURL'; @@ -20,52 +31,31 @@ import resolvePlayerFromCommand, { } from '../core/discord/resolvePlayerFromCommand'; import { CommandRegistryRaw } from '../events/interactionCreate'; import { AccountInfo } from '../types/accountInfo'; -import { PlayerClanInfo } from '../types/playerClanData'; + +type EvolutionStatType = 'player' | 'tank'; async function render( { region, id }: ResolvedPlayer, { start, end, name }: ResolvedPeriod, type: 'player' | 'tank', - tankId?: number, + tankId: number | null, ) { - let nameDiscriminator: string | undefined; - let image: string | undefined; - - if (type === 'player') { - const clan = ( - await getWargamingResponse( - `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, - ) - )[id]?.clan; - - if (clan) nameDiscriminator = `[${clan.tag}]`; - image = clan - ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` - : undefined; - } else { - nameDiscriminator = `(${await resolveTankName(tankId!)})`; - image = (await tankopedia)[tankId!]?.images.normal; - } - - let histories: Histories; - - if (type === 'player') { - histories = await getPlayerHistories(region, id, { - start, - end, - includeLatestHistories: true, - includePreviousHistories: true, - }); - } else { - histories = await getTankHistories(region, id, { - tankId: tankId!, - start, - end, - includeLatestHistories: true, - includePreviousHistories: true, - }); - } - + const clan = (await getPlayerClanInfo(region, id))[id]?.clan; + const logo = clan ? getClanLogo(clan.emblem_set_id) : undefined; + const histories = await (type === 'player' + ? getPlayerHistories(region, id, { + start, + end, + includeLatestHistories: true, + includePreviousHistories: true, + }) + : getTankHistories(region, id, { + tankId: tankId!, + start, + end, + includeLatestHistories: true, + includePreviousHistories: true, + })); const plot = histories.map( (history) => [ @@ -91,7 +81,7 @@ async function render( @@ -129,26 +119,32 @@ async function render( export const evolutionCommand = new Promise( async (resolve) => { - const command = await addStatTypeSubCommandGroups( - new SlashCommandBuilder() - .setName('evolution') - .setDescription('Evolution of statistics'), - // (option) => - // option - // .addStringOption((option) => - // addStatPropertyOptions(option) - // .setName('x') - // .setDescription('X axis') - // .setRequired(false), - // ) - // .addStringOption((option) => - // addStatPropertyOptions(option) - // .setName('y') - // .setDescription('Y axis') - // .setRequired(false), - // ) - // .addStringOption(addUsernameChoices), - ); + let playerGroup: SlashCommandSubcommandGroupBuilder; + let tankGroup: SlashCommandSubcommandGroupBuilder; + const command = new SlashCommandBuilder() + .setName('evolution') + .setDescription('Evolution of statistics') + .addSubcommandGroup((option) => { + playerGroup = option + .setName('player') + .setDescription('Player statistics'); + + return playerGroup; + }) + .addSubcommandGroup((option) => { + tankGroup = option.setName('tank').setDescription('Tank statistics'); + + return tankGroup; + }); + + await Promise.all([ + addPeriodSubCommands(playerGroup!, (option) => + option.addStringOption(addUsernameChoices), + ), + addPeriodSubCommands(tankGroup!, (option) => + option.addStringOption(addUsernameChoices), + ), + ]); resolve({ inProduction: true, @@ -157,35 +153,35 @@ export const evolutionCommand = new Promise( command, async handler(interaction) { - const commandGroup = interaction.options.getSubcommandGroup( - true, - ) as StatType; - const player = await resolvePlayerFromCommand(interaction); - const period = resolvePeriodFromCommand(player.region, interaction); + const commandGroup = + interaction.options.getSubcommandGroup() as EvolutionStatType; + const resolvedPlayer = await resolvePlayerFromCommand(interaction); + const resolvedPeriod = resolvePeriodFromCommand( + resolvedPlayer.region, + interaction, + ); const tankIdRaw = interaction.options.getString('tank')!; const tankId = commandGroup === 'tank' ? await resolveTankId(tankIdRaw) : null; const start = interaction.options.getInteger('start'); const end = interaction.options.getInteger('end'); const { nickname } = ( - await getWargamingResponse( - `https://api.wotblitz.${player.region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${player.id}`, - ) - )[player.id]; + await getAccountInfo(resolvedPlayer.region, resolvedPlayer.id) + )[resolvedPlayer.id]; const path = interactionToURL(interaction, { - ...player, + ...resolvedPlayer, tankId, start, end, }); return [ - await render(commandGroup, period, player, tankId), + await render(resolvedPlayer, resolvedPeriod, commandGroup, tankId), primaryButton(path, 'Refresh'), linkButton( - `https://www.blitzstars.com/player/${player.region}/${nickname}${ - commandGroup === 'tank' ? `/tank/${tankId!}` : '' - }`, + `https://www.blitzstars.com/player/${ + resolvedPlayer.region + }/${nickname}${commandGroup === 'tank' ? `/tank/${tankId!}` : ''}`, 'BlitzStars', ), ]; @@ -194,21 +190,20 @@ export const evolutionCommand = new Promise( autocomplete: (interaction) => { autocompleteUsername(interaction); autocompleteTanks(interaction); - // autocompleteStatProperty(interaction, 'x'); }, async button(interaction) { const url = new URL(`https://example.com/${interaction.customId}`); - const path = url.pathname.split('/').filter(Boolean); - const commandGroup = path[1] as StatType; + const path = url.pathname.split('/'); + const commandGroup = path[1] as EvolutionStatType; const player = await resolvePlayerFromButton(interaction); const period = resolvePeriodFromButton(player.region, interaction); return await render( - commandGroup, - period, player, - parseInt(url.searchParams.get('tankId')!), + period, + commandGroup, + parseInt(url.searchParams.get('tankId') ?? '0') || null, ); }, }); diff --git a/src/commands/fullStats.tsx b/src/commands/fullStats.tsx index 67ed931c9..da0674517 100644 --- a/src/commands/fullStats.tsx +++ b/src/commands/fullStats.tsx @@ -7,12 +7,12 @@ 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 { playerClanInfo } from '../core/blitz/playerClanInfo'; +import { getPlayerClanInfo } from '../core/blitz/getPlayerClanInfo'; import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription'; import { getTierWeights } from '../core/blitzkrieg/getTierWeights'; import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; -import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; +import addStatTypeSubCommands from '../core/discord/addStatTypeSubCommands'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteTanks from '../core/discord/autocompleteTanks'; import autocompleteUsername from '../core/discord/autocompleteUsername'; @@ -37,7 +37,7 @@ async function render( filters: StatFilters, ) { const nickname = (await getAccountInfo(region, id))[id].nickname; - const clan = (await playerClanInfo(region, id))[id]?.clan; + 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); const { stats, supplementary, filteredOrder } = await filterStats( @@ -66,7 +66,7 @@ async function render( export const fullStatsCommand = new Promise( async (resolve) => { - const command = await addStatTypeSubCommandGroups( + const command = await addStatTypeSubCommands( new SlashCommandBuilder() .setName('full-stats') .setDescription('Full in-game statistics'), diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index b670cd098..0a12af782 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -6,11 +6,11 @@ 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 { playerClanInfo } from '../core/blitz/playerClanInfo'; +import { getPlayerClanInfo } from '../core/blitz/getPlayerClanInfo'; import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription'; import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; -import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; +import addStatTypeSubCommands from '../core/discord/addStatTypeSubCommands'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteTanks from '../core/discord/autocompleteTanks'; import autocompleteUsername from '../core/discord/autocompleteUsername'; @@ -35,7 +35,7 @@ async function render( filters: StatFilters, ) { const nickname = (await getAccountInfo(region, id))[id].nickname; - const clan = (await playerClanInfo(region, id))[id]?.clan; + 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); const { stats, supplementary } = await filterStats(diffedTankStats, filters); @@ -58,7 +58,7 @@ async function render( } export const statsCommand = new Promise(async (resolve) => { - const command = await addStatTypeSubCommandGroups( + const command = await addStatTypeSubCommands( new SlashCommandBuilder() .setName('stats') .setDescription('Regular battles statistics'), diff --git a/src/core/blitz/playerClanInfo.ts b/src/core/blitz/getPlayerClanInfo.ts similarity index 87% rename from src/core/blitz/playerClanInfo.ts rename to src/core/blitz/getPlayerClanInfo.ts index 151449b89..db30e7f0a 100644 --- a/src/core/blitz/playerClanInfo.ts +++ b/src/core/blitz/getPlayerClanInfo.ts @@ -3,7 +3,7 @@ import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID import { PlayerClanInfo } from '../../types/playerClanData'; import getWargamingResponse from './getWargamingResponse'; -export function playerClanInfo(region: Region, id: number) { +export function getPlayerClanInfo(region: Region, id: number) { return getWargamingResponse( `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ); diff --git a/src/core/discord/addPeriodSubCommands.ts b/src/core/discord/addPeriodSubCommands.ts index b8ca9c488..fa900898a 100644 --- a/src/core/discord/addPeriodSubCommands.ts +++ b/src/core/discord/addPeriodSubCommands.ts @@ -1,10 +1,14 @@ -import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js'; +import { + SlashCommandBuilder, + SlashCommandSubcommandBuilder, + SlashCommandSubcommandGroupBuilder, +} from 'discord.js'; -export type PeriodType = 'today' | '30' | '60' | '90' | 'custom'; +export type PeriodType = 'today' | '30' | '60' | '90' | 'career' | 'custom'; export type PeriodSize = 'today' | `${number}` | 'career'; export default function addPeriodSubCommands( - option: SlashCommandBuilder, + option: SlashCommandBuilder | SlashCommandSubcommandGroupBuilder, extra: ( option: SlashCommandSubcommandBuilder, ) => SlashCommandSubcommandBuilder = (option) => option, @@ -22,6 +26,9 @@ export default function addPeriodSubCommands( .addSubcommand((option) => extra(option.setName('90').setDescription('90 days')), ) + .addSubcommand((option) => + extra(option.setName('career').setDescription('Career')), + ) .addSubcommand((option) => extra( option diff --git a/src/core/discord/addStatTypeSubCommandGroups.ts b/src/core/discord/addStatTypeSubCommands.ts similarity index 87% rename from src/core/discord/addStatTypeSubCommandGroups.ts rename to src/core/discord/addStatTypeSubCommands.ts index 5da5655e7..b528c0886 100644 --- a/src/core/discord/addStatTypeSubCommandGroups.ts +++ b/src/core/discord/addStatTypeSubCommands.ts @@ -1,16 +1,22 @@ -import { SlashCommandBuilder, SlashCommandSubcommandBuilder } from 'discord.js'; +import { + SlashCommandBuilder, + SlashCommandSubcommandBuilder, + SlashCommandSubcommandGroupBuilder, +} from 'discord.js'; import { TREE_TYPE_NAMES, TreeTypeString } from '../../components/Tanks'; import { tankopediaInfo } from '../blitz/tankopedia'; import addPeriodSubCommands from './addPeriodSubCommands'; import addTankChoices from './addTankChoices'; import addTierChoices from './addTierChoices'; -export default async function addStatTypeSubCommandGroups( - option: SlashCommandBuilder, +export default async function addStatTypeSubCommands< + OptionType extends SlashCommandBuilder | SlashCommandSubcommandGroupBuilder, +>( + option: OptionType, extra: ( option: SlashCommandSubcommandBuilder, ) => SlashCommandSubcommandBuilder = (option) => option, -) { +): Promise { const awaitedTankopediaInfo = await tankopediaInfo; addPeriodSubCommands(option, (option) => {