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 { proto } from '../../WAProto/index.js'
|
||||||
import type { Chat, Contact, LIDMapping, WAMessage } from '../Types'
|
import type { Chat, Contact, LIDMapping, WAMessage } from '../Types'
|
||||||
import { WAMessageStubType } from '../Types'
|
import { WAMessageStubType } from '../Types'
|
||||||
|
import { isHostedLidUser, isHostedPnUser, isLidUser, isPnUser } from '../WABinary'
|
||||||
import { toNumber } from './generics'
|
import { toNumber } from './generics'
|
||||||
import type { ILogger } from './logger.js'
|
import type { ILogger } from './logger.js'
|
||||||
import { normalizeMessageContent } from './messages'
|
import { normalizeMessageContent } from './messages'
|
||||||
@@ -10,6 +11,24 @@ import { downloadContentFromMessage } from './messages-media'
|
|||||||
|
|
||||||
const inflatePromise = promisify(inflate)
|
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) => {
|
export const downloadHistory = async (msg: proto.Message.IHistorySyncNotification, options: RequestInit) => {
|
||||||
const stream = await downloadContentFromMessage(msg, 'md-msg-hist', { options })
|
const stream = await downloadContentFromMessage(msg, 'md-msg-hist', { options })
|
||||||
const bufferArray: Buffer[] = []
|
const bufferArray: Buffer[] = []
|
||||||
@@ -54,6 +73,21 @@ export const processHistoryMessage = (item: proto.IHistorySync, logger?: ILogger
|
|||||||
phoneNumber: chat.pnJid || undefined
|
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 || []
|
const msgs = chat.messages || []
|
||||||
delete 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