diff --git a/convex/liveChat.ts b/convex/liveChat.ts index 94109ef..d4bde33 100644 --- a/convex/liveChat.ts +++ b/convex/liveChat.ts @@ -763,15 +763,22 @@ export const getTicketChatHistory = query({ // Timeout de maquina offline: 5 minutos sem heartbeat const MACHINE_OFFLINE_TIMEOUT_MS = 5 * 60 * 1000 -// Mutation interna para encerrar sessões de máquinas offline (chamada pelo cron) -// Nova lógica: só encerra se a MÁQUINA estiver offline, não por inatividade de chat -// Isso permite que usuários mantenham o chat aberto sem precisar enviar mensagens +// Timeout de inatividade do chat: 12 horas sem atividade +// Isso evita acumular sessoes abertas indefinidamente quando usuario esquece de encerrar +const CHAT_INACTIVITY_TIMEOUT_MS = 12 * 60 * 60 * 1000 + +// Mutation interna para encerrar sessões inativas (chamada pelo cron) +// Critérios de encerramento: +// 1. Máquina offline (5 min sem heartbeat) +// 2. Chat inativo (12 horas sem atividade) - mesmo se máquina online +// 3. Ticket órfão (sem máquina vinculada) export const autoEndInactiveSessions = mutation({ args: {}, handler: async (ctx) => { - console.log("cron: autoEndInactiveSessions iniciado (verificando maquinas offline)") + console.log("cron: autoEndInactiveSessions iniciado") const now = Date.now() const offlineCutoff = now - MACHINE_OFFLINE_TIMEOUT_MS + const inactivityCutoff = now - CHAT_INACTIVITY_TIMEOUT_MS // Limitar a 50 sessões por execução para evitar timeout do cron (30s) const maxSessionsPerRun = 50 @@ -784,6 +791,7 @@ export const autoEndInactiveSessions = mutation({ let endedCount = 0 let checkedCount = 0 + const reasons: Record = {} for (const session of activeSessions) { checkedCount++ @@ -812,6 +820,36 @@ export const autoEndInactiveSessions = mutation({ createdAt: now, }) endedCount++ + reasons["ticket_sem_maquina"] = (reasons["ticket_sem_maquina"] ?? 0) + 1 + continue + } + + // Verificar inatividade do chat (12 horas sem atividade) + // Isso tem prioridade sobre o status da máquina + const chatIsInactive = session.lastActivityAt < inactivityCutoff + if (chatIsInactive) { + await ctx.db.patch(session._id, { + status: "ENDED", + endedAt: now, + }) + await ctx.db.insert("ticketEvents", { + ticketId: session.ticketId, + type: "LIVE_CHAT_ENDED", + payload: { + sessionId: session._id, + agentId: session.agentId, + agentName: session.agentSnapshot?.name ?? "Sistema", + durationMs: now - session.startedAt, + startedAt: session.startedAt, + endedAt: now, + autoEnded: true, + reason: "inatividade_chat", + inactiveForMs: now - session.lastActivityAt, + }, + createdAt: now, + }) + endedCount++ + reasons["inatividade_chat"] = (reasons["inatividade_chat"] ?? 0) + 1 continue } @@ -819,7 +857,7 @@ export const autoEndInactiveSessions = mutation({ const lastHeartbeatAt = await getLastHeartbeatAt(ctx, ticket.machineId) const machineIsOnline = lastHeartbeatAt !== null && lastHeartbeatAt > offlineCutoff - // Se máquina está online, manter sessão ativa + // Se máquina está online e chat está ativo, manter sessão if (machineIsOnline) { continue } @@ -849,10 +887,12 @@ export const autoEndInactiveSessions = mutation({ }) endedCount++ + reasons["maquina_offline"] = (reasons["maquina_offline"] ?? 0) + 1 } - console.log(`cron: verificadas ${checkedCount} sessoes, encerradas ${endedCount} (maquinas offline)`) - return { endedCount, checkedCount, hasMore: activeSessions.length === maxSessionsPerRun } + const reasonsSummary = Object.entries(reasons).map(([r, c]) => `${r}=${c}`).join(", ") + console.log(`cron: verificadas ${checkedCount} sessoes, encerradas ${endedCount} (${reasonsSummary || "nenhuma"})`) + return { endedCount, checkedCount, reasons, hasMore: activeSessions.length === maxSessionsPerRun } }, })