From 9a9ea3a71a4ec4a38a23268b8a2be805b862f75b Mon Sep 17 00:00:00 2001 From: tresabhi Date: Tue, 16 May 2023 02:02:50 +0000 Subject: [PATCH] feat: lenient username and clan lookup --- src/commands/debug.ts | 2 +- src/commands/eligible.ts | 6 +-- src/commands/help.ts | 2 +- src/commands/inactive.ts | 34 +++++-------- src/commands/searchplayers.ts | 8 ++- src/commands/stats.ts | 38 +++++--------- src/commands/tanks.ts | 26 ++++++---- src/commands/tankstats.ts | 6 +-- src/commands/verify.ts | 6 +-- src/core/autocomplete/clan.ts | 29 +++++++++++ src/core/autocomplete/username.ts | 20 ++------ src/core/blitz/getBlitzAccount.ts | 33 ++++++++++++ src/core/blitz/getBlitzClan.ts | 32 ++++++++++++ src/core/blitz/getClan.ts | 57 --------------------- src/core/blitz/listAccountsPanServer.ts | 64 +++++++++++++++++++++++ src/core/blitz/listClansPanServer.ts | 64 +++++++++++++++++++++++ src/core/blitz/validateUsername.ts | 65 ------------------------ src/core/options/addClanOption.ts | 9 ++++ src/types/{players.ts => accountList.ts} | 0 19 files changed, 292 insertions(+), 209 deletions(-) create mode 100644 src/core/autocomplete/clan.ts create mode 100644 src/core/blitz/getBlitzAccount.ts create mode 100644 src/core/blitz/getBlitzClan.ts delete mode 100644 src/core/blitz/getClan.ts create mode 100644 src/core/blitz/listAccountsPanServer.ts create mode 100644 src/core/blitz/listClansPanServer.ts delete mode 100644 src/core/blitz/validateUsername.ts create mode 100644 src/core/options/addClanOption.ts rename src/types/{players.ts => accountList.ts} (100%) diff --git a/src/commands/debug.ts b/src/commands/debug.ts index 9aad2b627..9f1121277 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -11,7 +11,7 @@ const executionStart = new Date().getTime(); export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index 142c7de45..55c7c3577 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -4,7 +4,7 @@ import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { NEGATIVE_COLOR, POSITIVE_COLOR } from '../constants/colors.js'; import { BlitzServer } from '../constants/servers.js'; import usernameAutocomplete from '../core/autocomplete/username.js'; -import validateUsername from '../core/blitz/validateUsername.js'; +import getBlitzAccount from '../core/blitz/getBlitzAccount.js'; import blitzStarsLinks from '../core/blitzstars/blitzStarsLinks.js'; import getBlitzStarsAccount from '../core/blitzstars/getBlitzStarsAccount.js'; import poweredByBlitzStars from '../core/blitzstars/poweredByBlitzStars.js'; @@ -19,7 +19,7 @@ const CLANS = { export default { inProduction: false, - inDevelopment: true, + inDevelopment: false, inPublic: false, command: new SlashCommandBuilder() @@ -42,7 +42,7 @@ export default { const clan = interaction.options.getString('clan') as keyof typeof CLANS; const name = interaction.options.getString('username')!; const server = interaction.options.getString('server') as BlitzServer; - const blitzAccount = await validateUsername(interaction, name, server); + const blitzAccount = await getBlitzAccount(interaction, name, server); if (!blitzAccount) return; diff --git a/src/commands/help.ts b/src/commands/help.ts index 30a58f9e1..3dd121dfc 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -11,7 +11,7 @@ import { client } from '../index.js'; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() diff --git a/src/commands/inactive.ts b/src/commands/inactive.ts index 11edbc442..7969d166b 100644 --- a/src/commands/inactive.ts +++ b/src/commands/inactive.ts @@ -2,33 +2,26 @@ import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { NEGATIVE_COLOR, POSITIVE_COLOR } from '../constants/colors.js'; -import { BlitzServer } from '../constants/servers.js'; -import getClan from '../core/blitz/getClan.js'; +import clanAutocomplete from '../core/autocomplete/clan.js'; +import getBlitzClan from '../core/blitz/getBlitzClan.js'; import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; import cmdName from '../core/interaction/cmdName.js'; -import addServerChoices from '../core/options/addServerChoices.js'; +import addClanOption from '../core/options/addClanOption.js'; import { args } from '../core/process/args.js'; import { AccountInfo } from '../types/accountInfo.js'; import { ClanInfo } from '../types/clanInfo.js'; -import { Clan } from '../types/clanList.js'; const DEFAULT_THRESHOLD = 7; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() .setName(cmdName('inactive')) .setDescription('Lists all inactive players') - .addStringOption(addServerChoices) - .addStringOption((option) => - option - .setName('clan') - .setDescription('The clan name or tag you are checking') - .setRequired(true), - ) + .addStringOption(addClanOption) .addNumberOption((option) => option .setName('threshold') @@ -40,19 +33,18 @@ export default { async execute(interaction) { const clanName = interaction.options.getString('clan')!; - const server = interaction.options.getString('server') as BlitzServer; - - const clan = (await getClan(interaction, clanName, server)) as Clan; + const clan = await getBlitzClan(interaction, clanName); if (!clan) return; + const { server, id } = clan; const threshold = interaction.options.getNumber('threshold')! ?? DEFAULT_THRESHOLD; const time = new Date().getTime() / 1000; const clanInfo = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/clans/info/?application_id=${args['wargaming-application-id']}&clan_id=${clan.clan_id}`, + `https://api.wotblitz.${server}/wotb/clans/info/?application_id=${args['wargaming-application-id']}&clan_id=${id}`, ); if (!clanInfo) return; - const memberIds = clanInfo[clan.clan_id].members_ids; + const memberIds = clanInfo[id].members_ids; const accountInfo = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/account/info/?application_id=${ args['wargaming-application-id'] @@ -82,8 +74,8 @@ export default { .setColor(hasInactiveMembers ? NEGATIVE_COLOR : POSITIVE_COLOR) .setTitle( hasInactiveMembers - ? `${markdownEscape(clan.name)}'s inactive members` - : `No inactive members in ${markdownEscape(clan.name)}`, + ? `${markdownEscape(clanInfo[id].name)}'s inactive members` + : `No inactive members in ${markdownEscape(clanInfo[id].name)}`, ) .setDescription( hasInactiveMembers @@ -96,7 +88,9 @@ export default { }); console.log( - `Displaying inactive members in ${clan.name} for ${threshold} days`, + `Displaying inactive members in ${clanInfo[id].name} for ${threshold} days`, ); }, + + autocomplete: clanAutocomplete, } satisfies CommandRegistry; diff --git a/src/commands/searchplayers.ts b/src/commands/searchplayers.ts index 5f4a53d79..2d6a5b2b6 100644 --- a/src/commands/searchplayers.ts +++ b/src/commands/searchplayers.ts @@ -7,10 +7,10 @@ import cmdName from '../core/interaction/cmdName.js'; import addServerChoices from '../core/options/addServerChoices.js'; import addUsernameOption from '../core/options/addUsernameOption.js'; import { args } from '../core/process/args.js'; -import { AccountList } from '../types/players.js'; +import { AccountList } from '../types/accountList.js'; export default { - inDevelopment: true, + inDevelopment: false, inProduction: true, inPublic: true, @@ -47,9 +47,7 @@ export default { `\`\`\`${ players.length === 0 ? 'No players found.' - : players - .map((player) => markdownEscape(player.nickname)) - .join('\n') + : players.map((player) => player.nickname).join('\n') }\`\`\``, ), ], diff --git a/src/commands/stats.ts b/src/commands/stats.ts index ec13d2825..5d99408c3 100644 --- a/src/commands/stats.ts +++ b/src/commands/stats.ts @@ -2,16 +2,14 @@ import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { SKILLED_COLOR } from '../constants/colors.js'; -import { BlitzServer } from '../constants/servers.js'; import usernameAutocomplete from '../core/autocomplete/username.js'; +import getBlitzAccount from '../core/blitz/getBlitzAccount.js'; import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import validateUsername from '../core/blitz/validateUsername.js'; import blitzStarsLinks from '../core/blitzstars/blitzStarsLinks.js'; import getBlitzStarsAccount from '../core/blitzstars/getBlitzStarsAccount.js'; import poweredByBlitzStars from '../core/blitzstars/poweredByBlitzStars.js'; import cleanTable from '../core/interaction/cleanTable.js'; import cmdName from '../core/interaction/cmdName.js'; -import addServerChoices from '../core/options/addServerChoices.js'; import addUsernameOption from '../core/options/addUsernameOption.js'; import { args } from '../core/process/args.js'; import { AccountInfo, AllStats } from '../types/accountInfo.js'; @@ -21,7 +19,7 @@ type Period = 'today' | '30' | '90' | 'career'; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() @@ -39,29 +37,25 @@ export default { ) .setRequired(true), ) - .addStringOption(addServerChoices) .addStringOption(addUsernameOption), async execute(interaction) { const period = interaction.options.getString('period') as Period; const username = interaction.options.getString('username')!; - const server = interaction.options.getString('server') as BlitzServer; - const blitzAccount = await validateUsername(interaction, username, server); + const blitzAccount = await getBlitzAccount(interaction, username); if (!blitzAccount) return; + const { id, server } = blitzAccount; let stats: BlitzStartsComputedPeriodicStatistics; const accountInfo = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${blitzAccount.account_id}`, + `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, ); if (!accountInfo) return; - const blitzStarsAccount = await getBlitzStarsAccount( - interaction, - blitzAccount.account_id, - ); + const blitzStarsAccount = await getBlitzStarsAccount(interaction, id); if (!blitzStarsAccount) return; if (period === 'today') { const a1 = blitzStarsAccount.statistics; - const a2 = accountInfo[blitzAccount.account_id].statistics; + const a2 = accountInfo[id].statistics; const battles = a2.all.battles - a1.all.battles; function pb(value: (allStats: AllStats) => number) { @@ -70,14 +64,6 @@ export default { function diff(value: (allStats: AllStats) => number) { return value(a2.all) - value(a1.all); } - function span( - value1: (allStats: AllStats) => number, - value2: (allStats: AllStats) => number, - ) { - return ( - (value1(a2.all) - value1(a1.all)) / (value2(a2.all) - value2(a1.all)) - ); - } stats = { all: { @@ -119,8 +105,10 @@ export default { new EmbedBuilder() .setColor(SKILLED_COLOR) .setTitle( - `${markdownEscape(blitzAccount.nickname)}'s ${ - period === 'career' ? period : `${period} day` + `${markdownEscape(blitzStarsAccount.nickname)}'s ${ + period === 'career' + ? period + : `${period === 'today' ? 'today' : `${period} day`}` } stats`, ) .setDescription( @@ -195,14 +183,14 @@ export default { (stats.all.battles - stats.all.survived_battles) ).toFixed(2), ], - ])}\n\n${blitzStarsLinks(server, blitzAccount.nickname)}` + ])}\n\n${blitzStarsLinks(server, blitzStarsAccount.nickname)}` : 'No battles played in this period.', ), ), ], }); - console.log(`Showing stats for ${blitzAccount.nickname}`); + console.log(`Showing stats for ${blitzStarsAccount.nickname}`); }, autocomplete: usernameAutocomplete, diff --git a/src/commands/tanks.ts b/src/commands/tanks.ts index b681a5aa2..09b29c3d5 100644 --- a/src/commands/tanks.ts +++ b/src/commands/tanks.ts @@ -2,15 +2,14 @@ import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { SKILLED_COLOR } from '../constants/colors.js'; -import { BlitzServer } from '../constants/servers.js'; import usernameAutocomplete from '../core/autocomplete/username.js'; +import getBlitzAccount from '../core/blitz/getBlitzAccount.js'; import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import validateUsername from '../core/blitz/validateUsername.js'; import { TANK_TYPE_EMOJIS, tankopedia } from '../core/blitzstars/tankopedia.js'; import cmdName from '../core/interaction/cmdName.js'; -import addServerChoices from '../core/options/addServerChoices.js'; import addUsernameOption from '../core/options/addUsernameOption.js'; import { args } from '../core/process/args.js'; +import { AccountInfo } from '../types/accountInfo.js'; import { TanksStats } from '../types/tanksStats.js'; import resolveTankName from '../utilities/resolveTankName.js'; @@ -37,7 +36,7 @@ const COMP_TANKS = [ export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() @@ -51,20 +50,23 @@ export default { .setMaxValue(10) .setRequired(true), ) - .addStringOption(addServerChoices) .addStringOption(addUsernameOption), async execute(interaction) { const tier = interaction.options.getInteger('tier')!; const name = interaction.options.getString('username')!; - const server = interaction.options.getString('server') as BlitzServer; - const account = await validateUsername(interaction, name, server); + const account = await getBlitzAccount(interaction, name); if (!account) return; + const { id, server } = account; + const accountInfo = await getWargamingResponse( + `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, + ); + if (!accountInfo) return; const tankStats = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${account.account_id}`, + `https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`, ); if (!tankStats) return; - const tanks = tankStats[account.account_id] + const tanks = tankStats[id] .map((tankData) => tankopedia.data[tankData.tank_id]) .filter((tank) => tank.tier === tier); @@ -72,7 +74,7 @@ export default { embeds: [ new EmbedBuilder() .setTitle( - `${markdownEscape(account.nickname)}'s owned ${ + `${markdownEscape(accountInfo[id].nickname)}'s owned ${ tier === null ? '' : `tier ${tier} ` }tanks`, ) @@ -102,7 +104,9 @@ export default { ], }); - console.log(`Displaying ${account.nickname}'s owned tanks in tier ${tier}`); + console.log( + `Displaying ${accountInfo[id].nickname}'s owned tanks in tier ${tier}`, + ); }, autocomplete: usernameAutocomplete, diff --git a/src/commands/tankstats.ts b/src/commands/tankstats.ts index b27af84b3..541a8ede4 100644 --- a/src/commands/tankstats.ts +++ b/src/commands/tankstats.ts @@ -4,7 +4,7 @@ import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { BlitzServer } from '../constants/servers.js'; import tanksAutocomplete from '../core/autocomplete/tanks.js'; import usernameAutocomplete from '../core/autocomplete/username.js'; -import validateUsername from '../core/blitz/validateUsername.js'; +import getBlitzAccount from '../core/blitz/getBlitzAccount.js'; import { tankopedia } from '../core/blitzstars/tankopedia.js'; import getPeriodicStats, { Period, @@ -22,7 +22,7 @@ type Periods = '30' | '60' | '90'; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() @@ -48,7 +48,7 @@ export default { const name = interaction.options.getString('username')!; const tankId = interaction.options.getString('tank')!; const period = interaction.options.getString('period') as Periods; - const blitzAccount = await validateUsername(interaction, name, server); + const blitzAccount = await getBlitzAccount(interaction, name, server); if (!blitzAccount) return; const tank = tankopedia.data[tankId as unknown as number]; diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 31802fb70..99287921c 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -5,8 +5,8 @@ import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { NEGATIVE_COLOR, POSITIVE_COLOR } from '../constants/colors.js'; import { BlitzServer } from '../constants/servers.js'; import usernameAutocomplete from '../core/autocomplete/username.js'; +import getBlitzAccount from '../core/blitz/getBlitzAccount.js'; import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; -import validateUsername from '../core/blitz/validateUsername.js'; import cmdName from '../core/interaction/cmdName.js'; import addServerChoices from '../core/options/addServerChoices.js'; import addUsernameOption from '../core/options/addUsernameOption.js'; @@ -15,7 +15,7 @@ import { PlayerClanData } from '../types/playerClanData.js'; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() @@ -27,7 +27,7 @@ export default { async execute(interaction) { const name = interaction.options.getString('username')!; const server = interaction.options.getString('server') as BlitzServer; - const account = await validateUsername(interaction, name, server); + const account = await getBlitzAccount(interaction, name, server); if (!account) return; const clanData = await getWargamingResponse( `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${account.account_id}&extra=clan`, diff --git a/src/core/autocomplete/clan.ts b/src/core/autocomplete/clan.ts new file mode 100644 index 000000000..307665bf3 --- /dev/null +++ b/src/core/autocomplete/clan.ts @@ -0,0 +1,29 @@ +import { + ApplicationCommandOptionChoiceData, + AutocompleteInteraction, + CacheType, +} from 'discord.js'; +import { BLITZ_SERVERS } from '../../constants/servers.js'; +import listClansPanServer from '../blitz/listClansPanServer.js'; + +export default async function clanAutocomplete( + interaction: AutocompleteInteraction, +) { + const focusedOption = interaction.options.getFocused(true); + if (focusedOption.name !== 'clan') return; + const players = await listClansPanServer(focusedOption.value); + + await interaction.respond( + players + ? players.map( + (player) => + ({ + name: `${player.name} (${BLITZ_SERVERS[player.server]})`, + value: `${player.server}/${player.clan_id}`, + } satisfies ApplicationCommandOptionChoiceData), + ) + : [], + ); + + console.log(`Clan autocomplete for ${focusedOption.value}`); +} diff --git a/src/core/autocomplete/username.ts b/src/core/autocomplete/username.ts index 03501dcaa..6bd85a2e7 100644 --- a/src/core/autocomplete/username.ts +++ b/src/core/autocomplete/username.ts @@ -3,33 +3,23 @@ import { AutocompleteInteraction, CacheType, } from 'discord.js'; -import { BlitzServer } from '../../constants/servers.js'; -import { AccountList } from '../../types/players.js'; -import getWargamingResponse from '../blitz/getWargamingResponse.js'; -import { args } from '../process/args.js'; +import { BLITZ_SERVERS } from '../../constants/servers.js'; +import listAccountsPanServer from '../blitz/listAccountsPanServer.js'; export default async function usernameAutocomplete( interaction: AutocompleteInteraction, ) { const focusedOption = interaction.options.getFocused(true); if (focusedOption.name !== 'username') return; - const server = interaction.options.getString('server') as BlitzServer; - - const players = await getWargamingResponse( - `https://api.wotblitz.${ - server ?? 'com' - }/wotb/account/list/?application_id=${ - args['wargaming-application-id'] - }&search=${focusedOption.value}&limit=10`, - ); + const players = await listAccountsPanServer(focusedOption.value); await interaction.respond( players ? players.map( (player) => ({ - name: player.nickname, - value: `${player.account_id}`, + name: `${player.nickname} (${BLITZ_SERVERS[player.server]})`, + value: `${player.server}/${player.account_id}`, } satisfies ApplicationCommandOptionChoiceData), ) : [], diff --git a/src/core/blitz/getBlitzAccount.ts b/src/core/blitz/getBlitzAccount.ts new file mode 100644 index 000000000..9c2a1975d --- /dev/null +++ b/src/core/blitz/getBlitzAccount.ts @@ -0,0 +1,33 @@ +import { CacheType, ChatInputCommandInteraction } from 'discord.js'; +import { BlitzServer } from '../../constants/servers.js'; +import errorEmbed from '../interaction/errorEmbed.js'; +import listAccountsPanServer from './listAccountsPanServer.js'; + +export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/; + +export default async function getBlitzAccount( + interaction: ChatInputCommandInteraction, + username: string, +): Promise<{ server: BlitzServer; id: number } | null> { + if (serverAndIdPattern.test(username)) { + const [server, accountId] = username.split('/'); + return { server: server as BlitzServer, id: Number(accountId) }; + } else { + const accounts = await listAccountsPanServer(username); + + if (accounts[0]) { + return { server: accounts[0].server, id: accounts[0].account_id }; + } else { + await interaction.editReply({ + embeds: [ + errorEmbed( + 'Could not find user', + `I couldn't find user \`${username}\`. Try selecting a username from the search result.`, + ), + ], + }); + + return null; + } + } +} diff --git a/src/core/blitz/getBlitzClan.ts b/src/core/blitz/getBlitzClan.ts new file mode 100644 index 000000000..381dfbe7d --- /dev/null +++ b/src/core/blitz/getBlitzClan.ts @@ -0,0 +1,32 @@ +import { CacheType, ChatInputCommandInteraction } from 'discord.js'; +import { BlitzServer } from '../../constants/servers.js'; +import errorEmbed from '../interaction/errorEmbed.js'; +import { serverAndIdPattern } from './getBlitzAccount.js'; +import listClansPanServer from './listClansPanServer.js'; + +export default async function getBlitzClan( + interaction: ChatInputCommandInteraction, + clan: string, +): Promise<{ server: BlitzServer; id: number } | null> { + if (serverAndIdPattern.test(clan)) { + const [server, accountId] = clan.split('/'); + return { server: server as BlitzServer, id: Number(accountId) }; + } else { + const accounts = await listClansPanServer(clan); + + if (accounts[0]) { + return { server: accounts[0].server, id: accounts[0].clan_id }; + } else { + await interaction.editReply({ + embeds: [ + errorEmbed( + 'Could not find user', + `I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`, + ), + ], + }); + + return null; + } + } +} diff --git a/src/core/blitz/getClan.ts b/src/core/blitz/getClan.ts deleted file mode 100644 index 203fd1502..000000000 --- a/src/core/blitz/getClan.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - CacheType, - ChatInputCommandInteraction, - EmbedBuilder, -} from 'discord.js'; -import markdownEscape from 'markdown-escape'; -import { NEGATIVE_COLOR } from '../../constants/colors.js'; -import { BLITZ_SERVERS, BlitzServer } from '../../constants/servers.js'; -import { ClanList } from '../../types/clanList.js'; -import { args } from '../process/args.js'; -import getWargamingResponse from './getWargamingResponse.js'; - -export default async function getClan( - interaction: ChatInputCommandInteraction, - name: string, - server: BlitzServer, -) { - const serverName = BLITZ_SERVERS[server]; - - const clans = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/clans/list/?application_id=${args['wargaming-application-id']}&search=${name}`, - ); - - if ( - clans.length > 0 && - (clans[0].name.toLowerCase() === name.toLowerCase() || - clans[0].tag.toLowerCase() === name.toLowerCase()) - ) { - return clans[0]; - } else { - // no exact match - await interaction.editReply({ - embeds: [ - new EmbedBuilder() - .setColor(NEGATIVE_COLOR) - .setTitle(`Clan not found`) - .setDescription( - `I couldn't find "${markdownEscape( - name, - )}" in the ${serverName} server. I found ${ - clans ? (clans.length < 100 ? clans.length : 'over 100') : 0 - } similarly spelled clan${clans?.length !== 1 ? 's' : ''}. ${ - clans && clans.length > 0 - ? `Did you mean "${markdownEscape( - clans[0].name, - )}" or "${markdownEscape(clans[0].tag)}"? ` - : '' - }Re-run the command, don't make typos.`, - ), - ], - }); - - console.log(`Clan not found for ${name} in ${serverName} server.`); - - return null; - } -} diff --git a/src/core/blitz/listAccountsPanServer.ts b/src/core/blitz/listAccountsPanServer.ts new file mode 100644 index 000000000..ad4524255 --- /dev/null +++ b/src/core/blitz/listAccountsPanServer.ts @@ -0,0 +1,64 @@ +import { BlitzServer } from '../../constants/servers.js'; +import { Account, AccountList } from '../../types/accountList.js'; +import { args } from '../process/args.js'; +import getWargamingResponse from './getWargamingResponse.js'; + +export type AccountListWithServer = (Account & { + server: 'com' | 'eu' | 'asia'; +})[]; + +export default function listAccountsPanServer(search: string, limit = 9) { + return new Promise(async (resolve) => { + const trimmed = search.trim(); + if (trimmed.length < 3 && trimmed.length > 100) resolve([]); + + const normalizedLimit = Math.round(limit / 3); + const accountList: AccountListWithServer = []; + let done = 0; + + function afterResponse() { + done++; + + if (done === 3) { + resolve(accountList); + } + } + + getWargamingResponse( + `https://api.wotblitz.com/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`, + ).then((value) => { + if (value) + accountList.push( + ...value.map((account) => ({ + ...account, + server: 'com' as BlitzServer, + })), + ); + afterResponse(); + }); + getWargamingResponse( + `https://api.wotblitz.eu/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`, + ).then((value) => { + if (value) + accountList.push( + ...value.map((account) => ({ + ...account, + server: 'eu' as BlitzServer, + })), + ); + afterResponse(); + }); + getWargamingResponse( + `https://api.wotblitz.asia/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`, + ).then((value) => { + if (value) + accountList.push( + ...value.map((account) => ({ + ...account, + server: 'asia' as BlitzServer, + })), + ); + afterResponse(); + }); + }); +} diff --git a/src/core/blitz/listClansPanServer.ts b/src/core/blitz/listClansPanServer.ts new file mode 100644 index 000000000..a7d6e0622 --- /dev/null +++ b/src/core/blitz/listClansPanServer.ts @@ -0,0 +1,64 @@ +import { BlitzServer } from '../../constants/servers.js'; +import { Clan, ClanList } from '../../types/clanList.js'; +import { args } from '../process/args.js'; +import getWargamingResponse from './getWargamingResponse.js'; + +export type ClanListWithServer = (Clan & { + server: 'com' | 'eu' | 'asia'; +})[]; + +export default function listClansPanServer(search: string, limit = 9) { + return new Promise(async (resolve) => { + const trimmed = search.trim(); + if (trimmed.length < 3 && trimmed.length > 100) resolve([]); + + const normalizedLimit = Math.round(limit / 3); + const accountList: ClanListWithServer = []; + let done = 0; + + function afterResponse() { + done++; + + if (done === 3) { + resolve(accountList); + } + } + + getWargamingResponse( + `https://api.wotblitz.com/wotb/clans/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`, + ).then((value) => { + if (value) + accountList.push( + ...value.map((account) => ({ + ...account, + server: 'com' as BlitzServer, + })), + ); + afterResponse(); + }); + getWargamingResponse( + `https://api.wotblitz.eu/wotb/clans/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`, + ).then((value) => { + if (value) + accountList.push( + ...value.map((account) => ({ + ...account, + server: 'eu' as BlitzServer, + })), + ); + afterResponse(); + }); + getWargamingResponse( + `https://api.wotblitz.asia/wotb/clans/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`, + ).then((value) => { + if (value) + accountList.push( + ...value.map((account) => ({ + ...account, + server: 'asia' as BlitzServer, + })), + ); + afterResponse(); + }); + }); +} diff --git a/src/core/blitz/validateUsername.ts b/src/core/blitz/validateUsername.ts deleted file mode 100644 index 6e56cd7a1..000000000 --- a/src/core/blitz/validateUsername.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { CacheType, ChatInputCommandInteraction } from 'discord.js'; -import { BLITZ_SERVERS, BlitzServer } from '../../constants/servers.js'; -import { AccountInfo } from '../../types/accountInfo.js'; -import { Account, AccountList } from '../../types/players.js'; -import errorEmbed from '../interaction/errorEmbed.js'; -import { args } from '../process/args.js'; -import getWargamingResponse from './getWargamingResponse.js'; - -export default async function validateUsername( - interaction: ChatInputCommandInteraction, - username: string, - server: BlitzServer, -) { - const serverName = BLITZ_SERVERS[server]; - - if (Number.isNaN(Number(username))) { - // string like "tresabhi" - const accountList = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/account/list/?application_id=${args['wargaming-application-id']}&limit=1&search=${username}`, - ); - - if ( - accountList?.[0]?.nickname.toLowerCase().trim() === - username.toLowerCase().trim() - ) { - return accountList[0]; - } - - await interaction.editReply({ - embeds: [ - errorEmbed( - 'Could not find user', - `I couldn't find user "${username}" in the ${serverName} server. Did you select a username from the search result?`, - ), - ], - }); - - return null; - } else { - // number like 1041988373 - const accountInfo = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${username}`, - ); - - if (accountInfo) { - const account = accountInfo[username as unknown as number]; - - return { - account_id: account.account_id, - nickname: account.nickname, - } satisfies Account; - } - - await interaction.editReply({ - embeds: [ - errorEmbed( - 'Could not find user', - `I couldn't find the user by id \`${username}\` in the ${serverName} server. Did you select a username from the search result?`, - ), - ], - }); - - return null; - } -} diff --git a/src/core/options/addClanOption.ts b/src/core/options/addClanOption.ts new file mode 100644 index 000000000..392efb7c4 --- /dev/null +++ b/src/core/options/addClanOption.ts @@ -0,0 +1,9 @@ +import { SlashCommandStringOption } from 'discord.js'; + +export default function addClanOption(option: SlashCommandStringOption) { + return option + .setName('clan') + .setDescription('The clan name or tag you are checking') + .setAutocomplete(true) + .setRequired(true); +} diff --git a/src/types/players.ts b/src/types/accountList.ts similarity index 100% rename from src/types/players.ts rename to src/types/accountList.ts