@adatechnology/meta-whatsapp-module 0.2.0 → 0.3.1

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.d.cts CHANGED
@@ -1629,7 +1629,7 @@ declare class SessionRepository {
1629
1629
  setFlowPosition(companyId: string, whatsappNumber: string, flowKey: string | null, currentNodeId: string | null): Promise<void>;
1630
1630
  touchInbound(companyId: string, whatsappNumber: string): Promise<void>;
1631
1631
  hoursSinceLastInbound(companyId: string, whatsappNumber: string): Promise<number | undefined>;
1632
- setMode(companyId: string, whatsappNumber: string, mode: SessionMode, assignedUserId?: string | null): Promise<void>;
1632
+ setMode(companyId: string, whatsappNumber: string, mode: SessionMode, assignedUserId?: string | null, clearHumanRequest?: boolean): Promise<void>;
1633
1633
  takeover(companyId: string, whatsappNumber: string, agentUserId: string): Promise<void>;
1634
1634
  release(companyId: string, whatsappNumber: string): Promise<void>;
1635
1635
  /**
@@ -2396,6 +2396,14 @@ type InboundMessageEffectsJob = {
2396
2396
  message: WhatsAppMessage;
2397
2397
  savedMessageId: string;
2398
2398
  media?: InboundMediaDescriptor;
2399
+ /**
2400
+ * Nome de perfil de quem mandou. Vai no JOB e não é relido depois porque o job pode passar por
2401
+ * fila: o payload da Meta não existe mais quando o worker roda.
2402
+ *
2403
+ * Opcional, e job antigo sem o campo continua válido — é o que permite subir o módulo sem drenar
2404
+ * a fila antes.
2405
+ */
2406
+ profileName?: string;
2399
2407
  receivedAt: number;
2400
2408
  };
