@adatechnology/meta-whatsapp-module 0.2.0-rc.29 → 0.2.0-rc.30
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.cjs +129 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -3
- package/dist/index.d.ts +109 -3
- package/dist/index.js +124 -33
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
2
2
|
import { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
|
|
3
3
|
import * as _adatechnology_meta_whatsapp_contracts from '@adatechnology/meta-whatsapp-contracts';
|
|
4
|
-
import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
|
|
4
|
+
import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, WhatsAppMessage, WhatsAppStatus, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
|
|
5
5
|
export { PREVIEW_MEDIA_ID_PREFIX, TranscriptionMode, resolvePreviewUploadId, toPreviewMediaId } from '@adatechnology/meta-whatsapp-contracts';
|
|
6
6
|
import { WhatsAppMessageProvider } from '@adatechnology/meta-whatsapp-provider';
|
|
7
|
-
export { WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
|
|
7
|
+
export { WEBHOOK_CLAIM_TTL_SECONDS, WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
|
|
8
8
|
|
|
9
9
|
type MetaWhatsAppDatabase = PgDatabase<PgQueryResultHKT, any, any>;
|
|
10
10
|
type DrizzleMigrateFunction = (db: never, config: {
|
|
@@ -2316,6 +2316,15 @@ declare class FlowMediaRepository {
|
|
|
2316
2316
|
|
|
2317
2317
|
interface NonceStoreInterface {
|
|
2318
2318
|
setIfAbsent(key: string, ttlSeconds: number): Promise<boolean>;
|
|
2319
|
+
/**
|
|
2320
|
+
* Estende a chave já reivindicada para a janela cheia. Um SET simples, sem NX.
|
|
2321
|
+
*
|
|
2322
|
+
* Opcional só por compatibilidade com hosts que ainda não o implementam: sem ele o claim curto
|
|
2323
|
+
* expira, e a Meta pode reentregar uma entrega já processada — trabalho repetido, que a dedupe
|
|
2324
|
+
* por `waMessageId` ainda segura antes de virar efeito visível ao cliente. Implementar é o
|
|
2325
|
+
* caminho correto.
|
|
2326
|
+
*/
|
|
2327
|
+
confirm?(key: string, ttlSeconds: number): Promise<void>;
|
|
2319
2328
|
}
|
|
2320
2329
|
|
|
2321
2330
|
declare function verifyWebhookChallenge(params: {
|
|
@@ -2329,11 +2338,101 @@ declare function verifyWebhookSignature(params: {
|
|
|
2329
2338
|
signatureHeader: string | null | undefined;
|
|
2330
2339
|
appSecret: string;
|
|
2331
2340
|
}): void;
|
|
2341
|
+
/**
|
|
2342
|
+
* Reivindica a entrega por pouco tempo. O par obrigatório é `confirmWebhookDelivery` ao fim do
|
|
2343
|
+
* processamento — ver `WEBHOOK_CLAIM_TTL_SECONDS` para o porquê dos dois tempos.
|
|
2344
|
+
*/
|
|
2332
2345
|
declare function claimWebhookDelivery(params: {
|
|
2333
2346
|
nonceStore: NonceStoreInterface;
|
|
2334
2347
|
signatureHeader: string;
|
|
2335
2348
|
ttlSeconds?: number;
|
|
2336
2349
|
}): Promise<boolean>;
|
|
2350
|
+
/** Só depois da entrega processada por inteiro: é isto que fecha a janela anti-replay. */
|
|
2351
|
+
declare function confirmWebhookDelivery(params: {
|
|
2352
|
+
nonceStore: NonceStoreInterface;
|
|
2353
|
+
signatureHeader: string;
|
|
2354
|
+
ttlSeconds?: number;
|
|
2355
|
+
}): Promise<void>;
|
|
2356
|
+
|
|
2357
|
+
/**
|
|
2358
|
+
* Copyright (c) 2026 Ada Technology. MIT License.
|
|
2359
|
+
*
|
|
2360
|
+
* Separa a entrega do webhook em duas metades: o que precisa acontecer antes de responder à Meta
|
|
2361
|
+
* (persistir a mensagem, que é escrita local e rápida) e o que pode esperar alguns milissegundos
|
|
2362
|
+
* (os hooks do host, que costumam falar com rede — motor de fluxo, IA, integrações).
|
|
2363
|
+
*
|
|
2364
|
+
* A segunda metade rodava dentro da requisição do webhook. Um deploy no meio de uma conversa
|
|
2365
|
+
* matava a chamada em voo, e o cliente ficava sem resposta sem que nada registrasse a perda. Com
|
|
2366
|
+
* uma fila no host, derrubar o processo vira atraso: o job espera e é retomado.
|
|
2367
|
+
*/
|
|
2368
|
+
|
|
2369
|
+
type InboundMediaDescriptor = {
|
|
2370
|
+
sourceMediaId: string;
|
|
2371
|
+
mimeType: string;
|
|
2372
|
+
filename?: string;
|
|
2373
|
+
};
|
|
2374
|
+
type InboundMessageEffectsJob = {
|
|
2375
|
+
kind: 'message';
|
|
2376
|
+
companyId: string;
|
|
2377
|
+
message: WhatsAppMessage;
|
|
2378
|
+
savedMessageId: string;
|
|
2379
|
+
media?: InboundMediaDescriptor;
|
|
2380
|
+
receivedAt: number;
|
|
2381
|
+
};
|
|
2382
|
+
type InboundStatusEffectsJob = {
|
|
2383
|
+
kind: 'status';
|
|
2384
|
+
companyId: string;
|
|
2385
|
+
status: WhatsAppStatus;
|
|
2386
|
+
whatsappNumber: string;
|
|
2387
|
+
receivedAt: number;
|
|
2388
|
+
};
|
|
2389
|
+
type InboundDispatchJob = InboundMessageEffectsJob | InboundStatusEffectsJob;
|
|
2390
|
+
/**
|
|
2391
|
+
* Porta de fila. O módulo não escolhe a tecnologia — BullMQ, SQS, o que o host já tiver — mas
|
|
2392
|
+
* exige dela duas garantias, e sem as duas o padrão não entrega o que promete:
|
|
2393
|
+
*
|
|
2394
|
+
* 1. **Durabilidade**: o job sobrevive à morte do processo que o enfileirou. Uma fila em memória
|
|
2395
|
+
* reintroduz exatamente a perda que este desenho existe para evitar.
|
|
2396
|
+
* 2. **Retentativa com backoff**: o destino dos hooks (n8n, IA, API de terceiro) também cai em
|
|
2397
|
+
* deploy. Sem retry, o job falha uma vez e a conversa morre do mesmo jeito.
|
|
2398
|
+
*
|
|
2399
|
+
* `jobId` é estável e derivado da mensagem: reentrega da Meta e re-enfileiramento produzem o mesmo
|
|
2400
|
+
* id, e a fila descarta o segundo em vez de rodar o efeito duas vezes.
|
|
2401
|
+
*/
|
|
2402
|
+
interface InboundDispatchQueueInterface {
|
|
2403
|
+
enqueue(job: InboundDispatchJob, options: {
|
|
2404
|
+
jobId: string;
|
|
2405
|
+
}): Promise<void>;
|
|
2406
|
+
}
|
|
2407
|
+
declare function buildInboundJobId(job: InboundDispatchJob): string;
|
|
2408
|
+
declare function toSessionContract(row: SessionRow): ConversationSession;
|
|
2409
|
+
/**
|
|
2410
|
+
* Os efeitos de host de uma entrega já persistida. Vive fora do `ReceiveWebhookUseCase` porque
|
|
2411
|
+
* roda nos dois lados: inline, quando o host não configurou fila, e dentro do worker, quando
|
|
2412
|
+
* configurou. Um só corpo de regra para os dois caminhos — duas cópias divergiriam.
|
|
2413
|
+
*/
|
|
2414
|
+
declare class InboundEffectsDispatcher {
|
|
2415
|
+
private readonly params;
|
|
2416
|
+
constructor(params: {
|
|
2417
|
+
sessionRepository: SessionRepository;
|
|
2418
|
+
hooks?: MetaWhatsAppHooks;
|
|
2419
|
+
realtime?: RealtimeNotifierInterface;
|
|
2420
|
+
});
|
|
2421
|
+
run(job: InboundDispatchJob): Promise<void>;
|
|
2422
|
+
runMessageEffects(job: InboundMessageEffectsJob): Promise<void>;
|
|
2423
|
+
runStatusEffects(job: InboundStatusEffectsJob): Promise<void>;
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Ponto de entrada do worker do host: recebe o job que a fila devolveu e roda os efeitos.
|
|
2427
|
+
*
|
|
2428
|
+
* Deixe a exceção propagar. É ela que faz a fila contar a tentativa e reagendar com backoff;
|
|
2429
|
+
* capturar aqui para logar transforma falha recuperável em mensagem perdida em silêncio.
|
|
2430
|
+
*/
|
|
2431
|
+
declare class ProcessInboundDispatchUseCase {
|
|
2432
|
+
private readonly dispatcher;
|
|
2433
|
+
constructor(dispatcher: InboundEffectsDispatcher);
|
|
2434
|
+
execute(job: InboundDispatchJob): Promise<void>;
|
|
2435
|
+
}
|
|
2337
2436
|
|
|
2338
2437
|
type ReceiveWebhookParams = {
|
|
2339
2438
|
companyId: string;
|
|
@@ -2358,10 +2457,17 @@ declare class ReceiveWebhookUseCase {
|
|
|
2358
2457
|
startState: SessionState;
|
|
2359
2458
|
hooks?: MetaWhatsAppHooks;
|
|
2360
2459
|
realtime?: RealtimeNotifierInterface;
|
|
2460
|
+
/**
|
|
2461
|
+
* Sem fila o módulo se comporta como sempre: os hooks rodam dentro da requisição do webhook.
|
|
2462
|
+
* Configurá-la é o que faz a conversa sobreviver a um deploy no meio do atendimento.
|
|
2463
|
+
*/
|
|
2464
|
+
inboundQueue?: InboundDispatchQueueInterface;
|
|
2361
2465
|
});
|
|
2466
|
+
private readonly dispatcher;
|
|
2362
2467
|
execute(input: ReceiveWebhookParams): Promise<ReceiveWebhookResult>;
|
|
2363
2468
|
private handleMessage;
|
|
2364
2469
|
private handleStatus;
|
|
2470
|
+
private dispatch;
|
|
2365
2471
|
}
|
|
2366
2472
|
|
|
2367
2473
|
/**
|
|
@@ -2895,4 +3001,4 @@ type CreateSendProductListActionParams = {
|
|
|
2895
3001
|
*/
|
|
2896
3002
|
declare function createSendProductListAction(params: CreateSendProductListActionParams): FlowActionHandler;
|
|
2897
3003
|
|
|
2898
|
-
export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, claimWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
|
|
3004
|
+
export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type InboundDispatchJob, type InboundDispatchQueueInterface, InboundEffectsDispatcher, type InboundMediaDescriptor, type InboundMessageEffectsJob, type InboundStatusEffectsJob, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, ProcessInboundDispatchUseCase, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, buildInboundJobId, claimWebhookDelivery, confirmWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, toSessionContract, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
2
2
|
import { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
|
|
3
3
|
import * as _adatechnology_meta_whatsapp_contracts from '@adatechnology/meta-whatsapp-contracts';
|
|
4
|
-
import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
|
|
4
|
+
import { TranscriptionMode, SessionState, SessionMode, ConversationSummary, MessageDirection, MessageSender, MessageStatus, CacheInterface, FlowGraphData, FlowGraphSummary, LiveFlowPosition, WhatsAppSettings, RealtimeNotifierInterface, ChannelAdapterInterface, ObjectStorageInterface, FlowActionKind, FlowActionHandler, ConversationSession, WhatsAppMessage, WhatsAppStatus, MetaWhatsAppHooks, SubjectResolverInterface, CatalogPort } from '@adatechnology/meta-whatsapp-contracts';
|
|
5
5
|
export { PREVIEW_MEDIA_ID_PREFIX, TranscriptionMode, resolvePreviewUploadId, toPreviewMediaId } from '@adatechnology/meta-whatsapp-contracts';
|
|
6
6
|
import { WhatsAppMessageProvider } from '@adatechnology/meta-whatsapp-provider';
|
|
7
|
-
export { WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
|
|
7
|
+
export { WEBHOOK_CLAIM_TTL_SECONDS, WEBHOOK_NONCE_TTL_SECONDS } from '@adatechnology/meta-graph-core';
|
|
8
8
|
|
|
9
9
|
type MetaWhatsAppDatabase = PgDatabase<PgQueryResultHKT, any, any>;
|
|
10
10
|
type DrizzleMigrateFunction = (db: never, config: {
|
|
@@ -2316,6 +2316,15 @@ declare class FlowMediaRepository {
|
|
|
2316
2316
|
|
|
2317
2317
|
interface NonceStoreInterface {
|
|
2318
2318
|
setIfAbsent(key: string, ttlSeconds: number): Promise<boolean>;
|
|
2319
|
+
/**
|
|
2320
|
+
* Estende a chave já reivindicada para a janela cheia. Um SET simples, sem NX.
|
|
2321
|
+
*
|
|
2322
|
+
* Opcional só por compatibilidade com hosts que ainda não o implementam: sem ele o claim curto
|
|
2323
|
+
* expira, e a Meta pode reentregar uma entrega já processada — trabalho repetido, que a dedupe
|
|
2324
|
+
* por `waMessageId` ainda segura antes de virar efeito visível ao cliente. Implementar é o
|
|
2325
|
+
* caminho correto.
|
|
2326
|
+
*/
|
|
2327
|
+
confirm?(key: string, ttlSeconds: number): Promise<void>;
|
|
2319
2328
|
}
|
|
2320
2329
|
|
|
2321
2330
|
declare function verifyWebhookChallenge(params: {
|
|
@@ -2329,11 +2338,101 @@ declare function verifyWebhookSignature(params: {
|
|
|
2329
2338
|
signatureHeader: string | null | undefined;
|
|
2330
2339
|
appSecret: string;
|
|
2331
2340
|
}): void;
|
|
2341
|
+
/**
|
|
2342
|
+
* Reivindica a entrega por pouco tempo. O par obrigatório é `confirmWebhookDelivery` ao fim do
|
|
2343
|
+
* processamento — ver `WEBHOOK_CLAIM_TTL_SECONDS` para o porquê dos dois tempos.
|
|
2344
|
+
*/
|
|
2332
2345
|
declare function claimWebhookDelivery(params: {
|
|
2333
2346
|
nonceStore: NonceStoreInterface;
|
|
2334
2347
|
signatureHeader: string;
|
|
2335
2348
|
ttlSeconds?: number;
|
|
2336
2349
|
}): Promise<boolean>;
|
|
2350
|
+
/** Só depois da entrega processada por inteiro: é isto que fecha a janela anti-replay. */
|
|
2351
|
+
declare function confirmWebhookDelivery(params: {
|
|
2352
|
+
nonceStore: NonceStoreInterface;
|
|
2353
|
+
signatureHeader: string;
|
|
2354
|
+
ttlSeconds?: number;
|
|
2355
|
+
}): Promise<void>;
|
|
2356
|
+
|
|
2357
|
+
/**
|
|
2358
|
+
* Copyright (c) 2026 Ada Technology. MIT License.
|
|
2359
|
+
*
|
|
2360
|
+
* Separa a entrega do webhook em duas metades: o que precisa acontecer antes de responder à Meta
|
|
2361
|
+
* (persistir a mensagem, que é escrita local e rápida) e o que pode esperar alguns milissegundos
|
|
2362
|
+
* (os hooks do host, que costumam falar com rede — motor de fluxo, IA, integrações).
|
|
2363
|
+
*
|
|
2364
|
+
* A segunda metade rodava dentro da requisição do webhook. Um deploy no meio de uma conversa
|
|
2365
|
+
* matava a chamada em voo, e o cliente ficava sem resposta sem que nada registrasse a perda. Com
|
|
2366
|
+
* uma fila no host, derrubar o processo vira atraso: o job espera e é retomado.
|
|
2367
|
+
*/
|
|
2368
|
+
|
|
2369
|
+
type InboundMediaDescriptor = {
|
|
2370
|
+
sourceMediaId: string;
|
|
2371
|
+
mimeType: string;
|
|
2372
|
+
filename?: string;
|
|
2373
|
+
};
|
|
2374
|
+
type InboundMessageEffectsJob = {
|
|
2375
|
+
kind: 'message';
|
|
2376
|
+
companyId: string;
|
|
2377
|
+
message: WhatsAppMessage;
|
|
2378
|
+
savedMessageId: string;
|
|
2379
|
+
media?: InboundMediaDescriptor;
|
|
2380
|
+
receivedAt: number;
|
|
2381
|
+
};
|
|
2382
|
+
type InboundStatusEffectsJob = {
|
|
2383
|
+
kind: 'status';
|
|
2384
|
+
companyId: string;
|
|
2385
|
+
status: WhatsAppStatus;
|
|
2386
|
+
whatsappNumber: string;
|
|
2387
|
+
receivedAt: number;
|
|
2388
|
+
};
|
|
2389
|
+
type InboundDispatchJob = InboundMessageEffectsJob | InboundStatusEffectsJob;
|
|
2390
|
+
/**
|
|
2391
|
+
* Porta de fila. O módulo não escolhe a tecnologia — BullMQ, SQS, o que o host já tiver — mas
|
|
2392
|
+
* exige dela duas garantias, e sem as duas o padrão não entrega o que promete:
|
|
2393
|
+
*
|
|
2394
|
+
* 1. **Durabilidade**: o job sobrevive à morte do processo que o enfileirou. Uma fila em memória
|
|
2395
|
+
* reintroduz exatamente a perda que este desenho existe para evitar.
|
|
2396
|
+
* 2. **Retentativa com backoff**: o destino dos hooks (n8n, IA, API de terceiro) também cai em
|
|
2397
|
+
* deploy. Sem retry, o job falha uma vez e a conversa morre do mesmo jeito.
|
|
2398
|
+
*
|
|
2399
|
+
* `jobId` é estável e derivado da mensagem: reentrega da Meta e re-enfileiramento produzem o mesmo
|
|
2400
|
+
* id, e a fila descarta o segundo em vez de rodar o efeito duas vezes.
|
|
2401
|
+
*/
|
|
2402
|
+
interface InboundDispatchQueueInterface {
|
|
2403
|
+
enqueue(job: InboundDispatchJob, options: {
|
|
2404
|
+
jobId: string;
|
|
2405
|
+
}): Promise<void>;
|
|
2406
|
+
}
|
|
2407
|
+
declare function buildInboundJobId(job: InboundDispatchJob): string;
|
|
2408
|
+
declare function toSessionContract(row: SessionRow): ConversationSession;
|
|
2409
|
+
/**
|
|
2410
|
+
* Os efeitos de host de uma entrega já persistida. Vive fora do `ReceiveWebhookUseCase` porque
|
|
2411
|
+
* roda nos dois lados: inline, quando o host não configurou fila, e dentro do worker, quando
|
|
2412
|
+
* configurou. Um só corpo de regra para os dois caminhos — duas cópias divergiriam.
|
|
2413
|
+
*/
|
|
2414
|
+
declare class InboundEffectsDispatcher {
|
|
2415
|
+
private readonly params;
|
|
2416
|
+
constructor(params: {
|
|
2417
|
+
sessionRepository: SessionRepository;
|
|
2418
|
+
hooks?: MetaWhatsAppHooks;
|
|
2419
|
+
realtime?: RealtimeNotifierInterface;
|
|
2420
|
+
});
|
|
2421
|
+
run(job: InboundDispatchJob): Promise<void>;
|
|
2422
|
+
runMessageEffects(job: InboundMessageEffectsJob): Promise<void>;
|
|
2423
|
+
runStatusEffects(job: InboundStatusEffectsJob): Promise<void>;
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Ponto de entrada do worker do host: recebe o job que a fila devolveu e roda os efeitos.
|
|
2427
|
+
*
|
|
2428
|
+
* Deixe a exceção propagar. É ela que faz a fila contar a tentativa e reagendar com backoff;
|
|
2429
|
+
* capturar aqui para logar transforma falha recuperável em mensagem perdida em silêncio.
|
|
2430
|
+
*/
|
|
2431
|
+
declare class ProcessInboundDispatchUseCase {
|
|
2432
|
+
private readonly dispatcher;
|
|
2433
|
+
constructor(dispatcher: InboundEffectsDispatcher);
|
|
2434
|
+
execute(job: InboundDispatchJob): Promise<void>;
|
|
2435
|
+
}
|
|
2337
2436
|
|
|
2338
2437
|
type ReceiveWebhookParams = {
|
|
2339
2438
|
companyId: string;
|
|
@@ -2358,10 +2457,17 @@ declare class ReceiveWebhookUseCase {
|
|
|
2358
2457
|
startState: SessionState;
|
|
2359
2458
|
hooks?: MetaWhatsAppHooks;
|
|
2360
2459
|
realtime?: RealtimeNotifierInterface;
|
|
2460
|
+
/**
|
|
2461
|
+
* Sem fila o módulo se comporta como sempre: os hooks rodam dentro da requisição do webhook.
|
|
2462
|
+
* Configurá-la é o que faz a conversa sobreviver a um deploy no meio do atendimento.
|
|
2463
|
+
*/
|
|
2464
|
+
inboundQueue?: InboundDispatchQueueInterface;
|
|
2361
2465
|
});
|
|
2466
|
+
private readonly dispatcher;
|
|
2362
2467
|
execute(input: ReceiveWebhookParams): Promise<ReceiveWebhookResult>;
|
|
2363
2468
|
private handleMessage;
|
|
2364
2469
|
private handleStatus;
|
|
2470
|
+
private dispatch;
|
|
2365
2471
|
}
|
|
2366
2472
|
|
|
2367
2473
|
/**
|
|
@@ -2895,4 +3001,4 @@ type CreateSendProductListActionParams = {
|
|
|
2895
3001
|
*/
|
|
2896
3002
|
declare function createSendProductListAction(params: CreateSendProductListActionParams): FlowActionHandler;
|
|
2897
3003
|
|
|
2898
|
-
export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, claimWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
|
|
3004
|
+
export { type AttachFlowMediaParams, type AudioTranscriber, type CompanyDocumentView, type CompanyDocumentsPage, type ConversationDocumentView, type ConversationDocumentsPage, type CreateFlowGraphParams, CreateFlowGraphUseCase, type CreateMetaWhatsAppModuleParams, type CreateSendMediaActionParams, type CreateSendProductListActionParams, DEFAULT_FLOW_GRAPH_CACHE_TTL_SECONDS, type DeleteConversationParams, type DeleteConversationResult, DeleteConversationUseCase, DeleteFlowGraphUseCase, DocumentRepository, type DocumentRow, type DrizzleMigrateFunction, type ExportConversationParams, type ExportConversationResult, ExportConversationUseCase, FlowGraphCache, FlowGraphRepository, type FlowGraphRow, FlowInterpreter, type FlowMediaLocation, FlowMediaRepository, type FlowMediaRow, type FlowMediaTranscriptLogger, type FlowRunResult, type FlowStepInput, type FlowStepResult, GetFlowGraphUseCase, GetLiveFlowPositionsUseCase, type InboundDispatchJob, type InboundDispatchQueueInterface, InboundEffectsDispatcher, type InboundMediaDescriptor, type InboundMessageEffectsJob, type InboundStatusEffectsJob, type IngestInboundMediaParams, type IngestInboundMediaResult, IngestInboundMediaUseCase, type IngestTranscriptionOptions, type InsertMessageParams, InvalidFlowGraphError, type LinkDocumentParams, type ListCompanyDocumentsParams, ListCompanyDocumentsUseCase, type ListConversationDocumentsParams, ListConversationDocumentsUseCase, type ListConversationsFilters, type ListConversationsParams, ListConversationsUseCase, type ListDocumentsParams, type ListDocumentsResult, ListFlowGraphsUseCase, type ListMessagesParams, ListMessagesUseCase, type LogMessageParams, LogMessageUseCase, META_WHATSAPP_MIGRATIONS_TABLE, type MessageModerator, MessageRepository, type MessageRow, type MetaWhatsAppDatabase, type MetaWhatsAppModule, type MetaWhatsAppModuleConfig, type MetaWhatsAppModuleFeatures, type MetaWhatsAppModuleProviders, type MetaWhatsAppTranscriptionConfig, type NewDocumentRow, type NewFlowGraphRow, type NewFlowMediaRow, type NewMessageRow, type NewSessionRow, type NewSettingsRow, type NonceStoreInterface, OptimisticLockError, PRODUCT_LIST_LIMIT, type PreviewMediaSupport, ProcessInboundDispatchUseCase, type PurgeExpiredDocumentsParams, type PurgeExpiredDocumentsResult, PurgeExpiredDocumentsUseCase, type RealtimeRelay, type ReceiveWebhookParams, type ReceiveWebhookResult, ReceiveWebhookUseCase, type ReleaseConversationParams, ReleaseConversationUseCase, type ListMessagesParams$1 as RepositoryListMessagesParams, type ResolveTranscriptionPolicyDependencies, type RunMetaWhatsAppMigrationsParams, type SaveFlowGraphParams, SaveFlowGraphUseCase, type SaveTranscriptionByWaMessageIdParams, type SaveTranscriptionParams, type SendMediaParams, SendMessageUseCase, type SendTemplateParams, type SendTextParams, SessionRepository, type SessionRow, SettingsRepository, type SettingsRow, SseHub, type SseListener, type StorePreviewMediaParams, type StorePreviewMediaResult, StorePreviewMediaUseCase, TRANSCRIPTION_MODE, TRANSCRIPTION_STATUS, type TakeoverConversationParams, TakeoverConversationUseCase, type TicketStoreInterface, type TranscribeAudioDependencies, type TranscribeAudioParams, type TranscribeAudioResult, TranscribeAudioUseCase, type TranscriptionPolicy, type TranscriptionPolicyDefaults, type TranscriptionPolicyResolver, type TranscriptionStatus, type UpdateFlowMediaParams, WhatsAppChannelAdapter, buildInboundJobId, claimWebhookDelivery, confirmWebhookDelivery, createMetaWhatsAppModule, createSendMediaAction, createSendProductListAction, createTranscriptionPolicyResolver, documents, extractMediaDescriptor, flowGraphs, flowMedia, isAudioMimeType, isRetriableTranscriptionError, isUnsupportedTranscriptionError, issueSseTicket, messages, metaWhatsAppMigrationsFolder, metaWhatsAppSchema, redeemSseTicket, resolveFailureStatus, runMetaWhatsAppMigrations, sessions, settings, toSessionContract, transcriptionRetryAfterSeconds, verifyWebhookChallenge, verifyWebhookSignature };
|
package/dist/index.js
CHANGED
|
@@ -2005,7 +2005,7 @@ var WhatsAppChannelAdapter = class {
|
|
|
2005
2005
|
import { whatsAppWebhookPayloadSchema } from "@adatechnology/meta-whatsapp-contracts";
|
|
2006
2006
|
|
|
2007
2007
|
// src/channel/webhookSecurity.ts
|
|
2008
|
-
import { WEBHOOK_NONCE_TTL_SECONDS, buildWebhookDeliveryKey, isValidWebhookChallenge, isValidWebhookSignature } from "@adatechnology/meta-graph-core";
|
|
2008
|
+
import { WEBHOOK_CLAIM_TTL_SECONDS, WEBHOOK_NONCE_TTL_SECONDS, buildWebhookDeliveryKey, isValidWebhookChallenge, isValidWebhookSignature } from "@adatechnology/meta-graph-core";
|
|
2009
2009
|
import { InvalidWebhookSignatureError } from "@adatechnology/meta-whatsapp-contracts";
|
|
2010
2010
|
var WEBHOOK_NONCE_NAMESPACE = "meta-whatsapp";
|
|
2011
2011
|
function verifyWebhookChallenge(params) {
|
|
@@ -2017,14 +2017,21 @@ function verifyWebhookSignature(params) {
|
|
|
2017
2017
|
if (!isValidWebhookSignature(params)) throw new InvalidWebhookSignatureError();
|
|
2018
2018
|
}
|
|
2019
2019
|
__name(verifyWebhookSignature, "verifyWebhookSignature");
|
|
2020
|
-
|
|
2021
|
-
|
|
2020
|
+
function deliveryKey(signatureHeader) {
|
|
2021
|
+
return buildWebhookDeliveryKey({
|
|
2022
2022
|
namespace: WEBHOOK_NONCE_NAMESPACE,
|
|
2023
|
-
signatureHeader
|
|
2023
|
+
signatureHeader
|
|
2024
2024
|
});
|
|
2025
|
-
|
|
2025
|
+
}
|
|
2026
|
+
__name(deliveryKey, "deliveryKey");
|
|
2027
|
+
async function claimWebhookDelivery(params) {
|
|
2028
|
+
return params.nonceStore.setIfAbsent(deliveryKey(params.signatureHeader), params.ttlSeconds ?? WEBHOOK_CLAIM_TTL_SECONDS);
|
|
2026
2029
|
}
|
|
2027
2030
|
__name(claimWebhookDelivery, "claimWebhookDelivery");
|
|
2031
|
+
async function confirmWebhookDelivery(params) {
|
|
2032
|
+
await params.nonceStore.confirm?.(deliveryKey(params.signatureHeader), params.ttlSeconds ?? WEBHOOK_NONCE_TTL_SECONDS);
|
|
2033
|
+
}
|
|
2034
|
+
__name(confirmWebhookDelivery, "confirmWebhookDelivery");
|
|
2028
2035
|
|
|
2029
2036
|
// src/channel/IngestInboundMedia.use-case.ts
|
|
2030
2037
|
import { eq as eq7, and as and6 } from "drizzle-orm";
|
|
@@ -2345,7 +2352,11 @@ function extractMediaDescriptor(message) {
|
|
|
2345
2352
|
}
|
|
2346
2353
|
__name(extractMediaDescriptor, "extractMediaDescriptor");
|
|
2347
2354
|
|
|
2348
|
-
// src/channel/
|
|
2355
|
+
// src/channel/inboundDispatch.ts
|
|
2356
|
+
function buildInboundJobId(job) {
|
|
2357
|
+
return job.kind === "message" ? `wa-inbound-message:${job.message.id}` : `wa-inbound-status:${job.status.id}:${job.status.status}`;
|
|
2358
|
+
}
|
|
2359
|
+
__name(buildInboundJobId, "buildInboundJobId");
|
|
2349
2360
|
function toSessionContract(row) {
|
|
2350
2361
|
return {
|
|
2351
2362
|
id: row.id,
|
|
@@ -2366,6 +2377,65 @@ function toSessionContract(row) {
|
|
|
2366
2377
|
};
|
|
2367
2378
|
}
|
|
2368
2379
|
__name(toSessionContract, "toSessionContract");
|
|
2380
|
+
function extractAnswer(message) {
|
|
2381
|
+
const interactive = message.interactive;
|
|
2382
|
+
if (interactive?.button_reply) return interactive.button_reply.id;
|
|
2383
|
+
if (interactive?.list_reply) return interactive.list_reply.id;
|
|
2384
|
+
return message.text?.body;
|
|
2385
|
+
}
|
|
2386
|
+
__name(extractAnswer, "extractAnswer");
|
|
2387
|
+
var InboundEffectsDispatcher = class {
|
|
2388
|
+
static {
|
|
2389
|
+
__name(this, "InboundEffectsDispatcher");
|
|
2390
|
+
}
|
|
2391
|
+
params;
|
|
2392
|
+
constructor(params) {
|
|
2393
|
+
this.params = params;
|
|
2394
|
+
}
|
|
2395
|
+
async run(job) {
|
|
2396
|
+
if (job.kind === "message") return this.runMessageEffects(job);
|
|
2397
|
+
return this.runStatusEffects(job);
|
|
2398
|
+
}
|
|
2399
|
+
async runMessageEffects(job) {
|
|
2400
|
+
const { companyId, message, savedMessageId, media } = job;
|
|
2401
|
+
if (media) {
|
|
2402
|
+
await this.params.hooks?.onMediaReceived?.({
|
|
2403
|
+
companyId,
|
|
2404
|
+
messageId: savedMessageId,
|
|
2405
|
+
whatsappNumber: message.from,
|
|
2406
|
+
sourceMediaId: media.sourceMediaId,
|
|
2407
|
+
mimeType: media.mimeType,
|
|
2408
|
+
...media.filename ? {
|
|
2409
|
+
filename: media.filename
|
|
2410
|
+
} : {}
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
const sessionRow = await this.params.sessionRepository.getContext(companyId, message.from);
|
|
2414
|
+
if (!sessionRow) return;
|
|
2415
|
+
if (sessionRow.mode === "human") return;
|
|
2416
|
+
const outcome = await this.params.hooks?.onMessageReceived?.(message, toSessionContract(sessionRow));
|
|
2417
|
+
if (outcome?.outcome === "handled") return;
|
|
2418
|
+
void extractAnswer(message);
|
|
2419
|
+
}
|
|
2420
|
+
async runStatusEffects(job) {
|
|
2421
|
+
const sessionRow = await this.params.sessionRepository.getContext(job.companyId, job.whatsappNumber);
|
|
2422
|
+
await this.params.hooks?.onStatusUpdate?.(job.status, sessionRow ? toSessionContract(sessionRow) : null);
|
|
2423
|
+
}
|
|
2424
|
+
};
|
|
2425
|
+
var ProcessInboundDispatchUseCase = class {
|
|
2426
|
+
static {
|
|
2427
|
+
__name(this, "ProcessInboundDispatchUseCase");
|
|
2428
|
+
}
|
|
2429
|
+
dispatcher;
|
|
2430
|
+
constructor(dispatcher) {
|
|
2431
|
+
this.dispatcher = dispatcher;
|
|
2432
|
+
}
|
|
2433
|
+
async execute(job) {
|
|
2434
|
+
await this.dispatcher.run(job);
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2438
|
+
// src/channel/ReceiveWebhook.use-case.ts
|
|
2369
2439
|
function extractContent(message) {
|
|
2370
2440
|
if (message.text?.body) return message.text.body;
|
|
2371
2441
|
const interactive = message.interactive;
|
|
@@ -2378,13 +2448,6 @@ function extractContent(message) {
|
|
|
2378
2448
|
return message.image?.caption ?? message.document?.caption ?? null;
|
|
2379
2449
|
}
|
|
2380
2450
|
__name(extractContent, "extractContent");
|
|
2381
|
-
function extractAnswer(message) {
|
|
2382
|
-
const interactive = message.interactive;
|
|
2383
|
-
if (interactive?.button_reply) return interactive.button_reply.id;
|
|
2384
|
-
if (interactive?.list_reply) return interactive.list_reply.id;
|
|
2385
|
-
return message.text?.body;
|
|
2386
|
-
}
|
|
2387
|
-
__name(extractAnswer, "extractAnswer");
|
|
2388
2451
|
function extractPayload(message) {
|
|
2389
2452
|
const payload = {};
|
|
2390
2453
|
if (message.interactive) payload["interactive"] = message.interactive;
|
|
@@ -2405,7 +2468,17 @@ var ReceiveWebhookUseCase = class {
|
|
|
2405
2468
|
params;
|
|
2406
2469
|
constructor(params) {
|
|
2407
2470
|
this.params = params;
|
|
2471
|
+
this.dispatcher = new InboundEffectsDispatcher({
|
|
2472
|
+
sessionRepository: params.sessionRepository,
|
|
2473
|
+
...params.hooks ? {
|
|
2474
|
+
hooks: params.hooks
|
|
2475
|
+
} : {},
|
|
2476
|
+
...params.realtime ? {
|
|
2477
|
+
realtime: params.realtime
|
|
2478
|
+
} : {}
|
|
2479
|
+
});
|
|
2408
2480
|
}
|
|
2481
|
+
dispatcher;
|
|
2409
2482
|
async execute(input) {
|
|
2410
2483
|
verifyWebhookSignature({
|
|
2411
2484
|
rawBody: input.rawBody,
|
|
@@ -2444,6 +2517,10 @@ var ReceiveWebhookUseCase = class {
|
|
|
2444
2517
|
}
|
|
2445
2518
|
}
|
|
2446
2519
|
}
|
|
2520
|
+
await confirmWebhookDelivery({
|
|
2521
|
+
nonceStore: this.params.nonceStore,
|
|
2522
|
+
signatureHeader: input.signatureHeader
|
|
2523
|
+
});
|
|
2447
2524
|
return {
|
|
2448
2525
|
duplicate: false,
|
|
2449
2526
|
messagesProcessed,
|
|
@@ -2466,24 +2543,16 @@ var ReceiveWebhookUseCase = class {
|
|
|
2466
2543
|
});
|
|
2467
2544
|
if (!saved) return;
|
|
2468
2545
|
const media = extractMediaDescriptor(saved);
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
});
|
|
2480
|
-
}
|
|
2481
|
-
const sessionRow = await this.params.sessionRepository.getContext(companyId, message.from);
|
|
2482
|
-
if (!sessionRow) return;
|
|
2483
|
-
if (sessionRow.mode === "human") return;
|
|
2484
|
-
const outcome = await this.params.hooks?.onMessageReceived?.(message, toSessionContract(sessionRow));
|
|
2485
|
-
if (outcome?.outcome === "handled") return;
|
|
2486
|
-
void extractAnswer(message);
|
|
2546
|
+
await this.dispatch({
|
|
2547
|
+
kind: "message",
|
|
2548
|
+
companyId,
|
|
2549
|
+
message,
|
|
2550
|
+
savedMessageId: saved.id,
|
|
2551
|
+
...media ? {
|
|
2552
|
+
media
|
|
2553
|
+
} : {},
|
|
2554
|
+
receivedAt: Date.now()
|
|
2555
|
+
});
|
|
2487
2556
|
}
|
|
2488
2557
|
async handleStatus(companyId, status) {
|
|
2489
2558
|
const updated = await this.params.messageRepository.updateMessageStatus(companyId, status.id, status.status);
|
|
@@ -2492,8 +2561,24 @@ var ReceiveWebhookUseCase = class {
|
|
|
2492
2561
|
waMessageId: status.id,
|
|
2493
2562
|
status: status.status
|
|
2494
2563
|
});
|
|
2495
|
-
|
|
2496
|
-
|
|
2564
|
+
await this.dispatch({
|
|
2565
|
+
kind: "status",
|
|
2566
|
+
companyId,
|
|
2567
|
+
status,
|
|
2568
|
+
whatsappNumber: updated.whatsappNumber,
|
|
2569
|
+
receivedAt: Date.now()
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
// Com fila configurada, os efeitos saem da requisição do webhook; sem ela, rodam aqui mesmo e o
|
|
2573
|
+
// comportamento é o de sempre. É o mesmo `InboundEffectsDispatcher` nos dois caminhos.
|
|
2574
|
+
async dispatch(job) {
|
|
2575
|
+
if (this.params.inboundQueue) {
|
|
2576
|
+
await this.params.inboundQueue.enqueue(job, {
|
|
2577
|
+
jobId: buildInboundJobId(job)
|
|
2578
|
+
});
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2581
|
+
await this.dispatcher.run(job);
|
|
2497
2582
|
}
|
|
2498
2583
|
};
|
|
2499
2584
|
|
|
@@ -2806,6 +2891,7 @@ export {
|
|
|
2806
2891
|
FlowMediaRepository,
|
|
2807
2892
|
GetFlowGraphUseCase,
|
|
2808
2893
|
GetLiveFlowPositionsUseCase,
|
|
2894
|
+
InboundEffectsDispatcher,
|
|
2809
2895
|
IngestInboundMediaUseCase,
|
|
2810
2896
|
InvalidFlowGraphError,
|
|
2811
2897
|
ListCompanyDocumentsUseCase,
|
|
@@ -2819,6 +2905,7 @@ export {
|
|
|
2819
2905
|
OptimisticLockError,
|
|
2820
2906
|
PREVIEW_MEDIA_ID_PREFIX,
|
|
2821
2907
|
PRODUCT_LIST_LIMIT,
|
|
2908
|
+
ProcessInboundDispatchUseCase,
|
|
2822
2909
|
PurgeExpiredDocumentsUseCase,
|
|
2823
2910
|
ReceiveWebhookUseCase,
|
|
2824
2911
|
ReleaseConversationUseCase,
|
|
@@ -2832,9 +2919,12 @@ export {
|
|
|
2832
2919
|
TRANSCRIPTION_STATUS,
|
|
2833
2920
|
TakeoverConversationUseCase,
|
|
2834
2921
|
TranscribeAudioUseCase,
|
|
2922
|
+
WEBHOOK_CLAIM_TTL_SECONDS,
|
|
2835
2923
|
WEBHOOK_NONCE_TTL_SECONDS,
|
|
2836
2924
|
WhatsAppChannelAdapter,
|
|
2925
|
+
buildInboundJobId,
|
|
2837
2926
|
claimWebhookDelivery,
|
|
2927
|
+
confirmWebhookDelivery,
|
|
2838
2928
|
createMetaWhatsAppModule,
|
|
2839
2929
|
createSendMediaAction,
|
|
2840
2930
|
createSendProductListAction,
|
|
@@ -2857,6 +2947,7 @@ export {
|
|
|
2857
2947
|
sessions,
|
|
2858
2948
|
settings,
|
|
2859
2949
|
toPreviewMediaId,
|
|
2950
|
+
toSessionContract,
|
|
2860
2951
|
transcriptionRetryAfterSeconds,
|
|
2861
2952
|
verifyWebhookChallenge,
|
|
2862
2953
|
verifyWebhookSignature
|