fix: extract LID-PN mappings from conversation objects in history sync (#2282)
* fix: extract LID-PN mappings from conversation objects in history sync * fix: extract PN from userReceipt when pnJid is missing for LID chats
This commit is contained in:
committed by
GitHub
parent
52fcad2b9c
commit
f829b6d7a8
@@ -3,6 +3,7 @@ import { inflate } from 'zlib'
|
||||
import { proto } from '../../WAProto/index.js'
|
||||
import type { Chat, Contact, LIDMapping, WAMessage } from '../Types'
|
||||
import { WAMessageStubType } from '../Types'
|
||||
import { isHostedLidUser, isHostedPnUser, isLidUser, isPnUser } from '../WABinary'
|
||||
import { toNumber } from './generics'
|
||||
import type { ILogger } from './logger.js'
|
||||
import { normalizeMessageContent } from './messages'
|
||||
@@ -10,6 +11,24 @@ import { downloadContentFromMessage } from './messages-media'
|
||||
|
||||
const inflatePromise = promisify(inflate)
|
||||
|
||||
const extractPnFromMessages = (messages: proto.IHistorySyncMsg[]): string | undefined => {
|
||||
for (const msgItem of messages) {
|
||||
const message = msgItem.message
|
||||
// Only extract from outgoing messages (fromMe: true) in 1:1 chats
|
||||
// because userReceipt.userJid is the recipient's JID
|
||||
if (!message?.key?.fromMe || !message.userReceipt?.length) {
|
||||
continue
|
||||
}
|
||||
|
||||
const userJid = message.userReceipt[0]?.userJid
|
||||
if (userJid && (isPnUser(userJid) || isHostedPnUser(userJid))) {
|
||||
return userJid
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const downloadHistory = async (msg: proto.Message.IHistorySyncNotification, options: RequestInit) => {
|
||||
const stream = await downloadContentFromMessage(msg, 'md-msg-hist', { options })
|
||||
const bufferArray: Buffer[] = []
|
||||
@@ -54,6 +73,21 @@ export const processHistoryMessage = (item: proto.IHistorySync, logger?: ILogger
|
||||
phoneNumber: chat.pnJid || undefined
|
||||
})
|
||||
|
||||
const chatId = chat.id!
|
||||
const isLid = isLidUser(chatId) || isHostedLidUser(chatId)
|
||||
const isPn = isPnUser(chatId) || isHostedPnUser(chatId)
|
||||
if (isLid && chat.pnJid) {
|
||||
lidPnMappings.push({ lid: chatId, pn: chat.pnJid })
|
||||
} else if (isPn && chat.lidJid) {
|
||||
lidPnMappings.push({ lid: chat.lidJid, pn: chatId })
|
||||
} else if (isLid && !chat.pnJid) {
|
||||
// Fallback: extract PN from userReceipt in messages when pnJid is missing
|
||||
const pnFromReceipt = extractPnFromMessages(chat.messages || [])
|
||||
if (pnFromReceipt) {
|
||||
lidPnMappings.push({ lid: chatId, pn: pnFromReceipt })
|
||||
}
|
||||
}
|
||||
|
||||
const msgs = chat.messages || []
|
||||
delete chat.messages
|
||||
|
||||
|
||||
@@ -91,4 +91,272 @@ describe('processHistoryMessage', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('LID-PN mapping extraction from conversations', () => {
|
||||
it('should extract mapping when chat.id is LID and pnJid exists', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '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 extract mapping when chat.id is PN and lidJid exists', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '1234567890123@s.whatsapp.net',
|
||||
lidJid: '11111111111111@lid'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
expect(result.lidPnMappings).toContainEqual({
|
||||
lid: '11111111111111@lid',
|
||||
pn: '1234567890123@s.whatsapp.net'
|
||||
})
|
||||
})
|
||||
|
||||
it('should not extract mapping for group chats', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '123456789012345678@g.us',
|
||||
lidJid: '11111111111111@lid',
|
||||
pnJid: '1234567890123@s.whatsapp.net'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
expect(result.lidPnMappings).toEqual([])
|
||||
})
|
||||
|
||||
it('should combine mappings from phoneNumberToLidMappings and conversations', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
phoneNumberToLidMappings: [{ lidJid: '11111111111111@lid', pnJid: '1111111111111@s.whatsapp.net' }],
|
||||
conversations: [
|
||||
{
|
||||
id: '22222222222222@lid',
|
||||
pnJid: '2222222222222@s.whatsapp.net'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
expect(result.lidPnMappings).toHaveLength(2)
|
||||
expect(result.lidPnMappings).toContainEqual({
|
||||
lid: '11111111111111@lid',
|
||||
pn: '1111111111111@s.whatsapp.net'
|
||||
})
|
||||
expect(result.lidPnMappings).toContainEqual({
|
||||
lid: '22222222222222@lid',
|
||||
pn: '2222222222222@s.whatsapp.net'
|
||||
})
|
||||
})
|
||||
|
||||
it('should extract mapping for hosted LID users', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '11111111111111@hosted.lid',
|
||||
pnJid: '1234567890123@hosted'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
expect(result.lidPnMappings).toContainEqual({
|
||||
lid: '11111111111111@hosted.lid',
|
||||
pn: '1234567890123@hosted'
|
||||
})
|
||||
})
|
||||
|
||||
it('should extract mapping for hosted PN users', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '1234567890123@hosted',
|
||||
lidJid: '11111111111111@hosted.lid'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
expect(result.lidPnMappings).toContainEqual({
|
||||
lid: '11111111111111@hosted.lid',
|
||||
pn: '1234567890123@hosted'
|
||||
})
|
||||
})
|
||||
|
||||
it('should extract mapping from userReceipt when pnJid is missing and chat.id is LID', () => {
|
||||
// Based on real-world case: LID chat without pnJid but userReceipt contains PN
|
||||
// See: https://github.com/WhiskeySockets/Baileys/pull/2282#issuecomment-3777941679
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '211071956705386@lid',
|
||||
// pnJid is intentionally missing
|
||||
messages: [
|
||||
{
|
||||
message: {
|
||||
key: {
|
||||
remoteJid: '211071956705386@lid',
|
||||
fromMe: true,
|
||||
id: '3EB052FF8D9D00646C9994'
|
||||
},
|
||||
messageTimestamp: 1768320044,
|
||||
userReceipt: [
|
||||
{
|
||||
userJid: '5518999991234@s.whatsapp.net',
|
||||
receiptTimestamp: 1768320045,
|
||||
readTimestamp: 1768327083
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
expect(result.lidPnMappings).toContainEqual({
|
||||
lid: '211071956705386@lid',
|
||||
pn: '5518999991234@s.whatsapp.net'
|
||||
})
|
||||
})
|
||||
|
||||
it('should not extract mapping from userReceipt when pnJid already exists', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '211071956705386@lid',
|
||||
pnJid: '5518888881234@s.whatsapp.net', // pnJid exists
|
||||
messages: [
|
||||
{
|
||||
message: {
|
||||
key: {
|
||||
remoteJid: '211071956705386@lid',
|
||||
fromMe: true,
|
||||
id: '3EB052FF8D9D00646C9994'
|
||||
},
|
||||
userReceipt: [
|
||||
{
|
||||
userJid: '5518999991234@s.whatsapp.net' // different PN
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
// Should use pnJid, not userReceipt
|
||||
expect(result.lidPnMappings).toContainEqual({
|
||||
lid: '211071956705386@lid',
|
||||
pn: '5518888881234@s.whatsapp.net'
|
||||
})
|
||||
// Should NOT contain the userReceipt PN
|
||||
expect(result.lidPnMappings).not.toContainEqual({
|
||||
lid: '211071956705386@lid',
|
||||
pn: '5518999991234@s.whatsapp.net'
|
||||
})
|
||||
})
|
||||
|
||||
it('should not extract mapping from userReceipt when fromMe is false', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '211071956705386@lid',
|
||||
messages: [
|
||||
{
|
||||
message: {
|
||||
key: {
|
||||
remoteJid: '211071956705386@lid',
|
||||
fromMe: false, // Not from me
|
||||
id: '3EB052FF8D9D00646C9994'
|
||||
},
|
||||
userReceipt: [
|
||||
{
|
||||
userJid: '5518999991234@s.whatsapp.net'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
// Should not extract mapping when fromMe is false
|
||||
expect(result.lidPnMappings).not.toContainEqual({
|
||||
lid: '211071956705386@lid',
|
||||
pn: '5518999991234@s.whatsapp.net'
|
||||
})
|
||||
})
|
||||
|
||||
it('should not extract mapping from userReceipt when userJid is also a LID', () => {
|
||||
const historySync: proto.IHistorySync = {
|
||||
syncType: proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP,
|
||||
conversations: [
|
||||
{
|
||||
id: '211071956705386@lid',
|
||||
messages: [
|
||||
{
|
||||
message: {
|
||||
key: {
|
||||
remoteJid: '211071956705386@lid',
|
||||
fromMe: true,
|
||||
id: '3EB052FF8D9D00646C9994'
|
||||
},
|
||||
userReceipt: [
|
||||
{
|
||||
userJid: '152230971891797@lid' // Also a LID, not a PN
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const result = processHistoryMessage(historySync)
|
||||
|
||||
// Should not create a LID->LID mapping
|
||||
expect(result.lidPnMappings).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user