diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index 404c962c6..d4dd2a2c2 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -11,7 +11,7 @@ import addUsernameOption from '../core/discord/addUsernameOption.js'; import cleanTable, { TableInput } from '../core/discord/cleanTable.js'; import negativeEmbed from '../core/discord/negativeEmbed.js'; import positiveEmbed from '../core/discord/positiveEmbed.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; import { AccountInfo } from '../types/accountInfo.js'; @@ -48,7 +48,7 @@ export default { async handler(interaction) { const clan = interaction.options.getString('clan') as SkilledClan; - const { id, server } = await resolvePlayer(interaction); + const { id, server } = await resolvePlayerFromCommand(interaction); const accountInfo = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, ); diff --git a/src/commands/evolution.tsx b/src/commands/evolution.tsx index f1ee55b12..d389e7ed7 100644 --- a/src/commands/evolution.tsx +++ b/src/commands/evolution.tsx @@ -2,7 +2,7 @@ import { SlashCommandBuilder } from 'discord.js'; import resolveTankId from '../core/blitz/resolveTankId.js'; import addStatsSubCommandGroups from '../core/discord/addStatsSubCommandGroups.js'; import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import tanksAutocomplete from '../core/discord/tanksAutocomplete.js'; import usernameAutocomplete from '../core/discord/usernameAutocomplete.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; @@ -24,7 +24,7 @@ export default { const commandGroup = interaction.options.getSubcommandGroup( true, ) as StatType; - const player = await resolvePlayer(interaction); + const player = await resolvePlayerFromCommand(interaction); const period = resolvePeriodFromCommand(interaction); const tankIdRaw = interaction.options.getString('tank')!; const tankId = commandGroup === 'tank' ? resolveTankId(tankIdRaw) : 0; diff --git a/src/commands/ownedtanks.tsx b/src/commands/ownedtanks.tsx index 14815e287..7fb28c870 100644 --- a/src/commands/ownedtanks.tsx +++ b/src/commands/ownedtanks.tsx @@ -13,7 +13,7 @@ import { tankopedia, } from '../core/blitz/tankopedia.js'; import addUsernameOption from '../core/discord/addUsernameOption.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import usernameAutocomplete from '../core/discord/usernameAutocomplete.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; @@ -71,7 +71,7 @@ export default { async handler(interaction) { const tier = Number(interaction.options.getString('tier')); - const account = await resolvePlayer(interaction); + const account = await resolvePlayerFromCommand(interaction); const { id, server } = account; const accountInfo = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, diff --git a/src/commands/playerachievements.ts b/src/commands/playerachievements.ts index c2fbcb471..b8f9872a4 100644 --- a/src/commands/playerachievements.ts +++ b/src/commands/playerachievements.ts @@ -5,7 +5,7 @@ import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; import addUsernameOption from '../core/discord/addUsernameOption.js'; import cleanTable, { TableInputEntry } from '../core/discord/cleanTable.js'; import infoEmbed from '../core/discord/infoEmbed.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import usernameAutocomplete from '../core/discord/usernameAutocomplete.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; @@ -35,7 +35,7 @@ export default { async handler(interaction) { const sortBy = (interaction.options.getString('sort') ?? 'name') as SortBy; - const account = await resolvePlayer(interaction); + const account = await resolvePlayerFromCommand(interaction); const { id, server } = account; const accounts = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, diff --git a/src/commands/playerinfo.ts b/src/commands/playerinfo.ts index 7e07d56c4..b7349f0a8 100644 --- a/src/commands/playerinfo.ts +++ b/src/commands/playerinfo.ts @@ -4,7 +4,7 @@ import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; import addUsernameOption from '../core/discord/addUsernameOption.js'; import cleanTable from '../core/discord/cleanTable.js'; import infoEmbed from '../core/discord/infoEmbed.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import usernameAutocomplete from '../core/discord/usernameAutocomplete.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; @@ -21,7 +21,7 @@ export default { .addStringOption(addUsernameOption), async handler(interaction) { - const account = await resolvePlayer(interaction); + const account = await resolvePlayerFromCommand(interaction); const { id, server } = account; const accounts = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, diff --git a/src/commands/stats.tsx b/src/commands/stats.tsx index 4458b99da..f2d99b0b8 100644 --- a/src/commands/stats.tsx +++ b/src/commands/stats.tsx @@ -8,7 +8,8 @@ import linkButton from '../core/discord/linkButton.js'; import primaryButton from '../core/discord/primaryButton.js'; import resolvePeriodFromButton from '../core/discord/resolvePeriodFromButton.js'; import resolvePeriodFromCommand from '../core/discord/resolvePeriodFromCommand.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromButton from '../core/discord/resolvePlayerFromButton.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import tanksAutocomplete from '../core/discord/tanksAutocomplete.js'; import usernameAutocomplete from '../core/discord/usernameAutocomplete.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; @@ -30,7 +31,7 @@ export default { true, ) as StatType; const subcommand = interaction.options.getSubcommand() as Period; - const player = await resolvePlayer(interaction); + const player = await resolvePlayerFromCommand(interaction); const period = resolvePeriodFromCommand(interaction); const tankIdRaw = interaction.options.getString('tank')!; const tankId = commandGroup === 'tank' ? resolveTankId(tankIdRaw) : 0; @@ -59,7 +60,7 @@ export default { const path = url.pathname.split('/').filter(Boolean); const commandGroup = path[1] as StatType; const period = resolvePeriodFromButton(interaction); - const player = await resolvePlayer(interaction); + const player = await resolvePlayerFromButton(interaction); return await stats( commandGroup, diff --git a/src/commands/today.tsx b/src/commands/today.tsx index a1b46d234..786148753 100644 --- a/src/commands/today.tsx +++ b/src/commands/today.tsx @@ -1,6 +1,6 @@ import { SlashCommandBuilder } from 'discord.js'; import addUsernameOption from '../core/discord/addUsernameOption.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import usernameAutocomplete from '../core/discord/usernameAutocomplete.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; import today from '../renderers/today.js'; @@ -16,7 +16,7 @@ export default { .addStringOption(addUsernameOption), async handler(interaction) { - const player = await resolvePlayer(interaction); + const player = await resolvePlayerFromCommand(interaction); return await today(player); }, diff --git a/src/commands/verify.ts b/src/commands/verify.ts index b3cef4b84..6888a7455 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -5,7 +5,7 @@ import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; import addUsernameOption from '../core/discord/addUsernameOption.js'; import negativeEmbed from '../core/discord/negativeEmbed.js'; import positiveEmbed from '../core/discord/positiveEmbed.js'; -import resolvePlayer from '../core/discord/resolvePlayer.js'; +import resolvePlayerFromCommand from '../core/discord/resolvePlayerFromCommand.js'; import usernameAutocomplete from '../core/discord/usernameAutocomplete.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { CommandRegistry } from '../events/interactionCreate/index.js'; @@ -23,7 +23,7 @@ export default { .addStringOption((option) => addUsernameOption(option).setRequired(true)), async handler(interaction) { - const blitzAccount = await resolvePlayer(interaction); + const blitzAccount = await resolvePlayerFromCommand(interaction); const { id, server } = blitzAccount; const accountInfo = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, diff --git a/src/core/blitz/getBlitzClan.ts b/src/core/blitz/getBlitzClan.ts index 91a6aaf63..908950824 100644 --- a/src/core/blitz/getBlitzClan.ts +++ b/src/core/blitz/getBlitzClan.ts @@ -1,6 +1,6 @@ import { CacheType, ChatInputCommandInteraction } from 'discord.js'; import { BlitzServer } from '../../constants/servers.js'; -import { serverAndIdPattern } from '../discord/resolvePlayer.js'; +import { serverAndIdPattern } from '../discord/resolvePlayerFromCommand.js'; import errorWithCause from '../node/errorWithCause.js'; import listClansPanServer from './listClansPanServer.js'; diff --git a/src/core/discord/resolvePlayer.ts b/src/core/discord/resolvePlayer.ts deleted file mode 100644 index 003a49e6c..000000000 --- a/src/core/discord/resolvePlayer.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - ButtonInteraction, - CacheType, - ChatInputCommandInteraction, -} from 'discord.js'; -import { CYCLIC_API } from '../../constants/cyclic.js'; -import { BlitzServer } from '../../constants/servers.js'; -import listAccountsPanServer, { - usernamePatternWithoutPosition, -} from '../blitz/listAccountsPanServer.js'; -import errorWithCause from '../node/errorWithCause.js'; - -export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/; - -export interface ResolvedPlayer { - server: BlitzServer; - id: number; -} - -export default async function resolvePlayer( - interaction: - | ChatInputCommandInteraction - | ButtonInteraction, -) { - if (interaction instanceof ChatInputCommandInteraction) { - const commandUsername = interaction.options.getString('username'); - const displayName = interaction.guild?.members.cache - .get(interaction.user.id) - ?.displayName.match(usernamePatternWithoutPosition)?.[0]; - const username = commandUsername ?? displayName; - - if (username === undefined) { - throw errorWithCause( - 'Username could not be inferred', - "You didn't provide me a username in the command nor was I able to infer your Blitz username from your Discord nickname. Try providing a username manually in the command or using the `/verify` command to set your Discord nickname to your Blitz username.", - ); - } - - if (serverAndIdPattern.test(username)) { - const [server, accountId] = username.split('/'); - return { - server: server as BlitzServer, - id: Number(accountId), - } satisfies ResolvedPlayer; - } else { - const accounts = await listAccountsPanServer(username); - - if (accounts[0]) { - return { - server: accounts[0].server, - id: accounts[0].account_id, - } satisfies ResolvedPlayer; - } else { - throw errorWithCause( - 'Could not find user', - `I couldn't find user \`${username}\`. Try providing a valid username manually or using the \`/verify\` command to set your Discord nickname to your Blitz username.`, - ); - } - } - } else { - const url = new URL(`${CYCLIC_API}/${interaction.customId}`); - return { - id: parseInt(url.searchParams.get('id')!), - server: url.searchParams.get('server') as BlitzServer, - } satisfies ResolvedPlayer; - } -} diff --git a/src/core/discord/resolvePlayerFromButton.ts b/src/core/discord/resolvePlayerFromButton.ts new file mode 100644 index 000000000..c6b956359 --- /dev/null +++ b/src/core/discord/resolvePlayerFromButton.ts @@ -0,0 +1,9 @@ +import { ButtonInteraction, CacheType } from 'discord.js'; +import { CYCLIC_API } from '../../constants/cyclic.js'; +import resolvePlayerFromURL from '../express/resolvePlayerFromURL.js'; + +export default async function resolvePlayerFromButton( + interaction: ButtonInteraction, +) { + return resolvePlayerFromURL(`${CYCLIC_API}/${interaction.customId}`); +} diff --git a/src/core/discord/resolvePlayerFromCommand.ts b/src/core/discord/resolvePlayerFromCommand.ts new file mode 100644 index 000000000..1707ad7c3 --- /dev/null +++ b/src/core/discord/resolvePlayerFromCommand.ts @@ -0,0 +1,52 @@ +import { CacheType, ChatInputCommandInteraction } from 'discord.js'; +import { BlitzServer } from '../../constants/servers.js'; +import listAccountsPanServer, { + usernamePatternWithoutPosition, +} from '../blitz/listAccountsPanServer.js'; +import errorWithCause from '../node/errorWithCause.js'; + +export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/; + +export interface ResolvedPlayer { + server: BlitzServer; + id: number; +} + +export default async function resolvePlayerFromCommand( + interaction: ChatInputCommandInteraction, +) { + const commandUsername = interaction.options.getString('username'); + const displayName = interaction.guild?.members.cache + .get(interaction.user.id) + ?.displayName.match(usernamePatternWithoutPosition)?.[0]; + const username = commandUsername ?? displayName; + + if (username === undefined) { + throw errorWithCause( + 'Username could not be inferred', + "You didn't provide me a username in the command nor was I able to infer your Blitz username from your Discord nickname. Try providing a username manually in the command or using the `/verify` command to set your Discord nickname to your Blitz username.", + ); + } + + if (serverAndIdPattern.test(username)) { + const [server, accountId] = username.split('/'); + return { + server: server as BlitzServer, + id: Number(accountId), + } satisfies ResolvedPlayer; + } else { + const accounts = await listAccountsPanServer(username); + + if (accounts[0]) { + return { + server: accounts[0].server, + id: accounts[0].account_id, + } satisfies ResolvedPlayer; + } else { + throw errorWithCause( + 'Could not find user', + `I couldn't find user \`${username}\`. Try providing a valid username manually or using the \`/verify\` command to set your Discord nickname to your Blitz username.`, + ); + } + } +} diff --git a/src/core/express/resolvePlayerFromRequest.ts b/src/core/express/resolvePlayerFromRequest.ts new file mode 100644 index 000000000..81f34fb2d --- /dev/null +++ b/src/core/express/resolvePlayerFromRequest.ts @@ -0,0 +1,7 @@ +import { Request } from 'express'; +import { CYCLIC_API } from '../../constants/cyclic.js'; +import resolvePlayerFromURL from './resolvePlayerFromURL.js'; + +export default async function resolvePlayerFromRequest(request: Request) { + return resolvePlayerFromURL(`${CYCLIC_API}${request.path}`); +} diff --git a/src/core/express/resolvePlayerFromURL.ts b/src/core/express/resolvePlayerFromURL.ts new file mode 100644 index 000000000..1c9e4f2ff --- /dev/null +++ b/src/core/express/resolvePlayerFromURL.ts @@ -0,0 +1,11 @@ +import { BlitzServer } from '../../constants/servers.js'; +import { ResolvedPlayer } from '../discord/resolvePlayerFromCommand.js'; + +export default async function resolvePlayerFromURL(urlString: string) { + const url = new URL(urlString); + + return { + id: parseInt(url.searchParams.get('id')!), + server: url.searchParams.get('server') as BlitzServer, + } satisfies ResolvedPlayer; +} diff --git a/src/renderers/evolution.tsx b/src/renderers/evolution.tsx index 28d9dde9f..9b1af6974 100644 --- a/src/renderers/evolution.tsx +++ b/src/renderers/evolution.tsx @@ -10,7 +10,7 @@ import { tankopedia } from '../core/blitz/tankopedia.js'; import getPlayerHistories from '../core/blitzstars/getPlayerHistories.js'; import getTankHistories from '../core/blitzstars/getTankHistories.js'; import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand.js'; -import { ResolvedPlayer } from '../core/discord/resolvePlayer.js'; +import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { AccountInfo } from '../types/accountInfo.js'; import { Histories } from '../types/histories.js'; diff --git a/src/renderers/stats.tsx b/src/renderers/stats.tsx index d61c51a23..c999673b1 100644 --- a/src/renderers/stats.tsx +++ b/src/renderers/stats.tsx @@ -13,7 +13,7 @@ import { Tier, tankopedia } from '../core/blitz/tankopedia.js'; import getTankStatsOverTime from '../core/blitzstars/getTankStatsOverTime.js'; import { tankAverages } from '../core/blitzstars/tankAverages.js'; import { ResolvedPeriod } from '../core/discord/resolvePeriodFromCommand.js'; -import { ResolvedPlayer } from '../core/discord/resolvePlayer.js'; +import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { AccountInfo, diff --git a/src/renderers/today.tsx b/src/renderers/today.tsx index 81ad56736..f84c76bdc 100644 --- a/src/renderers/today.tsx +++ b/src/renderers/today.tsx @@ -14,7 +14,7 @@ import getPeriodNow from '../core/blitzstars/getPeriodNow.js'; import getTankStatsOverTime from '../core/blitzstars/getTankStatsOverTime.js'; import getTimeDaysAgo from '../core/blitzstars/getTimeDaysAgo.js'; import { tankAverages } from '../core/blitzstars/tankAverages.js'; -import { ResolvedPlayer } from '../core/discord/resolvePlayer.js'; +import { ResolvedPlayer } from '../core/discord/resolvePlayerFromCommand.js'; import { WARGAMING_APPLICATION_ID } from '../core/node/args.js'; import { AccountInfo, AllStats } from '../types/accountInfo.js'; import { PlayerClanData } from '../types/playerClanData.js';