Skip to content

Commit

Permalink
fix: improve high order function for error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Guilhermeasper committed Aug 29, 2024
1 parent be9294a commit 94abad1
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 94 deletions.
58 changes: 26 additions & 32 deletions src/bot/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
import { logger } from '@utils/logger';
import { mongoConnection } from '@database/mongo';
import { safeExecute } from '@utils/errorHandling';
import MinecraftServerStatus from '@utils/minecraftServerStatus';
import * as commands from '@commands';
import * as events from '@events';

Expand Down Expand Up @@ -54,24 +53,17 @@ export class Bot {
});
}

start() {
async start() {
this._loadTextCommands();
this._loadSlashCommands();
this._sendSlashCommands();
await this._sendSlashCommands();
this._loadEvents();
this._startMongo();
this._startMinecraftServer();
this._client.login(process.env.MARQUINHOS_TOKEN);
await this._startMongo();
await this._client.login(process.env.MARQUINHOS_TOKEN);
}

private _startMongo() {
mongoConnection();
}

private _startMinecraftServer() {
const minecraftServer = MinecraftServerStatus.getInstance();
minecraftServer.init(this._client);
minecraftServer.start();
private async _startMongo() {
await mongoConnection();
}

private _loadSlashCommands() {
Expand Down Expand Up @@ -121,37 +113,39 @@ export class Bot {
eventsArray.forEach((event: BotEvent) => {
if (event.once) {
this._client.once(event.name, (...args) => {
safeExecute(event.execute.bind(this, ...args), this._client)();
safeExecute(event.execute.bind(this, ...args))();
});
} else {
this._client.on(event.name, (...args) => {
safeExecute(event.execute.bind(this, ...args), this._client)();
safeExecute(event.execute.bind(this, ...args))();
});
}

logger.info(`Successfully loaded event ${event.name}`);
});
}

private _sendSlashCommands() {
private async _sendSlashCommands() {
const rest = new REST({ version: '10' }).setToken(
process.env.MARQUINHOS_TOKEN
process.env.MARQUINHOS_TOKEN as string
);

rest
.put(Routes.applicationCommands(process.env.MARQUINHOS_CLIENT_ID), {
body: this._slashCommands.map((command) => command.toJSON()),
})
.then((data: any) => {
if (!data.length) {
logger.warn('No slash commands loaded');
return;
try {
const data = (await rest.put(
Routes.applicationCommands(process.env.MARQUINHOS_CLIENT_ID as string),
{
body: this._slashCommands.map((command) => command.toJSON()),
}
logger.info(`Successfully loaded ${data.length} slash command(s)`);
})
.catch((e) => {
logger.error('Error loading slash commands');
logger.error(e);
});
)) as SlashCommandBuilder[];

if (!data?.length) {
logger.warn('No slash commands loaded');
return;
}

logger.info(`Successfully loaded ${data.length} slash command(s)`);
} catch (error: any) {
throw new Error(error);
}
}
}
5 changes: 1 addition & 4 deletions src/bot/events/interactionCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ export const interactionCreate: BotEvent = {
: ''
}`
);
safeExecute(
command.execute.bind(this, interaction),
interaction.client
)();
safeExecute(command.execute.bind(this, interaction))();
} else if (interaction.isAutocomplete()) {
const command = interaction.client.slashCommands.get(
interaction.commandName
Expand Down
2 changes: 1 addition & 1 deletion src/bot/events/messageCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const messageCreate: BotEvent = {
args ? args.join(' ') : ''
}`
);
safeExecute(command.execute.bind(this, message, args), message.client)();
safeExecute(command.execute.bind(this, message, args))();
},
};

Expand Down
15 changes: 8 additions & 7 deletions src/database/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ export const mongoConnection = async () => {
const MONGO_DATABASE_NAME = process.env.MARQUINHOS_MONGO_DATABASE_NAME;
if (!MONGO_URI) return logger.info(`Mongo URI not found`);
if (!MONGO_DATABASE_NAME) return logger.info(`Mongo database name not found`);
return await mongoose
.connect(`${MONGO_URI}/${MONGO_DATABASE_NAME}`)
.then(() => logger.info('MongoDB connection has been established.'))
.catch((error) => {
logger.error('MongoDB connection has been failed');
logger.error(error);
});
try {
const mongoConnection = await mongoose.connect(
`${MONGO_URI}/${MONGO_DATABASE_NAME}`
);
if (!mongoConnection) throw new Error('MongoDB connection failed');
} catch (error) {
throw new Error(`MongoDB connection failed: ${error}`);
}
};
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { config } from 'dotenv';
import { Bot } from '@marquinhos/bot';
import { safeExecute } from './utils/errorHandling';
process.env.ROOT_DIR = __dirname;
config();

const marquinhos = new Bot();

marquinhos.start();
safeExecute(marquinhos.start.bind(marquinhos))();
5 changes: 4 additions & 1 deletion src/utils/botError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import { BotErrorLogLevel } from '@marquinhos/types';
class BotError extends Error {
discordMessage: Message | CommandInteraction;
logLevel: BotErrorLogLevel = 'error';
origin = 'Unknown';

constructor(
message: string,
discordMessage: Message | CommandInteraction,
logLevel?: BotErrorLogLevel
logLevel?: BotErrorLogLevel,
origin: string = 'Unknown'
) {
super(message);
this.name = 'MarquinhosError';
this.discordMessage = discordMessage;
this.logLevel = logLevel ?? 'error';
this.origin = origin;
}
}

Expand Down
111 changes: 63 additions & 48 deletions src/utils/errorHandling.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,80 @@
import { logger } from '@utils/logger';
import BotError from '@utils/botError';
import { Client, TextChannel } from 'discord.js';
import axios from 'axios';

export const safeExecute = (fn: Function, client: Client) => {
export function safeExecute(fn: Function) {
return function () {
fn()?.catch((error: BotError) => commandErrorHandler(error, client));
try {
const result = fn();
if (result instanceof Promise) {
result.catch((error: BotError) => {
commandErrorHandler(error);
});
}
} catch (error) {
commandErrorHandler(error as BotError);
}
};
};
}

const commandErrorHandler = async (error: BotError, client: Client) => {
const commandErrorHandler = async (error: BotError) => {
await sendErrorMessage(error);
switch (error.logLevel) {
case 'warn':
logger.warn(`${error} while running ${error.discordMessage}`);
logger.warn(`${error?.stack ?? error.message}`);
break;
case 'info':
logger.info(`${error} while running ${error.discordMessage}`);
logger.info(`${error?.stack ?? error.message}`);
break;
default:
try {
const errorStackTraceChunks = error.stack?.match(/.{1,2048}/gs);
if (!errorStackTraceChunks?.length) {
await sendErrorMessage(
client,
error.message,
'No stack trace available'
);
} else if (errorStackTraceChunks.length === 1) {
await sendErrorMessage(
client,
error.message,
errorStackTraceChunks[0]
);
} else {
for (const [index, chunk] of errorStackTraceChunks.entries()) {
await sendErrorMessage(
client,
`${error.message} ${index + 1}/${errorStackTraceChunks.length}`,
chunk
);
}
}
} catch (error: any) {
logger.error(
`Error while trying to send error message to error channel\n${error.stack}`
);
}
logger.error(
`${error} while running ${error.discordMessage}\n${error.stack} `
);
logger.error(`${error?.stack ?? error.message}`);
break;
}
};

async function sendErrorMessage(
client: Client,
title: string,
description: string
) {
await (
client.channels.cache.get(
process.env.MARQUINHOS_ERROR_CHANNEL_ID || ''
) as TextChannel
)?.send(`### ⚠️⚠️⚠️ ${title} ⚠️⚠️⚠️\n\n\`\`\`ansi\n${description}\`\`\``);
async function sendErrorMessage(error: BotError) {
const title = error.message;
const description = error?.stack || 'No stack trace';

try {
await axios.post(
encodeURI(process.env.MARQUINHOS_ERROR_WEBHOOK || ''),
{
username: 'Marquinhos Error Notifier',
avatar_url: 'https://i.imgur.com/M4k2OVe.png',
embeds: [
{
title: `Search error on StackOverflow`,
url: `https://www.google.com/search?q=${encodeURI(
title
)}%20site:stackoverflow.com`,
description: `\`\`\`${description.slice(0, 1024)}\`\`\``,
color: 0xff0000,
footer: {
text: 'The operation has failed successfully!',
},
fields: [
{
name: 'Level',
value: error.logLevel || 'error',
inline: true,
},
{
name: 'Origin',
value: error.origin || 'Unknown',
inline: true,
},
],
},
],
},
{
headers: {
'Content-Type': 'application/json',
},
}
);
} catch (error) {
console.error(error);
}
}

0 comments on commit 94abad1

Please sign in to comment.