diff --git a/src/commands/debug.ts b/src/commands/debug.ts index 84b71db09..2c93c0430 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -3,6 +3,7 @@ import packageJSON from '../../package.json' assert { type: 'json' }; import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { SKILLED_COLOR } from '../constants/colors.js'; import { client } from '../index.js'; +import cmdName from '../utilities/cmdName.js'; import getClientId from '../utilities/getClientId.js'; import { tankAverages } from '../utilities/tankAverages.js'; import { tankopedia } from '../utilities/tankopedia.js'; @@ -11,11 +12,11 @@ const executionStart = new Date().getTime(); export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() - .setName('debug') + .setName(cmdName('debug')) .setDescription('Debug information about the bot'), async execute(interaction) { diff --git a/src/commands/eligible.ts b/src/commands/eligible.ts index 2c027d95e..43339b393 100644 --- a/src/commands/eligible.ts +++ b/src/commands/eligible.ts @@ -6,6 +6,7 @@ import { BlitzServer } from '../constants/servers.js'; import addIGNOption from '../utilities/addIGNOption.js'; import addServerChoices from '../utilities/addServerChoices.js'; import blitzLinks from '../utilities/blitzLinks.js'; +import cmdName from '../utilities/cmdName.js'; import getBlitzAccount from '../utilities/getBlitzAccount.js'; import getBlitzStarsAccount from '../utilities/getBlitzStarsAccount.js'; import poweredByBlitzStars from '../utilities/poweredByBlitzStars.js'; @@ -21,7 +22,7 @@ export default { inPublic: false, command: new SlashCommandBuilder() - .setName('eligible') + .setName(cmdName('eligible')) .setDescription("Gets the user's eligibility for Skilled clans") .addStringOption((option) => option @@ -47,7 +48,6 @@ export default { const blitzStarsAccount = await getBlitzStarsAccount( interaction, blitzAccount.account_id, - name, ); if (!blitzStarsAccount) return; @@ -133,7 +133,7 @@ export default { )}'s shortcomings for ${CLANS[clan].name} (${ clan === 'sklld' ? '30 days stats' : 'career stats' }):\n\n${issues.join('\n')}` - }\n\n${blitzLinks(blitzStarsAccount)}`, + }\n\n${blitzLinks(server, blitzAccount.nickname)}`, ), ), ], diff --git a/src/commands/help.ts b/src/commands/help.ts index 15865ba12..fd693a4ab 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -7,14 +7,15 @@ import { } from '../behaviors/interactionCreate.js'; import { SKILLED_COLOR } from '../constants/colors.js'; import { client } from '../index.js'; +import cmdName from '../utilities/cmdName.js'; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() - .setName('help') + .setName(cmdName('help')) .setDescription('All the help you need about the bot'), async execute(interaction) { diff --git a/src/commands/inactive.ts b/src/commands/inactive.ts index c9ce90d18..7623b87bb 100644 --- a/src/commands/inactive.ts +++ b/src/commands/inactive.ts @@ -8,6 +8,7 @@ import { ClanInfo } from '../types/clanInfo.js'; import { Clan } from '../types/clanList.js'; import addServerChoices from '../utilities/addServerChoices.js'; import { args } from '../utilities/args.js'; +import cmdName from '../utilities/cmdName.js'; import getClan from '../utilities/getClan.js'; import getWargamingResponse from '../utilities/getWargamingResponse.js'; @@ -15,11 +16,11 @@ const DEFAULT_THRESHOLD = 7; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() - .setName('inactive') + .setName(cmdName('inactive')) .setDescription('Lists all inactive players') .addStringOption((option) => addServerChoices(option).setRequired(true)) .addStringOption((option) => diff --git a/src/commands/searchplayers.ts b/src/commands/searchplayers.ts index 52160191b..aab60098c 100644 --- a/src/commands/searchplayers.ts +++ b/src/commands/searchplayers.ts @@ -6,15 +6,16 @@ import { Players } from '../types/players.js'; import addIGNOption from '../utilities/addIGNOption.js'; import addServerChoices from '../utilities/addServerChoices.js'; import { args } from '../utilities/args.js'; +import cmdName from '../utilities/cmdName.js'; import getWargamingResponse from '../utilities/getWargamingResponse.js'; export default { - inDevelopment: true, + inDevelopment: false, inProduction: true, inPublic: true, command: new SlashCommandBuilder() - .setName('searchplayers') + .setName(cmdName('searchplayers')) .setDescription('Search players in a Blitz server') .addStringOption(addServerChoices) .addStringOption(addIGNOption) diff --git a/src/commands/stats.ts b/src/commands/stats.ts index 73379000e..0eeac6bd0 100644 --- a/src/commands/stats.ts +++ b/src/commands/stats.ts @@ -4,11 +4,12 @@ import { CommandRegistry } from '../behaviors/interactionCreate.js'; import { SKILLED_COLOR } from '../constants/colors.js'; import { BlitzServer } from '../constants/servers.js'; import { AccountInfo, AllStats } from '../types/accountInfo.js'; -import { PeriodStatistics, PlayerStatistics } from '../types/statistics.js'; +import { BlitzStartsComputedPeriodicStatistics } from '../types/statistics.js'; import addIGNOption from '../utilities/addIGNOption.js'; import addServerChoices from '../utilities/addServerChoices.js'; import { args } from '../utilities/args.js'; import blitzLinks from '../utilities/blitzLinks.js'; +import cmdName from '../utilities/cmdName.js'; import getBlitzAccount from '../utilities/getBlitzAccount.js'; import getBlitzStarsAccount from '../utilities/getBlitzStarsAccount.js'; import getWargamingResponse from '../utilities/getWargamingResponse.js'; @@ -16,13 +17,20 @@ import poweredByBlitzStars from '../utilities/poweredByBlitzStars.js'; type Period = 'today' | '30' | '90' | 'career'; +const daysInPeriod: Record = { + today: 1 * 24 * 60 * 60, + 30: 30 * 24 * 60 * 60, + 90: 90 * 24 * 60 * 60, + career: Infinity, +}; + export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() - .setName('stats') + .setName(cmdName('stats')) .setDescription("Gets the user's in-game statistics") .addStringOption((option) => option @@ -44,88 +52,21 @@ export default { const name = interaction.options.getString('name')!; const server = interaction.options.getString('server') as BlitzServer; const blitzAccount = await getBlitzAccount(interaction, name, server); - if (!blitzAccount) return; - - const blitzStarsAccount = (await getBlitzStarsAccount( + 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}`, + ); + if (!accountInfo) return; + const blitzStarsAccount = await getBlitzStarsAccount( interaction, blitzAccount.account_id, - name, - )) as PlayerStatistics; + ); if (!blitzStarsAccount) return; - async function reply(stats: PeriodStatistics) { - await interaction.editReply({ - embeds: [ - poweredByBlitzStars( - new EmbedBuilder() - .setColor(SKILLED_COLOR) - .setTitle( - `${markdownEscape(blitzStarsAccount.nickname)}'s ${ - period === 'career' ? period : `${period} day` - } stats`, - ) - .setDescription( - `${ - period === 'today' - ? '**⚠ "Today" stats are in development. Some stats are not available/wrong.**\n\n' - : '' - }${[ - ['Winrate', `${stats.special.winrate.toFixed(2)}%`], - ['Survival', `${stats.special.survivalRate.toFixed(2)}%`], - [ - 'Accuracy', - `${((stats.all.hits / stats.all.shots) * 100).toFixed(2)}%`, - ], - ['WN8', stats.wn8.toFixed(0)], - ['WN7', stats.wn7.toFixed(0)], - [], - ['Battles', stats.all.battles], - ['Wins', stats.all.wins], - ['Losses', stats.all.losses], - ['Average tier', stats.avg_tier.toFixed(2)], - [], - [ - 'Shots per battle', - (stats.all.shots / stats.all.battles).toFixed(2), - ], - ['Hits per battle', stats.special.hpb.toFixed(2)], - ['Damage per battle', stats.special.dpb.toFixed(0)], - ['Kills per battle', stats.special.kpb.toFixed(2)], - ['Spots per battle', stats.special.spb.toFixed(2)], - [ - 'XP per battle', - (stats.all.xp / stats.all.battles).toFixed(0), - ], - [], - ['Damage ratio', stats.special.damageRatio.toFixed(2)], - ['Kills to death ratio', stats.special.kdr.toFixed(2)], - ] - .filter((array) => array[1] !== `${-Infinity}`) - .map((array) => - array.length === 0 ? '' : `**${array[0]}**: ${array[1]}`, - ) - .join('\n')}\n\n${blitzLinks(blitzStarsAccount)}`, - ), - ), - ], - }); - - console.log(`Showing ${blitzStarsAccount.nickname}'s ${period} stats`); - } - - let stats: PeriodStatistics | null = null; - - if (period === 'career') { - stats = blitzStarsAccount.statistics; - } else if (period === 'today') { - const accountInfo = await getWargamingResponse( - `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${blitzAccount.account_id}`, - ); - + if (period === 'today') { const a1 = blitzStarsAccount.statistics; const a2 = accountInfo[blitzAccount.account_id].statistics; - const battles = a2.all.battles - a1.all.battles; function pb(value: (allStats: AllStats) => number) { @@ -159,41 +100,112 @@ export default { max_xp: pb((a) => a.max_xp), max_xp_tank_id: pb((a) => a.max_xp_tank_id), shots: diff((a) => a.shots), - spotted: pb((a) => a.spotted), - survived_battles: pb((a) => a.survived_battles), - win_and_survived: pb((a) => a.win_and_survived), + spotted: diff((a) => a.spotted), + survived_battles: diff((a) => a.survived_battles), + win_and_survived: diff((a) => a.win_and_survived), wins: diff((a) => a.wins), xp: diff((a) => a.xp), }, - special: { - winrate: pb((a) => a.wins) * 100, - dpb: pb((a) => a.damage_dealt), - survivalRate: pb((a) => a.survived_battles) * 100, - damageRatio: span( - (a) => a.damage_dealt, - (a) => a.damage_received, - ), - hitRate: pb((a) => a.hits / a.shots), - hpb: pb((a) => a.hits), - kdr: - diff((a) => a.frags) / diff((a) => a.battles - a.survived_battles), - kpb: pb((a) => a.frags), - spb: pb((a) => a.spotted), - }, - avg_tier: -Infinity, - pwp: -Infinity, wn7: -Infinity, wn8: -Infinity, }; - - reply(stats); } else { - stats = blitzStarsAccount[`period${period}d`]; + stats = + period === 'career' + ? blitzStarsAccount.statistics + : blitzStarsAccount[`period${period}d`]; } + if (!stats!) return; - if (stats) { - reply(stats); - } + await interaction.editReply({ + embeds: [ + poweredByBlitzStars( + new EmbedBuilder() + .setColor(SKILLED_COLOR) + .setTitle( + `${markdownEscape(blitzAccount.nickname)}'s ${ + period === 'career' ? period : `${period} day` + } stats`, + ) + .setDescription( + `${ + period === 'today' + ? '**⚠ "Today" stats are in development. Some stats are not available/wrong.**\n\n' + : '' + }${[ + [ + 'Winrate', + `${(100 * (stats.all.wins / stats.all.battles)).toFixed(2)}%`, + ], + [ + 'Survival', + `${( + 100 * + (stats.all.survived_battles / stats.all.battles) + ).toFixed(2)}%`, + ], + [ + 'Accuracy', + `${((stats.all.hits / stats.all.shots) * 100).toFixed(2)}%`, + ], + ['WN8', stats.wn8.toFixed(0)], + ['WN7', stats.wn7.toFixed(0)], + [], + ['Battles', stats.all.battles], + ['Wins', stats.all.wins], + ['Losses', stats.all.losses], + ['Average tier', stats.avg_tier.toFixed(2)], + [], + [ + 'Shots per battle', + (stats.all.shots / stats.all.battles).toFixed(2), + ], + [ + 'Hits per battle', + (stats.all.hits / stats.all.battles).toFixed(2), + ], + [ + 'Damage per battle', + (stats.all.damage_dealt / stats.all.battles).toFixed(0), + ], + [ + 'Kills per battle', + (stats.all.frags / stats.all.battles).toFixed(2), + ], + [ + 'Spots per battle', + (stats.all.spotted / stats.all.battles).toFixed(2), + ], + [ + 'XP per battle', + (stats.all.xp / stats.all.battles).toFixed(0), + ], + [], + [ + 'Damage ratio', + (stats.all.damage_dealt / stats.all.damage_received).toFixed( + 2, + ), + ], + [ + 'Kills to death ratio', + ( + stats.all.frags / + (stats.all.battles - stats.all.survived_battles) + ).toFixed(2), + ], + ] + .filter((array) => array[1] !== `${-Infinity}`) + .map((array) => + array.length === 0 ? '' : `**${array[0]}**: ${array[1]}`, + ) + .join('\n')}\n\n${blitzLinks(server, blitzAccount.nickname)}`, + ), + ), + ], + }); + + console.log(`Showing stats for ${blitzAccount.nickname}`); }, } satisfies CommandRegistry; diff --git a/src/commands/tanks.ts b/src/commands/tanks.ts index 11c565717..73a0582ed 100644 --- a/src/commands/tanks.ts +++ b/src/commands/tanks.ts @@ -7,6 +7,7 @@ import { TanksStats } from '../types/tanksStats.js'; import addIGNOption from '../utilities/addIGNOption.js'; import addServerChoices from '../utilities/addServerChoices.js'; import { args } from '../utilities/args.js'; +import cmdName from '../utilities/cmdName.js'; import getBlitzAccount from '../utilities/getBlitzAccount.js'; import getWargamingResponse from '../utilities/getWargamingResponse.js'; import { TANK_TYPE_EMOJIS, tankopedia } from '../utilities/tankopedia.js'; @@ -34,11 +35,11 @@ const COMP_TANKS = [ export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() - .setName('tanks') + .setName(cmdName('tanks')) .setDescription("Shows a player's owned tanks") .addIntegerOption((option) => option diff --git a/src/commands/verify.ts b/src/commands/verify.ts index d4766c425..c51047f6e 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -8,16 +8,17 @@ import { PlayerClanData } from '../types/playerClanData.js'; import addIGNOption from '../utilities/addIGNOption.js'; import addServerChoices from '../utilities/addServerChoices.js'; import { args } from '../utilities/args.js'; +import cmdName from '../utilities/cmdName.js'; import getBlitzAccount from '../utilities/getBlitzAccount.js'; import getWargamingResponse from '../utilities/getWargamingResponse.js'; export default { inProduction: true, - inDevelopment: true, + inDevelopment: false, inPublic: true, command: new SlashCommandBuilder() - .setName('verify') + .setName(cmdName('verify')) .setDescription("Set's the user's username to their in-game name") .addStringOption(addServerChoices) .addStringOption(addIGNOption), diff --git a/src/embeds/badNicknameFormat.ts b/src/embeds/badNicknameFormat.ts index f5950c710..bae003964 100644 --- a/src/embeds/badNicknameFormat.ts +++ b/src/embeds/badNicknameFormat.ts @@ -1,4 +1,4 @@ -import errorEmbed from '../utilities/errorEmbed.js'; +import errorEmbed from './errorEmbed.js'; export const badNicknameFormat = errorEmbed( 'Bad nickname format', diff --git a/src/utilities/errorEmbed.ts b/src/embeds/errorEmbed.ts similarity index 100% rename from src/utilities/errorEmbed.ts rename to src/embeds/errorEmbed.ts diff --git a/src/embeds/noMember.ts b/src/embeds/noMember.ts index 15167af5a..9737ece2a 100644 --- a/src/embeds/noMember.ts +++ b/src/embeds/noMember.ts @@ -1,4 +1,4 @@ -import errorEmbed from '../utilities/errorEmbed.js'; +import errorEmbed from './errorEmbed.js'; export const noMember = errorEmbed( 'User of command is not a member', diff --git a/src/embeds/notTrackedByBlitzStars.ts b/src/embeds/notTrackedByBlitzStars.ts new file mode 100644 index 000000000..907f3342c --- /dev/null +++ b/src/embeds/notTrackedByBlitzStars.ts @@ -0,0 +1,6 @@ +import errorEmbed from './errorEmbed.js'; + +export const notTrackedByBlitzStars = errorEmbed( + 'No data to display', + 'Player is not tracked by BlitzStars.', +); diff --git a/src/types/playerStats.ts b/src/types/playerStats.ts index cd78fcdc6..5f9e31662 100644 --- a/src/types/playerStats.ts +++ b/src/types/playerStats.ts @@ -1,4 +1,4 @@ -import { AllStats } from './accountInfo.js'; +import { BlitzStartsComputedPeriodicStatistics } from './statistics.js'; export type PlayerStats = { clan: { @@ -6,12 +6,7 @@ export type PlayerStats = { name: string; tag: string; }; - statistics: { - all: AllStats; - wn8: number; - wn7: number; - average_tier: number; - }; + statistics: BlitzStartsComputedPeriodicStatistics; _id: string; last_battle_time: number; nickname: string; diff --git a/src/types/statistics.ts b/src/types/statistics.ts index 102fdcc9e..0f051d96a 100644 --- a/src/types/statistics.ts +++ b/src/types/statistics.ts @@ -1,16 +1,20 @@ import { AllStats, SpecialStats } from './accountInfo.js'; -export interface PeriodStatistics { +export interface BlitzStartsComputedPeriodicStatistics { all: AllStats; - special: SpecialStats; wn7: number; wn8: number; avg_tier: number; +} + +export interface PeriodStatistics + extends BlitzStartsComputedPeriodicStatistics { + special: SpecialStats; pwp: number; } -export interface PlayerStatistics { +export interface PlayerPeriodicStatsCollection { statistics: PeriodStatistics; period30d: PeriodStatistics; period90d: PeriodStatistics; diff --git a/src/utilities/blitzLinks.ts b/src/utilities/blitzLinks.ts index 4462e1310..e3aa3256a 100644 --- a/src/utilities/blitzLinks.ts +++ b/src/utilities/blitzLinks.ts @@ -1,9 +1,5 @@ -import { PlayerStatistics } from '../types/statistics.js'; +import { BlitzServer } from '../constants/servers.js'; -export default function blitzLinks(data?: PlayerStatistics) { - return `${ - data - ? `[View full stats](https://www.blitzstars.com/player/${data.region}/${data.nickname}) • ` - : '' - }[Support BlitzStars](https://www.blitzstars.com/supporters)`; +export default function blitzLinks(server: BlitzServer, nickname: string) { + return `[View full stats](https://www.blitzstars.com/player/${server}/${nickname}) • [Support BlitzStars](https://www.blitzstars.com/supporters)`; } diff --git a/src/utilities/cmdName.ts b/src/utilities/cmdName.ts new file mode 100644 index 000000000..fe316b4a0 --- /dev/null +++ b/src/utilities/cmdName.ts @@ -0,0 +1,5 @@ +import isDev from './isDev.js'; + +export default function cmdName(name: string) { + return isDev() ? `${name}dev` : name; +} diff --git a/src/utilities/getBlitzStarsAccount.ts b/src/utilities/getBlitzStarsAccount.ts index d07cb1da7..d107a4cea 100644 --- a/src/utilities/getBlitzStarsAccount.ts +++ b/src/utilities/getBlitzStarsAccount.ts @@ -1,34 +1,27 @@ import { CacheType, ChatInputCommandInteraction } from 'discord.js'; -import markdownEscape from 'markdown-escape'; import fetch from 'node-fetch'; -import { PlayerStatistics } from '../types/statistics.js'; -import errorEmbed from './errorEmbed.js'; +import { notTrackedByBlitzStars } from '../embeds/notTrackedByBlitzStars.js'; +import { PlayerPeriodicStatsCollection } from '../types/statistics.js'; export default async function getBlitzStarsAccount( interaction: ChatInputCommandInteraction, accountId: number, - name: string, ) { try { const response = await fetch( `https://www.blitzstars.com/api/top/player/${accountId}`, ); - const parsed = (await response.json()) as PlayerStatistics; + const parsed = (await response.json()) as PlayerPeriodicStatsCollection; if (parsed.statistics) { return parsed; } else throw new Error('No data to display'); } catch (error) { await interaction.editReply({ - embeds: [ - errorEmbed( - 'No data to display', - `${markdownEscape(name)} is not tracked by BlitzStars.`, - ), - ], + embeds: [notTrackedByBlitzStars], }); - console.log(`${name} is not tracked by BlitzStars.`); + console.log(`Not tracked by BlitzStars.`); return null; } diff --git a/src/utilities/getWargamingResponse.ts b/src/utilities/getWargamingResponse.ts index 12a341037..2a8501c54 100644 --- a/src/utilities/getWargamingResponse.ts +++ b/src/utilities/getWargamingResponse.ts @@ -12,6 +12,7 @@ export default async function getWargamingResponse( if (parsed.status === 'ok') { return parsed.data; } else { - throw new Error(`Wargaming response status ${parsed.status}`); + console.error(`Wargaming response status ${parsed.status}`); + return null; } }