diff --git a/src/commands/link.tsx b/src/commands/link.tsx index fc773c893..d7b9fb014 100644 --- a/src/commands/link.tsx +++ b/src/commands/link.tsx @@ -12,7 +12,7 @@ import embedNegative from '../core/discord/embedNegative'; import { CommandRegistry } from '../events/interactionCreate'; import { theme } from '../stitches.config'; import { AccountInfo } from '../types/accountInfo'; -import { PlayerClanData } from '../types/playerClanData'; +import { PlayerClanInfo } from '../types/playerClanData'; const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/; @@ -99,7 +99,7 @@ export const verifyCommand: CommandRegistry = { getWargamingResponse( `https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, ), - getWargamingResponse( + getWargamingResponse( `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ), ]); diff --git a/src/commands/ownedTanks.tsx b/src/commands/ownedTanks.tsx index a8bd2e0cb..17297119a 100644 --- a/src/commands/ownedTanks.tsx +++ b/src/commands/ownedTanks.tsx @@ -21,7 +21,7 @@ import autocompleteUsername from '../core/discord/autocompleteUsername'; import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand'; import { CommandRegistry } from '../events/interactionCreate'; import { AccountInfo } from '../types/accountInfo'; -import { PlayerClanData } from '../types/playerClanData'; +import { PlayerClanInfo } from '../types/playerClanData'; const COMP_TANKS = [ // light tanks @@ -70,7 +70,7 @@ export const ownedTanksCommand: CommandRegistry = { })), ) ).filter((tank) => tank.tankopedia?.tier === tier); - const clanData = await getWargamingResponse( + const clanData = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ); const groupedTanks: Record = {}; diff --git a/src/commands/ratings.tsx b/src/commands/ratings.tsx index 13626733e..e6506b3b7 100644 --- a/src/commands/ratings.tsx +++ b/src/commands/ratings.tsx @@ -26,7 +26,7 @@ import { octokit } from '../core/github/octokit'; import throwError from '../core/node/throwError'; import { CommandRegistryRaw } from '../events/interactionCreate'; import { AccountInfo } from '../types/accountInfo'; -import { PlayerClanData } from '../types/playerClanData'; +import { PlayerClanInfo } from '../types/playerClanData'; export interface RatingsPlayer { spa_id: number; @@ -313,7 +313,7 @@ export const ratingsCommand = new Promise( lastPlayerIndex + 1, ); const ids = trimmed.map((player) => player.id).join(','); - const clanData = await getWargamingResponse( + const clanData = await getWargamingResponse( `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${ids}&extra=clan`, ); @@ -377,7 +377,7 @@ export const ratingsCommand = new Promise( `https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, ); const clan = ( - await getWargamingResponse( + await getWargamingResponse( `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ) )[id]?.clan; @@ -437,7 +437,7 @@ export const ratingsCommand = new Promise( playerIndex + halfRange + 1, ); const ids = trimmed.map((player) => player.id).join(','); - const clanData = await getWargamingResponse( + const clanData = await getWargamingResponse( `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${ids}&extra=clan`, ); diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index fd1916db2..b6f3e48f2 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -1,20 +1,22 @@ import { SlashCommandBuilder } from 'discord.js'; import AllStatsOverview from '../components/AllStatsOverview'; import NoData, { NoDataType } from '../components/NoData'; -import { TreeTypeString } from '../components/Tanks'; import TitleBar from '../components/TitleBar'; import Wrapper from '../components/Wrapper'; -import { WARGAMING_APPLICATION_ID } from '../constants/wargamingApplicationID'; import { StatFilters, filterStats } from '../core/blitz/filterStats'; -import getWargamingResponse from '../core/blitz/getWargamingResponse'; +import { getAccountInfo } from '../core/blitz/getAccountInfo'; +import { getClanLogo } from '../core/blitz/getClanLogo'; +import { playerClanInfo } from '../core/blitz/playerClanInfo'; import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription'; import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; import getDiffedTankStats from '../core/blitzstars/getDiffedTankStats'; -import { PeriodType } from '../core/discord/addPeriodSubCommands'; import addStatTypeSubCommandGroups from '../core/discord/addStatTypeSubCommandGroups'; import addUsernameChoices from '../core/discord/addUsernameChoices'; import autocompleteTanks from '../core/discord/autocompleteTanks'; import autocompleteUsername from '../core/discord/autocompleteUsername'; +import { getCustomPeriodParams } from '../core/discord/getCustomPeriodParams'; +import { getFiltersFromButton } from '../core/discord/getFiltersFromButton'; +import { getFiltersFromCommand } from '../core/discord/getFiltersFromCommand'; import interactionToURL from '../core/discord/interactionToURL'; import { refreshButton } from '../core/discord/refreshButton'; import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton'; @@ -26,27 +28,15 @@ import resolvePlayerFromCommand, { ResolvedPlayer, } from '../core/discord/resolvePlayerFromCommand'; import { CommandRegistryRaw } from '../events/interactionCreate'; -import { AccountInfo } from '../types/accountInfo'; -import { PlayerClanData } from '../types/playerClanData'; export default async function render( { region, id }: ResolvedPlayer, - { start, end, name: statsName }: ResolvedPeriod, + { start, end, name }: ResolvedPeriod, filters: StatFilters, ) { - const nickname = ( - await getWargamingResponse( - `https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, - ) - )[id].nickname; - const clan = ( - await getWargamingResponse( - `https://api.wotblitz.${region}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, - ) - )[id]?.clan; - const clanImage = clan - ? `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${clan.emblem_set_id}.png` - : undefined; + const nickname = (await getAccountInfo(region, id))[id].nickname; + const clan = (await playerClanInfo(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); @@ -55,7 +45,7 @@ export default async function render( {!stats?.battles && } @@ -86,34 +76,18 @@ export const statsCommand = new Promise(async (resolve) => { resolvedPlayer.region, interaction, ); - const filters = { - nation: interaction.options.getString('nation') ?? undefined, - tier: - parseInt(interaction.options.getString('tier') ?? '0') || undefined, - tankType: interaction.options.getString('tank-type') ?? undefined, - treeType: - (interaction.options.getString('tree-type') as - | TreeTypeString - | undefined) ?? undefined, - tank: - parseInt(interaction.options.getString('tank') ?? '0') || undefined, - } satisfies StatFilters; + const filters = getFiltersFromCommand(interaction); const path = interactionToURL(interaction, { ...resolvedPlayer, - ...((interaction.options.getSubcommand() as PeriodType) === 'custom' - ? { - start: interaction.options.getInteger('start', true), - end: interaction.options.getInteger('end', true), - } - : undefined), + ...getCustomPeriodParams(interaction), ...filters, }); - return [ - await render(resolvedPlayer, resolvedPeriod, filters), + return Promise.all([ + render(resolvedPlayer, resolvedPeriod, filters), refreshButton(interaction, path), - await getBlitzStarsLinkButton(resolvedPlayer.region, resolvedPlayer.id), - ]; + getBlitzStarsLinkButton(resolvedPlayer.region, resolvedPlayer.id), + ]); }, autocomplete: (interaction) => { @@ -122,19 +96,11 @@ export const statsCommand = new Promise(async (resolve) => { }, async button(interaction) { - const url = new URL(`https://example.com${interaction.customId}`); const player = await resolvePlayerFromButton(interaction); const period = resolvePeriodFromButton(player.region, interaction); + const filters = getFiltersFromButton(interaction); - return await render(player, period, { - nation: url.searchParams.get('nation') ?? undefined, - tank: parseInt(url.searchParams.get('tank') ?? '0') || undefined, - tankType: url.searchParams.get('tank-type') ?? undefined, - treeType: - (url.searchParams.get('tree-type') as TreeTypeString | undefined) ?? - undefined, - tier: parseInt(url.searchParams.get('tier') ?? '0') || undefined, - }); + return await render(player, period, filters); }, }); }); diff --git a/src/core/blitz/getAccountInfo.ts b/src/core/blitz/getAccountInfo.ts new file mode 100644 index 000000000..04ff4e29c --- /dev/null +++ b/src/core/blitz/getAccountInfo.ts @@ -0,0 +1,11 @@ +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( + `https://api.wotblitz.${region}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, + ); +} diff --git a/src/core/blitz/getClanLogo.ts b/src/core/blitz/getClanLogo.ts new file mode 100644 index 000000000..17354e727 --- /dev/null +++ b/src/core/blitz/getClanLogo.ts @@ -0,0 +1,3 @@ +export function getClanLogo(emblemId: number) { + return `https://wotblitz-gc.gcdn.co/icons/clanEmblems1x/clan-icon-v2-${emblemId}.png` +} \ No newline at end of file diff --git a/src/core/blitz/playerClanInfo.ts b/src/core/blitz/playerClanInfo.ts new file mode 100644 index 000000000..151449b89 --- /dev/null +++ b/src/core/blitz/playerClanInfo.ts @@ -0,0 +1,10 @@ +import { Region } from '../../constants/regions'; +import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID'; +import { PlayerClanInfo } from '../../types/playerClanData'; +import getWargamingResponse from './getWargamingResponse'; + +export function playerClanInfo(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/getCustomPeriodParams.ts b/src/core/discord/getCustomPeriodParams.ts new file mode 100644 index 000000000..55b7b8aa0 --- /dev/null +++ b/src/core/discord/getCustomPeriodParams.ts @@ -0,0 +1,14 @@ +import { ChatInputCommandInteraction } from 'discord.js'; +import { PeriodType } from './addPeriodSubCommands'; + +export function getCustomPeriodParams( + interaction: ChatInputCommandInteraction, +) { + if ((interaction.options.getSubcommand() as PeriodType) === 'custom') + return { + start: interaction.options.getInteger('start', true), + end: interaction.options.getInteger('end', true), + }; + + return undefined; +} diff --git a/src/core/discord/getFiltersFromButton.ts b/src/core/discord/getFiltersFromButton.ts new file mode 100644 index 000000000..cb26b8464 --- /dev/null +++ b/src/core/discord/getFiltersFromButton.ts @@ -0,0 +1,17 @@ +import { ButtonInteraction } from 'discord.js'; +import { TreeTypeString } from '../../components/Tanks'; +import { StatFilters } from '../blitz/filterStats'; + +export function getFiltersFromButton(interaction: ButtonInteraction) { + const url = new URL(`https://example.com/${interaction.customId}`); + + return { + nation: url.searchParams.get('nation') ?? undefined, + tank: parseInt(url.searchParams.get('tank') ?? '0') || undefined, + tankType: url.searchParams.get('tank-type') ?? undefined, + treeType: + (url.searchParams.get('tree-type') as TreeTypeString | undefined) ?? + undefined, + tier: parseInt(url.searchParams.get('tier') ?? '0') || undefined, + } satisfies StatFilters; +} diff --git a/src/core/discord/getFiltersFromCommand.ts b/src/core/discord/getFiltersFromCommand.ts new file mode 100644 index 000000000..a622cdd5b --- /dev/null +++ b/src/core/discord/getFiltersFromCommand.ts @@ -0,0 +1,18 @@ +import { ChatInputCommandInteraction } from 'discord.js'; +import { TreeTypeString } from '../../components/Tanks'; +import { StatFilters } from '../blitz/filterStats'; + +export function getFiltersFromCommand( + interaction: ChatInputCommandInteraction, +) { + return { + nation: interaction.options.getString('nation') ?? undefined, + tier: parseInt(interaction.options.getString('tier') ?? '0') || undefined, + tankType: interaction.options.getString('tank-type') ?? undefined, + treeType: + (interaction.options.getString('tree-type') as + | TreeTypeString + | undefined) ?? undefined, + tank: parseInt(interaction.options.getString('tank') ?? '0') || undefined, + } satisfies StatFilters; +} diff --git a/src/core/discord/resolvePeriodFromButton.ts b/src/core/discord/resolvePeriodFromButton.ts index 18f2ba18f..4188ba50d 100644 --- a/src/core/discord/resolvePeriodFromButton.ts +++ b/src/core/discord/resolvePeriodFromButton.ts @@ -1,13 +1,43 @@ -import { ButtonInteraction, CacheType } from 'discord.js'; +import { ButtonInteraction } from 'discord.js'; import { Region } from '../../constants/regions'; -import resolvePeriodFromURL from './resolvePeriodFromURL'; +import getPeriodNow from '../blitzstars/getPeriodNow'; +import getPeriodStart from '../blitzstars/getPeriodStart'; +import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; +import { PeriodType } from './addPeriodSubCommands'; +import { + ResolvedPeriod, + getPeriodOptionName, +} from './resolvePeriodFromCommand'; export default function resolvePeriodFromButton( - server: Region, - interaction: ButtonInteraction, + region: Region, + interaction: ButtonInteraction, ) { - return resolvePeriodFromURL( - server, - `https://example.com/${interaction.customId}`, - ); + let name: string; + let start: number; + let end: number; + const url = new URL(`https://example.com/${interaction.customId}`); + const path = url.pathname.split('/'); + const periodSubcommand = path[2] as PeriodType; + + if (periodSubcommand === 'custom') { + const startRaw = parseInt(url!.searchParams.get('start')!); + const endRaw = parseInt(url!.searchParams.get('end')!); + const startDaysAgoMin = Math.min(startRaw, endRaw); + const endDaysAgoMax = Math.max(startRaw, endRaw); + + name = `${startDaysAgoMin} - ${endDaysAgoMax} days`; + start = getTimeDaysAgo(region, endDaysAgoMax); + end = getTimeDaysAgo(region, startDaysAgoMin); + } else { + name = getPeriodOptionName(periodSubcommand); + start = getPeriodStart(region, periodSubcommand); + end = getPeriodNow(); + } + + return { + name, + start, + end, + } satisfies ResolvedPeriod; } diff --git a/src/core/discord/resolvePeriodFromURL.ts b/src/core/discord/resolvePeriodFromURL.ts deleted file mode 100644 index 0ddca139d..000000000 --- a/src/core/discord/resolvePeriodFromURL.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Region } from '../../constants/regions'; -import getPeriodNow from '../blitzstars/getPeriodNow'; -import getPeriodStart from '../blitzstars/getPeriodStart'; -import getTimeDaysAgo from '../blitzstars/getTimeDaysAgo'; -import { PeriodType } from './addPeriodSubCommands'; -import { - ResolvedPeriod, - getPeriodOptionName, -} from './resolvePeriodFromCommand'; - -export default function resolvePeriodFromURL( - region: Region, - urlString: string, -) { - let name: string; - let start: number; - let end: number; - const url = new URL(urlString); - const path = url.pathname.split('/'); - const periodSubcommand = path[2] as PeriodType; - - if (periodSubcommand === 'custom') { - const startRaw = parseInt(url!.searchParams.get('start')!); - const endRaw = parseInt(url!.searchParams.get('end')!); - const startDaysAgoMin = Math.min(startRaw, endRaw); - const endDaysAgoMax = Math.max(startRaw, endRaw); - - name = `${startDaysAgoMin} - ${endDaysAgoMax} days`; - start = getTimeDaysAgo(region, endDaysAgoMax); - end = getTimeDaysAgo(region, startDaysAgoMin); - } else { - name = getPeriodOptionName(periodSubcommand); - start = getPeriodStart(region, periodSubcommand); - end = getPeriodNow(); - } - - return { - name, - start, - end, - } satisfies ResolvedPeriod; -} diff --git a/src/core/discord/resolvePlayerFromButton.ts b/src/core/discord/resolvePlayerFromButton.ts index eb57e5120..8d5a5a4b1 100644 --- a/src/core/discord/resolvePlayerFromButton.ts +++ b/src/core/discord/resolvePlayerFromButton.ts @@ -1,8 +1,14 @@ -import { ButtonInteraction, CacheType } from 'discord.js'; -import resolvePlayerFromURL from './resolvePlayerFromURL'; +import { ButtonInteraction } from 'discord.js'; +import { Region } from '../../constants/regions'; +import { ResolvedPlayer } from './resolvePlayerFromCommand'; export default async function resolvePlayerFromButton( - interaction: ButtonInteraction, + interaction: ButtonInteraction, ) { - return resolvePlayerFromURL(`https://exmaple.com/${interaction.customId}`); + const url = new URL(`https://exmaple.com/${interaction.customId}`); + + return { + id: parseInt(url.searchParams.get('id')!), + region: url.searchParams.get('region') as Region, + } satisfies ResolvedPlayer; } diff --git a/src/core/discord/resolvePlayerFromURL.ts b/src/core/discord/resolvePlayerFromURL.ts deleted file mode 100644 index 33c369a0c..000000000 --- a/src/core/discord/resolvePlayerFromURL.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Region } from '../../constants/regions'; -import { ResolvedPlayer } from '../discord/resolvePlayerFromCommand'; - -export default function resolvePlayerFromURL(urlString: string) { - const url = new URL(urlString); - - return { - id: parseInt(url.searchParams.get('id')!), - region: url.searchParams.get('region') as Region, - } satisfies ResolvedPlayer; -} diff --git a/src/renderers/evolution.tsx b/src/renderers/evolution.tsx index 2f140de09..d245afcc2 100644 --- a/src/renderers/evolution.tsx +++ b/src/renderers/evolution.tsx @@ -12,7 +12,7 @@ import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand'; import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand'; import { AccountInfo } from '../types/accountInfo'; import { Histories } from '../types/histories'; -import { PlayerClanData } from '../types/playerClanData'; +import { PlayerClanInfo } from '../types/playerClanData'; import { StatType } from './stats'; export default async function evolution( @@ -26,7 +26,7 @@ export default async function evolution( if (type === 'player') { const clan = ( - await getWargamingResponse( + await getWargamingResponse( `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ) )[id]?.clan; diff --git a/src/renderers/fullStats.tsx b/src/renderers/fullStats.tsx index bcd5641b4..067d6b0b1 100644 --- a/src/renderers/fullStats.tsx +++ b/src/renderers/fullStats.tsx @@ -18,7 +18,7 @@ import { AllStats, SupplementaryStats, } from '../types/accountInfo'; -import { PlayerClanData } from '../types/playerClanData'; +import { PlayerClanInfo } from '../types/playerClanData'; import { MultiTankFilters, StatType } from './stats'; export default async function fullStats( @@ -36,7 +36,7 @@ export default async function fullStats( if (type === 'player' || type === 'multi-tank') { const clan = ( - await getWargamingResponse( + await getWargamingResponse( `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ) )[id]?.clan; diff --git a/src/renderers/today.tsx b/src/renderers/today.tsx index c63afb8da..481ce7f10 100644 --- a/src/renderers/today.tsx +++ b/src/renderers/today.tsx @@ -17,7 +17,7 @@ import getTimeDaysAgo from '../core/blitzstars/getTimeDaysAgo'; import { tankAverages } from '../core/blitzstars/tankAverages'; import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand'; import { AccountInfo, AllStats } from '../types/accountInfo'; -import { PlayerClanData } from '../types/playerClanData'; +import { PlayerClanInfo } from '../types/playerClanData'; import { PossiblyPromise } from '../types/possiblyPromise'; export default async function today( @@ -36,7 +36,7 @@ export default async function today( const accountInfo = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, ); - const clanData = await getWargamingResponse( + const clanData = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}&extra=clan`, ); const careerTankStatsRaw = await getTankStats(server, id); diff --git a/src/types/playerClanData.ts b/src/types/playerClanData.ts index 4dab4ff4d..63bbd0fcf 100644 --- a/src/types/playerClanData.ts +++ b/src/types/playerClanData.ts @@ -1,4 +1,4 @@ -export type PlayerClanData = { +export type PlayerClanInfo = { [accountId: number]: { account_id: number; joined_at: number;