From a039291407216a52f5663ae3b56e95d30fbd5dd0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 21:16:53 +0000 Subject: [PATCH] chore: remove documentation files from PR Documentation files removed as requested - implementation only. https://claude.ai/code/session_01SoNUGBEWbJwWWws3F2fuzh --- DEVICE_MIGRATION_LID_PN.md | 582 ---------------------------- LID_PN_AUTO_MERGE_IMPLEMENTATION.md | 341 ---------------- 2 files changed, 923 deletions(-) delete mode 100644 DEVICE_MIGRATION_LID_PN.md delete mode 100644 LID_PN_AUTO_MERGE_IMPLEMENTATION.md diff --git a/DEVICE_MIGRATION_LID_PN.md b/DEVICE_MIGRATION_LID_PN.md deleted file mode 100644 index 9efd95b5..00000000 --- a/DEVICE_MIGRATION_LID_PN.md +++ /dev/null @@ -1,582 +0,0 @@ -# WhatsApp Device Migration - LID/PN (Identificadores) - -## 📋 Índice -1. [O que é LID/PN](#o-que-é-lidpn) -2. [Por que existe](#por-que-existe) -3. [Como funciona](#como-funciona) -4. [Quando usa LID vs PN](#quando-usa-lid-vs-pn) -5. [Fluxo de Migração](#fluxo-de-migração) -6. [Como aparece nos logs](#como-aparece-nos-logs) -7. [Implementação técnica](#implementação-técnica) - ---- - -## O que é LID/PN - -### PN (Phone Number) -- **Formato**: `12345@s.whatsapp.net` ou `12345:5@s.whatsapp.net` -- **Significado**: Identificador baseado no número de telefone -- **Exemplo**: Se seu número é +55 11 98765-4321, o PN seria `5511987654321@s.whatsapp.net` -- **Com device**: `5511987654321:5@s.whatsapp.net` (device 5 = WhatsApp Web/Desktop) - -### LID (Long-lived Identifier) -- **Formato**: `aaaaa@lid` ou `aaaaa:5@lid` -- **Significado**: Identificador de longa duração desvinculado do número de telefone -- **Exemplo**: `a1b2c3d4e5@lid` -- **Com device**: `a1b2c3d4e5:5@lid` (device 5 = WhatsApp Web/Desktop) - -### Device Numbers -- **0**: Dispositivo principal (celular) -- **1-98**: Dispositivos companheiros (Web, Desktop, etc) -- **99**: Dispositivo "hosted" (companion device especial) - ---- - -## Por que existe - -### Problema Original (Apenas PN) -Quando o WhatsApp usava apenas números de telefone como identificadores: -- **Troca de número**: Usuário perde todo histórico de conversas criptografadas -- **Portabilidade**: Dificulta migração entre operadoras -- **Privacidade**: Número de telefone exposto em todos os metadados -- **Sessões Signal**: Precisavam ser recriadas ao trocar número - -### Solução (LID) -Com LID, o WhatsApp pode: -- **Manter identidade**: Mesmo trocando de número, mantém o mesmo LID -- **Privacidade**: Não expõe número de telefone em todas comunicações -- **Migração suave**: Transição transparente PN → LID -- **Sessões preservadas**: Criptografia mantida mesmo com mudança de número - ---- - -## Como funciona - -### Arquitetura de Armazenamento - -``` -Database (SignalDataTypeMap['lid-mapping']): -├─ Forward Mapping: pn:{phoneUser} → {lidUser} -│ Exemplo: "pn:5511987654321" → "a1b2c3d4e5" -│ -├─ Reverse Mapping: {lidUser}_reverse → {phoneUser} -│ Exemplo: "a1b2c3d4e5_reverse" → "5511987654321" -│ -└─ Device List: device-list:{userId} → [devices...] - Exemplo: "device-list:5511987654321" → ["0", "1", "5", "99"] -``` - -### LRU Cache (In-Memory) -```javascript -Cache Configuration: -- TTL: 3 dias (259.200.000 ms) -- Max Size: 50.000 entradas -- Auto Purge: Ativado -- Update Age on Get: Sim (renova TTL ao acessar) -``` - -### Estrutura de Dados - -```typescript -type LIDMapping = { - pn: string // "12345@s.whatsapp.net" - lid: string // "aaaaa@lid" -} - -// Armazenamento -{ - 'lid-mapping': { - 'pn:12345': 'aaaaa', // Forward - 'aaaaa_reverse': '12345', // Reverse - }, - 'device-list': { - '12345': ['0', '1', '5', '99'] // Devices conhecidos - } -} -``` - ---- - -## Quando usa LID vs PN - -### Usa PN (Phone Number) -1. **Primeiro contato**: Quando nunca conversou com o usuário -2. **Antes da migração**: Sistema ainda não recebeu o LID do servidor -3. **Fallback**: Se lookup do LID falhar -4. **Compatibilidade**: Dispositivos antigos que não suportam LID - -### Usa LID (Long-lived ID) -1. **Após migração bem-sucedida**: Quando `migrateSession()` completou -2. **LID disponível**: Quando existe mapeamento em cache/database -3. **Usuário interagiu**: Depois que o usuário enviou/recebeu mensagem -4. **Prioridade**: LID sempre tem prioridade sobre PN quando disponível - -### Decisão no Envio de Mensagem - -```typescript -// Arquivo: messages-send.ts:346-370 - -const isLidUser = requestedLidUsers.has(user) // Usuário tem LID? - -const finalJid = isLidUser - ? jidEncode(user, item.server, item.device) // USA LID - : jidEncode(item.user, item.server, item.device) // USA PN - -// Exemplo: -// isLidUser = true: "a1b2c3d4e5:5@lid" -// isLidUser = false: "5511987654321:5@s.whatsapp.net" -``` - ---- - -## Fluxo de Migração - -### 1. Login/Conexão (Trigger Inicial) - -``` -socket.ts:1462-1487 - -1. Cliente conecta ao WhatsApp - └─> Envia credenciais - -2. Servidor responde com SUCCESS - └─> ← SERVIDOR ENVIA O LID - -3. Sistema armazena mapeamento próprio - └─> await lidMapping.storeLIDPNMappings([{ - lid: "a1b2c3d4e5@lid", - pn: "5511987654321@s.whatsapp.net" - }]) - -4. Cria device list - └─> device-list[5511987654321] = ["0", "1", "5", "99"] - -5. TRIGGER MIGRAÇÃO EM MASSA - └─> await signalRepository.migrateSession( - "5511987654321@s.whatsapp.net", // FROM (PN) - "a1b2c3d4e5@lid" // TO (LID) - ) -``` - -### 2. Migração de Sessão Signal - -``` -libsignal.ts:484-635 - -migrateSession(fromPN, toLID): -├─ Validação -│ └─ Apenas suporta PN → LID (não LID → PN) -│ -├─ Busca devices do device-list -│ └─ ["0", "1", "5", "99"] -│ -├─ Filtra devices com sessão Signal existente -│ └─ [0, 5] (apenas esses têm sessão ativa) -│ -├─ Cria operações de migração para cada device: -│ ├─ Device 0: -│ │ FROM: 5511987654321:0@s.whatsapp.net -│ │ TO: a1b2c3d4e5:0@lid -│ │ -│ └─ Device 5: -│ FROM: 5511987654321:5@s.whatsapp.net -│ TO: a1b2c3d4e5:5@lid -│ -├─ Executa transação atômica: -│ ├─ Copia sessão: session[a1b2c3d4e5:0@lid] = session[5511987654321:0@s.whatsapp.net] -│ ├─ Deleta antiga: session[5511987654321:0@s.whatsapp.net] = null -│ ├─ Copia sessão: session[a1b2c3d4e5:5@lid] = session[5511987654321:5@s.whatsapp.net] -│ └─ Deleta antiga: session[5511987654321:5@s.whatsapp.net] = null -│ -├─ Cacheia migrações completadas (TTL: 7 dias) -│ └─ migratedSessionCache.add("5511987654321:0") -│ -└─ Retorna: { migrated: 2, skipped: 0, total: 2 } -``` - -### 3. Descoberta de Devices no Envio - -``` -messages-send.ts:303-403 - -Ao enviar mensagem para usuário: -├─ Executa USyncQuery -│ └─> .withDeviceProtocol() -│ └─> .withLIDProtocol() -│ -├─ Servidor responde com: -│ ├─ Device list: [0, 1, 5, 99] -│ ├─ LID: "a1b2c3d4e5" -│ └─ Identity keys de cada device -│ -├─ Sistema armazena: -│ ├─ device-list[user] = [devices...] -│ └─> Permite migração em massa futura -│ -└─ Prioriza uso de LID se disponível -``` - -### 4. USync Protocol (Busca de LID) - -``` -socket.ts:470-493 - -pnFromLIDUSync(jids: string[]): -├─ Cria USyncQuery -│ └─> .withLIDProtocol() -│ └─> .withContext('background') -│ -├─ Adiciona usuários -│ └─> .withUser(new USyncUser().withId(jid)) -│ -├─ Executa query no servidor WhatsApp -│ └─> const results = await executeUSyncQuery(usyncQuery) -│ -└─ Retorna mapeamentos: - [{ - pn: "5511987654321@s.whatsapp.net", - lid: "a1b2c3d4e5@lid" - }] -``` - ---- - -## Como aparece nos logs - -### Cenário 1: Primeiro Contato (Apenas PN) - -``` -[INFO] Sending message to: 5511987654321@s.whatsapp.net -[DEBUG] No LID mapping found, using PN -[DEBUG] Querying devices for: 5511987654321@s.whatsapp.net -[DEBUG] USyncQuery: withDeviceProtocol, withLIDProtocol -[INFO] Devices found: [0, 1, 5] -[INFO] LID received from server: a1b2c3d4e5 -[DEBUG] Storing LID mapping: 5511987654321 → a1b2c3d4e5 -[DEBUG] Storing device list: [0, 1, 5] -[INFO] Message encrypted for PN: 5511987654321:0@s.whatsapp.net -``` - -### Cenário 2: Após Interação (Usuário envia mensagem) - -``` -[INFO] Message received from: 5511987654321@s.whatsapp.net -[DEBUG] Checking for LID mapping... -[DEBUG] LID found in cache: a1b2c3d4e5@lid -[INFO] Triggering session migration... -[DEBUG] migrateSession(5511987654321@s.whatsapp.net → a1b2c3d4e5@lid) -[DEBUG] Found devices in device-list: [0, 1, 5] -[DEBUG] Migrating device 0: session copied and old deleted -[DEBUG] Migrating device 5: session copied and old deleted -[INFO] Migration completed: { migrated: 2, skipped: 0 } -[DEBUG] Cached migration for 7 days -``` - -### Cenário 3: Envio Subsequente (Usa LID) - -``` -[INFO] Sending message to: 5511987654321@s.whatsapp.net -[DEBUG] Checking for LID mapping... -[DEBUG] LID found in cache: a1b2c3d4e5@lid (cache hit) -[DEBUG] Using LID for encryption -[INFO] Message encrypted for LID: a1b2c3d4e5:0@lid -[INFO] Message encrypted for LID: a1b2c3d4e5:5@lid -[DEBUG] Encryption used migrated session (no re-establishment needed) -``` - -### Cenário 4: Cache Miss (Busca no Database) - -``` -[INFO] Sending message to: 5511988888888@s.whatsapp.net -[DEBUG] Cache miss for PN: 5511988888888 -[DEBUG] Fetching from database: lid-mapping:pn:5511988888888 -[DEBUG] Database hit: found LID mapping → b2c3d4e5f6 -[DEBUG] Updating cache (TTL: 3 days) -[INFO] Using LID: b2c3d4e5f6:0@lid -``` - -### Cenário 5: USync Fallback (Não está no DB) - -``` -[INFO] Sending message to: 5511977777777@s.whatsapp.net -[DEBUG] Cache miss for PN: 5511977777777 -[DEBUG] Database miss: no mapping found -[DEBUG] Triggering USync fetch... -[DEBUG] USyncQuery with LIDProtocol for: 5511977777777 -[INFO] USync response received: LID = c3d4e5f6g7 -[DEBUG] Storing LID mapping in database -[DEBUG] Updating cache -[INFO] Using LID: c3d4e5f6g7:0@lid -``` - -### Exemplo de Log Real Completo - -``` -2026-02-09 10:15:32 [socket.ts:1462] Connection success, lid=a1b2c3d4e5 -2026-02-09 10:15:32 [lid-mapping.ts:245] storeLIDPNMappings: 1 mapping(s) -2026-02-09 10:15:32 [lid-mapping.ts:267] Phase 1: Validated 1, cache miss 1 -2026-02-09 10:15:32 [lid-mapping.ts:305] Phase 3: Stored 1 new mapping(s) -2026-02-09 10:15:32 [socket.ts:1475] Stored device-list for user: 5511987654321 -2026-02-09 10:15:32 [libsignal.ts:491] migrateSession: PN → LID -2026-02-09 10:15:32 [libsignal.ts:522] Found 2 devices: [0, 5] -2026-02-09 10:15:32 [libsignal.ts:589] Migrated device 0: a1b2c3d4e5:0@lid -2026-02-09 10:15:32 [libsignal.ts:589] Migrated device 5: a1b2c3d4e5:5@lid -2026-02-09 10:15:32 [libsignal.ts:627] Migration result: {migrated:2, skipped:0} - -... usuário interage ... - -2026-02-09 10:20:15 [messages-send.ts:346] Preparing message for 5511987654321 -2026-02-09 10:20:15 [lid-mapping.ts:158] getLIDForPN: 5511987654321 -2026-02-09 10:20:15 [lid-mapping.ts:164] Cache hit: a1b2c3d4e5 -2026-02-09 10:20:15 [messages-send.ts:370] Using LID: a1b2c3d4e5:0@lid -2026-02-09 10:20:15 [messages-send.ts:370] Using LID: a1b2c3d4e5:5@lid -2026-02-09 10:20:15 [libsignal.ts:180] Encrypting with session: a1b2c3d4e5:0@lid -2026-02-09 10:20:15 [libsignal.ts:180] Encrypting with session: a1b2c3d4e5:5@lid -2026-02-09 10:20:15 [socket.ts:892] Message sent successfully -``` - ---- - -## Implementação Técnica - -### Arquivos Principais - -``` -src/ -├── Signal/ -│ ├── lid-mapping.ts (1.166 linhas) -│ │ └─> LIDMappingStore: Cache LRU + Database + USync -│ │ -│ └── libsignal.ts -│ └─> migrateSession(): Migração PN → LID -│ -├── Socket/ -│ ├── socket.ts -│ │ ├─> Inicialização: Armazena LID próprio -│ │ └─> Trigger migração no login -│ │ -│ └── messages-send.ts -│ ├─> Device query com USyncQuery -│ └─> Decisão LID vs PN -│ -├── Utils/ -│ └── sync-action-utils.ts -│ └─> LID mapping events -│ -└── Types/ - └── Auth.ts - └─> type LIDMapping = { pn, lid } -``` - -### LIDMappingStore - Operações Principais - -```typescript -class LIDMappingStore { - // 1. ARMAZENAR MAPEAMENTOS - async storeLIDPNMappings(mappings: LIDMapping[]): Promise { - // 3 fases: Validate → Fetch → Store - // Batch size: 100 (configurável) - // Retry: 3 tentativas com backoff exponencial - } - - // 2. BUSCAR LID PARA PN (com device) - async getLIDForPN(pn: string): Promise { - // Input: "5511987654321:5@s.whatsapp.net" - // Output: "a1b2c3d4e5:5@lid" - // Flow: Cache → Database → USync (fallback) - // Request coalescing: Deduplica requests concorrentes - } - - // 3. BUSCAR PN PARA LID (reverse) - async getPNForLID(lid: string): Promise { - // Input: "a1b2c3d4e5:5@lid" - // Output: "5511987654321:5@s.whatsapp.net" - // Flow: Cache → Database → USync (fallback) - } - - // 4. OPERAÇÕES EM LOTE - async getLIDsForPNs(pns: string[]): Promise> { - // Processa múltiplos PNs em paralelo - // Batch size configurable - } - - async getPNsForLIDs(lids: string[]): Promise> { - // Processa múltiplos LIDs em paralelo - } -} -``` - -### Request Coalescing (Deduplicação) - -```typescript -// Problema: 10 chamadas simultâneas para o mesmo PN -const promises = Array(10).fill(null).map(() => - lidMapping.getLIDForPN("5511987654321@s.whatsapp.net") -) - -// Sem coalescing: 10 queries ao database ❌ -// Com coalescing: 1 query compartilhada ✅ - -// Implementação: -pendingRequests.set(key, dbPromise) -// Requests subsequentes reutilizam a mesma Promise -return pendingRequests.get(key) -``` - -### Configuração de Ambiente - -```bash -# LID Cache TTL (Tempo de vida) -BAILEYS_LID_CACHE_TTL_MS=259200000 # 3 dias (min: 60s, max: 30 dias) - -# Tamanho máximo do cache -BAILEYS_LID_MAX_CACHE_SIZE=50000 # 50k entradas (min: 100, max: 1M) - -# Batch size para operações em lote -BAILEYS_LID_BATCH_SIZE=100 # (min: 1, max: 1000) - -# Retry attempts -BAILEYS_LID_RETRY_ATTEMPTS=3 # (min: 1, max: 10) -BAILEYS_LID_RETRY_DELAY_MS=1000 # 1s base + exponential backoff - -# Features -BAILEYS_LID_CACHE_AUTO_PURGE=true # Auto cleanup de cache -BAILEYS_LID_UPDATE_AGE_ON_GET=true # Renova TTL ao acessar -BAILEYS_LID_METRICS=false # Estatísticas detalhadas -BAILEYS_LID_DEBUG=false # Logs de debug -``` - -### Estatísticas (Metrics) - -```typescript -getStats() { - return { - cacheSize: 15234, // Entradas no cache - cacheHits: 45678, // Acertos no cache - cacheMisses: 1234, // Falhas de cache - cacheHitRate: 0.973, // 97.3% hit rate - databaseHits: 892, // Buscas no database - databaseMisses: 342, // Não encontrado no DB - usyncFetches: 342, // Fallback para USync - usyncFailures: 5, // Falhas no USync - mappingsStored: 16789, // Total armazenado - invalidMappings: 23, // Rejeitados (inválidos) - operationsInProgress: 3 // Operações ativas (UAF protection) - } -} -``` - -### Device Migration Cache - -```typescript -// Cache de migrações completadas (evita re-processar) -migratedSessionCache = new Map() -// Key: "5511987654321:0" (PN + device) -// Value: timestamp da migração - -// TTL: 7 dias -const MIGRATION_CACHE_TTL = 7 * 24 * 60 * 60 * 1000 - -// Cleanup automático de entradas expiradas -setInterval(() => { - const now = Date.now() - for (const [key, timestamp] of migratedSessionCache.entries()) { - if (now - timestamp > MIGRATION_CACHE_TTL) { - migratedSessionCache.delete(key) - } - } -}, 60 * 60 * 1000) // Executa a cada 1 hora -``` - ---- - -## Resumo Executivo - -### O Fluxo Completo em Etapas - -``` -1. LOGIN - └─> Servidor envia LID do usuário próprio - └─> Sistema armazena: PN ↔ LID (self mapping) - └─> Trigger migração de sessões Signal: PN → LID - -2. PRIMEIRO ENVIO PARA CONTATO - └─> Usa PN (não tem LID ainda) - └─> USyncQuery descobre devices + LID - └─> Armazena LID mapping + device list - -3. USUÁRIO INTERAGE (envia mensagem) - └─> Sistema detecta PN no from - └─> Busca LID (cache/database/usync) - └─> Trigger migração de sessão para esse contato - -4. ENVIOS SUBSEQUENTES - └─> Usa LID (já migrado) - └─> Cache hit (rápido) - └─> Encriptação usa sessão LID -``` - -### Por que você vê PN e LID nos logs? - -``` -Momento 1: Primeira interação -├─ Sistema ainda usa PN -├─ Descobre LID via USync -└─ Logs mostram: "using PN" → "LID discovered" - -Momento 2: Migração acontece -├─> Sessões Signal copiadas PN → LID -└─> Logs mostram: "migrated 2 devices" - -Momento 3: Próximas mensagens -├─> Sistema usa LID -└─> Logs mostram: "using LID" (cache hit) -``` - -### Benefícios da Implementação - -✅ **Performance** -- Cache LRU com 97%+ hit rate -- Request coalescing evita duplicação -- Batch operations reduzem roundtrips - -✅ **Confiabilidade** -- Retry com exponential backoff -- Graceful degradation (fallback para PN) -- Transaction support para atomicidade - -✅ **Segurança** -- Sessões Signal preservadas -- Identity keys validados -- Detecção de device reinstall - -✅ **Escalabilidade** -- Cache configurável (até 1M entradas) -- Batch size ajustável -- Auto purge de dados antigos - -✅ **Observabilidade** -- Métricas detalhadas -- Debug logging configurável -- Statistics para monitoramento - ---- - -## Conclusão - -O sistema de **Device Migration com LID/PN** é uma implementação **production-grade** que permite ao WhatsApp migrar usuários de identificadores baseados em número de telefone (PN) para identificadores de longa duração (LID) de forma **transparente**, **eficiente** e **segura**. - -A transição acontece automaticamente quando: -1. ✅ Usuário loga (recebe LID do servidor) -2. ✅ Envia mensagem (descobre LID do destinatário) -3. ✅ Recebe mensagem (descobre LID do remetente) - -Após migração, **todas as comunicações usam LID**, preservando: -- 🔐 Sessões criptográficas Signal -- 🆔 Identidade mesmo com troca de número -- 🚀 Performance com cache inteligente -- 🔄 Compatibilidade com sistemas antigos (fallback PN) - -**Nos logs, você verá**: -- **PN** → Primeira interação ou fallback -- **LID** → Após descoberta e migração (estado ideal) diff --git a/LID_PN_AUTO_MERGE_IMPLEMENTATION.md b/LID_PN_AUTO_MERGE_IMPLEMENTATION.md deleted file mode 100644 index 964e4c91..00000000 --- a/LID_PN_AUTO_MERGE_IMPLEMENTATION.md +++ /dev/null @@ -1,341 +0,0 @@ -# Implementação: Merge Automático LID/PN - -## 📋 Resumo - -Esta implementação resolve o problema de duplicação de conversas causado por identificadores LID (Long-lived ID) e PN (Phone Number) do WhatsApp, implementando merge automático na API (Opção A+). - -## 🎯 Objetivos Alcançados - -✅ **Consolidação de eventos**: `lid-mapping.update` agora é bufferável -✅ **Merge automático**: API detecta e notifica mudanças LID→PN -✅ **Backward compatible**: ZPRO antigo continua funcionando -✅ **Zero duplicação**: Consumidores recebem notificação para unificar chats - -## 📝 Mudanças Implementadas - -### 1. **Event Buffer** (`src/Utils/event-buffer.ts`) - -#### Adicionado `lid-mapping.update` ao array de eventos bufferáveis: -```typescript -const BUFFERABLE_EVENT = [ - // ... eventos existentes - 'lid-mapping.update' // ← NOVO -] as const -``` - -**Benefício**: Eventos LID mapping agora são consolidados em batches junto com mensagens, reduzindo 50% de eventos separados. - -#### Adicionado lógica de append para `lid-mapping.update`: -```typescript -case 'lid-mapping.update': - const lidMappings = eventData as BaileysEventMap['lid-mapping.update'] - for (const mapping of lidMappings) { - const key = `${mapping.lid}-${mapping.pn}` - if (!data.lidMappings[key]) { - data.lidMappings[key] = mapping - } - } - break -``` - -#### Adicionado consolidação em `consolidateEvents()`: -```typescript -const lidMappingList = Object.values(data.lidMappings) -if (lidMappingList.length) { - map['lid-mapping.update'] = lidMappingList -} -``` - -#### Adicionado inicialização em `makeBufferData()`: -```typescript -return { - // ... campos existentes - lidMappings: {} // ← NOVO -} -``` - ---- - -### 2. **Tipos** (`src/Types/Events.ts`) - -#### Adicionado `lidMappings` ao `BufferedEventData`: -```typescript -export type BufferedEventData = { - // ... campos existentes - lidMappings: { [key: string]: LIDMapping } // ← NOVO -} -``` - ---- - -### 3. **Tipos de Chat** (`src/Types/Chat.ts`) - -#### Estendido `ChatUpdate` com campos de merge: -```typescript -export type ChatUpdate = Partial< - Chat & { - // ... campos existentes - /** indicates if this chat was merged from LID to PN */ - merged?: boolean - /** previous chat ID before merge (LID format) */ - previousId?: string - /** timestamp when the merge occurred */ - mergedAt?: number - } -> -``` - -**Nota**: Campos são **opcionais** para manter backward compatibility. - ---- - -### 4. **Socket de Chats** (`src/Socket/chats.ts`) - -#### Implementado merge automático no handler `lid-mapping.update`: - -```typescript -ev.on('lid-mapping.update', async (mappings) => { - try { - // 1. Armazenar mapeamentos (lógica existente) - const result = await signalRepository.lidMapping.storeLIDPNMappings(mappings) - - // 2. NOVO: Notificar consumidores sobre merge - for (const mapping of mappings) { - const lidUser = jidNormalizedUser(mapping.lid) - const pnUser = jidNormalizedUser(mapping.pn) - - if (lidUser && pnUser && lidUser !== pnUser) { - // Emite evento de chat update com metadados de merge - ev.emit('chats.update', [{ - id: pnUser, - merged: true, - previousId: lidUser, - mergedAt: Date.now() - }]) - } - } - } catch (error) { - logger.warn({ count: mappings.length, error }, 'Failed to store LID-PN mappings') - } -}) -``` - -**Comportamento**: -1. API detecta novo mapeamento LID→PN -2. Armazena mapeamento internamente -3. Emite evento `chats.update` com campos `merged`, `previousId`, `mergedAt` -4. ZPRO recebe notificação e pode unificar chats localmente - ---- - -## 🔄 Fluxo de Funcionamento - -### Antes (Problema): -``` -1. Mensagem chega com LID - └─> ZPRO cria chat: "123@lid" - -2. Cliente interage, descobre PN - └─> API emite lid-mapping.update (evento SEPARADO) - └─> ZPRO recebe mensagem com PN - └─> ZPRO cria OUTRO chat: "5511999@s.whatsapp.net" - -RESULTADO: 2 CONVERSAS DUPLICADAS ❌ -``` - -### Depois (Solução): -``` -1. Mensagem chega com LID - └─> ZPRO cria chat: "123@lid" - -2. Cliente interage, descobre PN - └─> API detecta LID→PN mapping - └─> API emite em BATCH consolidado: - ├─ messages.upsert (mensagens) - ├─ lid-mapping.update (mapeamento) - └─ chats.update ({ merged: true, previousId: "123@lid" }) - -3. ZPRO recebe batch - └─> Detecta campo "merged: true" - └─> Renomeia chat de "123@lid" para "5511999@s.whatsapp.net" - └─> Unifica mensagens - -RESULTADO: 1 CONVERSA UNIFICADA ✅ -``` - ---- - -## 💻 Como ZPRO Deve Implementar - -### Código Exemplo: - -```typescript -// ZPRO precisa apenas escutar chats.update -sock.ev.on('chats.update', async (chats) => { - for (const chat of chats) { - // Detectar merge - if (chat.merged && chat.previousId) { - console.log(`Chat merged: ${chat.previousId} → ${chat.id}`) - - // 1. Renomear chat no banco de dados - await database.chats.update( - { id: chat.previousId }, - { id: chat.id } - ) - - // 2. Atualizar mensagens (opcional, se armazenadas separadamente) - await database.messages.updateMany( - { chatId: chat.previousId }, - { chatId: chat.id } - ) - - // 3. Atualizar UI - updateChatInUI(chat.previousId, chat.id) - } - } -}) -``` - -**Backward Compatibility**: -```typescript -// ZPRO ANTIGO (sem suporte a merge) -sock.ev.on('chats.update', async (chats) => { - for (const chat of chats) { - updateChat(chat.id, chat.unreadCount) - // Ignora campos "merged", "previousId" automaticamente ✅ - } -}) -// Funciona sem quebrar! Apenas não aproveita o merge. -``` - ---- - -## 📊 Performance - -### Impacto de Performance (120 instâncias, 1200 msgs/dia): - -| Métrica | Antes | Depois | Melhoria | -|---------|-------|--------|----------| -| **Eventos emitidos** | 2 separados | 1 batch | 50% ↓ | -| **Latência buffer** | N/A | +30-100ms | Imperceptível | -| **Processamento ZPRO** | 2 rodadas | 1 rodada | 50% ↓ | -| **Memória API** | N/A | +7MB/instância | Desprezível | -| **CPU** | N/A | <1% | Desprezível | - -**Conclusão**: Zero impacto negativo, melhorias significativas em consolidação. - ---- - -## ✅ Checklist de Testes - -### Testes na API (Baileys): -- [x] `lid-mapping.update` é bufferável -- [x] Eventos consolidam em batch -- [x] `chats.update` contém campos `merged`, `previousId`, `mergedAt` -- [ ] Teste unitário de consolidação de buffer -- [ ] Teste de integração com 10-120 instâncias -- [ ] Benchmark de performance - -### Testes no ZPRO: -- [ ] ZPRO recebe campo `merged: true` -- [ ] ZPRO renomeia chat corretamente -- [ ] ZPRO unifica mensagens -- [ ] Backward compatibility (ZPRO antigo não quebra) -- [ ] UI atualiza após merge - ---- - -## 🚀 Deployment - -### Fase 1: Deploy da API ✅ -```bash -# Implementação completa -git checkout claude/lid-pn-auto-merge-implementation-c96aba -git push origin claude/lid-pn-auto-merge-implementation-c96aba - -# Após review e merge -# Deploy em produção -``` - -### Fase 2: Atualização do ZPRO (Opcional) -```bash -# ZPRO pode atualizar quando quiser para aproveitar merge -# Não há pressa - backward compatible! -``` - ---- - -## 📚 Referências - -- [DEVICE_MIGRATION_LID_PN.md](./DEVICE_MIGRATION_LID_PN.md) - Documentação completa sobre LID/PN -- [Análise de Performance](./DEVICE_MIGRATION_LID_PN.md#-teste-de-escala-10-a-120-instâncias) - Detalhes de performance - ---- - -## 🎓 Notas Importantes - -### 1. **Por que não fazer merge de mensagens na API?** - -**Resposta**: Merge de mensagens seria muito complexo e custoso: -- API não tem acesso direto ao banco de mensagens do ZPRO -- Cada consumidor pode ter estrutura de dados diferente -- Melhor deixar consumidor decidir como mergear - -### 2. **O que acontece se ZPRO não implementar o merge?** - -**Resposta**: Continua funcionando, apenas com duplicação: -- ZPRO recebe eventos normalmente -- Campos `merged`, `previousId` são ignorados -- Sistema não quebra (backward compatible) -- Quando ZPRO implementar, merge será automático - -### 3. **Quantos eventos ZPRO recebe?** - -**Antes**: -``` -1. messages.upsert (buffered) -2. lid-mapping.update (IMMEDIATE - não bufferável) -Total: 2 eventos em momentos diferentes -``` - -**Depois**: -``` -1. Batch consolidado: - - messages.upsert - - lid-mapping.update - - chats.update (com merged: true) -Total: 1 evento batch -``` - -### 4. **Campos sem underscore** - -Conforme solicitado, **TODOS os campos não usam `_` prefix**: -- ✅ `merged` (não `_merged`) -- ✅ `previousId` (não `_previousId`) -- ✅ `mergedAt` (não `_mergedAt`) - ---- - -## 🔧 Troubleshooting - -### Problema: ZPRO continua duplicando chats - -**Solução**: ZPRO precisa implementar handler de `merged: true` - -### Problema: Performance degradou - -**Solução**: Verificar configuração de buffer: -```bash -BAILEYS_BUFFER_TIMEOUT_MS=30000 # 30s padrão -BAILEYS_BUFFER_MAX_SIZE=5000 # Limite de eventos -``` - -### Problema: Eventos não consolidam - -**Solução**: Verificar se `lid-mapping.update` está em BUFFERABLE_EVENT - ---- - -**Implementado por**: Claude (Opção A+) -**Data**: 2026-02-09 -**Branch**: `claude/lid-pn-auto-merge-implementation-c96aba`