feat(engine): typed WaId + persistent lid->phone resolution table + message from-filter#374
Conversation
…essage from-filter Implements rmyndharis#349 (the typed-WaId + resolution-table follow-up to rmyndharis#342). - WaId value object (src/engine/identity/wa-id.value.ts): in-memory only, serializes byte-identically to today's neutral JID; three-valued refersToSamePerson. - lid_mappings table on the data connection (global, last-write-wins, nullable phone for a negative cache) + portable pg/sqlite migration; LidMappingStore loads on boot and writes through, keeping resolvePhone synchronous. - Back resolvePhone with the table; populate it at runtime from the lid<->phone pairs the Baileys engine actually carries: inbound senderPn/participantPn, chats.phoneNumberShare, contacts (jid), and history sync. - from-filter on GET messages that resolves a phone to its lids, so a lid-resolved match becomes a hit (closes the silent-miss gap); covered by a test. - On-demand POST :chatId/history/sync (Baileys fetchMessageHistory) to backfill mappings on an already-authed session. - Canonicalize Baileys contact/chat listing ids to @c.us (read-back folds the neutral id back to the engine dialect so send/mark-read round-trip). RESOLVE_LID_TO_PHONE resolves internally into the table and gates only what is exposed (privacy flag, not a correctness toggle), per maintainer guidance on rmyndharis#349.
…llback Small maintainer touch-ups on top of the contributor's work: - pass a LidMappingStoreService fake as EngineFactory's new 4th constructor arg in all engine.factory.spec call sites (they constructed with 3 args, so the new wiring was exercised as undefined) - DROP INDEX/TABLE IF EXISTS in the migration down() so rollback is safe when the table was created by the synchronize path (auto-named index) - add a migration spec mirroring the sibling AddBaileysStoredMessages test
|
Thank you for this — it's a genuinely thorough piece of work, and I appreciate how carefully you staged the persistence and kept I hope you don't mind — I pushed three small maintainer touch-ups directly to the branch so we could land it without a round-trip:
Everything else is yours, and the design decisions are unchanged. One note for release framing: the contact/chat listing-id flip to Merging now. Thanks again for the careful work — this is a strong foundation for the identity arc. |
…debug logging (#375) Pass shouldSyncHistoryMessage: () => true so the Baileys initial contacts/chats/app-state sync actually runs (full-archive download stays opt-in via BAILEYS_SYNC_FULL_HISTORY); make the chatId history filter dialect-agnostic (@c.us also returns @s.whatsapp.net-stored rows); add an opt-in, silent-by-default BAILEYS_LOG_LEVEL logger. Builds on #374.
|
Thanks @rmyndharis appreciate your additional fixes and kind words |
Closes #349.
Typed
WaIdvalue object + a persistent, cross-sessionlid -> phoneresolution table + afromfilter on message history. On top ofmain@ v0.4.5.Composes with the v0.4.5 Baileys work (not duplicates)
recordKeyLidMappings(key)(inboundsenderPn/participantPn). This PR lets it feed the durable table: the method callsaddLidMappings, which now writes through to the persistentlid_mappingstable - so fix(baileys): resolve @lid senders to a phone number (#362) #372's inbound capture now persists cross-session for free.resolveContactNameis merged with this PR's contact/chat id canonicalization intoNeutralChat(neutral@c.usid and resolved name).Motivation
Lid resolution was non-deterministic and ephemeral: a lid sender could be unresolved (
@lid) early and resolved (@c.us) later once thelid -> pnmap populated, and that map lived only in one Baileys session's memory (lost on restart, not shared across sessions). So a phone-basedfromfilter matched a DM but silently missed the same person's already-resolved group message. A durable, shared table closes that; a typedWaIdmakes "a lid whose phone we don't know" first-class in the type.New
src/engine/identity/wa-id.value.ts- theWaIdvalue object over thewa-id.tsprimitives. In-memory only;toNeutral()/toString()/toJSON()yield today's neutral string; three-valuedrefersToSamePerson()(matched / didn't match / couldn't tell).src/engine/identity/lid-mapping.entity.ts+src/database/migrations/1781200000000-AddLidMappings.ts-LidMappingon thedataconnection (portable Postgres + SQLite migration).src/engine/identity/lid-mapping-store.service.ts- boot-loads the table into an in-memory map, synchronous forward (getCached) + reverse (lidsForPhone) lookups, write-throughremember().Wired (no contract changes)
EngineFactory -> BaileysPlugin -> adapter config -> BaileysSessionStore, mirroring the existingmessageStoreseam.addLidMappings/upsertContactswrite through to the table;resolvePhonefalls back to the table's in-memory cache (kept synchronous).GET /api/sessions/:sessionId/messages?from=resolves through the table - a phone matches@c.us/@s.whatsapp.netstored ids and any lid resolving to it.@c.us(the feat(engine): engine-neutral WhatsApp identities (Baileys inbound conformance) #342-domain conformance that was deferred), merged with fix(baileys): show saved/contact names in the Chats list (#369) #370's name resolution; read-back lookups fold a neutral id back to the engine dialect so the round-trip still hits.Acceptance criteria (from your review)
WaIdis internal; a spec assertsWaId.toNeutral()equalstoNeutralJid()for every representative id.dataconnection. Entity ondata, portable migration, boot-verified.111 -> 628999, filteringfrom=628999returns the lid-authored row; a control test shows the prior silent miss.Settled open questions
WaIdin-memory. ✅6.7.23has nosignalRepository.lidMappinglid->phone lookup, so unknown lids degrade to "couldn't tell" (@lid); resolution is fed by passive sources (inboundsenderPn/participantPnvia fix(baileys): resolve @lid senders to a phone number (#362) #372,phoneNumberShare, contacts, history sync). ✅RESOLVE_LID_TO_PHONEresolves internally into the table and gates only what's exposed (privacy flag, not a correctness toggle). ✅Deferred (issue #349 scope steps 3-6)
Module-by-module migration of consumers onto
WaId(behind the existing string fields); active, lazy per-misslid->phonelookup. The lid key-flip limitation you're tracking under #349 is addressed at the foundation (durable, cross-session identity); re-keying the specific in-memory consumers is part of the deferred migration.A follow-up PR (
feat/baileys-sync-hardening) stacks Baileys sync hardening on top of this.