Dead/stale removals (grep-confirmed no consumers):
- config: drop 9 unread CONFIG keys (ROLE_TO_PING_ID, SIGNATURE,
REMINDER_*, RENAME_LOG_CHANNEL_ID, SETTINGS_*); remove their
ALLOWED_CONFIG_KEYS entries and the orphaned settings-site UI fields
- configSchema: delete unreachable json/string_or_json validators
- models: drop unused ticketTag field
- gmail-poll: remove unused isPollSuspended export
- utils: remove dead htmlToTextWithBlocks/decodeHtmlEntities/BLOCK_TAG_REGEX
- internalApi: remove router._allowedKeys (test it served is gone)
- discord client: drop unused GuildPresences privileged intent
- broccolini-discord: remove dormant /api 503 gate (no /api routes)
Fixes:
- context-menu ticket create now uses makeTicketName('unclaimed', ...)
instead of the contract-violating ticket-<n> name
- drop write-only pending.userId from both close paths
Dedup / simplify:
- new services/transcript.js shares the transcript text/date/header
builders between the button and force-close paths (had drifted)
- resolveEscalationCategoryId() replaces 3 copies of the category logic
- ticketChannelOverwrites() shares the create-permission array between
the two interactive ticket-create paths
- finalizeBody() shares the email-cleanup tail in parseGmailMessage
- getTicketActionRow drops its never-passed options arg;
sendTicketNotificationEmail drops its always-null subjectLine arg
- hoist invariant guild lookup out of the auto-close/unclaim loops
- drop redundant lastActivity write (and now-dead updateTicketActivity)
- /help lists all current commands and the right-click apps
72 lines
2.2 KiB
JavaScript
72 lines
2.2 KiB
JavaScript
/**
|
||
* Ticket action row builder – Close, Claim, Escalate (if tier < 3), Deescalate (if tier >= 2).
|
||
* Used by handlers/buttons.js and handlers/commands.js.
|
||
*/
|
||
const { ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionFlagsBits } = require('discord.js');
|
||
const { CONFIG } = require('../config');
|
||
|
||
/**
|
||
* permissionOverwrites for a Discord-originated ticket channel: deny @everyone,
|
||
* allow the creating user and the staff ping role. Used by the button and
|
||
* context-menu creation paths (the email/gmail path differs — no Discord
|
||
* creator — and builds its own overwrites).
|
||
* @param {import('discord.js').Guild} guild
|
||
* @param {string} creatorId - Discord user ID of the ticket creator
|
||
*/
|
||
function ticketChannelOverwrites(guild, creatorId) {
|
||
const allow = [
|
||
PermissionFlagsBits.ViewChannel,
|
||
PermissionFlagsBits.SendMessages,
|
||
PermissionFlagsBits.ReadMessageHistory
|
||
];
|
||
return [
|
||
{ id: guild.id, deny: [PermissionFlagsBits.ViewChannel] },
|
||
{ id: creatorId, allow },
|
||
{ id: CONFIG.ROLE_ID_TO_PING, allow }
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Build the standard ticket action row (Close, Claim, optionally Escalate, optionally Deescalate).
|
||
* @param {Object} ticket - Ticket with escalationTier (0, 1, 2) and optionally escalated
|
||
* @returns {ActionRowBuilder}
|
||
*/
|
||
function getTicketActionRow(ticket) {
|
||
const tier = ticket.escalationTier ?? (ticket.escalated ? 1 : 0);
|
||
const row = new ActionRowBuilder();
|
||
|
||
row.addComponents(
|
||
new ButtonBuilder()
|
||
.setCustomId('close_ticket')
|
||
.setLabel(CONFIG.BUTTON_LABEL_CLOSE)
|
||
.setEmoji(CONFIG.BUTTON_EMOJI_CLOSE)
|
||
.setStyle(ButtonStyle.Secondary),
|
||
new ButtonBuilder()
|
||
.setCustomId('claim_ticket')
|
||
.setLabel(CONFIG.BUTTON_LABEL_CLAIM)
|
||
.setEmoji(CONFIG.BUTTON_EMOJI_CLAIM)
|
||
.setStyle(ButtonStyle.Secondary)
|
||
);
|
||
|
||
if (tier < 2) {
|
||
row.addComponents(
|
||
new ButtonBuilder()
|
||
.setCustomId('escalate_ticket')
|
||
.setLabel('Escalate')
|
||
.setStyle(ButtonStyle.Secondary)
|
||
);
|
||
}
|
||
if (tier >= 1) {
|
||
row.addComponents(
|
||
new ButtonBuilder()
|
||
.setCustomId('deescalate_ticket')
|
||
.setLabel('Deescalate')
|
||
.setStyle(ButtonStyle.Secondary)
|
||
);
|
||
}
|
||
|
||
return row;
|
||
}
|
||
|
||
module.exports = { getTicketActionRow, ticketChannelOverwrites };
|