rename path: fix env-var mismatch, gut canRename gate, add primary-bot fallback on 401/403/429
- secondary rename-bot token was set as RENAME_TOKEN in .env but utils/renamer.js reads RENAMER_BOT; silently no-op'd every rename (host .env renamed separately)
- services/tickets.js canRename gutted to an always-ok shim; Mongo 2/10min per-channel gate is redundant since renames flow through RENAMER_BOT's own bucket. Ticket.renameCount / renameWindowStart remain as orphan fields (no migration)
- handlers/buttons.js + commands.js: drop the four "Channel renamed too quickly" else-branches and the rename-countdown label suffix; replace .catch(() => {}) with .catch(err => logError('rename', err)...)
- services/channelQueue.js: executeRename falls back to channel.setName(currentName) when renamer throws err.fallback === true (401/403/429); classifies non-fallback errors as renameQueue:token/permission (401/403) or renameQueue:secondary-bot ratelimited (429)
- utils/renamer.js: on 401/403 throw err.fallback=true immediately; on 429 respect retry_after up to 2000ms then throw err.fallback=true
- docs: align CLAUDE.md, docs/api/DISCORD_API_VALIDATION.md, docs/architecture/CRITICAL_FILES_AND_HOW_IT_WORKS.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ const {
|
||||
} = require('discord.js');
|
||||
const { mongoose } = require('../db-connection');
|
||||
const { CONFIG } = require('../config');
|
||||
const { canRename, makeTicketName, resolveCreatorNickname, minutesFromMs, getOrCreateTicketCategory, cleanupEmptyOverflowCategory, createDiscordTicketAsThread, checkTicketCreationRateLimit, getSenderLocal, toDiscordSafeName } = require('../services/tickets');
|
||||
const { makeTicketName, resolveCreatorNickname, getOrCreateTicketCategory, cleanupEmptyOverflowCategory, createDiscordTicketAsThread, checkTicketCreationRateLimit, getSenderLocal, toDiscordSafeName } = require('../services/tickets');
|
||||
const { sendTicketClosedEmail } = require('../services/gmail');
|
||||
const { getTicketActionRow } = require('../utils/ticketComponents');
|
||||
const { sanitizeEmbedText, truncateEmbedDescription, truncateEmbedField, enforceEmbedLimit } = require('../utils');
|
||||
@@ -26,6 +26,7 @@ const { runEscalation, runDeescalation } = require('./commands');
|
||||
const { trackInteraction, trackError } = require('./analytics');
|
||||
const { pendingCloses } = require('./pendingCloses');
|
||||
const { increment } = require('../services/patternStore');
|
||||
const { logError } = require('../services/debugLog');
|
||||
|
||||
const Ticket = mongoose.model('Ticket');
|
||||
const Transcript = mongoose.model('Transcript');
|
||||
@@ -348,23 +349,11 @@ async function handleClaim(interaction, ticket) {
|
||||
const claimerEmoji = CONFIG.STAFF_EMOJIS.get(interaction.user.id) || CONFIG.CLAIMER_EMOJI_FALLBACK;
|
||||
const creatorNickname = await resolveCreatorNickname(guild, freshTicket);
|
||||
|
||||
const renameInfo = await canRename(freshTicket);
|
||||
if (renameInfo.ok) {
|
||||
const state = freshTicket.escalated ? 'escalated-claimed' : 'claimed';
|
||||
const newName = makeTicketName(state, freshTicket, creatorNickname, claimerEmoji);
|
||||
enqueueRename(interaction.channel, newName).catch(() => {});
|
||||
} else {
|
||||
const unlockAtMs = Date.now() + renameInfo.waitMs;
|
||||
const unlockAtUnix = Math.floor(unlockAtMs / 1000);
|
||||
await enqueueSend(interaction.channel,
|
||||
`Channel renamed too quickly. Try again <t:${unlockAtUnix}:R>.`
|
||||
);
|
||||
}
|
||||
const state = freshTicket.escalated ? 'escalated-claimed' : 'claimed';
|
||||
const newName = makeTicketName(state, freshTicket, creatorNickname, claimerEmoji);
|
||||
enqueueRename(interaction.channel, newName).catch(err => logError('rename', err).catch(() => {}));
|
||||
|
||||
const baseLabel = `Unclaim (${claimerLabel})`;
|
||||
const label = renameInfo.ok
|
||||
? baseLabel
|
||||
: `${baseLabel} – rename in ${minutesFromMs(renameInfo.waitMs)}m`;
|
||||
const label = `Unclaim (${claimerLabel})`;
|
||||
|
||||
btnClose
|
||||
.setCustomId('close_ticket')
|
||||
@@ -404,16 +393,7 @@ async function handleClaim(interaction, ticket) {
|
||||
|
||||
const creatorNicknameUnclaim = await resolveCreatorNickname(guild, freshTicket);
|
||||
const unclaimState = (freshTicket.escalationTier ?? 0) >= 1 ? 'escalated' : 'unclaimed';
|
||||
const renameInfo = await canRename(freshTicket);
|
||||
if (renameInfo.ok) {
|
||||
enqueueRename(interaction.channel, makeTicketName(unclaimState, freshTicket, creatorNicknameUnclaim)).catch(() => {});
|
||||
} else {
|
||||
const unlockAtMs = Date.now() + renameInfo.waitMs;
|
||||
const unlockAtUnix = Math.floor(unlockAtMs / 1000);
|
||||
await enqueueSend(interaction.channel,
|
||||
`Channel renamed too quickly. Try again <t:${unlockAtUnix}:R>.`
|
||||
);
|
||||
}
|
||||
enqueueRename(interaction.channel, makeTicketName(unclaimState, freshTicket, creatorNicknameUnclaim)).catch(err => logError('rename', err).catch(() => {}));
|
||||
|
||||
btnClose
|
||||
.setCustomId('close_ticket')
|
||||
@@ -731,8 +711,9 @@ async function handleTicketModal(interaction) {
|
||||
const actionRow = getTicketActionRow({ escalationTier: 0 });
|
||||
|
||||
enforceEmbedLimit([welcomeEmbed, infoEmbed, resourcesEmbed]);
|
||||
let welcomeMsg;
|
||||
try {
|
||||
const welcomeMsg = await enqueueSend(channel, {
|
||||
welcomeMsg = await enqueueSend(channel, {
|
||||
content: `Hey There ${interaction.user} 🥦`,
|
||||
embeds: [welcomeEmbed, infoEmbed, resourcesEmbed],
|
||||
components: [actionRow]
|
||||
|
||||
Reference in New Issue
Block a user