2401
2409
  type InboundStatusEffectsJob = {
package/dist/index.d.ts CHANGED
@@ -1629,7 +1629,7 @@ declare class SessionRepository {
1629
1629
  setFlowPosition(companyId: string, whatsappNumber: string, flowKey: string | null, currentNodeId: string | null): Promise<void>;
1630
1630
  touchInbound(companyId: string, whatsappNumber: string): Promise<void>;
1631
1631
  hoursSinceLastInbound(companyId: string, whatsappNumber: string): Promise<number | undefined>;
1632
- setMode(companyId: string, whatsappNumber: string, mode: SessionMode, assignedUserId?: string | null): Promise<void>;
1632
+ setMode(companyId: string, whatsappNumber: string, mode: SessionMode, assignedUserId?: string | null, clearHumanRequest?: boolean): Promise<void>;
1633
1633
  takeover(companyId: string, whatsappNumber: string, agentUserId: string): Promise<void>;
1634
1634
  release(companyId: string, whatsappNumber: string): Promise<void>;
1635
1635
  /**
@@ -2396,6 +2396,14 @@ type InboundMessageEffectsJob = {
2396
2396
  message: WhatsAppMessage;
2397
2397
  savedMessageId: string;
2398
2398
  media?: InboundMediaDescriptor;
2399
+ /**
2400
+ * Nome de perfil de quem mandou. Vai no JOB e não é relido depois porque o job pode passar por
2401
+ * fila: o payload da Meta não existe mais quando o worker roda.
2402
+ *
2403
+ * Opcional, e job antigo sem o campo continua válido — é o que permite subir o módulo sem drenar
2404
+ * a fila antes.
2405
+ */
2406
+ profileName?: string;
2399
2407
  receivedAt: number;
2400
2408
  };
2401
2409
  type InboundStatusEffectsJob = {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
- // ../../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.15.40_@swc+helpers@0.5.23__jiti@2.7.0_postcss@8.5.26_tsx@4.23.1_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/esm_shims.js
4
+ // ../../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.16.2_@swc+helpers@0.5.23__jiti@2.7.0_postcss@8.5.28_tsx@4.23.13_typescript@5.9.3_yaml@2.9.1/node_modules/tsup/assets/esm_shims.js
5
5
  import path from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  var getFilename = /* @__PURE__ */ __name(() => fileURLToPath(import.meta.url), "getFilename");
@@ -416,20 +416,33 @@ var SessionRepository = class {
416
416
  if (!session?.lastInboundAt) return void 0;
417
417
  return (Date.now() - session.lastInboundAt.getTime()) / (1e3 * 60 * 60);
418
418
  }
419
- async setMode(companyId, whatsappNumber, mode, assignedUserId) {
419
+ // `clearHumanRequest` é opt-in: quem chama `setMode` direto para outros fins (ex.: trocar de
420
+ // atendente sem mexer na fila) continua sem afetar `humanRequestedAt`. Só `takeover`/`release`
421
+ // pedem a limpeza, porque só eles representam "o pedido de atendimento foi resolvido".
422
+ async setMode(companyId, whatsappNumber, mode, assignedUserId, clearHumanRequest = false) {
420
423
  await this.db.update(sessions).set({
421
424
  mode,
422
425
  assignedUserId: assignedUserId ?? null,
426
+ ...clearHumanRequest ? {
427
+ humanRequestedAt: null
428
+ } : {},
423
429
  updatedAt: sql2`now()`
424
430
  }).where(and(eq(sessions.companyId, companyId), eq(sessions.whatsappNumber, whatsappNumber)));
425
431
  }
426
432
  // T3.3 — takeover: atendente assume a conversa, tirando-a do modo bot.
433
+ //
434
+ // Limpa `humanRequestedAt`: sem isso a conversa nunca sai da fila `waitingHuman` da inbox,
435
+ // mesmo depois de atendida — o filtro é só `humanRequestedAt is not null` (ver
436
+ // `listByContextFilters`), então o campo tem que voltar a null quando o pedido é resolvido.
427
437
  async takeover(companyId, whatsappNumber, agentUserId) {
428
- await this.setMode(companyId, whatsappNumber, "human", agentUserId);
438
+ await this.setMode(companyId, whatsappNumber, "human", agentUserId, true);
429
439
  }
430
440
  // T3.3 — release: devolve a conversa ao bot.
441
+ //
442
+ // Também limpa `humanRequestedAt` pelo mesmo motivo do takeover: se o cliente pedir atendimento
443
+ // de novo depois, `requestHuman` grava um novo timestamp normalmente.
431
444
  async release(companyId, whatsappNumber) {
432
- await this.setMode(companyId, whatsappNumber, "bot", null);
445
+ await this.setMode(companyId, whatsappNumber, "bot", null, true);
433
446
  }
434
447
  /**
435
448
  * Apaga a sessão; a cascata das FKs leva mensagens e documentos.
@@ -2085,7 +2098,7 @@ var WhatsAppChannelAdapter = class {
2085
2098
  };
2086
2099
 
2087
2100
  // src/channel/ReceiveWebhook.use-case.ts
2088
- import { whatsAppWebhookPayloadSchema, whatsAppTemplateStatusUpdateSchema, whatsAppPhoneNumberQualityUpdateSchema, WHATSAPP_WEBHOOK_FIELDS } from "@adatechnology/meta-whatsapp-contracts";
2101
+ import { whatsAppWebhookPayloadSchema, resolveContactProfileName, whatsAppTemplateStatusUpdateSchema, whatsAppPhoneNumberQualityUpdateSchema, WHATSAPP_WEBHOOK_FIELDS } from "@adatechnology/meta-whatsapp-contracts";
2089
2102
 
2090
2103
  // src/channel/webhookSecurity.ts
2091
2104
  import { WEBHOOK_CLAIM_TTL_SECONDS, WEBHOOK_NONCE_TTL_SECONDS, buildWebhookDeliveryKey, isValidWebhookChallenge, isValidWebhookSignature } from "@adatechnology/meta-graph-core";
@@ -2496,7 +2509,9 @@ var InboundEffectsDispatcher = class {
2496
2509
  const sessionRow = await this.params.sessionRepository.getContext(companyId, message.from);
2497
2510
  if (!sessionRow) return;
2498
2511
  if (sessionRow.mode === "human") return;
2499
- const outcome = await this.params.hooks?.onMessageReceived?.(message, toSessionContract(sessionRow));
2512
+ const outcome = await this.params.hooks?.onMessageReceived?.(message, toSessionContract(sessionRow), job.profileName === void 0 ? {} : {
2513
+ profileName: job.profileName
2514
+ });
2500
2515
  if (outcome?.outcome === "handled") return;
2501
2516
  void extractAnswer(message);
2502
2517
  }
@@ -2625,7 +2640,11 @@ var ReceiveWebhookUseCase = class {
2625
2640
  continue;
2626
2641
  }
2627
2642
  for (const message of change.value.messages ?? []) {
2628
- await this.handleMessage(input.companyId, message);
2643
+ const profileName = resolveContactProfileName({
2644
+ contacts: change.value.contacts,
2645
+ from: message.from
2646
+ });
2647
+ await this.handleMessage(input.companyId, message, profileName);
2629
2648
  messagesProcessed++;
2630
2649
  }
2631
2650
  for (const status of change.value.statuses ?? []) {
@@ -2684,7 +2703,7 @@ var ReceiveWebhookUseCase = class {
2684
2703
  await this.params.hooks?.onPhoneNumberQualityUpdate?.(quality.data);
2685
2704
  return true;
2686
2705
  }
2687
- async handleMessage(companyId, message) {
2706
+ async handleMessage(companyId, message, profileName) {
2688
2707
  const saved = await this.params.logMessage.execute({
2689
2708
  companyId,
2690
2709
  whatsappNumber: message.from,
@@ -2707,6 +2726,9 @@ var ReceiveWebhookUseCase = class {
2707
2726
  ...media ? {
2708
2727
  media
2709
2728
  } : {},
2729
+ ...profileName ? {
2730
+ profileName
2731
+ } : {},
2710
2732
  receivedAt: Date.now()
2711
2733
  });
2712
2734
  }