Skip to content

Bot Multi-Canal (WhatsApp, Messenger, Instagram)

Visão Geral

O bot de pedidos conversacional do PopinaFlow é agnóstico de canal. A máquina de estados central (estado: ConversationStateMachine) delega todo envio e recebimento a um adaptador de canal (IChannelAdapter), permitindo suporte a novos canais sem alterar a lógica de negócio.

Status por canal:

CanalAdaptadorStatus de Produção
WhatsApp (Evolution API)WhatsAppChannelAdapterProdução
Facebook MessengerMessengerChannelAdapterEsqueleto — pendente Meta App Review
Instagram DirectInstagramChannelAdapterEsqueleto — pendente Meta App Review

Localização

CamadaArquivo
Interfacebackend/src/channels/channel-adapter.interface.ts
Adaptador WhatsAppbackend/src/channels/whatsapp.adapter.ts
Adaptador Messengerbackend/src/channels/messenger.adapter.ts
Adaptador Instagrambackend/src/channels/instagram.adapter.ts
Webhook Messengerbackend/src/channels/messenger-webhook.controller.ts
Webhook Instagrambackend/src/channels/instagram-webhook.controller.ts
Schema de configbackend/src/channels/schemas/channel-config.schema.ts
Módulobackend/src/channels/channels.module.ts

Interface IChannelAdapter

typescript
interface IChannelAdapter {
  readonly channel: ChannelType; // 'whatsapp' | 'messenger' | 'instagram'
  sendText(to: string, text: string, creds: ChannelCredentials): Promise<ChannelSendResult>;
  sendMedia(to: string, mediaUrl: string, caption: string, creds: ChannelCredentials): Promise<ChannelSendResult>;
  isConnected(creds: ChannelCredentials): Promise<boolean>;
}

Princípio: adaptadores são stateless — recebem credenciais descriptografadas em cada chamada e não armazenam estado de sessão.

Tipo de mensagem normalizada (ChannelInboundMessage)

Independente do canal, o payload inbound é normalizado para:

CampoTipoDescrição
externalUserIdstringID nativo do canal (E.164 para WhatsApp, PSID para Messenger/IG)
textstring | nullTexto da mensagem. null se somente mídia.
isAudiobooleantrue para mensagens de voz (encaminhadas ao Whisper ASR)
messageIdstringID nativo da mensagem (dedup / auditoria)
audioDurationSecnumber?Duração do áudio em segundos
mediaRefstring?Referência opaca à mídia (media ID da Meta, msgId do Evolution)

Credenciais por Canal (ChannelCredentials)

Armazenadas criptografadas (AES-256-GCM) em documentos ChannelConfig por tenant:

CampoWhatsAppMessenger/IG
instanceIdNome da instância EvolutionMeta phone number ID / Page ID
accessTokenEncryptedToken EvolutionPage Access Token
webhookSecretHMAC secret da instânciaApp Secret (para X-Hub-Signature-256)

WhatsApp (Produção)

O WhatsAppChannelAdapter envolve o IWhatsAppProvider existente (Evolution API), que permanece em backend/src/whatsapp/providers/. Esta separação permite evolução independente:

  • IWhatsAppProvider → notificações admin e fluxos de pedido legados
  • IChannelAdapter → bot de pedidos multi-canal

Messenger e Instagram (Esqueleto — Pendente Meta App Review)

Os adaptadores estão implementados mas não enviam mensagens em produção ainda. As chamadas à Graph API estão comentadas aguardando aprovação do Meta.

O que já está implementado e funcional:

  • verifyHubSignature() — valida o header X-Hub-Signature-256 via HMAC-SHA256(appSecret, rawBody) com comparação constant-time (timingSafeEqual)
  • Endpoint de verificação de webhook (GET) para o desafio de verificação do Meta (hub.challenge)
  • Estrutura de credenciais e deserialização

API Graph (quando ativado):

POST https://graph.facebook.com/v18.0/me/messages
{
  recipient: { id: PSID },
  message: { text: "..." },
  messaging_type: "RESPONSE",
  access_token: PAGE_TOKEN
}

Permissões Meta necessárias:

  • Messenger: pages_messaging
  • Instagram: instagram_manage_messages

Verificação de Assinatura Meta

Tanto Messenger quanto Instagram usam o mesmo esquema HMAC-SHA256:

HMAC-SHA256(appSecret, rawRequestBody) → "sha256=<hex>"

Verificado no header X-Hub-Signature-256. Importante: o body deve ser lido como buffer raw antes de qualquer JSON.parse — caso contrário a assinatura não bate.

Roteamento de Mensagens Inbound

POST /webhooks/channels/messenger  (ou /instagram)

       ├── Verificar X-Hub-Signature-256
       ├── Normalizar payload → ChannelInboundMessage
       └── ConversationStateMachine.handle(tenantId, message, 'messenger')

              ├── Identificar usuário / iniciar sessão de pedido
              ├── Chamar AssistantService (ferramentas de pedido)
              └── IChannelAdapter.sendText() → resposta ao cliente

Habilitando Messenger/Instagram (Checklist Pré-App Review)

  1. Criar App Meta em developers.facebook.com
  2. Solicitar permissão pages_messaging (Messenger) ou instagram_manage_messages (IG)
  3. Configurar webhook URL: https://seu-dominio.com/webhooks/channels/messenger
  4. Adicionar Page Access Token nas credenciais do tenant via Admin → Integrações → Canais
  5. Descomentar o bloco // TODO: enable after Meta App Review nos adaptadores
  6. Ativar o canal em ChannelConfig.enabled = true

Relacionados

Lançado sob a licença MIT.