audit week 2 [ARCH-001]: split handlers/commands.js into submodules
The 1028-line handlers/commands.js bundled escalation logic + force-close
flow + /response tag CRUD + /panel + /signature + context-menu handlers +
several config-toggle slash commands. After the dispatch-table refactor it
was still a god module. Split into handlers/commands/ with one file per
topic; require('./commands') resolves to handlers/commands/index.js
(handlers/commands.js is removed).
Layout:
helpers.js — requireStaffRole, fetchLoggingChannel
(cross-submodule, kept here to avoid cycles with index.js)
escalation.js — runEscalation, runDeescalation, handleEscalate, handleDeescalate
(run* are still exported via index.js for handlers/buttons.js)
close.js — handleForceClose, handleCancelClose, handleCloseTimer
+ finalizeForceClose / postTranscript (timer callback)
response.js — handleResponse + send/create/edit/delete/list subcommands
+ handleAutocomplete (only /response autocompletes)
panel.js — handlePanel, buildPanelButtonRow, handleSignature
contextMenu.js — handleCreateTicketFromMessage, handleViewUserTickets
index.js — dispatch tables, handleCommand/handleContextMenu, plus the
short-and-not-thematic handlers (notifydm, add, remove,
transfer, move, topic, staffthread, pinmessages, gmailpoll,
help) and the public re-exports.
No behavior change — every imported name, every Discord call, every DB
write, every embed, every reply payload preserved verbatim. Public surface
of require('./commands') is still { handleCommand, handleContextMenu,
handleAutocomplete, runEscalation, runDeescalation }.
Largest single module is now index.js at 299 lines; others are 33–214.
This commit is contained in:
133
handlers/commands/panel.js
Normal file
133
handlers/commands/panel.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* /panel — create a ticket-creation panel embed in a chosen channel.
|
||||
* Also hosts /signature (modal for staff personal email signature) since
|
||||
* both are user-facing UX-flow commands without their own dedicated module.
|
||||
*/
|
||||
const {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
EmbedBuilder,
|
||||
MessageFlags,
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle
|
||||
} = require('discord.js');
|
||||
const { mongoose } = require('../../db-connection');
|
||||
const { CONFIG } = require('../../config');
|
||||
const { enqueueSend } = require('../../services/channelQueue');
|
||||
|
||||
const StaffSignature = mongoose.model('StaffSignature');
|
||||
|
||||
async function handlePanel(interaction) {
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
const panelType = interaction.options.getString('type') || null; // 'thread' | 'category' | 'both' or null
|
||||
const title = interaction.options.getString('title') || 'Indifferent Broccoli Tickets';
|
||||
const description = interaction.options.getString('description') ||
|
||||
'Need help? Click below to create a ticket. 🎟';
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(title)
|
||||
.setDescription(description)
|
||||
.setColor(0x2ecc71)
|
||||
.setThumbnail(CONFIG.LOGO_URL || null)
|
||||
.setFooter({ text: 'Indifferent Broccoli Tickets' });
|
||||
|
||||
const row = buildPanelButtonRow(panelType);
|
||||
|
||||
try {
|
||||
await enqueueSend(channel, { embeds: [embed], components: [row] });
|
||||
await interaction.reply({ content: `Panel created in ${channel}!`, flags: MessageFlags.Ephemeral });
|
||||
} catch (err) {
|
||||
console.error('Panel creation error:', err);
|
||||
await interaction.reply({ content: 'Failed to create panel.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
|
||||
function buildPanelButtonRow(panelType) {
|
||||
if (panelType === 'both') {
|
||||
return new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('open_ticket_thread')
|
||||
.setLabel('Create ticket (thread)')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setEmoji('🧵'),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('open_ticket_channel')
|
||||
.setLabel('Create ticket (channel)')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setEmoji('📁')
|
||||
);
|
||||
}
|
||||
if (panelType === 'thread') {
|
||||
return new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('open_ticket_thread')
|
||||
.setLabel('Create ticket')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setEmoji('🧵')
|
||||
);
|
||||
}
|
||||
if (panelType === 'category') {
|
||||
return new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('open_ticket_channel')
|
||||
.setLabel('Create ticket')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setEmoji('📁')
|
||||
);
|
||||
}
|
||||
return new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('open_ticket')
|
||||
.setLabel('Create ticket')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setEmoji('✅')
|
||||
);
|
||||
}
|
||||
|
||||
async function handleSignature(interaction) {
|
||||
try {
|
||||
const existingSignature = await StaffSignature.findOne({ userId: interaction.user.id }).lean();
|
||||
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId(`signature_modal_${interaction.user.id}`)
|
||||
.setTitle('Staff Signature Settings');
|
||||
|
||||
const valedictionInput = new TextInputBuilder()
|
||||
.setCustomId('valediction')
|
||||
.setLabel('Valediction (e.g. "Best regards", "Thanks")')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setRequired(false)
|
||||
.setValue(existingSignature?.valediction || '');
|
||||
|
||||
const displayNameInput = new TextInputBuilder()
|
||||
.setCustomId('display_name')
|
||||
.setLabel('Display Name (e.g. "Support Team")')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setRequired(false)
|
||||
.setValue(existingSignature?.displayName || '');
|
||||
|
||||
const taglineInput = new TextInputBuilder()
|
||||
.setCustomId('tagline')
|
||||
.setLabel('Tagline (e.g. "Technical Support Specialist")')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setRequired(false)
|
||||
.setValue(existingSignature?.tagline || '');
|
||||
|
||||
modal.addComponents(
|
||||
new ActionRowBuilder().addComponents(valedictionInput),
|
||||
new ActionRowBuilder().addComponents(displayNameInput),
|
||||
new ActionRowBuilder().addComponents(taglineInput)
|
||||
);
|
||||
|
||||
await interaction.showModal(modal);
|
||||
} catch (err) {
|
||||
console.error('Signature command error:', err);
|
||||
if (!interaction.replied && !interaction.deferred) {
|
||||
await interaction.reply({ content: 'Failed to open signature settings.', flags: MessageFlags.Ephemeral }).catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { handlePanel, handleSignature };
|
||||
Reference in New Issue
Block a user