leonardo há 1 semana atrás
pai
commit
9e29ce6ad4

+ 5 - 6
src/chat/loginCommand.js

@@ -186,9 +186,7 @@ function buscarOsPorStatus(idCliente, status, limit, fim) {
     order: "DESC",
     limit,
     incluirMensagens: true
-  })
-    .then((r) => (Array.isArray(r?.data) ? r.data : []))
-    .catch(() => []);
+  }).then((r) => (Array.isArray(r?.data) ? r.data : []));
 }
 
 // OS aberta é sempre relevante pro atendimento (chamado em andamento agora), então
@@ -263,7 +261,7 @@ async function montarRespostaCliente(login) {
     buscarContratosCliente(idCliente).catch(() => FALHA_ERP),
     buscarFaturasAbertas(idCliente).catch(() => FALHA_ERP),
     buscarFaturasPagas(idCliente, FATURAS_PAGAS_LIMIT).catch(() => FALHA_ERP),
-    buscarOsRecentes(idCliente),
+    buscarOsRecentes(idCliente).catch(() => FALHA_ERP),
     buscarCpeAcs(login.pppoe)
   ]);
 
@@ -283,8 +281,9 @@ async function montarRespostaCliente(login) {
       ? "_Não foi possível consultar o financeiro no ERP agora._"
       : formatarFinanceiro(faturasAbertas, faturasPagas),
     "",
-    `### Últimas OS (${osList.length})`,
-    formatarOs(osList)
+    ...(osList === FALHA_ERP
+      ? ["### Últimas OS", "_Não foi possível consultar as OS no ERP agora._"]
+      : [`### Últimas OS (${osList.length})`, formatarOs(osList)])
   ];
 
   return { answer: linhas.join("\n"), sources: [] };

+ 1 - 1
src/controllers/Conversas.Controller.js

@@ -13,7 +13,7 @@ export const ConversasController = {
 
   ListarMensagens: async function (req, res, next) {
     try {
-      const results = await listarMensagens(req.params.id, req.query.after);
+      const results = await listarMensagens(req.params.id, req.query.afterId);
       res.json({ results });
     } catch (err) {
       next(err);

+ 15 - 5
src/services/atendimentosService.js

@@ -18,7 +18,17 @@ function ifbotAuthHeaders() {
   return { [authHeader]: value };
 }
 
-async function buscarJsonDeUrl(urlStr, extraHeaders = {}) {
+function mesmaOrigemIfbot(urlStr) {
+  try {
+    return new URL(urlStr).origin === new URL(config.ifbot.baseUrl).origin;
+  } catch {
+    return false;
+  }
+}
+
+// O token do ifbot só vai para a origem do ifbot, avaliado a cada salto: SourceUrl pode ser
+// qualquer URL https pública (ingest por URL) e um redirect pode levar para outro host.
+async function buscarJsonDeUrl(urlStr, { autenticarIfbot = false } = {}) {
   let currentUrl = urlStr;
   let res;
   for (let hop = 0; ; hop += 1) {
@@ -28,7 +38,7 @@ async function buscarJsonDeUrl(urlStr, extraHeaders = {}) {
       headers: {
         "User-Agent": "Mozilla/5.0 star-oraculo/1.0",
         Accept: "application/json",
-        ...extraHeaders
+        ...(autenticarIfbot && mesmaOrigemIfbot(currentUrl) ? ifbotAuthHeaders() : {})
       },
       redirect: "manual",
       signal: AbortSignal.timeout(config.http.defaultTimeoutMs)
@@ -231,7 +241,7 @@ function montarPayloadDeProtocolo(json, fallback) {
 
 export async function importarProtocolo(item, baseUrl) {
   const mensagensUrl = `${baseUrl}/protocolos/${item.Id}/mensagens`;
-  const json = await buscarJsonDeUrl(mensagensUrl, ifbotAuthHeaders());
+  const json = await buscarJsonDeUrl(mensagensUrl, { autenticarIfbot: true });
 
   const payload = montarPayloadDeProtocolo(json, {
     Protocolo: { Id: item.Id, Codigo: item.Codigo, Status: 1 },
@@ -256,7 +266,7 @@ export async function sincronizarAtendimentoPorId(id) {
 
   const baseUrl = config.ifbot.baseUrl.replace(/\/+$/, "");
   const mensagensUrl = atendimento.SourceUrl || `${baseUrl}/protocolos/${id}/mensagens`;
-  const json = await buscarJsonDeUrl(mensagensUrl, ifbotAuthHeaders());
+  const json = await buscarJsonDeUrl(mensagensUrl, { autenticarIfbot: true });
 
   const payload = montarPayloadDeProtocolo(json, {
     Protocolo: { Id: atendimento.Id, Codigo: atendimento.Codigo, Status: atendimento.Status },
@@ -278,7 +288,7 @@ export async function sincronizarProtocolosAbertos({ pagina = 1, limite = 50, in
   if (inicio) params.set("inicio", inicio);
   if (fim) params.set("fim", fim);
 
-  const listaJson = await buscarJsonDeUrl(`${baseUrl}/protocolos/abertos?${params.toString()}`, ifbotAuthHeaders());
+  const listaJson = await buscarJsonDeUrl(`${baseUrl}/protocolos/abertos?${params.toString()}`, { autenticarIfbot: true });
   const lista = protocolosAbertosSchema.safeParse(listaJson);
   if (!lista.success) throw new BadRequestError("invalid_protocolos_payload");
 

+ 6 - 2
src/services/whatsappService.js

@@ -228,9 +228,13 @@ export async function listarConversas() {
   return WhatsappConversa.query().orderBy("UltimaMensagemEm", "desc").orderBy("UpdatedAt", "desc");
 }
 
-export async function listarMensagens(conversaId, after) {
+// O polling pagina por Id (auto-increment = ordem de gravação), não por Timestamp: entrada usa o
+// horário da Meta e saída o do servidor, então uma mensagem do cliente que chega atrasada pelo
+// webhook pode ter Timestamp anterior ao último já exibido e nunca seria trazida.
+export async function listarMensagens(conversaId, afterId) {
   const query = WhatsappMensagem.query().where("ConversaId", conversaId).orderBy("Timestamp", "asc").orderBy("Id", "asc");
-  if (after) query.where("Timestamp", ">", new Date(after));
+  const cursor = Number(afterId);
+  if (Number.isInteger(cursor) && cursor > 0) query.where("Id", ">", cursor);
   return query;
 }