feat: bug fixes and error safety

This commit is contained in:
TresAbhi committed 2023-05-28 16:56:15 +00:00
1 parent 0b6b2ecedc
commit 780d9d5f8e
25 files changed
+136 -182

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "blitzkrieg", "name": "blitzkrieg",
"version": "1.3.0", "version": "1.3.1",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
-13
View File
@@ -1,13 +0,0 @@
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: { trace: 'on-first-retry' },
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
});
+3 -3
View File
@@ -1,6 +1,6 @@
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import { EmbedBuilder, SlashCommandBuilder } from 'discord.js';
import packageJSON from '../../package.json' assert { type: 'json' }; import packageJSON from '../../package.json' assert { type: 'json' };
import { INFO_COLOR } from '../constants/colors.js'; import { ACCENT_COLOR } from '../constants/colors.js';
import { tankopedia } from '../core/blitzstars/tankopedia.js'; import { tankopedia } from '../core/blitzstars/tankopedia.js';
import cmdName from '../core/interaction/cmdName.js'; import cmdName from '../core/interaction/cmdName.js';
import getClientId from '../core/process/getClientId.js'; import getClientId from '../core/process/getClientId.js';
@@ -11,7 +11,7 @@ const executionStart = new Date().getTime();
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -26,7 +26,7 @@ export default {
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setTitle(`${client.user?.username} debug information`) .setTitle(`${client.user?.username} debug information`)
.setColor(INFO_COLOR) .setColor(ACCENT_COLOR)
.setDescription( .setDescription(
`**Version**: ${packageJSON.version}\n**Uptime**: ${Math.floor( `**Version**: ${packageJSON.version}\n**Uptime**: ${Math.floor(
uptime / 1000 / 60 / 60 / 24, uptime / 1000 / 60 / 60 / 24,
+3 -3
View File
@@ -1,6 +1,6 @@
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import { EmbedBuilder, SlashCommandBuilder } from 'discord.js';
import discord from '../../discord.json' assert { type: 'json' }; import discord from '../../discord.json' assert { type: 'json' };
import { INFO_COLOR } from '../constants/colors.js'; import { ACCENT_COLOR } from '../constants/colors.js';
import cmdName from '../core/interaction/cmdName.js'; import cmdName from '../core/interaction/cmdName.js';
import { import {
CommandRegistry, CommandRegistry,
@@ -11,7 +11,7 @@ import { client } from '../index.js';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -23,7 +23,7 @@ export default {
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setTitle(`${client.user?.username} help`) .setTitle(`${client.user?.username} help`)
.setColor(INFO_COLOR) .setColor(ACCENT_COLOR)
.setDescription( .setDescription(
`**About**\n${ `**About**\n${
client.user?.username client.user?.username
+1 -4
View File
@@ -15,7 +15,7 @@ const DEFAULT_THRESHOLD = 7;
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -34,7 +34,6 @@ export default {
async execute(interaction) { async execute(interaction) {
const clanName = interaction.options.getString('clan')!; const clanName = interaction.options.getString('clan')!;
const clan = await getBlitzClan(interaction, clanName); const clan = await getBlitzClan(interaction, clanName);
if (!clan) return;
const { server, id } = clan; const { server, id } = clan;
const threshold = const threshold =
@@ -43,14 +42,12 @@ export default {
const clanInfo = await getWargamingResponse<ClanInfo>( const clanInfo = await getWargamingResponse<ClanInfo>(
`https://api.wotblitz.${server}/wotb/clans/info/?application_id=${args['wargaming-application-id']}&clan_id=${id}`, `https://api.wotblitz.${server}/wotb/clans/info/?application_id=${args['wargaming-application-id']}&clan_id=${id}`,
); );
if (!clanInfo) return;
const memberIds = clanInfo[id].members_ids; const memberIds = clanInfo[id].members_ids;
const accountInfo = await getWargamingResponse<AccountInfo>( const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${ `https://api.wotblitz.${server}/wotb/account/info/?application_id=${
args['wargaming-application-id'] args['wargaming-application-id']
}&account_id=${memberIds.join(',')}`, }&account_id=${memberIds.join(',')}`,
); );
if (!accountInfo) return;
const inactiveInfo = memberIds const inactiveInfo = memberIds
.map((memberId) => { .map((memberId) => {
const member = accountInfo[memberId]; const member = accountInfo[memberId];
+28 -35
View File
@@ -1,11 +1,11 @@
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import { SlashCommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape'; import markdownEscape from 'markdown-escape';
import { INFO_COLOR } from '../constants/colors.js';
import usernameAutocomplete from '../core/autocomplete/username.js'; import usernameAutocomplete from '../core/autocomplete/username.js';
import getBlitzAccount from '../core/blitz/getBlitzAccount.js'; import getBlitzAccount from '../core/blitz/getBlitzAccount.js';
import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import { TANK_TYPE_EMOJIS, tankopedia } from '../core/blitzstars/tankopedia.js'; import { TANK_TYPE_EMOJIS, tankopedia } from '../core/blitzstars/tankopedia.js';
import cmdName from '../core/interaction/cmdName.js'; import cmdName from '../core/interaction/cmdName.js';
import infoEmbed from '../core/interaction/infoEmbed.js';
import addUsernameOption from '../core/options/addUsernameOption.js'; import addUsernameOption from '../core/options/addUsernameOption.js';
import { args } from '../core/process/args.js'; import { args } from '../core/process/args.js';
import { CommandRegistry } from '../events/interactionCreate.js'; import { CommandRegistry } from '../events/interactionCreate.js';
@@ -36,7 +36,7 @@ const COMP_TANKS = [
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -56,51 +56,44 @@ export default {
const tier = interaction.options.getInteger('tier')!; const tier = interaction.options.getInteger('tier')!;
const name = interaction.options.getString('username')!; const name = interaction.options.getString('username')!;
const account = await getBlitzAccount(interaction, name); const account = await getBlitzAccount(interaction, name);
if (!account) return;
const { id, server } = account; const { id, server } = account;
const accountInfo = await getWargamingResponse<AccountInfo>( const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accountInfo) return;
const tankStats = await getWargamingResponse<TanksStats>( const tankStats = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!tankStats) return;
const tanks = tankStats[id] const tanks = tankStats[id]
.map((tankData) => tankopedia.data[tankData.tank_id]) .map((tankData) => tankopedia.data[tankData.tank_id])
.filter((tank) => tank?.tier === tier); .filter((tank) => tank?.tier === tier);
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [
new EmbedBuilder() infoEmbed(
.setTitle( `${markdownEscape(accountInfo[id].nickname)}'s owned ${
`${markdownEscape(accountInfo[id].nickname)}'s owned ${ tier === null ? '' : `tier ${tier} `
tier === null ? '' : `tier ${tier} ` }tanks`,
}tanks`, `${
) tanks.length === 0
.setDescription( ? `No tanks found in tier ${tier}`
`${ : tanks
tanks.length === 0 .map(
? `No tanks found in tier ${tier}` (tank) =>
: tanks `${TANK_TYPE_EMOJIS[tank.type]} ${resolveTankName(
.map( tank,
(tank) => )} ${tank.is_premium ? '⭐' : ''}${
`${TANK_TYPE_EMOJIS[tank.type]} ${resolveTankName( COMP_TANKS.includes(tank.tank_id) ? '🏆' : ''
tank, }`,
)} ${tank.is_premium ? '⭐' : ''}${ )
COMP_TANKS.includes(tank.tank_id) ? '🏆' : '' .join('\n')
}`, }\n\n**Legend**\n⭐ = Premium/Collector\n${
) tier === 10 ? '🏆 = Comp Tank\n' : ''
.join('\n') }${TANK_TYPE_EMOJIS.heavyTank} = Heavy\n${
}\n\n**Legend**\n⭐ = Premium/Collector\n${ TANK_TYPE_EMOJIS.mediumTank
tier === 10 ? '🏆 = Comp Tank\n' : '' } = Medium\n${TANK_TYPE_EMOJIS.lightTank} = Light\n${
}${TANK_TYPE_EMOJIS.heavyTank} = Heavy\n${ TANK_TYPE_EMOJIS['AT-SPG']
TANK_TYPE_EMOJIS.mediumTank } = Tank Destroyer`,
} = Medium\n${TANK_TYPE_EMOJIS.lightTank} = Light\n${ ),
TANK_TYPE_EMOJIS['AT-SPG']
} = Tank Destroyer`,
)
.setColor(INFO_COLOR),
], ],
}); });
+1 -4
View File
@@ -17,7 +17,7 @@ type SortBy = 'name' | 'count';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -38,17 +38,14 @@ export default {
const sortBy = (interaction.options.getString('sort') ?? 'name') as SortBy; const sortBy = (interaction.options.getString('sort') ?? 'name') as SortBy;
const name = interaction.options.getString('username')!; const name = interaction.options.getString('username')!;
const account = await getBlitzAccount(interaction, name); const account = await getBlitzAccount(interaction, name);
if (!account) return;
const { id, server } = account; const { id, server } = account;
const accounts = await getWargamingResponse<AccountInfo>( const accounts = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accounts) return;
const accountsAchievements = const accountsAchievements =
await getWargamingResponse<AccountAchievements>( await getWargamingResponse<AccountAchievements>(
`https://api.wotblitz.${server}/wotb/account/achievements/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/achievements/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accountsAchievements) return;
const accountAchievements = accountsAchievements[id]; const accountAchievements = accountsAchievements[id];
const compound = { const compound = {
...accountAchievements.achievements, ...accountAchievements.achievements,
+1 -3
View File
@@ -13,7 +13,7 @@ import { AccountInfo } from '../types/accountInfo.js';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -24,12 +24,10 @@ export default {
async execute(interaction) { async execute(interaction) {
const name = interaction.options.getString('username')!; const name = interaction.options.getString('username')!;
const account = await getBlitzAccount(interaction, name); const account = await getBlitzAccount(interaction, name);
if (!account) return;
const { id, server } = account; const { id, server } = account;
const accounts = await getWargamingResponse<AccountInfo>( const accounts = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accounts) return;
const accountInfo = accounts[id]; const accountInfo = accounts[id];
interaction.editReply({ interaction.editReply({
+1 -2
View File
@@ -12,7 +12,7 @@ import { ClanList } from '../types/clanList.js';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -35,7 +35,6 @@ export default {
const clanList = await getWargamingResponse<ClanList>( const clanList = await getWargamingResponse<ClanList>(
`https://api.wotblitz.${server}/wotb/clans/list/?application_id=${args['wargaming-application-id']}&search=${clan}&limit=${limit}`, `https://api.wotblitz.${server}/wotb/clans/list/?application_id=${args['wargaming-application-id']}&search=${clan}&limit=${limit}`,
); );
if (!clanList) return;
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [
+20 -21
View File
@@ -1,9 +1,9 @@
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import { SlashCommandBuilder } from 'discord.js';
import markdownEscape from 'markdown-escape'; import markdownEscape from 'markdown-escape';
import { INFO_COLOR } from '../constants/colors.js';
import { BLITZ_SERVERS, BlitzServer } from '../constants/servers.js'; import { BLITZ_SERVERS, BlitzServer } from '../constants/servers.js';
import getWargamingResponse from '../core/blitz/getWargamingResponse.js'; import getWargamingResponse from '../core/blitz/getWargamingResponse.js';
import cmdName from '../core/interaction/cmdName.js'; import cmdName from '../core/interaction/cmdName.js';
import infoEmbed from '../core/interaction/infoEmbed.js';
import addServerChoices from '../core/options/addServerChoices.js'; import addServerChoices from '../core/options/addServerChoices.js';
import addUsernameOption from '../core/options/addUsernameOption.js'; import addUsernameOption from '../core/options/addUsernameOption.js';
import { args } from '../core/process/args.js'; import { args } from '../core/process/args.js';
@@ -12,7 +12,7 @@ import { AccountList } from '../types/accountList.js';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -34,27 +34,26 @@ export default {
const server = interaction.options.getString('server') as BlitzServer; const server = interaction.options.getString('server') as BlitzServer;
const name = interaction.options.getString('username')!; const name = interaction.options.getString('username')!;
const limit = interaction.options.getInteger('limit') ?? 25; const limit = interaction.options.getInteger('limit') ?? 25;
const players = await getWargamingResponse<AccountList>( const normalizedSearch = encodeURIComponent(name.trim());
`https://api.wotblitz.${server}/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${name}&limit=${limit}`, const players =
); normalizedSearch.length >= 3 && normalizedSearch.length <= 100
if (!players) return; ? await getWargamingResponse<AccountList>(
`https://api.wotblitz.${server}/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${normalizedSearch}&limit=${limit}`,
)
: [];
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [
new EmbedBuilder() infoEmbed(
.setColor(INFO_COLOR) `Player search for "${markdownEscape(name)}" in ${
.setTitle( BLITZ_SERVERS[server]
`Player search for "${markdownEscape(name)}" in ${ }`,
BLITZ_SERVERS[server] `\`\`\`${
}`, players.length === 0
) ? 'No players found.'
.setDescription( : players.map((player) => player.nickname).join('\n')
`\`\`\`${ }\`\`\``,
players.length === 0 ),
? 'No players found.'
: players.map((player) => player.nickname).join('\n')
}\`\`\``,
),
], ],
}); });
+1 -1
View File
@@ -9,7 +9,7 @@ import { CommandRegistry } from '../events/interactionCreate.js';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
+1 -6
View File
@@ -35,7 +35,7 @@ const periodNames: Record<Period, string> = {
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -59,19 +59,15 @@ export default {
const period = interaction.options.getString('period') as Period; const period = interaction.options.getString('period') as Period;
const username = interaction.options.getString('username')!; const username = interaction.options.getString('username')!;
const blitzAccount = await getBlitzAccount(interaction, username); const blitzAccount = await getBlitzAccount(interaction, username);
if (!blitzAccount) return;
const { id, server } = blitzAccount; const { id, server } = blitzAccount;
let stats: BlitzStartsComputedPeriodicStatistics; let stats: BlitzStartsComputedPeriodicStatistics;
const accountInfo = await getWargamingResponse<AccountInfo>( const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accountInfo) return;
const clanData = await getWargamingResponse<PlayerClanData>( const clanData = await getWargamingResponse<PlayerClanData>(
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${id}&extra=clan`, `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${id}&extra=clan`,
); );
if (!clanData) return;
const blitzStarsAccount = await getBlitzStarsAccount(interaction, id); const blitzStarsAccount = await getBlitzStarsAccount(interaction, id);
if (!blitzStarsAccount) return;
if (period === 'today') { if (period === 'today') {
const a1 = blitzStarsAccount.statistics; const a1 = blitzStarsAccount.statistics;
@@ -115,7 +111,6 @@ export default {
? blitzStarsAccount.statistics ? blitzStarsAccount.statistics
: blitzStarsAccount[`period${period}d`]; : blitzStarsAccount[`period${period}d`];
} }
if (!stats) return;
const image = await render( const image = await render(
<Wrapper> <Wrapper>
+1 -3
View File
@@ -23,7 +23,7 @@ type Periods = '30' | '60' | '90';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -48,12 +48,10 @@ export default {
const tankId = interaction.options.getString('tank')!; const tankId = interaction.options.getString('tank')!;
const period = interaction.options.getString('period') as Periods; const period = interaction.options.getString('period') as Periods;
const blitzAccount = await getBlitzAccount(interaction, name); const blitzAccount = await getBlitzAccount(interaction, name);
if (!blitzAccount) return;
const { server, id } = blitzAccount; const { server, id } = blitzAccount;
const accountInfo = await getWargamingResponse<AccountInfo>( const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accountInfo) return;
const tank = tankopedia.data[tankId as unknown as number]; const tank = tankopedia.data[tankId as unknown as number];
if (tank) { if (tank) {
+1 -6
View File
@@ -29,7 +29,7 @@ import resolveTankName from '../utilities/resolveTankName.js';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -40,7 +40,6 @@ export default {
async execute(interaction) { async execute(interaction) {
const username = interaction.options.getString('username')!; const username = interaction.options.getString('username')!;
const blitzAccount = await getBlitzAccount(interaction, username); const blitzAccount = await getBlitzAccount(interaction, username);
if (!blitzAccount) return;
const { id, server } = blitzAccount; const { id, server } = blitzAccount;
const tankStatsOverTime = await getTankStatsOverTime( const tankStatsOverTime = await getTankStatsOverTime(
server, server,
@@ -48,19 +47,15 @@ export default {
last5AM().getTime() / 1000, last5AM().getTime() / 1000,
new Date().getTime() / 1000, new Date().getTime() / 1000,
); );
if (!tankStatsOverTime) return;
const accountInfo = await getWargamingResponse<AccountInfo>( const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accountInfo) return;
const clanData = await getWargamingResponse<PlayerClanData>( const clanData = await getWargamingResponse<PlayerClanData>(
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${id}&extra=clan`, `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${id}&extra=clan`,
); );
if (!clanData) return;
const careerTankStatsRaw = await getWargamingResponse<TanksStats>( const careerTankStatsRaw = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!careerTankStatsRaw) return;
const careerStats: Record<number, AllStats> = {}; const careerStats: Record<number, AllStats> = {};
Object.entries(careerTankStatsRaw[id]).forEach(([, tankStats]) => { Object.entries(careerTankStatsRaw[id]).forEach(([, tankStats]) => {
+1 -4
View File
@@ -14,7 +14,7 @@ import { PlayerClanData } from '../types/playerClanData.js';
export default { export default {
inProduction: true, inProduction: true,
inDevelopment: false, inDevelopment: true,
inPublic: true, inPublic: true,
command: new SlashCommandBuilder() command: new SlashCommandBuilder()
@@ -25,16 +25,13 @@ export default {
async execute(interaction) { async execute(interaction) {
const name = interaction.options.getString('username')!; const name = interaction.options.getString('username')!;
const blitzAccount = await getBlitzAccount(interaction, name); const blitzAccount = await getBlitzAccount(interaction, name);
if (!blitzAccount) return;
const { id, server } = blitzAccount; const { id, server } = blitzAccount;
const accountInfo = await getWargamingResponse<AccountInfo>( const accountInfo = await getWargamingResponse<AccountInfo>(
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/account/info/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!accountInfo) return;
const clanData = await getWargamingResponse<PlayerClanData>( const clanData = await getWargamingResponse<PlayerClanData>(
`https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${id}&extra=clan`, `https://api.wotblitz.${server}/wotb/clans/accountinfo/?application_id=${args['wargaming-application-id']}&account_id=${id}&extra=clan`,
); );
if (!clanData) return;
const clan = clanData[id]?.clan; const clan = clanData[id]?.clan;
const clanTag = clan ? ` [${clan!.tag}]` : ''; const clanTag = clan ? ` [${clan!.tag}]` : '';
+1 -1
View File
@@ -1,3 +1,3 @@
export const INFO_COLOR = '#8e3cf5'; export const ACCENT_COLOR = '#ffffff';
export const POSITIVE_COLOR = '#82ff29'; export const POSITIVE_COLOR = '#82ff29';
export const NEGATIVE_COLOR = '#ff4747'; export const NEGATIVE_COLOR = '#ff4747';
+18 -12
View File
@@ -13,17 +13,23 @@ export default async function usernameAutocomplete(
if (focusedOption.name !== 'username') return; if (focusedOption.name !== 'username') return;
const players = await listAccountsPanServer(focusedOption.value); const players = await listAccountsPanServer(focusedOption.value);
await interaction.respond( try {
players await interaction.respond(
? players.map( players
(player) => ? players.map(
({ (player) =>
name: `${player.nickname} (${BLITZ_SERVERS[player.server]})`, ({
value: `${player.server}/${player.account_id}`, name: `${player.nickname} (${BLITZ_SERVERS[player.server]})`,
} satisfies ApplicationCommandOptionChoiceData<string>), value: `${player.server}/${player.account_id}`,
) } satisfies ApplicationCommandOptionChoiceData<string>),
: [], )
); : [],
);
console.log(`Username autocomplete for ${focusedOption.value}`); console.log(`Username autocomplete for ${focusedOption.value}`);
} catch (error) {
console.warn(
`Failed to autocomplete username for ${focusedOption.value} in time`,
);
}
} }
+2 -2
View File
@@ -8,7 +8,7 @@ export const serverAndIdPattern = /(com|eu|asia)\/[0-9]+/;
export default async function getBlitzAccount( export default async function getBlitzAccount(
interaction: ChatInputCommandInteraction<CacheType>, interaction: ChatInputCommandInteraction<CacheType>,
username: string, username: string,
): Promise<{ server: BlitzServer; id: number } | null> { ) {
if (serverAndIdPattern.test(username)) { if (serverAndIdPattern.test(username)) {
const [server, accountId] = username.split('/'); const [server, accountId] = username.split('/');
return { server: server as BlitzServer, id: Number(accountId) }; return { server: server as BlitzServer, id: Number(accountId) };
@@ -27,7 +27,7 @@ export default async function getBlitzAccount(
], ],
}); });
return null; throw new Error(`Could not find user "${username}"`);
} }
} }
} }
+3 -3
View File
@@ -7,7 +7,7 @@ import listClansPanServer from './listClansPanServer.js';
export default async function getBlitzClan( export default async function getBlitzClan(
interaction: ChatInputCommandInteraction<CacheType>, interaction: ChatInputCommandInteraction<CacheType>,
clan: string, clan: string,
): Promise<{ server: BlitzServer; id: number } | null> { ) {
if (serverAndIdPattern.test(clan)) { if (serverAndIdPattern.test(clan)) {
const [server, accountId] = clan.split('/'); const [server, accountId] = clan.split('/');
return { server: server as BlitzServer, id: Number(accountId) }; return { server: server as BlitzServer, id: Number(accountId) };
@@ -20,13 +20,13 @@ export default async function getBlitzClan(
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [
errorEmbed( errorEmbed(
'Could not find user', 'Could not find clan',
`I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`, `I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`,
), ),
], ],
}); });
return null; throw new Error(`Could not find clan "${clan}"`);
} }
} }
} }
+2 -3
View File
@@ -11,9 +11,8 @@ export default async function getWargamingResponse<Data extends object>(
if (parsed.status === 'ok') { if (parsed.status === 'ok') {
return parsed.data; return parsed.data;
} else { } else {
console.error( throw new Error(
`Wargaming response status ${parsed.status} ${parsed.error.message}`, `Wargaming response error status:"${parsed.status}" message: "${parsed.error.message}" url: "${url}"`,
); );
return null;
} }
} }
+41 -44
View File
@@ -8,50 +8,47 @@ export type AccountListWithServer = (Account & {
})[]; })[];
export default async function listAccountsPanServer(search: string, limit = 9) { export default async function listAccountsPanServer(search: string, limit = 9) {
const trimmed = search.trim(); const normalizedSearch = encodeURIComponent(search.trim());
if (trimmed.length < 3 && trimmed.length > 100) return [];
const normalizedLimit = Math.round(limit / 3); const normalizedLimit = Math.round(limit / 3);
return ( if (normalizedSearch.length >= 3 && normalizedSearch.length <= 100) {
await Promise.all([ return (
getWargamingResponse<AccountList>( await Promise.all([
`https://api.wotblitz.com/wotb/account/list/?application_id=${ getWargamingResponse<AccountList>(
args['wargaming-application-id'] `https://api.wotblitz.com/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`,
}&search=${encodeURIComponent(search)}&limit=${normalizedLimit}`, ).then(
).then( (value) =>
(value) => value &&
value && value.map((account) => ({
value.map((account) => ({ ...account,
...account, server: 'com' as BlitzServer,
server: 'com' as BlitzServer, })),
})), ),
), getWargamingResponse<AccountList>(
getWargamingResponse<AccountList>( `https://api.wotblitz.eu/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`,
`https://api.wotblitz.eu/wotb/account/list/?application_id=${ ).then(
args['wargaming-application-id'] (value) =>
}&search=${encodeURIComponent(search)}&limit=${normalizedLimit}`, value &&
).then( value.map((account) => ({
(value) => ...account,
value && server: 'eu' as BlitzServer,
value.map((account) => ({ })),
...account, ),
server: 'eu' as BlitzServer, getWargamingResponse<AccountList>(
})), `https://api.wotblitz.asia/wotb/account/list/?application_id=${args['wargaming-application-id']}&search=${search}&limit=${normalizedLimit}`,
), ).then(
getWargamingResponse<AccountList>( (value) =>
`https://api.wotblitz.asia/wotb/account/list/?application_id=${ value &&
args['wargaming-application-id'] value.map((account) => ({
}&search=${encodeURIComponent(search)}&limit=${normalizedLimit}`, ...account,
).then( server: 'asia' as BlitzServer,
(value) => })),
value && ),
value.map((account) => ({ ])
...account, )
server: 'asia' as BlitzServer, .filter(Boolean)
})), .flat();
), } else {
]) return [];
) }
.filter(Boolean)
.flat();
} }
+1 -3
View File
@@ -20,8 +20,6 @@ export default async function getBlitzStarsAccount(
embeds: [notTrackedByBlitzStars], embeds: [notTrackedByBlitzStars],
}); });
console.log(`Not tracked by BlitzStars.`); throw new Error(`${accountId} not tracked by BlitzStars.`);
return null;
} }
} }
@@ -51,7 +51,6 @@ export default async function getTankStatsOverTime(
const latestTankStatsRaw = await getWargamingResponse<TanksStats>( const latestTankStatsRaw = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`, `https://api.wotblitz.${server}/wotb/tanks/stats/?application_id=${args['wargaming-application-id']}&account_id=${id}`,
); );
if (!latestTankStatsRaw) return;
const tankHistoriesResponse = await fetch( const tankHistoriesResponse = await fetch(
`https://www.blitzstars.com/api/tankhistories/for/${id}/`, `https://www.blitzstars.com/api/tankhistories/for/${id}/`,
); );
+2 -2
View File
@@ -1,9 +1,9 @@
import { EmbedBuilder } from 'discord.js'; import { EmbedBuilder } from 'discord.js';
import { INFO_COLOR } from '../../constants/colors.js'; import { ACCENT_COLOR } from '../../constants/colors.js';
export default function infoEmbed(title: string, description: string) { export default function infoEmbed(title: string, description: string) {
return new EmbedBuilder() return new EmbedBuilder()
.setColor(INFO_COLOR) .setColor(ACCENT_COLOR)
.setTitle(title) .setTitle(title)
.setDescription(description); .setDescription(description);
} }
+2 -2
View File
@@ -1,6 +1,6 @@
import { EmbedBuilder, GuildMember, TextChannel } from 'discord.js'; import { EmbedBuilder, GuildMember, TextChannel } from 'discord.js';
import discord from '../../discord.json' assert { type: 'json' }; import discord from '../../discord.json' assert { type: 'json' };
import { INFO_COLOR } from '../constants/colors.js'; import { ACCENT_COLOR } from '../constants/colors.js';
export default async function (member: GuildMember) { export default async function (member: GuildMember) {
if (member.guild.id === discord.sklld_guild_id) { if (member.guild.id === discord.sklld_guild_id) {
@@ -12,7 +12,7 @@ export default async function (member: GuildMember) {
content: member.toString(), content: member.toString(),
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(INFO_COLOR) .setColor(ACCENT_COLOR)
.setTitle(`Welcome ${member.user.username}`) .setTitle(`Welcome ${member.user.username}`)
.setDescription( .setDescription(
`Welcome to the Skilled server, **${member.user.username}**! To continue, please use the \`/verify\` command.\n\nExample: \`/verify Europe Sabina244\``, `Welcome to the Skilled server, **${member.user.username}**! To continue, please use the \`/verify\` command.\n\nExample: \`/verify Europe Sabina244\``,