@automagik/omni 2.260907.3 → 2.260907.5

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
@@ -11260,6 +11260,8 @@ var init_types3 = __esm(() => {
11260
11260
  "message.delivered",
11261
11261
  "message.read",
11262
11262
  "message.failed",
11263
+ "message.pinned",
11264
+ "message.unpinned",
11263
11265
  "message.button_click",
11264
11266
  "message.poll",
11265
11267
  "message.poll_vote",
@@ -136390,7 +136392,7 @@ import { fileURLToPath } from "url";
136390
136392
  // package.json
136391
136393
  var package_default = {
136392
136394
  name: "@automagik/omni",
136393
- version: "2.260907.3",
136395
+ version: "2.260907.5",
136394
136396
  repository: {
136395
136397
  type: "git",
136396
136398
  url: "git+https://github.com/automagik-dev/omni.git",
@@ -139,6 +139,8 @@ var init_types2 = __esm(() => {
139
139
  "message.delivered",
140
140
  "message.read",
141
141
  "message.failed",
142
+ "message.pinned",
143
+ "message.unpinned",
142
144
  "message.button_click",
143
145
  "message.poll",
144
146
  "message.poll_vote",
@@ -44631,6 +44633,14 @@ class BaseChannelPlugin {
44631
44633
  const { instanceId, ...payload } = params;
44632
44634
  await this.publishEventInternal("reaction.removed", payload, instanceId);
44633
44635
  }
44636
+ async emitMessagePinned(params) {
44637
+ const { instanceId, ...payload } = params;
44638
+ await this.publishEventInternal("message.pinned", payload, instanceId);
44639
+ }
44640
+ async emitMessageUnpinned(params) {
44641
+ const { instanceId, ...payload } = params;
44642
+ await this.publishEventInternal("message.unpinned", payload, instanceId);
44643
+ }
44634
44644
  async emitButtonClick(params) {
44635
44645
  const { instanceId, ...payload } = params;
44636
44646
  await this.publishEventInternal("message.button_click", payload, instanceId);
@@ -250957,7 +250967,7 @@ var init_sentry_scrub = __esm(() => {
250957
250967
  var require_package7 = __commonJS((exports, module) => {
250958
250968
  module.exports = {
250959
250969
  name: "@omni/api",
250960
- version: "2.260907.3",
250970
+ version: "2.260907.5",
250961
250971
  private: true,
250962
250972
  type: "module",
250963
250973
  exports: {
@@ -352230,6 +352240,23 @@ async function linkReplyTarget(services, chatId, messageId, replyToExternalId) {
352230
352240
  });
352231
352241
  }
352232
352242
  }
352243
+ async function linkThreadRoot(services, chatId, messageId, messageExternalId, threadExternalId, repliedAt) {
352244
+ if (!threadExternalId || threadExternalId === messageExternalId)
352245
+ return;
352246
+ try {
352247
+ const rootMessageId = await services.messages.resolveReplyToMessage(chatId, threadExternalId);
352248
+ if (!rootMessageId)
352249
+ return;
352250
+ await services.messages.setThreadRoot(messageId, rootMessageId);
352251
+ await services.messages.recordThreadReply(rootMessageId, repliedAt);
352252
+ } catch (error3) {
352253
+ log90.debug("Could not link thread root", {
352254
+ chatId,
352255
+ threadExternalId,
352256
+ error: error3 instanceof Error ? error3.message : String(error3)
352257
+ });
352258
+ }
352259
+ }
352233
352260
  async function handleMessageReceived(services, payload, metadata, eventTimestamp, trustedTenantId, identity) {
352234
352261
  const channel5 = metadata.channelType ?? "whatsapp";
352235
352262
  const isHistorySync = metadata.ingestMode === "history-sync";
@@ -352282,6 +352309,7 @@ async function handleMessageReceived(services, payload, metadata, eventTimestamp
352282
352309
  if (created) {
352283
352310
  log90.debug("Created message", { externalId: payload.externalId, chatId: chat2.id });
352284
352311
  await linkReplyTarget(services, chat2.id, message2.id, payload.replyToId);
352312
+ await linkThreadRoot(services, chat2.id, message2.id, messageExternalId, truncate4(payload.threadId, 255), platformTimestamp ?? new Date(eventTimestamp));
352285
352313
  }
352286
352314
  await maybeRecordParticipantActivity(services, chat2.id, payload.from);
352287
352315
  maybeUpdateRecency(services, metadata.instanceId, chat2.id, rawPayload, payload, platformTimestamp ?? new Date(eventTimestamp), trustedTenantId);
@@ -352306,6 +352334,29 @@ function logMessageReceivedError(payload, error3) {
352306
352334
  longFields: Object.keys(longFields).length > 0 ? longFields : undefined
352307
352335
  });
352308
352336
  }
352337
+ async function handleMessagePinState(services, event, pinned) {
352338
+ const payload = event.payload;
352339
+ const metadata = event.metadata;
352340
+ if (!metadata.instanceId)
352341
+ return;
352342
+ const instanceId = metadata.instanceId;
352343
+ try {
352344
+ await runConsumerInTenantContext(services.db, event, async () => {
352345
+ const chat2 = await services.chats.findByExternalIdSmart(instanceId, payload.chatId);
352346
+ if (!chat2) {
352347
+ log90.debug("Chat not found for pin update", { chatId: payload.chatId });
352348
+ return;
352349
+ }
352350
+ await services.messages.setPinned(chat2.id, payload.messageId, pinned, payload.from);
352351
+ log90.debug("Updated message pin state", { chatId: chat2.id, messageId: payload.messageId, pinned });
352352
+ });
352353
+ } catch (error3) {
352354
+ log90.error("Failed to update pin state", {
352355
+ messageId: payload.messageId,
352356
+ error: String(error3)
352357
+ });
352358
+ }
352359
+ }
352309
352360
  async function setupMessagePersistence(eventBus, services) {
352310
352361
  try {
352311
352362
  await eventBus.subscribe("message.received", async (event) => {
@@ -352382,6 +352433,9 @@ async function setupMessagePersistence(eventBus, services) {
352382
352433
  replyToExternalId: truncate4(payload.replyToId, 255),
352383
352434
  threadExternalId: truncate4(payload.threadId, 255)
352384
352435
  });
352436
+ if (created2) {
352437
+ await linkThreadRoot(services, chat3.id, message3.id, messageExternalId, truncate4(payload.threadId, 255), new Date(event.timestamp));
352438
+ }
352385
352439
  return { chat: chat3, message: message3, created: created2 };
352386
352440
  });
352387
352441
  if (created) {
@@ -352484,6 +352538,22 @@ async function setupMessagePersistence(eventBus, services) {
352484
352538
  startFrom: "first",
352485
352539
  concurrency: 10
352486
352540
  });
352541
+ await eventBus.subscribe("message.pinned", (event) => handleMessagePinState(services, event, true), {
352542
+ durable: "message-persistence-pinned",
352543
+ queue: "message-persistence",
352544
+ maxRetries: 2,
352545
+ retryDelayMs: 500,
352546
+ startFrom: "first",
352547
+ concurrency: 10
352548
+ });
352549
+ await eventBus.subscribe("message.unpinned", (event) => handleMessagePinState(services, event, false), {
352550
+ durable: "message-persistence-unpinned",
352551
+ queue: "message-persistence",
352552
+ maxRetries: 2,
352553
+ retryDelayMs: 500,
352554
+ startFrom: "first",
352555
+ concurrency: 10
352556
+ });
352487
352557
  await eventBus.subscribe("instance.connected", async (event) => {
352488
352558
  const payload = event.payload;
352489
352559
  const instanceId = payload.instanceId;
@@ -360570,6 +360640,16 @@ class MessageService {
360570
360640
  async updateReplyToReference(id, replyToMessageId) {
360571
360641
  await this.db.update(messages2).set({ replyToMessageId, updatedAt: new Date }).where(eq(messages2.id, id));
360572
360642
  }
360643
+ async setThreadRoot(id, threadRootMessageId) {
360644
+ await this.db.update(messages2).set({ threadRootMessageId, updatedAt: new Date }).where(eq(messages2.id, id));
360645
+ }
360646
+ async recordThreadReply(rootMessageId, repliedAt) {
360647
+ await this.db.update(messages2).set({
360648
+ replyCount: sql`${messages2.replyCount} + 1`,
360649
+ latestReplyAt: sql`GREATEST(COALESCE(${messages2.latestReplyAt}, ${repliedAt}), ${repliedAt})`,
360650
+ updatedAt: new Date
360651
+ }).where(eq(messages2.id, rootMessageId));
360652
+ }
360573
360653
  }
360574
360654
  var MEDIA_BADGES2;
360575
360655
  var init_messages3 = __esm(() => {
@@ -389339,11 +389419,23 @@ class AscFlowClient {
389339
389419
  }
389340
389420
  return response;
389341
389421
  }
389342
- rawPost(path, payload, token) {
389343
- return this.rawRequest("POST", path, {}, payload, token);
389422
+ rawPost(path, payload, token, form = false) {
389423
+ return this.rawRequest("POST", path, {}, payload, token, form);
389344
389424
  }
389345
- async rawRequest(method, path, query, payload, token) {
389346
- const headers = { "content-type": "application/json" };
389425
+ async callForm(path, payload) {
389426
+ let response = await this.rawPost(path, payload, await this.getToken(), true);
389427
+ if (response.status === 401 && codErrorOf(response.body) === undefined) {
389428
+ response = await this.rawPost(path, payload, await this.getToken(true), true);
389429
+ }
389430
+ if (isPlatformOk(response))
389431
+ return response.body;
389432
+ const codError = codErrorOf(response.body);
389433
+ 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) });
389434
+ }
389435
+ async rawRequest(method, path, query, payload, token, form = false) {
389436
+ const headers = {
389437
+ "content-type": form ? "application/x-www-form-urlencoded" : "application/json"
389438
+ };
389347
389439
  if (token)
389348
389440
  headers.authorization = `Bearer ${token}`;
389349
389441
  const url = new URL(`${this.baseUrl}${path}`);
@@ -389354,7 +389446,7 @@ class AscFlowClient {
389354
389446
  response = await fetch(url.toString(), {
389355
389447
  method,
389356
389448
  headers,
389357
- ...payload === undefined ? {} : { body: JSON.stringify(payload) },
389449
+ ...payload === undefined ? {} : { body: form ? formEncode(payload) : JSON.stringify(payload) },
389358
389450
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
389359
389451
  });
389360
389452
  } catch (err2) {
@@ -389370,6 +389462,21 @@ class AscFlowClient {
389370
389462
  return { status: response.status, body };
389371
389463
  }
389372
389464
  }
389465
+ function formEncode(payload) {
389466
+ const params = new URLSearchParams;
389467
+ const walk = (value, key) => {
389468
+ if (Array.isArray(value)) {
389469
+ value.forEach((item, i) => walk(item, `${key}[${i}]`));
389470
+ } else if (typeof value === "object" && value !== null) {
389471
+ for (const [k2, v2] of Object.entries(value))
389472
+ walk(v2, key ? `${key}[${k2}]` : k2);
389473
+ } else if (value !== undefined && value !== null) {
389474
+ params.set(key, String(value));
389475
+ }
389476
+ };
389477
+ walk(payload, "");
389478
+ return params.toString();
389479
+ }
389373
389480
 
389374
389481
  // ../channel-asc-flow/src/utils/emoji.ts
389375
389482
  var MARKER = /##([0-9a-f]{2,6}(?:-[0-9a-f]{2,6})*)##/gi;
@@ -389431,10 +389538,21 @@ function parseInboundTurn(body) {
389431
389538
  codAtendimento,
389432
389539
  text,
389433
389540
  fromFallback: !typed,
389541
+ entradaDefasada: Boolean(typed) && Boolean(fallback) && typed !== fallback,
389434
389542
  phone: firstString(body.phone, body.telefone),
389435
389543
  ...messageId ? { messageId } : {}
389436
389544
  };
389437
389545
  }
389546
+ async function isReenvioDoFlow(plugin2, instanceId, turn, logger5) {
389547
+ if (turn.entradaDefasada && plugin2.hasSeenCod(instanceId, turn.codAtendimento)) {
389548
+ logger5.info("[asc-flow] chatInput is behind message \u2014 the flow re-sent an old input", {
389549
+ instanceId,
389550
+ codAtendimento: turn.codAtendimento
389551
+ });
389552
+ return true;
389553
+ }
389554
+ return plugin2.isStaleFlowReplay(instanceId, turn);
389555
+ }
389438
389556
  async function handleAscFlowWebhookRequest(request, plugin2, instanceId, verifyToken, dedupeCache) {
389439
389557
  const logger5 = plugin2.getLogger();
389440
389558
  if (request.method !== "POST") {
@@ -389485,7 +389603,7 @@ async function handleAscFlowWebhookRequest(request, plugin2, instanceId, verifyT
389485
389603
  });
389486
389604
  return pending();
389487
389605
  }
389488
- if (await plugin2.isStaleFlowReplay(instanceId, turn)) {
389606
+ if (await isReenvioDoFlow(plugin2, instanceId, turn, logger5)) {
389489
389607
  return pending();
389490
389608
  }
389491
389609
  const isRedelivery = turn.messageId ? dedupeCache.isDuplicate(instanceId, turn.messageId, "asc-flow", logger5) : plugin2.isRedeliveryOfTurnInFlight(instanceId, turn);
@@ -389541,6 +389659,15 @@ function planHandoff(mode, meta, fallbackServico, logger5) {
389541
389659
  }
389542
389660
  return { transfer: { codServico, codPrioridade }, fields };
389543
389661
  }
389662
+ var IDENTITY_FIELDS = [
389663
+ "nome_beneficiario_vq",
389664
+ "cpf_vq",
389665
+ "carteirinha_vq",
389666
+ "vinculo_vq",
389667
+ "plano_vq",
389668
+ "filial_vq"
389669
+ ];
389670
+ var IDENTITY_MAX_LENGTH = 200;
389544
389671
  function buildGenesysFields(read, logger5, mode) {
389545
389672
  const fields = {};
389546
389673
  const rawFila = read("handoffQueue", "fila_vq");
@@ -389566,6 +389693,12 @@ function buildGenesysFields(read, logger5, mode) {
389566
389693
  if (motivo)
389567
389694
  fields.motivo_transf_vq = motivo;
389568
389695
  }
389696
+ for (const key of IDENTITY_FIELDS) {
389697
+ const raw = read(key);
389698
+ if (typeof raw !== "string")
389699
+ continue;
389700
+ fields[key] = raw.replace(/\s+/g, " ").trim().slice(0, IDENTITY_MAX_LENGTH);
389701
+ }
389569
389702
  return fields;
389570
389703
  }
389571
389704
 
@@ -389573,16 +389706,9 @@ function buildGenesysFields(read, logger5, mode) {
389573
389706
  init_src2();
389574
389707
  var MAX_OPTIONS = 10;
389575
389708
  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
- }
389709
+ var TIPO_LISTA = 1;
389710
+ var TIPO_BOTOES = 2;
389711
+ var LIST_BUTTON_LABEL = "Op\xE7\xF5es";
389586
389712
  function foldTitle(value) {
389587
389713
  return value.normalize("NFD").replace(/\p{Mn}/gu, "").toLocaleLowerCase().split(/\s+/).filter(Boolean).join(" ");
389588
389714
  }
@@ -389595,37 +389721,50 @@ function titlesOf(interactive) {
389595
389721
  const sections = action?.sections ?? [];
389596
389722
  return (sections[0]?.rows ?? []).map((r) => r.title ?? "");
389597
389723
  }
389598
- function buildUra(body, buttons, listOptions = {}) {
389724
+ function splitBubbles(text) {
389725
+ return text.trim().split(/\n\s*\n/).map((b2) => b2.trim()).filter(Boolean);
389726
+ }
389727
+ function buildInteractive(body, buttons, listOptions = {}) {
389599
389728
  const trimmed = body.trim();
389600
389729
  if (!trimmed || trimmed.length > MAX_BODY_TEXT)
389601
389730
  return null;
389602
389731
  const replyButtons = (buttons ?? []).filter((b2) => !b2.url && b2.text?.trim());
389603
389732
  if (replyButtons.length === 0 || replyButtons.length > MAX_OPTIONS)
389604
389733
  return null;
389605
- const plan = planInteractive(trimmed, replyButtons, "Op\xE7\xF5es", listOptions);
389734
+ const plan = planInteractive(trimmed, replyButtons, LIST_BUTTON_LABEL, listOptions);
389606
389735
  if (!plan.interactive || plan.droppedRows > 0)
389607
389736
  return null;
389608
389737
  const type = plan.interactive.type;
389609
389738
  if (type !== "button" && type !== "list")
389610
389739
  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
- }
389740
+ const titles = titlesOf(plan.interactive);
389617
389741
  if (titles.length !== replyButtons.length || titles.some((t) => !t))
389618
389742
  return null;
389619
389743
  if (new Set(titles.map(foldTitle)).size !== titles.length)
389620
389744
  return null;
389745
+ if (type === "button") {
389746
+ return { tipo: TIPO_BOTOES, mensagem: trimmed, button: titles };
389747
+ }
389748
+ const action = plan.interactive.action;
389749
+ const sections = action?.sections ?? [];
389750
+ const rows = sections[0]?.rows ?? [];
389621
389751
  return {
389622
- ura_opcoes: Object.fromEntries(titles.map((title, i) => [String(i + 1), title])),
389623
- forcar_botoes: asButtons
389752
+ tipo: TIPO_LISTA,
389753
+ mensagem: trimmed,
389754
+ list: {
389755
+ texto_botao: action?.button || LIST_BUTTON_LABEL,
389756
+ secao: [
389757
+ {
389758
+ texto: sections[0]?.title ?? "",
389759
+ linhas: rows.map((row) => ({
389760
+ texto: row.title ?? "",
389761
+ descricao: row.description ?? ""
389762
+ }))
389763
+ }
389764
+ ]
389765
+ }
389624
389766
  };
389625
389767
  }
389626
- function splitBubbles(text) {
389627
- return text.trim().split(/\n\s*\n/).map((b2) => b2.trim()).filter(Boolean);
389628
- }
389629
389768
 
389630
389769
  // ../channel-asc-flow/src/utils/media.ts
389631
389770
  init_src2();
@@ -389936,6 +390075,19 @@ function normalizeBaseUrl(raw) {
389936
390075
  const trimmed = raw.trim().replace(/\/+$/, "");
389937
390076
  return trimmed.endsWith(REST_PREFIX) ? trimmed : `${trimmed}${REST_PREFIX}`;
389938
390077
  }
390078
+ function interactiveKindOf(params) {
390079
+ if (!params)
390080
+ return "text";
390081
+ return params.tipo === 2 ? "buttons" : "list";
390082
+ }
390083
+ function interactiveRowsOf(params) {
390084
+ if (!params)
390085
+ return 0;
390086
+ if (Array.isArray(params.button))
390087
+ return params.button.length;
390088
+ const list = params.list;
390089
+ return list?.secao?.[0]?.linhas?.length ?? 0;
390090
+ }
389939
390091
  function buildReadyBody(turn) {
389940
390092
  return {
389941
390093
  pronto: 1,
@@ -389944,8 +390096,7 @@ function buildReadyBody(turn) {
389944
390096
  bolhas: turn.bubbles,
389945
390097
  fila_vq: "",
389946
390098
  motivo_transf_vq: "",
389947
- ...turn.handoff ?? {},
389948
- ...turn.ura ?? {}
390099
+ ...turn.handoff ?? {}
389949
390100
  };
389950
390101
  }
389951
390102
 
@@ -389998,7 +390149,8 @@ class AscFlowPlugin extends BaseChannelPlugin {
389998
390149
  inFlight: new Map,
389999
390150
  lastSweepAt: Date.now(),
390000
390151
  seenCods: new Set,
390001
- lastAnswered: new Map
390152
+ lastAnswered: new Map,
390153
+ destinos: new Map
390002
390154
  });
390003
390155
  await this.updateInstanceStatus(instanceId, config2, {
390004
390156
  state: "connected",
@@ -390056,20 +390208,20 @@ class AscFlowPlugin extends BaseChannelPlugin {
390056
390208
  const text = resolveOutboundText(turnMessage, this.logger);
390057
390209
  const { rich, bubbles } = await this.prepareTurn(turnMessage, text);
390058
390210
  const lastBubble = bubbles[bubbles.length - 1] ?? "";
390059
- const ura = rich ? null : buildUra(lastBubble, content.buttons, listOptionsOf(content));
390211
+ const interativa = rich ? null : buildInteractive(lastBubble, content.buttons, listOptionsOf(content));
390060
390212
  const { delivered } = await this.deliver(state, cod, {
390061
390213
  text,
390062
390214
  bubbles,
390063
390215
  lastBubble,
390064
390216
  rich,
390065
- ura,
390217
+ interativa,
390066
390218
  message: turnMessage
390067
390219
  });
390068
390220
  if (!delivered && !polling)
390069
390221
  return this.refuseUndeliverable(instanceId, to, content.type);
390070
390222
  const handoff = meta.isHandoff === true ? await this.runHandoff(state, instanceId, cod, meta, delivered ? "" : lastBubble) : null;
390071
390223
  const handoffRefused = meta.isHandoff === true && handoff === null;
390072
- this.resolveTurn(state, to, buildReadyBody({ delivered, lastBubble, bubbles, handoff, ura }), answering, correlationId);
390224
+ this.resolveTurn(state, to, buildReadyBody({ delivered, lastBubble, bubbles, handoff }), answering, correlationId);
390073
390225
  if (handoffRefused)
390074
390226
  return this.refuseSend(instanceId, to, HANDOFF_REFUSED_ERROR);
390075
390227
  if (correlationId)
@@ -390078,7 +390230,7 @@ class AscFlowPlugin extends BaseChannelPlugin {
390078
390230
  await this.emitTurnSent(instanceId, turnMessage, messageId, {
390079
390231
  cod,
390080
390232
  bubbles: bubbles.length,
390081
- ura,
390233
+ interativa,
390082
390234
  delivered,
390083
390235
  handoff: handoff !== null
390084
390236
  });
@@ -390111,8 +390263,8 @@ class AscFlowPlugin extends BaseChannelPlugin {
390111
390263
  ascFlow: {
390112
390264
  codAtendimento: turn.cod,
390113
390265
  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,
390266
+ interactive: interactiveKindOf(turn.interativa),
390267
+ interactiveRows: interactiveRowsOf(turn.interativa),
390116
390268
  viaMensagem: turn.delivered,
390117
390269
  handoff: turn.handoff
390118
390270
  }
@@ -390226,15 +390378,56 @@ class AscFlowPlugin extends BaseChannelPlugin {
390226
390378
  const texto = turn.bubbles.length > 0 ? turn.bubbles : [OUTBOUND_MEDIA_FALLBACK_TEXT];
390227
390379
  return { delivered: await this.pushBubbles(state, cod, texto) > 0 };
390228
390380
  }
390229
- if (turn.ura) {
390381
+ if (turn.interativa) {
390230
390382
  await this.pushBubbles(state, cod, turn.bubbles.slice(0, -1));
390231
- if (await this.sendMensagem(state, cod, turn.lastBubble, { ...turn.ura, ...reply }))
390383
+ if (await this.sendInterativa(state, cod, turn.interativa))
390232
390384
  return { delivered: true };
390233
390385
  return { delivered: await this.pushBubbles(state, cod, [turn.lastBubble]) > 0 };
390234
390386
  }
390235
390387
  const pushed = await this.pushBubbles(state, cod, turn.bubbles);
390236
390388
  return { delivered: pushed > 0 };
390237
390389
  }
390390
+ async sendInterativa(state, cod, params) {
390391
+ try {
390392
+ const destino = await this.resolveDestino(state, cod);
390393
+ if (!destino)
390394
+ return false;
390395
+ await state.client.callForm("/sendMsgInterativaAvancado", {
390396
+ cod_conta: destino.codConta,
390397
+ tipo_envio: 1,
390398
+ bol_incluir_atual: 1,
390399
+ contato: { telefone: destino.telefone },
390400
+ msg_interativa_parametros: params
390401
+ });
390402
+ return true;
390403
+ } catch (err2) {
390404
+ this.logger.warn("[asc-flow] interactive message refused \u2014 degrading to the numbered text", {
390405
+ cod,
390406
+ err: String(err2)
390407
+ });
390408
+ return false;
390409
+ }
390410
+ }
390411
+ async resolveDestino(state, cod) {
390412
+ const chave = String(cod);
390413
+ const cacheado = state.destinos.get(chave);
390414
+ if (cacheado)
390415
+ return cacheado;
390416
+ const { status, body } = await state.client.get("/atendimento", { codigo_atendimento: cod });
390417
+ if (status !== 200 || typeof body !== "object" || body === null)
390418
+ return null;
390419
+ const dados = body;
390420
+ const contato = dados.contato ?? {};
390421
+ const codConta = String(dados.id_conta ?? "").trim();
390422
+ const telefone = String(contato.telefone ?? "").trim();
390423
+ if (!codConta || !telefone) {
390424
+ this.logger.warn("[asc-flow] atendimento carries no account/phone \u2014 no interactive message for it", { cod });
390425
+ return null;
390426
+ }
390427
+ const destino = { codConta, telefone };
390428
+ state.destinos.set(chave, destino);
390429
+ return destino;
390430
+ }
390238
390431
  async sendMensagem(state, cod, text, extra) {
390239
390432
  try {
390240
390433
  await state.client.call("/mensagem", {
@@ -397161,6 +397354,27 @@ function setupMessageHandlers2(app, instanceId, botUserId, callbacks, dmPolicyCo
397161
397354
  logger5.info("Message handlers registered", { instanceId });
397162
397355
  }
397163
397356
 
397357
+ // ../channel-slack/src/handlers/pins.ts
397358
+ function setupPinHandlers(app, instanceId, callbacks, logger5) {
397359
+ const handlePinEvent = (action) => async ({ event }) => {
397360
+ const evt = event;
397361
+ const item = evt.item;
397362
+ if (!item || item.type !== "message")
397363
+ return;
397364
+ const message2 = item.message;
397365
+ const channelId = item.channel ?? evt.channel_id ?? "";
397366
+ const messageTs = message2?.ts ?? "";
397367
+ if (!channelId || !messageTs)
397368
+ return;
397369
+ const userId = evt.user;
397370
+ logger5.debug(action === "pin" ? "Pin added" : "Pin removed", { instanceId, channelId, messageTs, userId });
397371
+ await callbacks.onPin(instanceId, messageTs, channelId, userId, action);
397372
+ };
397373
+ app.event("pin_added", handlePinEvent("pin"));
397374
+ app.event("pin_removed", handlePinEvent("unpin"));
397375
+ logger5.info("Pin handlers registered", { instanceId });
397376
+ }
397377
+
397164
397378
  // ../channel-slack/src/handlers/reactions.ts
397165
397379
  function setupReactionHandlers2(app, instanceId, botUserId, callbacks, logger5) {
397166
397380
  const resolveBotUserId = () => typeof botUserId === "function" ? botUserId() : botUserId;
@@ -398849,6 +399063,15 @@ class SlackPlugin extends BaseChannelPlugin {
398849
399063
  await this.handleReactionReceived(instId, messageId, chatId, userId, emoji, action);
398850
399064
  }
398851
399065
  }, this.logger);
399066
+ setupPinHandlers(connection.app, instanceId, {
399067
+ onPin: async (instId, messageId, chatId, userId, action) => {
399068
+ if (action === "pin") {
399069
+ await this.emitMessagePinned({ instanceId: instId, messageId, chatId, from: userId });
399070
+ } else {
399071
+ await this.emitMessageUnpinned({ instanceId: instId, messageId, chatId, from: userId });
399072
+ }
399073
+ }
399074
+ }, this.logger);
398852
399075
  setupAgentSessionHandlers(connection.app, instanceId, {
398853
399076
  onSessionStopped: async (instId, args) => {
398854
399077
  await this.handleAgentSessionStopped(instId, connection, args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260907.3",
3
+ "version": "2.260907.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/automagik-dev/omni.git",