QUAL-006 store ticket.creatorId on creation; legacy split-pop returned the
message ID for discord-msg-* tickets, breaking transcript DM, close
log, and channel rename for context-menu-created tickets. Adds the
field to the Ticket schema and writes a one-shot backfill script
(scripts/backfill-creatorId.js, dry-run by default).
QUEUE-001 add enqueueOverwrite + enqueueTopic to services/channelQueue.js
(chain on renameChains alongside enqueueMove). Migrate handleAdd /
handleRemove / handleMove / handleTopic so permissionOverwrites,
setParent, and setTopic no longer race pending renames or sends.
handleMove now uses the existing enqueueMove. Initial overwrites in
handleTicketModal stay inline; channel doesn't exist yet so no race.
DISCORD-001 replace ephemeral: true with flags: MessageFlags.Ephemeral across
broccolini-discord.js, handlers/sharedHelpers.js, handlers/buttons.js,
handlers/commands.js. runDeferred opts now take { flags } directly.
SEC-003 /gmailpoll min interval is 30s. Drop the 5s/10s slash-command
choices and clamp Math.max(30000, ms) in handleGmailPoll for
defense in depth.
QUAL-001 upgrade silent .catch(() => {}) on the lastActivity updateOne in
handlers/messages.js to log via logError, so transient Mongo errors
surface in the debug channel instead of disappearing.
QUAL-002 drop await from logError/logWarn calls in services/staffThread.js
and services/pinMessage.js — fire-and-forget per CLAUDE.md hard rule.
QUAL-003 wrap stray setTimeouts (handleConfirmCloseRequest force-close timer,
runFinalClose channel-delete + overflow-cleanup, checkAutoClose
delete-after-email) in trackTimeout via lazy require so they clear
on shutdown.
42 lines
1.5 KiB
JavaScript
42 lines
1.5 KiB
JavaScript
/**
|
|
* Auto-pin utility — pins a message with error handling and optional
|
|
* system message suppression.
|
|
*
|
|
* Discord rate-limits pin operations to approximately 5 per second per
|
|
* channel. Since pins only happen on ticket creation and escalation (low
|
|
* frequency), no additional rate limiting is needed. The bot requires
|
|
* MANAGE_MESSAGES permission to pin — if this is missing, the pin will
|
|
* fail with code 50013 and be caught by the catch block.
|
|
*/
|
|
const { CONFIG } = require('../config');
|
|
const { logWarn } = require('./debugLog');
|
|
|
|
/**
|
|
* Pin a message in a channel.
|
|
* @param {import('discord.js').Message} message
|
|
* @param {import('discord.js').Client} client
|
|
*/
|
|
async function pinMessage(message, client) {
|
|
try {
|
|
await message.pin();
|
|
|
|
if (CONFIG.PIN_SUPPRESS_SYSTEM_MESSAGE) {
|
|
await new Promise(r => setTimeout(r, 1000));
|
|
const systemMessages = await message.channel.messages.fetch({ limit: 5 });
|
|
const pinNotice = systemMessages.find(m =>
|
|
m.type === 6 && // MessageType.ChannelPinnedMessage
|
|
Date.now() - m.createdTimestamp < 10000
|
|
);
|
|
if (pinNotice) await pinNotice.delete().catch(() => {});
|
|
}
|
|
} catch (err) {
|
|
if (err.code === 30003) {
|
|
logWarn('pinMessage', `Max pins reached in channel #${message.channel.name} — could not pin message.`, client).catch(() => {});
|
|
} else {
|
|
logWarn('pinMessage', `Failed to pin message in #${message.channel.name}: ${err.message}`, client).catch(() => {});
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { pinMessage };
|