@automagik/omni 2.260907.3 → 2.260907.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -136390,7 +136390,7 @@ import { fileURLToPath } from "url";
136390
136390
  // package.json
136391
136391
  var package_default = {
136392
136392
  name: "@automagik/omni",
136393
- version: "2.260907.3",
136393
+ version: "2.260907.4",
136394
136394
  repository: {
136395
136395
  type: "git",
136396
136396
  url: "git+https://github.com/automagik-dev/omni.git",
@@ -250957,7 +250957,7 @@ var init_sentry_scrub = __esm(() => {
250957
250957
  var require_package7 = __commonJS((exports, module) => {
250958
250958
  module.exports = {
250959
250959
  name: "@omni/api",
250960
- version: "2.260907.3",
250960
+ version: "2.260907.4",
250961
250961
  private: true,
250962
250962
  type: "module",
250963
250963
  exports: {
@@ -389339,11 +389339,23 @@ class AscFlowClient {
389339
389339
  }
389340
389340
  return response;
389341
389341
  }
389342
- rawPost(path, payload, token) {
389343
- return this.rawRequest("POST", path, {}, payload, token);
389342
+ rawPost(path, payload, token, form = false) {
389343
+ return this.rawRequest("POST", path, {}, payload, token, form);
389344
389344
  }
389345
- async rawRequest(method, path, query, payload, token) {
389346
- const headers = { "content-type": "application/json" };
389345
+ async callForm(path, payload) {
389346
+ let response = await this.rawPost(path, payload, await this.getToken(), true);
389347
+ if (response.status === 401 && codErrorOf(response.body) === undefined) {
389348
+ response = await this.rawPost(path, payload, await this.getToken(true), true);
389349
+ }
389350
+ if (isPlatformOk(response))
389351
+ return response.body;
389352
+ const codError = codErrorOf(response.body);
389353
+ throw new AscFlowApiError(mapHttpStatusToAscFlowError(response.status, codError !== undefined), `ASC ${path} failed (HTTP ${response.status}${codError === undefined ? "" : `, cod_error ${codError}`})`, { httpStatus: response.status, operation: path, codError, raw: JSON.stringify(response.body).slice(0, 500) });
389354
+ }
389355
+ async rawRequest(method, path, query, payload, token, form = false) {
389356
+ const headers = {
389357
+ "content-type": form ? "application/x-www-form-urlencoded" : "application/json"
389358
+ };
389347
389359
  if (token)
389348
389360
  headers.authorization = `Bearer ${token}`;
389349
389361
  const url = new URL(`${this.baseUrl}${path}`);
@@ -389354,7 +389366,7 @@ class AscFlowClient {
389354
389366
  response = await fetch(url.toString(), {
389355
389367
  method,
389356
389368
  headers,
389357
- ...payload === undefined ? {} : { body: JSON.stringify(payload) },
389369
+ ...payload === undefined ? {} : { body: form ? formEncode(payload) : JSON.stringify(payload) },
389358
389370
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
389359
389371
  });
389360
389372
  } catch (err2) {
@@ -389370,6 +389382,21 @@ class AscFlowClient {
389370
389382
  return { status: response.status, body };
389371
389383
  }
389372
389384
  }
389385
+ function formEncode(payload) {
389386
+ const params = new URLSearchParams;
389387
+ const walk = (value, key) => {
389388
+ if (Array.isArray(value)) {
389389
+ value.forEach((item, i) => walk(item, `${key}[${i}]`));
389390
+ } else if (typeof value === "object" && value !== null) {
389391
+ for (const [k2, v2] of Object.entries(value))
389392
+ walk(v2, key ? `${key}[${k2}]` : k2);
389393
+ } else if (value !== undefined && value !== null) {
389394
+ params.set(key, String(value));
389395
+ }
389396
+ };
389397
+ walk(payload, "");
389398
+ return params.toString();
389399
+ }
389373
389400
 
389374
389401
  // ../channel-asc-flow/src/utils/emoji.ts
389375
389402
  var MARKER = /##([0-9a-f]{2,6}(?:-[0-9a-f]{2,6})*)##/gi;
@@ -389431,10 +389458,21 @@ function parseInboundTurn(body) {
389431
389458
  codAtendimento,
389432
389459
  text,
389433
389460
  fromFallback: !typed,
389461
+ entradaDefasada: Boolean(typed) && Boolean(fallback) && typed !== fallback,
389434
389462
  phone: firstString(body.phone, body.telefone),
389435
389463
  ...messageId ? { messageId } : {}
389436
389464
  };
389437
389465
  }
389466
+ async function isReenvioDoFlow(plugin2, instanceId, turn, logger5) {
389467
+ if (turn.entradaDefasada && plugin2.hasSeenCod(instanceId, turn.codAtendimento)) {
389468
+ logger5.info("[asc-flow] chatInput is behind message \u2014 the flow re-sent an old input", {
389469
+ instanceId,
389470
+ codAtendimento: turn.codAtendimento
389471
+ });
389472
+ return true;
389473
+ }
389474
+ return plugin2.isStaleFlowReplay(instanceId, turn);
389475
+ }
389438
389476
  async function handleAscFlowWebhookRequest(request, plugin2, instanceId, verifyToken, dedupeCache) {
389439
389477
  const logger5 = plugin2.getLogger();
389440
389478
  if (request.method !== "POST") {
@@ -389485,7 +389523,7 @@ async function handleAscFlowWebhookRequest(request, plugin2, instanceId, verifyT
389485
389523
  });
389486
389524
  return pending();
389487
389525
  }
389488
- if (await plugin2.isStaleFlowReplay(instanceId, turn)) {
389526
+ if (await isReenvioDoFlow(plugin2, instanceId, turn, logger5)) {
389489
389527
  return pending();
389490
389528
  }
389491
389529
  const isRedelivery = turn.messageId ? dedupeCache.isDuplicate(instanceId, turn.messageId, "asc-flow", logger5) : plugin2.isRedeliveryOfTurnInFlight(instanceId, turn);
@@ -389541,6 +389579,15 @@ function planHandoff(mode, meta, fallbackServico, logger5) {
389541
389579
  }
389542
389580
  return { transfer: { codServico, codPrioridade }, fields };
389543
389581
  }
389582
+ var IDENTITY_FIELDS = [
389583
+ "nome_beneficiario_vq",
389584
+ "cpf_vq",
389585
+ "carteirinha_vq",
389586
+ "vinculo_vq",
389587
+ "plano_vq",
389588
+ "filial_vq"
389589
+ ];
389590
+ var IDENTITY_MAX_LENGTH = 200;
389544
389591
  function buildGenesysFields(read, logger5, mode) {
389545
389592
  const fields = {};
389546
389593
  const rawFila = read("handoffQueue", "fila_vq");
@@ -389566,6 +389613,12 @@ function buildGenesysFields(read, logger5, mode) {
389566
389613
  if (motivo)
389567
389614
  fields.motivo_transf_vq = motivo;
389568
389615
  }
389616
+ for (const key of IDENTITY_FIELDS) {
389617
+ const raw = read(key);
389618
+ if (typeof raw !== "string")
389619
+ continue;
389620
+ fields[key] = raw.replace(/\s+/g, " ").trim().slice(0, IDENTITY_MAX_LENGTH);
389621
+ }
389569
389622
  return fields;
389570
389623
  }
389571
389624
 
@@ -389573,16 +389626,9 @@ function buildGenesysFields(read, logger5, mode) {
389573
389626
  init_src2();
389574
389627
  var MAX_OPTIONS = 10;
389575
389628
  var MAX_BODY_TEXT = 1024;
389576
- var MAX_BUTTONS = 3;
389577
- var BUTTON_TITLE_MAX = 20;
389578
- function shortenTitle(title) {
389579
- const value = title.trim();
389580
- if (value.length <= BUTTON_TITLE_MAX)
389581
- return value;
389582
- const head = value.slice(0, BUTTON_TITLE_MAX);
389583
- const boundary = head.lastIndexOf(" ");
389584
- return (boundary >= 8 ? head.slice(0, boundary) : head).trim();
389585
- }
389629
+ var TIPO_LISTA = 1;
389630
+ var TIPO_BOTOES = 2;
389631
+ var LIST_BUTTON_LABEL = "Op\xE7\xF5es";
389586
389632
  function foldTitle(value) {
389587
389633
  return value.normalize("NFD").replace(/\p{Mn}/gu, "").toLocaleLowerCase().split(/\s+/).filter(Boolean).join(" ");
389588
389634
  }
@@ -389595,37 +389641,50 @@ function titlesOf(interactive) {
389595
389641
  const sections = action?.sections ?? [];
389596
389642
  return (sections[0]?.rows ?? []).map((r) => r.title ?? "");
389597
389643
  }
389598
- function buildUra(body, buttons, listOptions = {}) {
389644
+ function splitBubbles(text) {
389645
+ return text.trim().split(/\n\s*\n/).map((b2) => b2.trim()).filter(Boolean);
389646
+ }
389647
+ function buildInteractive(body, buttons, listOptions = {}) {
389599
389648
  const trimmed = body.trim();
389600
389649
  if (!trimmed || trimmed.length > MAX_BODY_TEXT)
389601
389650
  return null;
389602
389651
  const replyButtons = (buttons ?? []).filter((b2) => !b2.url && b2.text?.trim());
389603
389652
  if (replyButtons.length === 0 || replyButtons.length > MAX_OPTIONS)
389604
389653
  return null;
389605
- const plan = planInteractive(trimmed, replyButtons, "Op\xE7\xF5es", listOptions);
389654
+ const plan = planInteractive(trimmed, replyButtons, LIST_BUTTON_LABEL, listOptions);
389606
389655
  if (!plan.interactive || plan.droppedRows > 0)
389607
389656
  return null;
389608
389657
  const type = plan.interactive.type;
389609
389658
  if (type !== "button" && type !== "list")
389610
389659
  return null;
389611
- let titles = titlesOf(plan.interactive);
389612
- let asButtons = type === "button";
389613
- if (!asButtons && replyButtons.length <= MAX_BUTTONS) {
389614
- titles = replyButtons.map((b2) => shortenTitle(b2.text ?? ""));
389615
- asButtons = true;
389616
- }
389660
+ const titles = titlesOf(plan.interactive);
389617
389661
  if (titles.length !== replyButtons.length || titles.some((t) => !t))
389618
389662
  return null;
389619
389663
  if (new Set(titles.map(foldTitle)).size !== titles.length)
389620
389664
  return null;
389665
+ if (type === "button") {
389666
+ return { tipo: TIPO_BOTOES, mensagem: trimmed, button: titles };
389667
+ }
389668
+ const action = plan.interactive.action;
389669
+ const sections = action?.sections ?? [];
389670
+ const rows = sections[0]?.rows ?? [];
389621
389671
  return {
389622
- ura_opcoes: Object.fromEntries(titles.map((title, i) => [String(i + 1), title])),
389623
- forcar_botoes: asButtons
389672
+ tipo: TIPO_LISTA,
389673
+ mensagem: trimmed,
389674
+ list: {
389675
+ texto_botao: action?.button || LIST_BUTTON_LABEL,
389676
+ secao: [
389677
+ {
389678
+ texto: sections[0]?.title ?? "",
389679
+ linhas: rows.map((row) => ({
389680
+ texto: row.title ?? "",
389681
+ descricao: row.description ?? ""
389682
+ }))
389683
+ }
389684
+ ]
389685
+ }
389624
389686
  };
389625
389687
  }
389626
- function splitBubbles(text) {
389627
- return text.trim().split(/\n\s*\n/).map((b2) => b2.trim()).filter(Boolean);
389628
- }
389629
389688
 
389630
389689
  // ../channel-asc-flow/src/utils/media.ts
389631
389690
  init_src2();
@@ -389936,6 +389995,19 @@ function normalizeBaseUrl(raw) {
389936
389995
  const trimmed = raw.trim().replace(/\/+$/, "");
389937
389996
  return trimmed.endsWith(REST_PREFIX) ? trimmed : `${trimmed}${REST_PREFIX}`;
389938
389997
  }
389998
+ function interactiveKindOf(params) {
389999
+ if (!params)
390000
+ return "text";
390001
+ return params.tipo === 2 ? "buttons" : "list";
390002
+ }
390003
+ function interactiveRowsOf(params) {
390004
+ if (!params)
390005
+ return 0;
390006
+ if (Array.isArray(params.button))
390007
+ return params.button.length;
390008
+ const list = params.list;
390009
+ return list?.secao?.[0]?.linhas?.length ?? 0;
390010
+ }
389939
390011
  function buildReadyBody(turn) {
389940
390012
  return {
389941
390013
  pronto: 1,
@@ -389944,8 +390016,7 @@ function buildReadyBody(turn) {
389944
390016
  bolhas: turn.bubbles,
389945
390017
  fila_vq: "",
389946
390018
  motivo_transf_vq: "",
389947
- ...turn.handoff ?? {},
389948
- ...turn.ura ?? {}
390019
+ ...turn.handoff ?? {}
389949
390020
  };
389950
390021
  }
389951
390022
 
@@ -389998,7 +390069,8 @@ class AscFlowPlugin extends BaseChannelPlugin {
389998
390069
  inFlight: new Map,
389999
390070
  lastSweepAt: Date.now(),
390000
390071
  seenCods: new Set,
390001
- lastAnswered: new Map
390072
+ lastAnswered: new Map,
390073
+ destinos: new Map
390002
390074
  });
390003
390075
  await this.updateInstanceStatus(instanceId, config2, {
390004
390076
  state: "connected",
@@ -390056,20 +390128,20 @@ class AscFlowPlugin extends BaseChannelPlugin {
390056
390128
  const text = resolveOutboundText(turnMessage, this.logger);
390057
390129
  const { rich, bubbles } = await this.prepareTurn(turnMessage, text);
390058
390130
  const lastBubble = bubbles[bubbles.length - 1] ?? "";
390059
- const ura = rich ? null : buildUra(lastBubble, content.buttons, listOptionsOf(content));
390131
+ const interativa = rich ? null : buildInteractive(lastBubble, content.buttons, listOptionsOf(content));
390060
390132
  const { delivered } = await this.deliver(state, cod, {
390061
390133
  text,
390062
390134
  bubbles,
390063
390135
  lastBubble,
390064
390136
  rich,
390065
- ura,
390137
+ interativa,
390066
390138
  message: turnMessage
390067
390139
  });
390068
390140
  if (!delivered && !polling)
390069
390141
  return this.refuseUndeliverable(instanceId, to, content.type);
390070
390142
  const handoff = meta.isHandoff === true ? await this.runHandoff(state, instanceId, cod, meta, delivered ? "" : lastBubble) : null;
390071
390143
  const handoffRefused = meta.isHandoff === true && handoff === null;
390072
- this.resolveTurn(state, to, buildReadyBody({ delivered, lastBubble, bubbles, handoff, ura }), answering, correlationId);
390144
+ this.resolveTurn(state, to, buildReadyBody({ delivered, lastBubble, bubbles, handoff }), answering, correlationId);
390073
390145
  if (handoffRefused)
390074
390146
  return this.refuseSend(instanceId, to, HANDOFF_REFUSED_ERROR);
390075
390147
  if (correlationId)
@@ -390078,7 +390150,7 @@ class AscFlowPlugin extends BaseChannelPlugin {
390078
390150
  await this.emitTurnSent(instanceId, turnMessage, messageId, {
390079
390151
  cod,
390080
390152
  bubbles: bubbles.length,
390081
- ura,
390153
+ interativa,
390082
390154
  delivered,
390083
390155
  handoff: handoff !== null
390084
390156
  });
@@ -390111,8 +390183,8 @@ class AscFlowPlugin extends BaseChannelPlugin {
390111
390183
  ascFlow: {
390112
390184
  codAtendimento: turn.cod,
390113
390185
  bubbles: turn.bubbles,
390114
- interactive: turn.ura ? turn.ura.forcar_botoes ? "buttons" : "list" : "text",
390115
- uraOptions: turn.ura ? Object.keys(turn.ura.ura_opcoes).length : 0,
390186
+ interactive: interactiveKindOf(turn.interativa),
390187
+ interactiveRows: interactiveRowsOf(turn.interativa),
390116
390188
  viaMensagem: turn.delivered,
390117
390189
  handoff: turn.handoff
390118
390190
  }
@@ -390226,15 +390298,56 @@ class AscFlowPlugin extends BaseChannelPlugin {
390226
390298
  const texto = turn.bubbles.length > 0 ? turn.bubbles : [OUTBOUND_MEDIA_FALLBACK_TEXT];
390227
390299
  return { delivered: await this.pushBubbles(state, cod, texto) > 0 };
390228
390300
  }
390229
- if (turn.ura) {
390301
+ if (turn.interativa) {
390230
390302
  await this.pushBubbles(state, cod, turn.bubbles.slice(0, -1));
390231
- if (await this.sendMensagem(state, cod, turn.lastBubble, { ...turn.ura, ...reply }))
390303
+ if (await this.sendInterativa(state, cod, turn.interativa))
390232
390304
  return { delivered: true };
390233
390305
  return { delivered: await this.pushBubbles(state, cod, [turn.lastBubble]) > 0 };
390234
390306
  }
390235
390307
  const pushed = await this.pushBubbles(state, cod, turn.bubbles);
390236
390308
  return { delivered: pushed > 0 };
390237
390309
  }
390310
+ async sendInterativa(state, cod, params) {
390311
+ try {
390312
+ const destino = await this.resolveDestino(state, cod);
390313
+ if (!destino)
390314
+ return false;
390315
+ await state.client.callForm("/sendMsgInterativaAvancado", {
390316
+ cod_conta: destino.codConta,
390317
+ tipo_envio: 1,
390318
+ bol_incluir_atual: 1,
390319
+ contato: { telefone: destino.telefone },
390320
+ msg_interativa_parametros: params
390321
+ });
390322
+ return true;
390323
+ } catch (err2) {
390324
+ this.logger.warn("[asc-flow] interactive message refused \u2014 degrading to the numbered text", {
390325
+ cod,
390326
+ err: String(err2)
390327
+ });
390328
+ return false;
390329
+ }
390330
+ }
390331
+ async resolveDestino(state, cod) {
390332
+ const chave = String(cod);
390333
+ const cacheado = state.destinos.get(chave);
390334
+ if (cacheado)
390335
+ return cacheado;
390336
+ const { status, body } = await state.client.get("/atendimento", { codigo_atendimento: cod });
390337
+ if (status !== 200 || typeof body !== "object" || body === null)
390338
+ return null;
390339
+ const dados = body;
390340
+ const contato = dados.contato ?? {};
390341
+ const codConta = String(dados.id_conta ?? "").trim();
390342
+ const telefone = String(contato.telefone ?? "").trim();
390343
+ if (!codConta || !telefone) {
390344
+ this.logger.warn("[asc-flow] atendimento carries no account/phone \u2014 no interactive message for it", { cod });
390345
+ return null;
390346
+ }
390347
+ const destino = { codConta, telefone };
390348
+ state.destinos.set(chave, destino);
390349
+ return destino;
390350
+ }
390238
390351
  async sendMensagem(state, cod, text, extra) {
390239
390352
  try {
390240
390353
  await state.client.call("/mensagem", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260907.3",
3
+ "version": "2.260907.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/automagik-dev/omni.git",