import { AxiosRequestConfig } from 'axios' import { promisify } from 'util' import { inflate } from 'zlib' import { proto } from '../../WAProto' import { Chat, Contact, WAMessageStubType } from '../Types' import { isJidUser } from '../WABinary' 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: AxiosRequestConfig ) => { const stream = await downloadContentFromMessage(msg, 'md-msg-hist', { options }) const bufferArray: Buffer[] = [] for await (const chunk of stream) { bufferArray.push(chunk) } let buffer = Buffer.concat(bufferArray) // decompress buffer buffer = await inflatePromise(buffer) const syncData = proto.HistorySync.decode(buffer) return syncData } export const processHistoryMessage = (item: proto.IHistorySync) => { const messages: proto.IWebMessageInfo[] = [] const contacts: Contact[] = [] const chats: Chat[] = [] switch (item.syncType) { case proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP: case proto.HistorySync.HistorySyncType.RECENT: case proto.HistorySync.HistorySyncType.FULL: for(const chat of item.conversations! as Chat[]) { contacts.push({ id: chat.id, name: chat.name || undefined }) const msgs = chat.messages || [] delete chat.messages delete chat.archived delete chat.muteEndTime delete chat.pinned for(const item of msgs) { const message = item.message! messages.push(message) 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], }) } } if(isJidUser(chat.id) && chat.readOnly && chat.archived) { delete chat.readOnly } 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, } } export const downloadAndProcessHistorySyncNotification = async( msg: proto.Message.IHistorySyncNotification, options: AxiosRequestConfig ) => { const 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 }