diff --git a/src/i18n/en-GB.yml b/src/i18n/en-GB.yml index 378e959..8f6f0ea 100644 --- a/src/i18n/en-GB.yml +++ b/src/i18n/en-GB.yml @@ -340,7 +340,6 @@ misc: Sorry, an unexpected error occurred. Please give this information to an administrator. fields: - code: Error code identifier: Identifier title: ⚠️ Something went wrong expired: @@ -375,6 +374,15 @@ misc: ratelimited: description: Try again in a few seconds. title: 🐢 Please slow down + role_error: + description: Sorry, the operation failed because of an invalid configuration. + fields: + for_admins: + name: For server administrators + value: > + An invalid user or role was supplied, which usually means a staff role has been deleted. + [Click here]({url}) for resolution instructions. + title: ⚠️ Something went wrong unknown_category: description: Please try a different category. title: ❌ That ticket category doesn't exist diff --git a/src/index.js b/src/index.js index e952b70..6554f04 100644 --- a/src/index.js +++ b/src/index.js @@ -76,7 +76,7 @@ process.on('uncaughtException', (error, origin) => { log.error(error); }); -process.on('warning', warning => log.warn(warning.stack)); +process.on('warning', warning => log.warn(warning.stack || warning)); const client = new Client(config, log); client.login().then(() => { diff --git a/src/lib/error.js b/src/lib/error.js new file mode 100644 index 0000000..f57c021 --- /dev/null +++ b/src/lib/error.js @@ -0,0 +1,80 @@ +const { getSUID } = require('./logging'); +const { + EmbedBuilder, + codeBlock, +} = require('discord.js'); + +/** + * + * @param {Object} event + * @param {import("discord.js").Interaction<"cached">} event.interaction + * @param {Error} event.error + * @returns + */ +module.exports.handleInteractionError = async event => { + const { + interaction, + error, + } = event; + const { client } = interaction; + + const ref = getSUID(); + client.log.error.buttons(ref); + + if (interaction.isAnySelectMenu()) { + client.log.error.menus(`"${event.menu.id}" menu execution error:`, error); + } else if (interaction.isButton()) { + client.log.error.buttons(`"${event.button.id}" button execution error:`, error); + } else if (interaction.isModalSubmit()) { + client.log.error.modals(`"${event.modal.id}" modal execution error:`, error); + } else if (interaction.isCommand()) { + client.log.error.commands(`"${event.command.name}" command execution error:`, error); + } + + + let locale = null; + if (interaction.guild) { + locale = (await client.prisma.guild.findUnique({ + select: { locale: true }, + where: { id: interaction.guild.id }, + })).locale; + } + const getMessage = client.i18n.getLocale(locale); + + const data = { + components: [], + embeds: [], + }; + + if (/Supplied parameter is not a User nor a Role/.test(error.message)) { + data.embeds.push( + new EmbedBuilder() + .setColor('Orange') + .setTitle(getMessage('misc.role_error.title')) + .setDescription(getMessage('misc.role_error.description')) + .addFields([ + { + name: getMessage('misc.role_error.fields.for_admins.name'), + value: getMessage('misc.role_error.fields.for_admins.value', { url: 'https://discordtickets.app/self-hosting/troubleshooting/#invalid-user-or-role' }), + }, + ]), + ); + } else { + data.embeds.push( + new EmbedBuilder() + .setColor('Orange') + .setTitle(getMessage('misc.error.title')) + .setDescription(getMessage('misc.error.description')) + .addFields([ + { + name: getMessage('misc.error.fields.identifier'), + value: codeBlock(ref), + }, + ]), + ); + } + + + + return interaction.reply(data).catch(() => interaction.editReply(data)); +}; diff --git a/src/lib/stats.js b/src/lib/stats.js index 5d58033..061861f 100644 --- a/src/lib/stats.js +++ b/src/lib/stats.js @@ -77,12 +77,12 @@ module.exports.sendToHouston = async client => { client.log.success('Posted client stats'); client.log.debug(res); } catch (res) { + client.log.warn('The following error is not important and can be safely ignored'); try { const json = await res.json(); client.log.error('An error occurred whilst posting stats:', json); } catch (error) { - client.log.error('An error occurred whilst posting stats and the response couldn\'t be parsed'); - client.log.error(error.message); + client.log.error('An error occurred whilst posting stats and the response couldn\'t be parsed:', error.message); } client.log.debug(res); } diff --git a/src/listeners/buttons/error.js b/src/listeners/buttons/error.js index 95f4ebf..708c969 100644 --- a/src/listeners/buttons/error.js +++ b/src/listeners/buttons/error.js @@ -1,9 +1,5 @@ const { Listener } = require('@eartharoid/dbf'); -const { - EmbedBuilder, - codeBlock, -} = require('discord.js'); -const { getSUID } = require('../../lib/logging'); +const { handleInteractionError } = require('../../lib/error'); module.exports = class extends Listener { constructor(client, options) { @@ -14,38 +10,7 @@ module.exports = class extends Listener { }); } - async run({ - button, - error, - interaction, - }) { - const ref = getSUID(); - this.client.log.error.buttons(ref); - this.client.log.error.buttons(`"${button.id}" button execution error:`, error); - let locale = null; - if (interaction.guild) { - locale = (await this.client.prisma.guild.findUnique({ - select: { locale: true }, - where: { id: interaction.guild.id }, - })).locale; - } - const getMessage = this.client.i18n.getLocale(locale); - const data = { - components: [], - embeds: [ - new EmbedBuilder() - .setColor('Orange') - .setTitle(getMessage('misc.error.title')) - .setDescription(getMessage('misc.error.description')) - .addFields([ - { - name: getMessage('misc.error.fields.identifier'), - value: codeBlock(' ' + ref + ' '), - }, - ]), - ], - }; - - interaction.reply(data).catch(() => interaction.editReply(data)); + async run(...params) { + return handleInteractionError(...params); } }; diff --git a/src/listeners/commands/error.js b/src/listeners/commands/error.js index 79b6eab..8cbfaf6 100644 --- a/src/listeners/commands/error.js +++ b/src/listeners/commands/error.js @@ -1,9 +1,5 @@ const { Listener } = require('@eartharoid/dbf'); -const { - EmbedBuilder, - codeBlock, -} = require('discord.js'); -const { getSUID } = require('../../lib/logging'); +const { handleInteractionError } = require('../../lib/error'); module.exports = class extends Listener { constructor(client, options) { @@ -14,38 +10,7 @@ module.exports = class extends Listener { }); } - async run({ - command, - error, - interaction, - }) { - const ref = getSUID(); - this.client.log.error.commands(ref); - this.client.log.error.commands(`"${command.name}" command execution error:`, error); - let locale = null; - if (interaction.guild) { - locale = (await this.client.prisma.guild.findUnique({ - select: { locale: true }, - where: { id: interaction.guild.id }, - })).locale; - } - const getMessage = this.client.i18n.getLocale(locale); - const data = { - components: [], - embeds: [ - new EmbedBuilder() - .setColor('Orange') - .setTitle(getMessage('misc.error.title')) - .setDescription(getMessage('misc.error.description')) - .addFields([ - { - name: getMessage('misc.error.fields.identifier'), - value: codeBlock(' ' + ref + ' '), - }, - ]), - ], - }; - - interaction.reply(data).catch(() => interaction.editReply(data)); + async run(...params) { + return handleInteractionError(...params); } }; diff --git a/src/listeners/menus/error.js b/src/listeners/menus/error.js index 586322c..f2af206 100644 --- a/src/listeners/menus/error.js +++ b/src/listeners/menus/error.js @@ -1,9 +1,5 @@ const { Listener } = require('@eartharoid/dbf'); -const { - EmbedBuilder, - codeBlock, -} = require('discord.js'); -const { getSUID } = require('../../lib/logging'); +const { handleInteractionError } = require('../../lib/error'); module.exports = class extends Listener { constructor(client, options) { @@ -14,38 +10,7 @@ module.exports = class extends Listener { }); } - async run({ - menu, - error, - interaction, - }) { - const ref = getSUID(); - this.client.log.error.menus(ref); - this.client.log.error.menus(`"${menu.id}" menu execution error:`, error); - let locale = null; - if (interaction.guild) { - locale = (await this.client.prisma.guild.findUnique({ - select: { locale: true }, - where: { id: interaction.guild.id }, - })).locale; - } - const getMessage = this.client.i18n.getLocale(locale); - const data = { - components: [], - embeds: [ - new EmbedBuilder() - .setColor('Orange') - .setTitle(getMessage('misc.error.title')) - .setDescription(getMessage('misc.error.description')) - .addFields([ - { - name: getMessage('misc.error.fields.identifier'), - value: codeBlock(' ' + ref + ' '), - }, - ]), - ], - }; - - interaction.reply(data).catch(() => interaction.editReply(data)); + async run(...params) { + return handleInteractionError(...params); } }; diff --git a/src/listeners/modals/error.js b/src/listeners/modals/error.js index 548a633..a9c5959 100644 --- a/src/listeners/modals/error.js +++ b/src/listeners/modals/error.js @@ -1,10 +1,5 @@ const { Listener } = require('@eartharoid/dbf'); -const { - EmbedBuilder, - codeBlock, -} = require('discord.js'); -const { getSUID } = require('../../lib/logging'); - +const { handleInteractionError } = require('../../lib/error'); module.exports = class extends Listener { constructor(client, options) { super(client, { @@ -14,38 +9,7 @@ module.exports = class extends Listener { }); } - async run({ - modal, - error, - interaction, - }) { - const ref = getSUID(); - this.client.log.error.modals(ref); - this.client.log.error.modals(`"${modal.id}" modal execution error:`, error); - let locale = null; - if (interaction.guild) { - locale = (await this.client.prisma.guild.findUnique({ - select: { locale: true }, - where: { id: interaction.guild.id }, - })).locale; - } - const getMessage = this.client.i18n.getLocale(locale); - const data = { - components: [], - embeds: [ - new EmbedBuilder() - .setColor('Orange') - .setTitle(getMessage('misc.error.title')) - .setDescription(getMessage('misc.error.description')) - .addFields([ - { - name: getMessage('misc.error.fields.identifier'), - value: codeBlock(' ' + ref + ' '), - }, - ]), - ], - }; - - interaction.reply(data).catch(() => interaction.editReply(data)); + async run(...params) { + return handleInteractionError(...params); } };