UNPKG

@vreden/meta

Version:

Baileys is a lightweight JavaScript library for interacting with the WhatsApp Web API using WebSocket.

1,118 lines (982 loc) 37.8 kB
"use strict" var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod } } Object.defineProperty(exports, "__esModule", { value: true }) const node_cache_1 = __importDefault(require("@cacheable/node-cache")) const boom_1 = require("@hapi/boom") const WAProto_1 = require("../../WAProto") const Defaults_1 = require("../Defaults") const Types_1 = require("../Types") const Utils_1 = require("../Utils") const make_mutex_1 = require("../Utils/make-mutex") const WABinary_1 = require("../WABinary") const WAUSync_1 = require("../WAUSync") const socket_1 = require("./socket") const MAX_SYNC_ATTEMPTS = 2 const makeChatsSocket = (config) => { const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config const sock = socket_1.makeSocket(config) const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, groupFetchAllParticipating } = sock let privacySettings let syncState = Types_1.SyncState.Connecting /** this mutex ensures that the notifications (receipts, messages etc.) are processed in order */ const processingMutex = make_mutex_1.makeMutex() // Timeout for AwaitingInitialSync State let awaitingSyncTimeout const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, useClones: false }) if (!config.placeholderResendCache) { config.placeholderResendCache = placeholderResendCache } /** helper function to fetch the given app state sync key */ const getAppStateSyncKey = async (keyId) => { const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]) return key } const fetchPrivacySettings = async (force = false) => { if (!privacySettings || force) { const { content } = await query({ tag: 'iq', attrs: { xmlns: 'privacy', to: WABinary_1.S_WHATSAPP_NET, type: 'get' }, content: [ { tag: 'privacy', attrs: {} } ] }) privacySettings = WABinary_1.reduceBinaryNodeToDictionary(content?.[0], 'category') } return privacySettings } /** helper function to run a privacy IQ query */ const privacyQuery = async (name, value) => { await query({ tag: 'iq', attrs: { xmlns: 'privacy', to: WABinary_1.S_WHATSAPP_NET, type: 'set' }, content: [{ tag: 'privacy', attrs: {}, content: [ { tag: 'category', attrs: { name, value } } ] }] }) } const updateMessagesPrivacy = async (value) => { await privacyQuery('messages', value) } const updateCallPrivacy = async (value) => { await privacyQuery('calladd', value) } const updateLastSeenPrivacy = async (value) => { await privacyQuery('last', value) } const updateOnlinePrivacy = async (value) => { await privacyQuery('online', value) } const updateProfilePicturePrivacy = async (value) => { await privacyQuery('profile', value) } const updateStatusPrivacy = async (value) => { await privacyQuery('status', value) } const updateReadReceiptsPrivacy = async (value) => { await privacyQuery('readreceipts', value) } const updateGroupsAddPrivacy = async (value) => { await privacyQuery('groupadd', value) } const updateDefaultDisappearingMode = async (duration) => { await query({ tag: 'iq', attrs: { xmlns: 'disappearing_mode', to: WABinary_1.S_WHATSAPP_NET, type: 'set' }, content: [{ tag: 'disappearing_mode', attrs: { duration: duration.toString() } }] }) } const getBotListV2 = async () => { const resp = await query({ tag: 'iq', attrs: { xmlns: 'bot', to: WABinary_1.S_WHATSAPP_NET, type: 'get' }, content: [{ tag: 'bot', attrs: { v: '2' } }] }) const botNode = WABinary_1.getBinaryNodeChild(resp, 'bot') const botList = [] for(const section of WABinary_1.getBinaryNodeChildren(botNode, 'section')) { if(section.attrs.type === 'all') { for(const bot of WABinary_1.getBinaryNodeChildren(section, 'bot')) { botList.push({ jid: bot.attrs.jid, personaId: bot.attrs['persona_id'] }) } } } return botList } const getLidUser = async (jid) => { if (!jid) { throw new boom_1.Boom('Please input a jid user') } if (!WABinary_1.isJidUser(jid)) { throw new boom_1.Boom('Invalid JID: Not a user JID!') } const targetJid = WABinary_1.jidNormalizedUser(jid) const usyncQuery = new WAUSync_1.USyncQuery() usyncQuery.protocols.push({ name: 'lid', getQueryElement: () => ({ tag: 'lid', attrs: {}, content: undefined }), getUserElement: () => null, parser: (node) => node.attrs.val }) usyncQuery.users.push({ id: targetJid }) const result = await sock.executeUSyncQuery(usyncQuery) if (result) { return result.list } } const fetchStatus = async (...jids) => { const usyncQuery = new WAUSync_1.USyncQuery().withStatusProtocol() for (const jid of jids) { usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid)) } const result = await sock.executeUSyncQuery(usyncQuery) if (result) { return result.list } } const fetchDisappearingDuration = async (...jids) => { const usyncQuery = new WAUSync_1.USyncQuery().withDisappearingModeProtocol() for (const jid of jids) { usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid)) } const result = await sock.executeUSyncQuery(usyncQuery) if (result) { return result.list } } /** update the profile picture for yourself or a group */ const updateProfilePicture = async (jid, content) => { let targetJid if (!jid) { throw new boom_1.Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update') } if (WABinary_1.jidNormalizedUser(jid) !== WABinary_1.jidNormalizedUser(authState.creds.me.id)) { targetJid = WABinary_1.jidNormalizedUser(jid) // in case it is someone other than us } const { img } = await Utils_1.generateProfilePicture(content) await query({ tag: 'iq', attrs: { target: targetJid, to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:profile:picture' }, content: [ { tag: 'picture', attrs: { type: 'image' }, content: img } ] }) } /** remove the profile picture for yourself or a group */ const removeProfilePicture = async (jid) => { let targetJid if (!jid) { throw new boom_1.Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update') } if (WABinary_1.jidNormalizedUser(jid) !== WABinary_1.jidNormalizedUser(authState.creds.me.id)) { targetJid = WABinary_1.jidNormalizedUser(jid) // in case it is someone other than us } await query({ tag: 'iq', attrs: { target: targetJid, to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:profile:picture' } }) } /** update the profile status for yourself */ const updateProfileStatus = async (status) => { await query({ tag: 'iq', attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'status' }, content: [ { tag: 'status', attrs: {}, content: Buffer.from(status, 'utf-8') } ] }) } const updateProfileName = async (name) => { await chatModify({ pushNameSetting: name }, '') } const fetchBlocklist = async () => { const result = await query({ tag: 'iq', attrs: { xmlns: 'blocklist', to: WABinary_1.S_WHATSAPP_NET, type: 'get' } }) const listNode = WABinary_1.getBinaryNodeChild(result, 'list') return WABinary_1.getBinaryNodeChildren(listNode, 'item').map((n) => n.attrs.jid) } const updateBlockStatus = async (jid, action) => { await query({ tag: 'iq', attrs: { xmlns: 'blocklist', to: WABinary_1.S_WHATSAPP_NET, type: 'set' }, content: [ { tag: 'item', attrs: { action, jid } } ] }) } const getBusinessProfile = async (jid) => { const results = await query({ tag: 'iq', attrs: { to: 's.whatsapp.net', xmlns: 'w:biz', type: 'get' }, content: [{ tag: 'business_profile', attrs: { v: '244' }, content: [{ tag: 'profile', attrs: { jid } }] }] }) const profileNode = WABinary_1.getBinaryNodeChild(results, 'business_profile') const profiles = WABinary_1.getBinaryNodeChild(profileNode, 'profile') if (profiles) { const address = WABinary_1.getBinaryNodeChild(profiles, 'address') const description = WABinary_1.getBinaryNodeChild(profiles, 'description') const website = WABinary_1.getBinaryNodeChild(profiles, 'website') const email = WABinary_1.getBinaryNodeChild(profiles, 'email') const category = WABinary_1.getBinaryNodeChild(WABinary_1.getBinaryNodeChild(profiles, 'categories'), 'category') const businessHours = WABinary_1.getBinaryNodeChild(profiles, 'business_hours') const businessHoursConfig = businessHours ? WABinary_1.getBinaryNodeChildren(businessHours, 'business_hours_config') : undefined const websiteStr = website?.content?.toString() return { wid: profiles?.attrs?.jid, address: address?.content?.toString(), description: description?.content?.toString() || '', website: websiteStr ? [websiteStr] : [], email: email?.content?.toString(), category: category?.content?.toString(), 'business_hours': { timezone: businessHours?.attrs?.timezone, 'business_config': businessHoursConfig?.map(({ attrs }) => attrs) } } } } const cleanDirtyBits = async (type, fromTimestamp) => { logger.info({ fromTimestamp }, 'clean dirty bits ' + type) await sendNode({ tag: 'iq', attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'urn:xmpp:whatsapp:dirty', id: generateMessageTag(), }, content: [ { tag: 'clean', attrs: { type, ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null), } } ] }) } const newAppStateChunkHandler = (isInitialSync) => { return { onMutation(mutation) { Utils_1.processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger) } } } const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => { // we use this to determine which events to fire // otherwise when we resync from scratch -- all notifications will fire const initialVersionMap = {} const globalMutationMap = {} await authState.keys.transaction(async () => { const collectionsToHandle = new Set(collections) // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from const attemptsMap = {} // keep executing till all collections are done // sometimes a single patch request will not return all the patches (God knows why) // so we fetch till they're all done (this is determined by the "has_more_patches" flag) while (collectionsToHandle.size) { const states = {} const nodes = [] for (const name of collectionsToHandle) { const result = await authState.keys.get('app-state-sync-version', [name]) let state = result[name] if (state) { if (typeof initialVersionMap[name] === 'undefined') { initialVersionMap[name] = state.version } } else { state = Utils_1.newLTHashState() } states[name] = state logger.info(`resyncing ${name} from v${state.version}`) nodes.push({ tag: 'collection', attrs: { name, version: state.version.toString(), // return snapshot if being synced from scratch 'return_snapshot': (!state.version).toString() } }) } const result = await query({ tag: 'iq', attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'w:sync:app:state', type: 'set' }, content: [ { tag: 'sync', attrs: {}, content: nodes } ] }) // extract from binary node const decoded = await Utils_1.extractSyncdPatches(result, config?.options) for (const key in decoded) { const name = key const { patches, hasMorePatches, snapshot } = decoded[name] try { if (snapshot) { const { state: newState, mutationMap } = await Utils_1.decodeSyncdSnapshot(name, snapshot, getAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot) states[name] = newState Object.assign(globalMutationMap, mutationMap) logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`) await authState.keys.set({ 'app-state-sync-version': { [name]: newState } }) } // only process if there are syncd patches if (patches.length) { const { state: newState, mutationMap } = await Utils_1.decodePatches(name, patches, states[name], getAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch) await authState.keys.set({ 'app-state-sync-version': { [name]: newState } }) logger.info(`synced ${name} to v${newState.version}`) initialVersionMap[name] = newState.version Object.assign(globalMutationMap, mutationMap) } if (hasMorePatches) { logger.info(`${name} has more patches...`) } else { // collection is done with sync collectionsToHandle.delete(name) } } catch (error) { // if retry attempts overshoot // or key not found const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS || error.output?.statusCode === 404 || error.name === 'TypeError' logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`) await authState.keys.set({ 'app-state-sync-version': { [name]: null } }) // increment number of retries attemptsMap[name] = (attemptsMap[name] || 0) + 1 if (isIrrecoverableError) { // stop retrying collectionsToHandle.delete(name) } } } } }, authState?.creds?.me?.id || 'resync-app-state') const { onMutation } = newAppStateChunkHandler(isInitialSync) for (const key in globalMutationMap) { onMutation(globalMutationMap[key]) } }) // source: https://github.com/WhiskeySockets/Baileys/pull/1677 const createCallLink = async (type, event, timeoutMs) => { type = type?.toLowerCase() if (!type || (type !== 'audio' && type !== 'video')) { throw new Error('Make sure the type is audio or video!') } const result = await query({ tag: 'call', attrs: { id: generateMessageTag(), to: '@call' }, content: [{ tag: 'link_create', attrs: { media: type }, content: event ? [{ tag: 'event', attrs: { start_time: String(event) } }] : undefined }] }, timeoutMs) const child = WABinary_1.getBinaryNodeChild(result, 'link_create') const token = child?.attrs?.token return type === 'audio' ? Defaults_1.CALL_AUDIO_PREFIX + token : Defaults_1.CALL_VIDEO_PREFIX + token } const sendPresenceUpdate = async (type, toJid) => { const me = authState.creds.me if (type === 'available' || type === 'unavailable') { if (!me.name) { logger.warn('no name present, ignoring presence update request...') return } ev.emit('connection.update', { isOnline: type === 'available' }) await sendNode({ tag: 'presence', attrs: { name: me.name.replace(/@/g, ''), type } }) } else { const { server } = WABinary_1.jidDecode(toJid) await sendNode({ tag: 'chatstate', attrs: { from: server === 'lid' ? me.lid : me.id, to: toJid }, content: [ { tag: type === 'recording' ? 'composing' : type, attrs: type === 'recording' ? { media: 'audio' } : {} } ] }) } } /** * @param toJid the jid to subscribe to * @param tcToken token for subscription, use if present */ const presenceSubscribe = (toJid, tcToken) => (sendNode({ tag: 'presence', attrs: { to: toJid, id: generateMessageTag(), type: 'subscribe' }, content: tcToken ? [ { tag: 'tctoken', attrs: {}, content: tcToken } ] : undefined })) const handlePresenceUpdate = ({ tag, attrs, content }) => { let presence const jid = attrs.from const participant = attrs.participant || attrs.from if (shouldIgnoreJid(jid) && jid !== '@s.whatsapp.net') { return } if (tag === 'presence') { presence = { lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available', lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined } } else if (Array.isArray(content)) { const [firstChild] = content let type = firstChild.tag if (type === 'paused') { type = 'available' } if (firstChild.attrs?.media === 'audio') { type = 'recording' } presence = { lastKnownPresence: type } } else { logger.error({ tag, attrs, content }, 'recv invalid presence node') } if (presence) { ev.emit('presence.update', { id: jid, presences: { [participant]: presence } }) } } const appPatch = async (patchCreate) => { const name = patchCreate.type const myAppStateKeyId = authState.creds.myAppStateKeyId if (!myAppStateKeyId) { throw new boom_1.Boom('App state key not present!', { statusCode: 400 }) } let initial let encodeResult await processingMutex.mutex(async () => { await authState.keys.transaction(async () => { logger.debug({ patch: patchCreate }, 'applying app patch') await resyncAppState([name], false) const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]) initial = currentSyncVersion || Utils_1.newLTHashState() encodeResult = await Utils_1.encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey) const { patch, state } = encodeResult const node = { tag: 'iq', attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:sync:app:state' }, content: [ { tag: 'sync', attrs: {}, content: [ { tag: 'collection', attrs: { name, version: (state.version - 1).toString(), 'return_snapshot': 'false' }, content: [ { tag: 'patch', attrs: {}, content: WAProto_1.proto.SyncdPatch.encode(patch).finish() } ] } ] } ] } await query(node) await authState.keys.set({ 'app-state-sync-version': { [name]: state } }) }, authState?.creds?.me?.id || 'app-patch') }) if (config.emitOwnEvents) { const { onMutation } = newAppStateChunkHandler(false) const { mutationMap } = await Utils_1.decodePatches(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version }, }], initial, getAppStateSyncKey, config.options, undefined, logger) for (const key in mutationMap) { onMutation(mutationMap[key]) } } } /** sending non-abt props may fix QR scan fail if server expects */ const fetchProps = async () => { const resultNode = await query({ tag: 'iq', attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'w', type: 'get', }, content: [ { tag: 'props', attrs: { protocol: '2', hash: authState?.creds?.lastPropHash || '' } } ] }) const propsNode = WABinary_1.getBinaryNodeChild(resultNode, 'props') let props = {} if (propsNode) { if (propsNode.attrs?.hash) { // on some clients, the hash is returning as undefined authState.creds.lastPropHash = propsNode?.attrs?.hash ev.emit('creds.update', authState.creds) } props = WABinary_1.reduceBinaryNodeToDictionary(propsNode, 'prop') } logger.debug('fetched props') return props } /** * modify a chat -- mark unread, read etc. * lastMessages must be sorted in reverse chronologically * requires the last messages till the last message received required for archive & unread */ const chatModify = (mod, jid) => { const patch = Utils_1.chatModificationToAppPatch(mod, jid) return appPatch(patch) } /** * Enable/Disable link preview privacy, not related to baileys link preview generation */ const updateDisableLinkPreviewsPrivacy = (isPreviewsDisabled) => { return chatModify({ disableLinkPreviews: { isPreviewsDisabled } }, '') } /** * Star or Unstar a message */ const star = (jid, messages, star) => { return chatModify({ star: { messages, star } }, jid) } /** * Add Or Edit Contact */ const addOrEditContact = (jid, contact) => { return chatModify({ contact }, jid) } /** * Remove Contact */ const removeContact = (jid) => { return chatModify({ contact: null }, jid) } /** * Adds label */ const addLabel = (jid, labels) => { return chatModify({ addLabel: { ...labels } }, jid) } /** * Adds label for the chats */ const addChatLabel = (jid, labelId) => { return chatModify({ addChatLabel: { labelId } }, jid) } /** * Removes label for the chat */ const removeChatLabel = (jid, labelId) => { return chatModify({ removeChatLabel: { labelId } }, jid) } /** * Adds label for the message */ const addMessageLabel = (jid, messageId, labelId) => { return chatModify({ addMessageLabel: { messageId, labelId } }, jid) } /** * Removes label for the message */ const removeMessageLabel = (jid, messageId, labelId) => { return chatModify({ removeMessageLabel: { messageId, labelId } }, jid) } /** * Removes Chats */ const clearMessage = (jid, key, timeStamp) => { return chatModify({ delete: true, lastMessages: [{ key: key, messageTimestamp: timeStamp }], }, jid) } /** * Add or Edit Quick Reply */ const addOrEditQuickReply = (quickReply) => { return chatModify({ quickReply }, '') } /** * Remove Quick Reply */ const removeQuickReply = (timestamp) => { return chatModify({ quickReply: { timestamp, deleted: true } }, '') } /** * queries need to be fired on connection open * help ensure parity with WA Web * */ const executeInitQueries = async () => { await Promise.all([ fetchProps(), fetchBlocklist(), fetchPrivacySettings(), ]) } const upsertMessage = ev.createBufferedFunction(async (msg, type) => { ev.emit('messages.upsert', { messages: [msg], type }) if (!!msg.pushName) { let jid = msg.key.fromMe ? authState.creds.me.id : (msg.key.participant || msg.key.remoteJid) jid = WABinary_1.jidNormalizedUser(jid) if (!msg.key.fromMe) { ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]) } // update our pushname too if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) { ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } }) } } const historyMsg = Utils_1.getHistoryMsg(msg.message) const shouldProcessHistoryMsg = historyMsg ? (shouldSyncHistoryMessage(historyMsg) && Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)) : false if (historyMsg && syncState === Types_1.SyncState.AwaitingInitialSync) { if (awaitingSyncTimeout) { clearTimeout(awaitingSyncTimeout) awaitingSyncTimeout = undefined } if (shouldProcessHistoryMsg) { syncState = Types_1.SyncState.Syncing logger.info('Transitioned to Syncing state') // Let doAppStateSync handle the final flush after it`s done } else { syncState = Types_1.SyncState.Online logger.info('History sync skipped, transitioning to Online state and flushing buffer') ev.flush() } } const doAppStateSync = async () => { if (syncState === Types_1.SyncState.Syncing) { logger.info('Doing app state sync') await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true) // Sync is complete, go online and flush everything syncState = Types_1.SyncState.Online logger.info('App state sync complete, transitioning to Online state and flushing buffer') ev.flush() const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1 ev.emit('creds.update', { accountSyncCounter }) } } await Promise.all([ (async () => { if (shouldProcessHistoryMsg) { await doAppStateSync() } })(), Utils_1.processMessage(msg, { signalRepository, shouldProcessHistoryMsg, placeholderResendCache, ev, creds: authState.creds, keyStore: authState.keys, logger, options: config.options, getMessage: config.getMessage, }) ]) if (msg.message?.protocolMessage?.appStateSyncKeyShare && syncState === Types_1.SyncState.Syncing) { logger.info('App state sync key arrived, triggering app state sync') await doAppStateSync() } }) ws.on('CB:presence', handlePresenceUpdate) ws.on('CB:chatstate', handlePresenceUpdate) ws.on('CB:ib,,dirty', async (node) => { const { attrs } = WABinary_1.getBinaryNodeChild(node, 'dirty') const type = attrs.type switch (type) { case 'account_sync': if (attrs.timestamp) { let { lastAccountSyncTimestamp } = authState.creds if (lastAccountSyncTimestamp) { await cleanDirtyBits('account_sync', lastAccountSyncTimestamp) } lastAccountSyncTimestamp = +attrs.timestamp ev.emit('creds.update', { lastAccountSyncTimestamp }) } break case 'groups': await groupFetchAllParticipating() await cleanDirtyBits('groups') break default: logger.info({ node }, 'received unknown sync') break } }) ev.on('connection.update', ({ connection, receivedPendingNotifications }) => { if (connection === 'open') { if (fireInitQueries) { executeInitQueries() .catch(error => onUnexpectedError(error, 'init queries')) } sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable') .catch(error => onUnexpectedError(error, 'presence update requests')) } if (!receivedPendingNotifications || syncState !== Types_1.SyncState.Connecting) { return } syncState = Types_1.AwaitingInitialSync logger.info('Connection is now AwaitingInitialSync, buffering events') ev.buffer() const willSyncHistory = shouldSyncHistoryMessage(WAProto_1.proto.Message.HistorySyncNotification.fromObject({ syncType: WAProto_1.proto.HistorySync.HistorySyncType.RECENT })) if (!willSyncHistory) { logger.info('History sync is disabled by config, not waiting for notification. Transitioning to Online.') syncState = Types_1.SyncState.Online setTimeout(() => ev.flush(), 0) return } logger.info('History sync is enabled, awaiting notification with a 20s timeout.') if (awaitingSyncTimeout) { clearTimeout(awaitingSyncTimeout) } awaitingSyncTimeout = setTimeout(() => { if (syncState === Types_1.SyncState.AwaitingInitialSync) { logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer') syncState = Types_1.SyncState.Online ev.flush() } }, 20_000) }) return { ...sock, star, addOrEditContact, removeContact, processingMutex, fetchPrivacySettings, upsertMessage, appPatch, createCallLink, sendPresenceUpdate, presenceSubscribe, getBotListV2, getLidUser, fetchBlocklist, fetchStatus, fetchDisappearingDuration, updateProfilePicture, removeProfilePicture, updateProfileStatus, updateProfileName, updateBlockStatus, updateCallPrivacy, updateMessagesPrivacy, updateLastSeenPrivacy, updateOnlinePrivacy, updateProfilePicturePrivacy, updateStatusPrivacy, updateReadReceiptsPrivacy, updateGroupsAddPrivacy, updateDefaultDisappearingMode, updateDisableLinkPreviewsPrivacy, getBusinessProfile, resyncAppState, chatModify, cleanDirtyBits, addLabel, addChatLabel, removeChatLabel, addMessageLabel, removeMessageLabel, clearMessage, addOrEditQuickReply, removeQuickReply } } module.exports = { makeChatsSocket }