feat(upstream): add comprehensive LID-PN mapping extraction for Baileys PR
This folder contains the files ready for upstream WhiskeySockets/Baileys PR: Changes to src/Utils/history.ts: - Add isPersonJid() helper to validate JID types for mapping eligibility - Add extractLidPnFromConversation() for conversation-level mapping extraction - Add extractLidPnFromMessage() for message-level mapping extraction - Modify processHistoryMessage() to extract from 3 sources with deduplication - Fix validation bug: use || (OR) instead of && (AND) to prevent poisoned mappings Changes to src/__tests__/Utils/history.test.ts: - 55 comprehensive tests covering all new functions and edge cases - Tests for all JID formats (@lid, @hosted.lid, @s.whatsapp.net, @hosted) - Tests for skipped JIDs (@g.us, @broadcast, @newsletter) - Tests for bug fix validation (person + group, person + broadcast, etc.) - Tests for deduplication across all three sources Related issues: - Closes WhiskeySockets/Baileys#2282 - Partially addresses WhiskeySockets/Baileys#2281
This commit is contained in:
@@ -0,0 +1,292 @@
|
|||||||
|
import { promisify } from 'util'
|
||||||
|
import { inflate } from 'zlib'
|
||||||
|
import { proto } from '../../WAProto/index.js'
|
||||||
|
import type { Chat, Contact, LIDMapping, WAMessage } from '../Types'
|
||||||
|
import { WAMessageStubType } from '../Types'
|
||||||
|
import { toNumber } from './generics'
|
||||||
|
import { normalizeMessageContent } from './messages'
|
||||||
|
import { downloadContentFromMessage } from './messages-media'
|
||||||
|
|
||||||
|
const inflatePromise = promisify(inflate)
|
||||||
|
|
||||||
|
export const downloadHistory = async (msg: proto.Message.IHistorySyncNotification, options: RequestInit) => {
|
||||||
|
const stream = await downloadContentFromMessage(msg, 'md-msg-hist', { options })
|
||||||
|
const bufferArray: Buffer[] = []
|
||||||
|
for await (const chunk of stream) {
|
||||||
|
bufferArray.push(chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer: Buffer = Buffer.concat(bufferArray)
|
||||||
|
|
||||||
|
// decompress buffer
|
||||||
|
buffer = await inflatePromise(buffer)
|
||||||
|
|
||||||
|
const syncData = proto.HistorySync.decode(buffer)
|
||||||
|
return syncData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a JID represents a person (not a group, broadcast, or newsletter).
|
||||||
|
* Only person JIDs can have valid LID-PN mappings.
|
||||||
|
*
|
||||||
|
* Valid person JID formats:
|
||||||
|
* - @s.whatsapp.net (standard phone number)
|
||||||
|
* - @lid (Logical ID)
|
||||||
|
* - @hosted (hosted phone number)
|
||||||
|
* - @hosted.lid (hosted Logical ID)
|
||||||
|
*
|
||||||
|
* Invalid (non-person) JID formats:
|
||||||
|
* - @g.us (groups)
|
||||||
|
* - @broadcast (broadcast lists)
|
||||||
|
* - @newsletter (channels)
|
||||||
|
*/
|
||||||
|
export function isPersonJid(jid: string | undefined): boolean {
|
||||||
|
if (!jid) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Person JIDs: @s.whatsapp.net, @lid, @hosted, @hosted.lid
|
||||||
|
if (
|
||||||
|
jid.endsWith('@s.whatsapp.net') ||
|
||||||
|
jid.endsWith('@lid') ||
|
||||||
|
jid.endsWith('@hosted') ||
|
||||||
|
jid.endsWith('@hosted.lid')
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-person JIDs: @g.us (groups), @broadcast, @newsletter
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a JID is a LID (Logical ID) format.
|
||||||
|
* LID formats: @lid or @hosted.lid
|
||||||
|
*/
|
||||||
|
function isLidJid(jid: string): boolean {
|
||||||
|
return jid.endsWith('@lid') || jid.endsWith('@hosted.lid')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a JID is a PN (Phone Number) format.
|
||||||
|
* PN formats: @s.whatsapp.net or @hosted
|
||||||
|
*/
|
||||||
|
function isPnJid(jid: string): boolean {
|
||||||
|
return jid.endsWith('@s.whatsapp.net') || jid.endsWith('@hosted')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts LID-PN mapping from a conversation object.
|
||||||
|
*
|
||||||
|
* Conversations can have:
|
||||||
|
* - A chat ID that is either LID or PN format
|
||||||
|
* - Optional lidJid property (alternate LID for the contact)
|
||||||
|
* - Optional pnJid property (alternate PN for the contact)
|
||||||
|
*
|
||||||
|
* Valid extraction scenarios:
|
||||||
|
* 1. Chat ID is LID + pnJid is present -> mapping found
|
||||||
|
* 2. Chat ID is PN + lidJid is present -> mapping found
|
||||||
|
* 3. lidJid + pnJid both present -> mapping found
|
||||||
|
*
|
||||||
|
* @returns LIDMapping if valid mapping found, undefined otherwise
|
||||||
|
*/
|
||||||
|
export function extractLidPnFromConversation(
|
||||||
|
chatId: string,
|
||||||
|
lidJid: string | undefined | null,
|
||||||
|
pnJid: string | undefined | null
|
||||||
|
): LIDMapping | undefined {
|
||||||
|
// Skip non-person chats (groups, broadcasts, newsletters)
|
||||||
|
if (!isPersonJid(chatId)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 1: Both lidJid and pnJid are provided
|
||||||
|
if (lidJid && pnJid && isLidJid(lidJid) && isPnJid(pnJid)) {
|
||||||
|
return { lid: lidJid, pn: pnJid }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2: Chat ID is LID format, pnJid provides the phone number
|
||||||
|
if (isLidJid(chatId) && pnJid && isPnJid(pnJid)) {
|
||||||
|
return { lid: chatId, pn: pnJid }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 3: Chat ID is PN format, lidJid provides the logical ID
|
||||||
|
if (isPnJid(chatId) && lidJid && isLidJid(lidJid)) {
|
||||||
|
return { lid: lidJid, pn: chatId }
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts LID-PN mapping from message fields.
|
||||||
|
*
|
||||||
|
* Messages can have:
|
||||||
|
* - remoteJid: Primary JID of the chat/sender
|
||||||
|
* - remoteJidAlt: Alternate JID format (LID if remoteJid is PN, or vice versa)
|
||||||
|
* - participant: Sender JID in group messages
|
||||||
|
* - participantAlt: Alternate format of participant JID
|
||||||
|
*
|
||||||
|
* For group messages, participant/participantAlt take priority over remoteJid/remoteJidAlt
|
||||||
|
* since remoteJid in groups is the group JID, not a person JID.
|
||||||
|
*
|
||||||
|
* @returns LIDMapping if valid mapping found, undefined otherwise
|
||||||
|
*/
|
||||||
|
export function extractLidPnFromMessage(
|
||||||
|
remoteJid: string | undefined | null,
|
||||||
|
remoteJidAlt: string | undefined | null,
|
||||||
|
participant: string | undefined | null,
|
||||||
|
participantAlt: string | undefined | null
|
||||||
|
): LIDMapping | undefined {
|
||||||
|
// Prefer participant fields (for group messages)
|
||||||
|
let primaryJid = participant || remoteJid
|
||||||
|
let altJid = participantAlt || remoteJidAlt
|
||||||
|
|
||||||
|
if (!primaryJid || !altJid) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRITICAL: Both JIDs MUST be person JIDs to create a valid mapping.
|
||||||
|
// Using || ensures we reject if EITHER is not a person JID.
|
||||||
|
// This prevents "poisoned" mappings where a group/broadcast/newsletter
|
||||||
|
// JID gets incorrectly paired with a person JID.
|
||||||
|
if (!isPersonJid(primaryJid) || !isPersonJid(altJid)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine which is LID and which is PN
|
||||||
|
if (isLidJid(primaryJid) && isPnJid(altJid)) {
|
||||||
|
return { lid: primaryJid, pn: altJid }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPnJid(primaryJid) && isLidJid(altJid)) {
|
||||||
|
return { lid: altJid, pn: primaryJid }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both are same type (both LID or both PN) - cannot create mapping
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export const processHistoryMessage = (item: proto.IHistorySync) => {
|
||||||
|
const messages: WAMessage[] = []
|
||||||
|
const contacts: Contact[] = []
|
||||||
|
const chats: Chat[] = []
|
||||||
|
|
||||||
|
// Use Map for O(1) deduplication of LID-PN mappings
|
||||||
|
const lidPnMap = new Map<string, LIDMapping>()
|
||||||
|
|
||||||
|
const addLidPnMapping = (mapping: LIDMapping | undefined): void => {
|
||||||
|
if (!mapping || !mapping.lid || !mapping.pn) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lidPnMap.set(mapping.lid, mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source 1: Extract from phoneNumberToLidMappings array (all sync types)
|
||||||
|
for (const m of item.phoneNumberToLidMappings || []) {
|
||||||
|
if (m.lidJid && m.pnJid) {
|
||||||
|
addLidPnMapping({ lid: m.lidJid, pn: m.pnJid })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.syncType) {
|
||||||
|
case proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP:
|
||||||
|
case proto.HistorySync.HistorySyncType.RECENT:
|
||||||
|
case proto.HistorySync.HistorySyncType.FULL:
|
||||||
|
case proto.HistorySync.HistorySyncType.ON_DEMAND:
|
||||||
|
for (const chat of item.conversations! as Chat[]) {
|
||||||
|
contacts.push({
|
||||||
|
id: chat.id!,
|
||||||
|
name: chat.name || undefined,
|
||||||
|
lid: chat.lidJid || undefined,
|
||||||
|
phoneNumber: chat.pnJid || undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
// Source 2: Extract from conversation metadata
|
||||||
|
addLidPnMapping(
|
||||||
|
extractLidPnFromConversation(chat.id!, chat.lidJid, chat.pnJid)
|
||||||
|
)
|
||||||
|
|
||||||
|
const msgs = chat.messages || []
|
||||||
|
delete chat.messages
|
||||||
|
|
||||||
|
for (const item of msgs) {
|
||||||
|
const message = item.message! as WAMessage
|
||||||
|
messages.push(message)
|
||||||
|
|
||||||
|
// Source 3: Extract from message alt fields
|
||||||
|
const key = message.key
|
||||||
|
if (key) {
|
||||||
|
addLidPnMapping(
|
||||||
|
extractLidPnFromMessage(
|
||||||
|
key.remoteJid,
|
||||||
|
(key as any).remoteJidAlt,
|
||||||
|
key.participant,
|
||||||
|
(key as any).participantAlt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chat.messages?.length) {
|
||||||
|
// keep only the most recent message in the chat array
|
||||||
|
chat.messages = [{ message }]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.key.fromMe && !chat.lastMessageRecvTimestamp) {
|
||||||
|
chat.lastMessageRecvTimestamp = toNumber(message.messageTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(message.messageStubType === WAMessageStubType.BIZ_PRIVACY_MODE_TO_BSP ||
|
||||||
|
message.messageStubType === WAMessageStubType.BIZ_PRIVACY_MODE_TO_FB) &&
|
||||||
|
message.messageStubParameters?.[0]
|
||||||
|
) {
|
||||||
|
contacts.push({
|
||||||
|
id: message.key.participant || message.key.remoteJid!,
|
||||||
|
verifiedName: message.messageStubParameters?.[0]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chats.push({ ...chat })
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case proto.HistorySync.HistorySyncType.PUSH_NAME:
|
||||||
|
for (const c of item.pushnames!) {
|
||||||
|
contacts.push({ id: c.id!, notify: c.pushname! })
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
chats,
|
||||||
|
contacts,
|
||||||
|
messages,
|
||||||
|
lidPnMappings: Array.from(lidPnMap.values()),
|
||||||
|
syncType: item.syncType,
|
||||||
|
progress: item.progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const downloadAndProcessHistorySyncNotification = async (
|
||||||
|
msg: proto.Message.IHistorySyncNotification,
|
||||||
|
options: RequestInit
|
||||||
|
) => {
|
||||||
|
let historyMsg: proto.HistorySync
|
||||||
|
if (msg.initialHistBootstrapInlinePayload) {
|
||||||
|
historyMsg = proto.HistorySync.decode(await inflatePromise(msg.initialHistBootstrapInlinePayload))
|
||||||
|
} else {
|
||||||
|
historyMsg = await downloadHistory(msg, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return processHistoryMessage(historyMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHistoryMsg = (message: proto.IMessage) => {
|
||||||
|
const normalizedContent = !!message ? normalizeMessageContent(message) : undefined
|
||||||
|
const anyHistoryMsg = normalizedContent?.protocolMessage?.historySyncNotification!
|
||||||
|
|
||||||
|
return anyHistoryMsg
|
||||||
|
}
|
||||||
@@ -0,0 +1,802 @@
|
|||||||
|
import { proto } from '../../../WAProto/index.js'
|
||||||
|
import {
|
||||||
|
processHistoryMessage,
|
||||||
|
extractLidPnFromConversation,
|
||||||
|
extractLidPnFromMessage,
|
||||||
|
isPersonJid
|
||||||
|
} from '../../Utils/history'
|
||||||
|
|
||||||
|
describe('isPersonJid', () => {
|
||||||
|
it('should return true for @s.whatsapp.net format', () => {
|
||||||
|
expect(isPersonJid('5511999999999@s.whatsapp.net')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return true for @lid format', () => {
|
||||||
|
expect(isPersonJid('123456789012345@lid')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return true for @hosted format', () => {
|
||||||
|
expect(isPersonJid('5511999999999@hosted')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return true for @hosted.lid format', () => {
|
||||||
|
expect(isPersonJid('123456789012345@hosted.lid')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for @g.us (groups)', () => {
|
||||||
|
expect(isPersonJid('123456789012345@g.us')).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for @broadcast', () => {
|
||||||
|
expect(isPersonJid('status@broadcast')).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for @newsletter (channels)', () => {
|
||||||
|
expect(isPersonJid('123456789012345@newsletter')).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for undefined', () => {
|
||||||
|
expect(isPersonJid(undefined)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for empty string', () => {
|
||||||
|
expect(isPersonJid('')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('extractLidPnFromMessage', () => {
|
||||||
|
describe('direct messages', () => {
|
||||||
|
it('should extract mapping when remoteJid is LID and remoteJidAlt is PN', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789012345@lid',
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should extract mapping when remoteJid is PN and remoteJidAlt is LID', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
'123456789012345@lid',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('group messages', () => {
|
||||||
|
it('should extract mapping from participant fields in groups', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789012345@g.us', // group JID
|
||||||
|
undefined,
|
||||||
|
'5511999999999@s.whatsapp.net', // participant
|
||||||
|
'123456789012345@lid' // participantAlt
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should prefer participant over remoteJid for mapping', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'5511888888888@s.whatsapp.net', // ignored
|
||||||
|
'987654321098765@lid', // ignored
|
||||||
|
'5511999999999@s.whatsapp.net', // participant used
|
||||||
|
'123456789012345@lid' // participantAlt used
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('edge cases', () => {
|
||||||
|
it('should return undefined when no alt JID is present', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when both are same type (LID)', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789012345@lid',
|
||||||
|
'987654321098765@lid',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when both are same type (PN)', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
'5511888888888@s.whatsapp.net',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined for newsletter messages', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789012345@newsletter',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
// CRITICAL: Edge cases for the && vs || fix (bug fix validation)
|
||||||
|
it('should return undefined when primary is person but alt is GROUP', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789012345@lid',
|
||||||
|
'123456789@g.us', // group JID
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
// With the fix (using ||), this should return undefined
|
||||||
|
// because group JIDs cannot be part of a valid LID-PN mapping
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when primary is person but alt is BROADCAST', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789012345@lid',
|
||||||
|
'status@broadcast', // broadcast JID
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when primary is person but alt is NEWSLETTER', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789012345@lid',
|
||||||
|
'123456789@newsletter', // newsletter JID
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when alt is person but primary is GROUP', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789@g.us', // group JID
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when alt is person but primary is BROADCAST', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'status@broadcast', // broadcast JID
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when alt is person but primary is NEWSLETTER', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789@newsletter', // newsletter JID
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when BOTH are non-person JIDs', () => {
|
||||||
|
const result = extractLidPnFromMessage(
|
||||||
|
'123456789@g.us', // group JID
|
||||||
|
'status@broadcast', // broadcast JID
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('extractLidPnFromConversation', () => {
|
||||||
|
describe('LID chat with pnJid', () => {
|
||||||
|
it('should extract mapping when chat ID is @lid format with pnJid', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'123456789012345@lid',
|
||||||
|
undefined,
|
||||||
|
'5511999999999@s.whatsapp.net'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should extract mapping when chat ID is @hosted.lid format with pnJid', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'123456789012345@hosted.lid',
|
||||||
|
undefined,
|
||||||
|
'5511999999999@hosted'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@hosted.lid',
|
||||||
|
pn: '5511999999999@hosted'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('PN chat with lidJid', () => {
|
||||||
|
it('should extract mapping when chat ID is @s.whatsapp.net format with lidJid', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
'123456789012345@lid',
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should extract mapping when chat ID is @hosted format with lidJid', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'5511999999999@hosted',
|
||||||
|
'123456789012345@hosted.lid',
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
lid: '123456789012345@hosted.lid',
|
||||||
|
pn: '5511999999999@hosted'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('edge cases', () => {
|
||||||
|
it('should return undefined for group chats', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'123456789012345@g.us',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined for newsletters (@newsletter)', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'123456789012345@newsletter',
|
||||||
|
'11111111111111@lid',
|
||||||
|
'5511999999999@s.whatsapp.net'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined for broadcast lists (@broadcast)', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'status@broadcast',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when no alternate JID is available', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'123456789012345@lid',
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined when both lidJid and pnJid are null', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined for LID chat with lidJid (no pnJid)', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'123456789012345@lid',
|
||||||
|
'987654321098765@lid',
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined for PN chat with pnJid (no lidJid)', () => {
|
||||||
|
const result = extractLidPnFromConversation(
|
||||||
|
'5511999999999@s.whatsapp.net',
|
||||||
|
undefined,
|
||||||
|
'5511888888888@s.whatsapp.net'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should extract LID-PN mappings from conversation lidJid/pnJid properties', () => {
|
||||||
|
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.lidPnMappings).toContainEqual({
|
||||||
|
lid: '11111111111111@lid',
|
||||||
|
pn: '1234567890123@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should NOT extract LID-PN mappings from group conversations', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '123456789@g.us',
|
||||||
|
name: 'Test Group',
|
||||||
|
lidJid: '11111111111111@lid',
|
||||||
|
pnJid: '1234567890123@s.whatsapp.net'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('LID-PN extraction from conversations', () => {
|
||||||
|
it('should extract LID-PN mapping when chat ID is LID with pnJid', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '11111111111111@lid',
|
||||||
|
name: 'LID User',
|
||||||
|
pnJid: '5511999999999@s.whatsapp.net'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([
|
||||||
|
{ lid: '11111111111111@lid', pn: '5511999999999@s.whatsapp.net' }
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should extract LID-PN mapping when chat ID is PN with lidJid', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '5511999999999@s.whatsapp.net',
|
||||||
|
name: 'PN User',
|
||||||
|
lidJid: '11111111111111@lid'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([
|
||||||
|
{ lid: '11111111111111@lid', pn: '5511999999999@s.whatsapp.net' }
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should deduplicate mappings from both sources', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
phoneNumberToLidMappings: [
|
||||||
|
{ lidJid: '11111111111111@lid', pnJid: '5511999999999@s.whatsapp.net' }
|
||||||
|
],
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '11111111111111@lid',
|
||||||
|
name: 'Same User',
|
||||||
|
pnJid: '5511999999999@s.whatsapp.net'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toHaveLength(1)
|
||||||
|
expect(result.lidPnMappings[0]).toEqual({
|
||||||
|
lid: '11111111111111@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should combine mappings from both sources', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
phoneNumberToLidMappings: [
|
||||||
|
{ lidJid: '11111111111111@lid', pnJid: '5511999999999@s.whatsapp.net' }
|
||||||
|
],
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '22222222222222@lid',
|
||||||
|
name: 'Different User',
|
||||||
|
pnJid: '5511888888888@s.whatsapp.net'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toHaveLength(2)
|
||||||
|
expect(result.lidPnMappings).toContainEqual({
|
||||||
|
lid: '11111111111111@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
expect(result.lidPnMappings).toContainEqual({
|
||||||
|
lid: '22222222222222@lid',
|
||||||
|
pn: '5511888888888@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not extract mapping from group chats', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '123456789012345@g.us',
|
||||||
|
name: 'Group Chat'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle hosted LID format', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '11111111111111@hosted.lid',
|
||||||
|
name: 'Hosted LID User',
|
||||||
|
pnJid: '5511999999999@hosted'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([
|
||||||
|
{ lid: '11111111111111@hosted.lid', pn: '5511999999999@hosted' }
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should extract mappings for all conversation sync types', () => {
|
||||||
|
const syncTypes = [
|
||||||
|
proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
proto.HistorySync.HistorySyncType.RECENT,
|
||||||
|
proto.HistorySync.HistorySyncType.FULL,
|
||||||
|
proto.HistorySync.HistorySyncType.ON_DEMAND
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const syncType of syncTypes) {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '11111111111111@lid',
|
||||||
|
pnJid: '5511999999999@s.whatsapp.net'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([
|
||||||
|
{ lid: '11111111111111@lid', pn: '5511999999999@s.whatsapp.net' }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not extract conversation mappings for PUSH_NAME sync type', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.PUSH_NAME,
|
||||||
|
pushnames: [
|
||||||
|
{ id: '5511999999999@s.whatsapp.net', pushname: 'User Name' }
|
||||||
|
],
|
||||||
|
phoneNumberToLidMappings: [
|
||||||
|
{ lidJid: '11111111111111@lid', pnJid: '5511999999999@s.whatsapp.net' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([
|
||||||
|
{ lid: '11111111111111@lid', pn: '5511999999999@s.whatsapp.net' }
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not extract mapping from newsletter conversations', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '123456789012345@newsletter',
|
||||||
|
name: 'News Channel'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not extract mapping from broadcast conversations', () => {
|
||||||
|
const historySync: proto.IHistorySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: 'status@broadcast',
|
||||||
|
name: 'Status'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toEqual([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('LID-PN extraction from messages (remoteJidAlt)', () => {
|
||||||
|
it('should extract mapping from message with remoteJidAlt', () => {
|
||||||
|
const historySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '5511999999999@s.whatsapp.net',
|
||||||
|
name: 'User',
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
message: {
|
||||||
|
key: {
|
||||||
|
remoteJid: '5511999999999@s.whatsapp.net',
|
||||||
|
remoteJidAlt: '11111111111111@lid',
|
||||||
|
id: 'MSG123',
|
||||||
|
fromMe: false
|
||||||
|
},
|
||||||
|
messageTimestamp: 1234567890
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} as unknown as proto.IHistorySync
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toContainEqual({
|
||||||
|
lid: '11111111111111@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should extract mapping from group message with participantAlt', () => {
|
||||||
|
const historySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '123456789012345@g.us',
|
||||||
|
name: 'Group Chat',
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
message: {
|
||||||
|
key: {
|
||||||
|
remoteJid: '123456789012345@g.us',
|
||||||
|
participant: '5511999999999@s.whatsapp.net',
|
||||||
|
participantAlt: '11111111111111@lid',
|
||||||
|
id: 'MSG456',
|
||||||
|
fromMe: false
|
||||||
|
},
|
||||||
|
messageTimestamp: 1234567890
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} as unknown as proto.IHistorySync
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toContainEqual({
|
||||||
|
lid: '11111111111111@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should deduplicate mappings from all three sources', () => {
|
||||||
|
const historySync = {
|
||||||
|
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||||
|
phoneNumberToLidMappings: [
|
||||||
|
{ lidJid: '11111111111111@lid', pnJid: '5511999999999@s.whatsapp.net' }
|
||||||
|
],
|
||||||
|
conversations: [
|
||||||
|
{
|
||||||
|
id: '11111111111111@lid',
|
||||||
|
name: 'User',
|
||||||
|
pnJid: '5511999999999@s.whatsapp.net',
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
message: {
|
||||||
|
key: {
|
||||||
|
remoteJid: '11111111111111@lid',
|
||||||
|
remoteJidAlt: '5511999999999@s.whatsapp.net',
|
||||||
|
id: 'MSG789',
|
||||||
|
fromMe: true
|
||||||
|
},
|
||||||
|
messageTimestamp: 1234567890
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} as unknown as proto.IHistorySync
|
||||||
|
|
||||||
|
const result = processHistoryMessage(historySync)
|
||||||
|
|
||||||
|
expect(result.lidPnMappings).toHaveLength(1)
|
||||||
|
expect(result.lidPnMappings[0]).toEqual({
|
||||||
|
lid: '11111111111111@lid',
|
||||||
|
pn: '5511999999999@s.whatsapp.net'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user