From a89736f89dd13b8580b4c8fb8cc884c855ba5c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas=20de=20Oliveira=20Lopes?= <55464917+jlucaso1@users.noreply.github.com> Date: Tue, 20 Jan 2026 07:39:29 -0300 Subject: [PATCH] fix: extract LID-PN mappings from history sync phoneNumberToLidMappings (#2268) --- src/Types/Events.ts | 4 +- src/Utils/history.ts | 10 ++- src/Utils/process-message.ts | 10 +++ src/__tests__/Utils/history.test.ts | 94 +++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/__tests__/Utils/history.test.ts diff --git a/src/Types/Events.ts b/src/Types/Events.ts index e4a73f2c..bf0d1d07 100644 --- a/src/Types/Events.ts +++ b/src/Types/Events.ts @@ -1,6 +1,6 @@ import type { Boom } from '@hapi/boom' import { proto } from '../../WAProto/index.js' -import type { AuthenticationCreds } from './Auth' +import type { AuthenticationCreds, LIDMapping } from './Auth' import type { WACallEvent } from './Call' import type { Chat, ChatUpdate, PresenceData } from './Chat' import type { Contact } from './Contact' @@ -36,7 +36,7 @@ export type BaileysEventMap = { 'chats.upsert': Chat[] /** update the given chats */ 'chats.update': ChatUpdate[] - 'lid-mapping.update': { lid: string; pn: string } + 'lid-mapping.update': LIDMapping /** delete chats with given ID */ 'chats.delete': string[] /** presence of contact in a chat updated */ diff --git a/src/Utils/history.ts b/src/Utils/history.ts index 7adbe09c..b8c1a504 100644 --- a/src/Utils/history.ts +++ b/src/Utils/history.ts @@ -1,7 +1,7 @@ import { promisify } from 'util' import { inflate } from 'zlib' import { proto } from '../../WAProto/index.js' -import type { Chat, Contact, WAMessage } from '../Types' +import type { Chat, Contact, LIDMapping, WAMessage } from '../Types' import { WAMessageStubType } from '../Types' import { toNumber } from './generics' import { normalizeMessageContent } from './messages' @@ -29,6 +29,13 @@ export const processHistoryMessage = (item: proto.IHistorySync) => { const messages: WAMessage[] = [] const contacts: Contact[] = [] const chats: Chat[] = [] + // Extract LID-PN mappings for all sync types + const lidPnMappings: LIDMapping[] = [] + for (const m of item.phoneNumberToLidMappings || []) { + if (m.lidJid && m.pnJid) { + lidPnMappings.push({ lid: m.lidJid, pn: m.pnJid }) + } + } switch (item.syncType) { case proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP: @@ -87,6 +94,7 @@ export const processHistoryMessage = (item: proto.IHistorySync) => { chats, contacts, messages, + lidPnMappings, syncType: item.syncType, progress: item.progress } diff --git a/src/Utils/process-message.ts b/src/Utils/process-message.ts index 975bee05..6817fb3b 100644 --- a/src/Utils/process-message.ts +++ b/src/Utils/process-message.ts @@ -287,6 +287,16 @@ const processMessage = async ( const data = await downloadAndProcessHistorySyncNotification(histNotification, options) + // Emit LID-PN mappings from history sync + // This is how WhatsApp Web learns mappings for chats with non-contacts + if (data.lidPnMappings?.length) { + logger?.debug({ count: data.lidPnMappings.length }, 'processing LID-PN mappings from history sync') + // eslint-disable-next-line max-depth + for (const mapping of data.lidPnMappings) { + ev.emit('lid-mapping.update', mapping) + } + } + ev.emit('messaging-history.set', { ...data, isLatest: histNotification.syncType !== proto.HistorySync.HistorySyncType.ON_DEMAND ? isLatest : undefined, diff --git a/src/__tests__/Utils/history.test.ts b/src/__tests__/Utils/history.test.ts new file mode 100644 index 00000000..2676fcb6 --- /dev/null +++ b/src/__tests__/Utils/history.test.ts @@ -0,0 +1,94 @@ +import { proto } from '../../../WAProto/index.js' +import { processHistoryMessage } from '../../Utils/history' + +describe('processHistoryMessage', () => { + describe('phoneNumberToLidMappings extraction', () => { + it('should extract LID-PN mappings from history sync payload', () => { + const historySync: proto.IHistorySync = { + syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP, + conversations: [], + phoneNumberToLidMappings: [ + { lidJid: '11111111111111@lid', pnJid: '1234567890123@s.whatsapp.net' }, + { lidJid: '22222222222222@lid', pnJid: '9876543210987@s.whatsapp.net' } + ] + } + + const result = processHistoryMessage(historySync) + + expect(result.lidPnMappings).toEqual([ + { lid: '11111111111111@lid', pn: '1234567890123@s.whatsapp.net' }, + { lid: '22222222222222@lid', pn: '9876543210987@s.whatsapp.net' } + ]) + }) + + it('should skip mappings with missing lidJid or pnJid', () => { + const historySync: proto.IHistorySync = { + syncType: proto.HistorySync.HistorySyncType.RECENT, + conversations: [], + phoneNumberToLidMappings: [ + { lidJid: undefined, pnJid: '1234567890123@s.whatsapp.net' }, + { lidJid: '11111111111111@lid', pnJid: undefined }, + { lidJid: '22222222222222@lid', pnJid: '9876543210987@s.whatsapp.net' } + ] + } + + const result = processHistoryMessage(historySync) + + expect(result.lidPnMappings).toEqual([{ lid: '22222222222222@lid', pn: '9876543210987@s.whatsapp.net' }]) + }) + + it('should return empty array when no mappings exist', () => { + const historySync: proto.IHistorySync = { + syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP, + conversations: [] + } + + const result = processHistoryMessage(historySync) + + expect(result.lidPnMappings).toEqual([]) + }) + + it('should process mappings regardless of sync type', () => { + const syncTypes = [proto.HistorySync.HistorySyncType.PUSH_NAME, proto.HistorySync.HistorySyncType.ON_DEMAND] + + for (const syncType of syncTypes) { + const historySync: proto.IHistorySync = { + syncType, + conversations: [], + pushnames: [], + phoneNumberToLidMappings: [{ lidJid: '11111111111111@lid', pnJid: '1234567890123@s.whatsapp.net' }] + } + + const result = processHistoryMessage(historySync) + + expect(result.lidPnMappings).toEqual([{ lid: '11111111111111@lid', pn: '1234567890123@s.whatsapp.net' }]) + } + }) + }) + + describe('conversations processing', () => { + it('should extract contacts with LID and PN from conversations', () => { + const historySync: proto.IHistorySync = { + syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP, + conversations: [ + { + id: '1234567890123@s.whatsapp.net', + name: 'Test User', + lidJid: '11111111111111@lid', + pnJid: '1234567890123@s.whatsapp.net' + } + ] + } + + const result = processHistoryMessage(historySync) + + expect(result.contacts).toHaveLength(1) + expect(result.contacts[0]).toEqual({ + id: '1234567890123@s.whatsapp.net', + name: 'Test User', + lid: '11111111111111@lid', + phoneNumber: '1234567890123@s.whatsapp.net' + }) + }) + }) +})