mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: mature error handling system
This commit is contained in:
1 parent
1875f30cf5
commit
524edac615
23 files changed
+87
-87
No files matched your search
@@ -13,7 +13,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 const SKILLED_CLANS: Record<SkilledClan, string> = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: false,
|
inPublic: false,
|
||||||
|
|
||||||
command: new SlashCommandBuilder()
|
command: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -40,7 +40,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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -13,7 +13,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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -40,7 +40,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()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import { AccountInfo, AllStats } 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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { BlitzServer } from '../../constants/servers.js';
|
||||||
import negativeEmbed from '../interaction/negativeEmbed.js';
|
import errorWithCause from '../process/errorWithCause.js';
|
||||||
import listAccountsPanServer, {
|
import listAccountsPanServer, {
|
||||||
usernamePatternWithoutPosition,
|
usernamePatternWithoutPosition,
|
||||||
} from './listAccountsPanServer.js';
|
} from './listAccountsPanServer.js';
|
||||||
@@ -17,17 +17,9 @@ export default async function getBlitzAccount(
|
|||||||
const username = commandUsername ?? displayName;
|
const username = commandUsername ?? displayName;
|
||||||
|
|
||||||
if (username === undefined) {
|
if (username === undefined) {
|
||||||
await interaction.editReply({
|
throw errorWithCause(
|
||||||
embeds: [
|
'Username could not be inferred',
|
||||||
negativeEmbed(
|
"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.",
|
||||||
'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.",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`No username provided commandUsername: ${commandUsername} displayName: ${displayName} username: ${username}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,16 +32,10 @@ export default async function getBlitzAccount(
|
|||||||
if (accounts[0]) {
|
if (accounts[0]) {
|
||||||
return { server: accounts[0].server, id: accounts[0].account_id };
|
return { server: accounts[0].server, id: accounts[0].account_id };
|
||||||
} else {
|
} else {
|
||||||
await interaction.editReply({
|
throw errorWithCause(
|
||||||
embeds: [
|
'Could not find user',
|
||||||
negativeEmbed(
|
`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.`,
|
||||||
'Could not find user',
|
);
|
||||||
`I couldn't find user \`${username}\`. Try providing a username manually or using the \`/verify\` command to set your Discord nickname to your Blitz username.`,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error(`Could not find user "${username}"`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||||
import { BlitzServer } from '../../constants/servers.js';
|
import { BlitzServer } from '../../constants/servers.js';
|
||||||
import negativeEmbed from '../interaction/negativeEmbed.js';
|
import errorWithCause from '../process/errorWithCause.js';
|
||||||
import { serverAndIdPattern } from './getBlitzAccount.js';
|
import { serverAndIdPattern } from './getBlitzAccount.js';
|
||||||
import listClansPanServer from './listClansPanServer.js';
|
import listClansPanServer from './listClansPanServer.js';
|
||||||
|
|
||||||
@@ -17,16 +17,10 @@ export default async function getBlitzClan(
|
|||||||
if (accounts[0]) {
|
if (accounts[0]) {
|
||||||
return { server: accounts[0].server, id: accounts[0].clan_id };
|
return { server: accounts[0].server, id: accounts[0].clan_id };
|
||||||
} else {
|
} else {
|
||||||
await interaction.editReply({
|
throw errorWithCause(
|
||||||
embeds: [
|
'Could not find clan',
|
||||||
negativeEmbed(
|
`I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`,
|
||||||
'Could not find clan',
|
);
|
||||||
`I couldn't find clan \`${clan}\`. Try selecting a username from the search result.`,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error(`Could not find clan "${clan}"`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||||
import { BLITZ_SERVERS, BlitzServer } from '../../constants/servers.js';
|
import { BLITZ_SERVERS, BlitzServer } from '../../constants/servers.js';
|
||||||
import { TanksStats } from '../../types/tanksStats.js';
|
import { TanksStats } from '../../types/tanksStats.js';
|
||||||
import negativeEmbed from '../interaction/negativeEmbed.js';
|
|
||||||
import { args } from '../process/args.js';
|
import { args } from '../process/args.js';
|
||||||
|
import errorWithCause from '../process/errorWithCause.js';
|
||||||
import getWargamingResponse from './getWargamingResponse.js';
|
import getWargamingResponse from './getWargamingResponse.js';
|
||||||
|
|
||||||
export default async function getTankStats(
|
export default async function getTankStats(
|
||||||
@@ -15,16 +15,10 @@ export default async function getTankStats(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (tankStats[id] === null) {
|
if (tankStats[id] === null) {
|
||||||
interaction.editReply({
|
throw errorWithCause(
|
||||||
embeds: [
|
'No tank stats available',
|
||||||
negativeEmbed(
|
`Wargaming says there is no tank stats for this account. This account may not have any battles/tanks or exist in the ${BLITZ_SERVERS[server]} server.`,
|
||||||
'No tank stats available',
|
);
|
||||||
`Wargaming says there is no tank stats for this account. This account may not have any battles/tanks or exist in the ${BLITZ_SERVERS[server]} server.`,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error(`No tank stats available for ${server}/${id}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tankStats[id];
|
return tankStats[id];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { WargamingResponse } from '../../types/wargamingResponse.js';
|
import { WargamingResponse } from '../../types/wargamingResponse.js';
|
||||||
|
import errorWithCause from '../process/errorWithCause.js';
|
||||||
|
|
||||||
export default async function getWargamingResponse<Data extends object>(
|
export default async function getWargamingResponse<Data extends object>(
|
||||||
url: string,
|
url: string,
|
||||||
@@ -11,8 +12,9 @@ export default async function getWargamingResponse<Data extends object>(
|
|||||||
if (parsed.status === 'ok') {
|
if (parsed.status === 'ok') {
|
||||||
return parsed.data;
|
return parsed.data;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw errorWithCause(
|
||||||
`Wargaming response error status:"${parsed.status}" message: "${parsed.error.message}" url: "${url}"`,
|
`Wargaming response error status:"${parsed.status}"`,
|
||||||
|
`Message: "${parsed.error.message}"\nURL: "${url}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
import { CacheType, ChatInputCommandInteraction } from 'discord.js';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import negativeEmbed from '../interaction/negativeEmbed.js';
|
import errorWithCause from '../process/errorWithCause.js';
|
||||||
import { TANKS, tankopedia } from './tankopedia.js';
|
import { TANKS, tankopedia } from './tankopedia.js';
|
||||||
|
|
||||||
export default async function resolveTankId(
|
export default async function resolveTankId(
|
||||||
@@ -13,16 +13,10 @@ export default async function resolveTankId(
|
|||||||
const searchResult = go(tank, TANKS, { keys: ['name'], limit: 1 });
|
const searchResult = go(tank, TANKS, { keys: ['name'], limit: 1 });
|
||||||
|
|
||||||
if (searchResult.length === 0) {
|
if (searchResult.length === 0) {
|
||||||
await interaction.editReply({
|
throw errorWithCause(
|
||||||
embeds: [
|
'Tank not found',
|
||||||
negativeEmbed(
|
`Could not find tank by the name "${tank}".`,
|
||||||
'Tank not found',
|
);
|
||||||
`Could not find tank by the name "${tank}".`,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error(`Tank not found by name "${tank}"`);
|
|
||||||
} else {
|
} else {
|
||||||
return searchResult[0].obj.tank_id;
|
return searchResult[0].obj.tank_id;
|
||||||
}
|
}
|
||||||
@@ -30,16 +24,10 @@ export default async function resolveTankId(
|
|||||||
if (tankopedia[number]) {
|
if (tankopedia[number]) {
|
||||||
return number;
|
return number;
|
||||||
} else {
|
} else {
|
||||||
await interaction.editReply({
|
throw errorWithCause(
|
||||||
embeds: [
|
'Tank not found',
|
||||||
negativeEmbed(
|
`Could not find tank by the ID "${number}".`,
|
||||||
'Tank not found',
|
);
|
||||||
`Could not find tank by the ID "${number}".`,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error(`Tank not found by ID "${number}"`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,5 +22,5 @@ export default async function wargamingError(
|
|||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Wargaming returned an error`);
|
console.log(`Wargaming returned an error`);
|
||||||
handleError(error, client, command);
|
handleError(error, command);
|
||||||
}
|
}
|
||||||
@@ -24,5 +24,5 @@ export default async function blitzStarsError(
|
|||||||
});
|
});
|
||||||
|
|
||||||
console.log(`BlitzStars returned an error`);
|
console.log(`BlitzStars returned an error`);
|
||||||
handleError(error, client, command);
|
handleError(error, command);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { argv } from 'process';
|
import { argv } from 'process';
|
||||||
|
import errorWithCause from './errorWithCause.js';
|
||||||
|
|
||||||
type ArgumentName = 'discord-token' | 'wargaming-application-id';
|
type ArgumentName = 'discord-token' | 'wargaming-application-id';
|
||||||
|
|
||||||
@@ -16,6 +17,9 @@ argv.forEach((arg) => {
|
|||||||
|
|
||||||
Object.keys(args).forEach((arg) => {
|
Object.keys(args).forEach((arg) => {
|
||||||
if (args[arg as ArgumentName] === '') {
|
if (args[arg as ArgumentName] === '') {
|
||||||
throw new Error(`Missing argument: ${arg}`);
|
throw errorWithCause(
|
||||||
|
`Missing argument: ${arg}`,
|
||||||
|
'Missing required auth key.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function errorWithCause(message: string, cause: string) {
|
||||||
|
return new Error(message, { cause });
|
||||||
|
}
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
import { Client, EmbedBuilder, TextChannel } from 'discord.js';
|
import { EmbedBuilder, TextChannel } from 'discord.js';
|
||||||
import discord from '../../discord.json' assert { type: 'json' };
|
import discord from '../../discord.json' assert { type: 'json' };
|
||||||
import { NEGATIVE_COLOR } from '../constants/colors.js';
|
import { NEGATIVE_COLOR } from '../constants/colors.js';
|
||||||
import isDev from '../core/process/isDev.js';
|
import isDev from '../core/process/isDev.js';
|
||||||
@@ -7,7 +7,7 @@ import { client } from '../index.js';
|
|||||||
const PROCESS_ERROR_EVENTS = ['uncaughtException'];
|
const PROCESS_ERROR_EVENTS = ['uncaughtException'];
|
||||||
const CLIENT_ERROR_EVENTS = ['error'];
|
const CLIENT_ERROR_EVENTS = ['error'];
|
||||||
|
|
||||||
export function handleError(error: Error, client: Client, command: string) {
|
export function handleError(error: Error, command: string) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -31,10 +31,10 @@ export function handleError(error: Error, client: Client, command: string) {
|
|||||||
export function registerErrorHandlers() {
|
export function registerErrorHandlers() {
|
||||||
if (!isDev()) {
|
if (!isDev()) {
|
||||||
PROCESS_ERROR_EVENTS.forEach((name) =>
|
PROCESS_ERROR_EVENTS.forEach((name) =>
|
||||||
process.on(name, (error) => handleError(error, client, 'process error')),
|
process.on(name, (error) => handleError(error, 'process error')),
|
||||||
);
|
);
|
||||||
CLIENT_ERROR_EVENTS.forEach((name) =>
|
CLIENT_ERROR_EVENTS.forEach((name) =>
|
||||||
client.on(name, (error) => handleError(error, client, 'client error')),
|
client.on(name, (error) => handleError(error, 'client error')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
|
ActionRowBuilder,
|
||||||
AutocompleteInteraction,
|
AutocompleteInteraction,
|
||||||
|
ButtonBuilder,
|
||||||
|
ButtonStyle,
|
||||||
CacheType,
|
CacheType,
|
||||||
ChatInputCommandInteraction,
|
ChatInputCommandInteraction,
|
||||||
Collection,
|
Collection,
|
||||||
@@ -11,9 +14,11 @@ import {
|
|||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { readdirSync } from 'fs';
|
import { readdirSync } from 'fs';
|
||||||
import discord from '../../discord.json' assert { type: 'json' };
|
import discord from '../../discord.json' assert { type: 'json' };
|
||||||
|
import negativeEmbed from '../core/interaction/negativeEmbed.js';
|
||||||
import { args } from '../core/process/args.js';
|
import { args } from '../core/process/args.js';
|
||||||
import getClientId from '../core/process/getClientId.js';
|
import getClientId from '../core/process/getClientId.js';
|
||||||
import isDev from '../core/process/isDev.js';
|
import isDev from '../core/process/isDev.js';
|
||||||
|
import { handleError } from './error.js';
|
||||||
|
|
||||||
export interface CommandRegistry {
|
export interface CommandRegistry {
|
||||||
inDevelopment: boolean;
|
inDevelopment: boolean;
|
||||||
@@ -101,6 +106,30 @@ export default async function interactionCreate(
|
|||||||
|
|
||||||
console.log(interaction.toString());
|
console.log(interaction.toString());
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
command.execute(interaction);
|
|
||||||
|
try {
|
||||||
|
await command.execute(interaction);
|
||||||
|
} catch (error) {
|
||||||
|
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setLabel('Get Help on Discord Server')
|
||||||
|
.setURL('https://discord.gg/nDt7AjGJQH')
|
||||||
|
.setStyle(ButtonStyle.Link),
|
||||||
|
);
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
negativeEmbed(
|
||||||
|
(error as Error).message,
|
||||||
|
`${
|
||||||
|
(error as Error).cause ?? 'No further information is available.'
|
||||||
|
}`,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
components: [actionRow],
|
||||||
|
});
|
||||||
|
|
||||||
|
handleError(error as Error, interaction.commandName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user