From 7fff9192b404b86291659be3d562e5f8c58dac2f Mon Sep 17 00:00:00 2001 From: indifferentketchup <159190319+indifferentketchup@users.noreply.github.com> Date: Thu, 9 Apr 2026 09:49:19 -0500 Subject: [PATCH] queue --- gmail-poll.js | 2 +- handlers/buttons.js | 12 +----- handlers/commands.js | 12 +----- services/channelQueue.js | 61 +++++++++++++++++++------------ services/tickets.js | 4 +- settings-site/public/index.html | 28 +++++++------- settings-site/public/js/app.js | 65 ++++++++++++++++++++++++++------- settings-site/server.js | 6 ++- 8 files changed, 115 insertions(+), 75 deletions(-) diff --git a/gmail-poll.js b/gmail-poll.js index ab9ef8f..92b63f1 100644 --- a/gmail-poll.js +++ b/gmail-poll.js @@ -43,7 +43,7 @@ async function poll(client) { try { pollCount++; if (pollCount % 10 === 0) { - if (totalProcessed > 0) { + if (totalProcessed > 0 || totalSkipped > 0 || totalErrors > 0) { logAutomation('Gmail poll summary', null, `polls: ${pollCount}, processed: ${totalProcessed}, skipped: ${totalSkipped}, errors: ${totalErrors}`).catch(() => {}); } pollCount = 0; totalProcessed = 0; totalSkipped = 0; totalErrors = 0; diff --git a/handlers/buttons.js b/handlers/buttons.js index 7d2ebdc..df847a5 100644 --- a/handlers/buttons.js +++ b/handlers/buttons.js @@ -341,11 +341,7 @@ async function handleClaim(interaction, ticket) { if (renameInfo.ok) { const state = freshTicket.escalated ? 'escalated-claimed' : 'claimed'; const newName = makeTicketName(state, freshTicket, creatorNickname, claimerEmoji); - try { - await enqueueRename(interaction.channel, newName); - } catch (e) { - console.error('Rename error (claim):', e); - } + enqueueRename(interaction.channel, newName).catch(() => {}); } else { const unlockAtMs = Date.now() + renameInfo.waitMs; const unlockAtUnix = Math.floor(unlockAtMs / 1000); @@ -399,11 +395,7 @@ async function handleClaim(interaction, ticket) { const unclaimState = (freshTicket.escalationTier ?? 0) >= 1 ? 'escalated' : 'unclaimed'; const renameInfo = await canRename(freshTicket); if (renameInfo.ok) { - try { - await enqueueRename(interaction.channel, makeTicketName(unclaimState, freshTicket, creatorNicknameUnclaim)); - } catch (e) { - console.error('Rename error (unclaim):', e); - } + enqueueRename(interaction.channel, makeTicketName(unclaimState, freshTicket, creatorNicknameUnclaim)).catch(() => {}); } else { const unlockAtMs = Date.now() + renameInfo.waitMs; const unlockAtUnix = Math.floor(unlockAtMs / 1000); diff --git a/handlers/commands.js b/handlers/commands.js index c0da1af..bc7c989 100644 --- a/handlers/commands.js +++ b/handlers/commands.js @@ -90,11 +90,7 @@ async function runEscalation(interaction, ticket, nextTier, reason) { const renameInfo = await canRename(ticket); if (renameInfo.ok) { const newName = makeTicketName('escalated', ticket, creatorNickname); - try { - await enqueueRename(interaction.channel, newName); - } catch (e) { - console.error('Rename error (escalate):', e); - } + enqueueRename(interaction.channel, newName).catch(() => {}); } else { const unlockAtMs = Date.now() + renameInfo.waitMs; const unlockAtUnix = Math.floor(unlockAtMs / 1000); @@ -204,11 +200,7 @@ async function runDeescalation(interaction, ticket) { const state = newTier === 0 ? 'unclaimed' : 'escalated'; const renameInfo = await canRename(ticket); if (renameInfo.ok) { - try { - await enqueueRename(interaction.channel, makeTicketName(state, ticket, creatorNickname)); - } catch (e) { - console.error('Rename error (deescalate):', e); - } + enqueueRename(interaction.channel, makeTicketName(state, ticket, creatorNickname)).catch(() => {}); } else { const unlockAtMs = Date.now() + renameInfo.waitMs; const unlockAtUnix = Math.floor(unlockAtMs / 1000); diff --git a/services/channelQueue.js b/services/channelQueue.js index 7066ec0..7cf153e 100644 --- a/services/channelQueue.js +++ b/services/channelQueue.js @@ -6,8 +6,9 @@ const RENAME_WINDOW_MS = 9 * 60 * 1000; const RENAME_LIMIT = 2; +const { logWarn } = require('../services/debugLog'); -// Per-channel state: { count, windowStart, queue: [{newName, resolve, reject}], processing } +// Per-channel state: { count, windowStart, queue: [{newName, started}], processing } const renameState = new Map(); function getOrInitState(channelId) { @@ -35,18 +36,17 @@ function processQueue(channel, state) { state.processing = false; // New window if (state.queue.length > 3) { - const { logWarn } = require('../services/debugLog'); logWarn('renameQueue', `Channel ${channel.name} has ${state.queue.length} renames queued`).catch(() => {}); } const item = state.queue.shift(); if (!item) return; + item.started = true; state.count = 1; state.windowStart = Date.now(); try { await executeRename(channel, item.newName); - item.resolve(); } catch (err) { - item.reject(err); + logWarn('renameQueue', `Queued rename failed for ${channel.name}: ${err.message || err}`).catch(() => {}); } // Continue processing remaining queue items processQueue(channel, state); @@ -55,29 +55,42 @@ function processQueue(channel, state) { } function enqueueRename(channel, newName) { - return new Promise((resolve, reject) => { - const state = getOrInitState(channel.id); - const now = Date.now(); + const state = getOrInitState(channel.id); + const now = Date.now(); - // Window expired — reset - if (now - state.windowStart >= RENAME_WINDOW_MS) { - state.count = 1; - state.windowStart = now; - executeRename(channel, newName).then(resolve).catch(reject); - return; - } + // Window expired — reset + if (now - state.windowStart >= RENAME_WINDOW_MS) { + state.count = 1; + state.windowStart = now; + executeRename(channel, newName).catch((err) => { + logWarn('renameQueue', `Immediate rename failed for ${channel.name}: ${err.message || err}`).catch(() => {}); + }); + return Promise.resolve(); + } - // Within window and under limit - if (state.count < RENAME_LIMIT) { - state.count++; - executeRename(channel, newName).then(resolve).catch(reject); - return; - } + // Within window and under limit + if (state.count < RENAME_LIMIT) { + state.count++; + executeRename(channel, newName).catch((err) => { + logWarn('renameQueue', `Immediate rename failed for ${channel.name}: ${err.message || err}`).catch(() => {}); + }); + return Promise.resolve(); + } - // At limit — queue it - state.queue.push({ newName, resolve, reject }); - processQueue(channel, state); - }); + // At limit — queue it + const queueSize = state.queue.length + 1; + const queuedItem = { newName, started: false }; + state.queue.push(queuedItem); + + // Only notify if this rename is still waiting after ~2s. + setTimeout(() => { + if (queuedItem.started) return; + const estMinutes = Math.max(1, Math.ceil((queueSize * RENAME_WINDOW_MS) / 60000)); + channel.send(`⏳ Channel will be renamed in ~${estMinutes} minute${estMinutes === 1 ? '' : 's'}.`).catch(() => {}); + }, 2000); + + processQueue(channel, state); + return Promise.resolve(); } function enqueueMove(channel, categoryId) { diff --git a/services/tickets.js b/services/tickets.js index 7a587d4..f532d09 100644 --- a/services/tickets.js +++ b/services/tickets.js @@ -613,7 +613,9 @@ async function reconcileDeletedTicketChannels(client) { console.error(`reconcileDeletedTicketChannels error for ${ticket.gmailThreadId}:`, err); } } - logAutomation('Reconcile run', null, `checked: ${checked}, reconciled: ${reconciled}`).catch(() => {}); + if (reconciled > 0) { + logAutomation('Reconcile run', null, `checked: ${checked}, reconciled: ${reconciled}`).catch(() => {}); + } return { checked, reconciled }; } diff --git a/settings-site/public/index.html b/settings-site/public/index.html index 86c9b4f..3a279ef 100644 --- a/settings-site/public/index.html +++ b/settings-site/public/index.html @@ -13,19 +13,19 @@ @@ -294,7 +294,7 @@
Log channel configuration (channels set in Channels section)
▼