refactor handleButton into a dispatch table
Each customId now maps to a named handler in one of two tables:
FREE_BUTTON_HANDLERS (open-ticket panel, tag-delete cancel — no ticket
lookup) or TICKET_BUTTON_HANDLERS (anything fired inside a ticket channel
— the dispatcher does the lookup once before delegating). The dynamic
`confirm_delete_tag::*` id is matched by prefix.
To find a button's logic, search handle<Name>Button or handleTagDelete*.
Other cleanups in the same pass:
- Move findTicketForChannel and runDeferred from handlers/commands.js to
the new handlers/sharedHelpers.js so both files share one source of
truth. runDeferred now also calls logError(verb, ...) — was logged ad
hoc in buttons.js, missing in commands.js. Strictly additive.
- Hoist three inline `require('../services/...')` calls (staffThread,
pinMessage, debugLog) to top imports.
- Collapse escalate_to_tier2 and escalate_to_tier3 into one
handleEscalateButton(interaction, ticket) that derives the tier from
customId. Same for confirm_close / confirm_close_with_email /
confirm_close_no_email — one handleConfirmCloseRequest deriving
sendEmail from customId.
- Decompose the 156-line handleConfirmClose into runFinalClose +
buildTranscriptText + formatDateForTranscript + renderTranscriptHeader
+ dmTranscriptToCreator + postCloseLogEntry. Each piece is testable in
isolation.
- Decompose handleClaim into applyClaim + applyUnclaim.
- Extract buildOpenTicketModal() and postTicketWelcomeEmbeds() so the
ticket-creation modal flow is readable top-to-bottom.
No behavior change. handleButton + handleTicketModal exports preserved;
24/24 modules load clean (sharedHelpers.js is the new one).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1060
handlers/buttons.js
1060
handlers/buttons.js
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,7 @@ const { setNotifyDm } = require('../services/staffSettings');
|
||||
const { pinMessage } = require('../services/pinMessage');
|
||||
const { logError, logTicketEvent } = require('../services/debugLog');
|
||||
const { pendingCloses } = require('./pendingCloses');
|
||||
const { findTicketForChannel, runDeferred } = require('./sharedHelpers');
|
||||
|
||||
const Ticket = mongoose.model('Ticket');
|
||||
const Tag = mongoose.model('Tag');
|
||||
@@ -53,37 +54,6 @@ async function requireStaffRole(interaction) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the ticket linked to this channel; reply with a friendly message
|
||||
* and return null if the channel is not a ticket. Returns the ticket on
|
||||
* success.
|
||||
*/
|
||||
async function findTicketForChannel(interaction) {
|
||||
const ticket = await Ticket.findOne({ discordThreadId: interaction.channel.id }).lean();
|
||||
if (!ticket) {
|
||||
await interaction.reply({ content: 'This channel is not linked to a ticket.', 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 <verb> this ticket."
|
||||
*/
|
||||
async function runDeferred(interaction, verb, fn, { ephemeral = false } = {}) {
|
||||
try {
|
||||
await interaction.deferReply({ ephemeral });
|
||||
await fn();
|
||||
} catch (err) {
|
||||
console.error(`${verb} error:`, err);
|
||||
const msg = `Failed to ${verb} this ticket.`;
|
||||
await interaction.editReply({ content: msg }).catch(() =>
|
||||
interaction.followUp({ content: msg, ephemeral: true }).catch(() => {})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Fetch the configured logging channel, or null if unset/missing. */
|
||||
async function fetchLoggingChannel(client) {
|
||||
if (!CONFIG.LOGGING_CHANNEL_ID) return null;
|
||||
|
||||
53
handlers/sharedHelpers.js
Normal file
53
handlers/sharedHelpers.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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 <verb> this ticket."
|
||||
* Errors are logged to console + DEBUGGING_CHANNEL_ID via logError(verb, ...).
|
||||
*
|
||||
* @param {import('discord.js').Interaction} interaction
|
||||
* @param {string} verb
|
||||
* @param {() => Promise<void>} 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 };
|
||||
Reference in New Issue
Block a user