/** * Shared helpers for slash-command and button handlers. * * Both handlers/commands.js and handlers/buttons.js use these to avoid * repeating the lookup-and-defer-and-try-catch pattern across 30+ branches. */ const { mongoose } = require('../db-connection'); const { logError } = require('../services/debugLog'); const Ticket = mongoose.model('Ticket'); /** * Look up the ticket linked to this channel; reply with `missingMessage` * (default: "This channel is not linked to a ticket.") and return null if * the channel is not a ticket. Returns the ticket on success. * * @param {import('discord.js').Interaction} interaction * @param {string} [missingMessage] */ async function findTicketForChannel(interaction, missingMessage = 'This channel is not linked to a ticket.') { const ticket = await Ticket.findOne({ discordThreadId: interaction.channel.id }).lean(); if (!ticket) { await interaction.reply({ content: missingMessage, ephemeral: true }); return null; } return ticket; } /** * Defer + run + log + reply on error. `verb` is the user-facing verb * (e.g. "escalate"); error messages render as "Failed to this ticket." * Errors are logged to console + DEBUGGING_CHANNEL_ID via logError(verb, ...). * * @param {import('discord.js').Interaction} interaction * @param {string} verb * @param {() => Promise} fn * @param {{ ephemeral?: boolean }} [opts] */ async function runDeferred(interaction, verb, fn, { ephemeral = false } = {}) { try { await interaction.deferReply({ ephemeral }); await fn(); } catch (err) { console.error(`${verb} error:`, err); logError(verb, err, interaction).catch(() => {}); const msg = `Failed to ${verb} this ticket.`; await interaction.editReply({ content: msg }).catch(() => interaction.followUp({ content: msg, ephemeral: true }).catch(() => {}) ); } } module.exports = { findTicketForChannel, runDeferred };