Bladeren bron

acrescentado transcricao

GabrielRamison 2 maanden geleden
bovenliggende
commit
71247ce3ce

+ 16 - 0
db/migrations/20260709160000_add_transcricao_atendimento_mensagens.cjs

@@ -0,0 +1,16 @@
+exports.up = async function (knex) {
+  await knex.schema.alterTable("atendimento_mensagens", (t) => {
+    t.text("Transcricao").nullable();
+  });
+  // busca fulltext do chat passa a cobrir também as transcrições de áudio
+  await knex.schema.raw("ALTER TABLE atendimento_mensagens DROP INDEX ft_body");
+  await knex.schema.raw("ALTER TABLE atendimento_mensagens ADD FULLTEXT INDEX ft_body_transcricao (Body, Transcricao)");
+};
+
+exports.down = async function (knex) {
+  await knex.schema.raw("ALTER TABLE atendimento_mensagens DROP INDEX ft_body_transcricao");
+  await knex.schema.raw("ALTER TABLE atendimento_mensagens ADD FULLTEXT INDEX ft_body (Body)");
+  await knex.schema.alterTable("atendimento_mensagens", (t) => {
+    t.dropColumn("Transcricao");
+  });
+};

+ 5 - 1
scripts/exportarDatasetTreinamento.js

@@ -64,8 +64,12 @@ function scrubTelefones(text) {
 }
 
 function mensagemTexto(m) {
+  const transcricao = typeof m.Transcricao === "string" ? m.Transcricao.trim() : "";
   let texto;
-  if (m.Resposta === 1) {
+  if (transcricao) {
+    // áudio transcrito pelo ifbot: o Body é só o nome do arquivo
+    texto = `[áudio] ${transcricao}`;
+  } else if (m.Resposta === 1) {
     texto = parseAtendenteBody(m.Body).texto;
   } else {
     texto = stripHtml(m.Body);

+ 2 - 1
src/middleware/schemas/Atendimento.Schema.js

@@ -24,7 +24,8 @@ const mensagemSchema = z.object({
   Tipodemidia: z.string().nullish(),
   Midia: z.string().nullish(),
   Autor: z.string().nullish(),
-  Citacao: z.string().nullish()
+  Citacao: z.string().nullish(),
+  Transcricao: z.string().nullish()
 }).passthrough();
 
 export const atendimentoPayloadSchema = z.object({

+ 3 - 3
src/services/atendimentosQueryService.js

@@ -112,8 +112,8 @@ async function buscarAtendimentosPorTexto({ termos, setor, status, limit = BUSCA
 
   let ranqueados = await AtendimentoMensagem.query()
     .select("AtendimentoId")
-    .select(AtendimentoMensagem.knex().raw("MAX(MATCH(Body) AGAINST(? IN NATURAL LANGUAGE MODE)) as relevancia", [termo]))
-    .whereRaw("MATCH(Body) AGAINST(? IN NATURAL LANGUAGE MODE)", [termo])
+    .select(AtendimentoMensagem.knex().raw("MAX(MATCH(Body, Transcricao) AGAINST(? IN NATURAL LANGUAGE MODE)) as relevancia", [termo]))
+    .whereRaw("MATCH(Body, Transcricao) AGAINST(? IN NATURAL LANGUAGE MODE)", [termo])
     .groupBy("AtendimentoId")
     .orderBy("relevancia", "desc")
     .limit(limit);
@@ -126,7 +126,7 @@ async function buscarAtendimentosPorTexto({ termos, setor, status, limit = BUSCA
     ranqueados = (
       await AtendimentoMensagem.query()
         .select("AtendimentoId")
-        .whereRaw("Body LIKE ?", [`%${termo}%`])
+        .whereRaw("(Body LIKE ? OR Transcricao LIKE ?)", [`%${termo}%`, `%${termo}%`])
         .groupBy("AtendimentoId")
         .limit(limit)
     ).map((r) => ({ AtendimentoId: r.AtendimentoId }));

+ 3 - 2
src/services/atendimentosService.js

@@ -140,7 +140,8 @@ export async function salvarAtendimento({ payload, setor, sourceUrl, abertura, t
       Tipodemidia: m.Tipodemidia ?? null,
       Midia: m.Midia ?? null,
       Autor: m.Autor ?? null,
-      Citacao: m.Citacao ?? null
+      Citacao: m.Citacao ?? null,
+      Transcricao: m.Transcricao ?? null
     }));
 
     // batch insert via knex: o Objection não suporta insert em lote no MySQL
@@ -148,7 +149,7 @@ export async function salvarAtendimento({ payload, setor, sourceUrl, abertura, t
       await trx(AtendimentoMensagem.tableName)
         .insert(mensagens.slice(i, i + MENSAGENS_BATCH))
         .onConflict("Id")
-        .merge(["AtendimentoId", "Body", "Resposta", "Timestamp", "Tipodemidia", "Midia", "Autor", "Citacao"]);
+        .merge(["AtendimentoId", "Body", "Resposta", "Timestamp", "Tipodemidia", "Midia", "Autor", "Citacao", "Transcricao"]);
     }
 
     await trx.commit();

+ 7 - 1
src/utils/atendimentoFormat.js

@@ -51,6 +51,8 @@ export function formatMensagem(m, clienteNome) {
   let label;
   let texto;
 
+  const transcricao = typeof m.Transcricao === "string" ? m.Transcricao.trim() : "";
+
   if (m.Resposta === 1) {
     const parsed = parseAtendenteBody(m.Body);
     label = parsed.nome ? `Atendente (${parsed.nome})` : "Atendente";
@@ -66,7 +68,11 @@ export function formatMensagem(m, clienteNome) {
     texto = stripHtml(m.Body);
   }
 
-  if (!texto && m.Tipodemidia && m.Tipodemidia !== "text") {
+  // áudios chegam com o Body sendo só o nome do arquivo; quando o ifbot manda
+  // a transcrição, ela substitui esse texto sem conteúdo
+  if (transcricao) {
+    texto = `[áudio] ${transcricao}`;
+  } else if (!texto && m.Tipodemidia && m.Tipodemidia !== "text") {
     texto = `[mídia: ${m.Tipodemidia}]`;
   }
   if (!texto) return null;