diff --git a/Example/example.ts b/Example/example.ts index 856a769..33db559 100644 --- a/Example/example.ts +++ b/Example/example.ts @@ -23,7 +23,7 @@ setInterval(() => { }, 10_000) // start a connection -const startSock = async () => { +const startSock = async() => { const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info') // fetch latest version of WA Web const { version, isLatest } = await fetchLatestBaileysVersion() @@ -49,7 +49,7 @@ const startSock = async () => { store?.bind(sock.ev) - const sendMessageWTyping = async (msg: AnyMessageContent, jid: string) => { + const sendMessageWTyping = async(msg: AnyMessageContent, jid: string) => { await sock.presenceSubscribe(jid) await delay(500) @@ -65,15 +65,15 @@ const startSock = async () => { // efficiently in a batch sock.ev.process( // events is a map for event name => event data - async (events) => { + async(events) => { // something about the connection changed // maybe it closed, or we received all offline message or connection opened - if (events['connection.update']) { + if(events['connection.update']) { const update = events['connection.update'] const { connection, lastDisconnect } = update - if (connection === 'close') { + if(connection === 'close') { // reconnect if not logged out - if ((lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) { + if((lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) { startSock() } else { console.log('Connection closed. You are logged out.') @@ -84,37 +84,37 @@ const startSock = async () => { } // credentials updated -- save them - if (events['creds.update']) { + if(events['creds.update']) { await saveCreds() } - if (events['labels.association']) { + if(events['labels.association']) { console.log(events['labels.association']) } - if (events['labels.edit']) { + if(events['labels.edit']) { console.log(events['labels.edit']) } - if (events.call) { + if(events.call) { console.log('recv call event', events.call) } // history received - if (events['messaging-history.set']) { + if(events['messaging-history.set']) { const { chats, contacts, messages, isLatest } = events['messaging-history.set'] console.log(`recv ${chats.length} chats, ${contacts.length} contacts, ${messages.length} msgs (is latest: ${isLatest})`) } // received a new message - if (events['messages.upsert']) { + if(events['messages.upsert']) { const upsert = events['messages.upsert'] console.log('recv messages ', JSON.stringify(upsert, undefined, 2)) - if (upsert.type === 'notify') { - for (const msg of upsert.messages) { - if (!msg.key.fromMe && doReplies) { + if(upsert.type === 'notify') { + for(const msg of upsert.messages) { + if(!msg.key.fromMe && doReplies) { console.log('replying to', msg.key.remoteJid) await sock!.readMessages([msg.key]) await sendMessageWTyping({ text: 'Hello there!' }, msg.key.remoteJid!) @@ -124,15 +124,15 @@ const startSock = async () => { } // messages updated like status delivered, message deleted etc. - if (events['messages.update']) { + if(events['messages.update']) { console.log( JSON.stringify(events['messages.update'], undefined, 2) ) - for (const { key, update } of events['messages.update']) { - if (update.pollUpdates) { + for(const { key, update } of events['messages.update']) { + if(update.pollUpdates) { const pollCreation = await getMessage(key) - if (pollCreation) { + if(pollCreation) { console.log( 'got poll update, aggregation: ', getAggregateVotesInPollMessage({ @@ -145,25 +145,25 @@ const startSock = async () => { } } - if (events['message-receipt.update']) { + if(events['message-receipt.update']) { console.log(events['message-receipt.update']) } - if (events['messages.reaction']) { + if(events['messages.reaction']) { console.log(events['messages.reaction']) } - if (events['presence.update']) { + if(events['presence.update']) { console.log(events['presence.update']) } - if (events['chats.update']) { + if(events['chats.update']) { console.log(events['chats.update']) } - if (events['contacts.update']) { - for (const contact of events['contacts.update']) { - if (typeof contact.imgUrl !== 'undefined') { + if(events['contacts.update']) { + for(const contact of events['contacts.update']) { + if(typeof contact.imgUrl !== 'undefined') { const newUrl = contact.imgUrl === null ? null : await sock!.profilePictureUrl(contact.id!).catch(() => null) @@ -174,7 +174,7 @@ const startSock = async () => { } } - if (events['chats.delete']) { + if(events['chats.delete']) { console.log('chats deleted ', events['chats.delete']) } } @@ -183,7 +183,7 @@ const startSock = async () => { return sock async function getMessage(key: WAMessageKey): Promise { - if (store) { + if(store) { const msg = await store.loadMessage(key.remoteJid!, key.id!) return msg?.message || undefined } diff --git a/src/Socket/chats.ts b/src/Socket/chats.ts index 057896d..1a88161 100644 --- a/src/Socket/chats.ts +++ b/src/Socket/chats.ts @@ -37,13 +37,13 @@ export const makeChatsSocket = (config: SocketConfig) => { const processingMutex = makeMutex() /** helper function to fetch the given app state sync key */ - const getAppStateSyncKey = async (keyId: string) => { + const getAppStateSyncKey = async(keyId: string) => { const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]) return key } - const fetchPrivacySettings = async (force: boolean = false) => { - if (!privacySettings || force) { + const fetchPrivacySettings = async(force: boolean = false) => { + if(!privacySettings || force) { const { content } = await query({ tag: 'iq', attrs: { @@ -62,7 +62,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } /** helper function to run a privacy IQ query */ - const privacyQuery = async (name: string, value: string) => { + const privacyQuery = async(name: string, value: string) => { await query({ tag: 'iq', attrs: { @@ -83,31 +83,31 @@ export const makeChatsSocket = (config: SocketConfig) => { }) } - const updateLastSeenPrivacy = async (value: WAPrivacyValue) => { + const updateLastSeenPrivacy = async(value: WAPrivacyValue) => { await privacyQuery('last', value) } - const updateOnlinePrivacy = async (value: WAPrivacyOnlineValue) => { + const updateOnlinePrivacy = async(value: WAPrivacyOnlineValue) => { await privacyQuery('online', value) } - const updateProfilePicturePrivacy = async (value: WAPrivacyValue) => { + const updateProfilePicturePrivacy = async(value: WAPrivacyValue) => { await privacyQuery('profile', value) } - const updateStatusPrivacy = async (value: WAPrivacyValue) => { + const updateStatusPrivacy = async(value: WAPrivacyValue) => { await privacyQuery('status', value) } - const updateReadReceiptsPrivacy = async (value: WAReadReceiptsValue) => { + const updateReadReceiptsPrivacy = async(value: WAReadReceiptsValue) => { await privacyQuery('readreceipts', value) } - const updateGroupsAddPrivacy = async (value: WAPrivacyValue) => { + const updateGroupsAddPrivacy = async(value: WAPrivacyValue) => { await privacyQuery('groupadd', value) } - const updateDefaultDisappearingMode = async (duration: number) => { + const updateDefaultDisappearingMode = async(duration: number) => { await query({ tag: 'iq', attrs: { @@ -125,7 +125,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } /** helper function to run a generic IQ query */ - const interactiveQuery = async (userNodes: BinaryNode[], queryNode: BinaryNode) => { + const interactiveQuery = async(userNodes: BinaryNode[], queryNode: BinaryNode) => { const result = await query({ tag: 'iq', attrs: { @@ -166,7 +166,7 @@ export const makeChatsSocket = (config: SocketConfig) => { return users } - const onWhatsApp = async (...jids: string[]) => { + const onWhatsApp = async(...jids: string[]) => { const results = await interactiveQuery( [ { @@ -190,12 +190,12 @@ export const makeChatsSocket = (config: SocketConfig) => { }).filter(item => item.exists) } - const fetchStatus = async (jid: string) => { + const fetchStatus = async(jid: string) => { const [result] = await interactiveQuery( [{ tag: 'user', attrs: { jid } }], { tag: 'status', attrs: {} } ) - if (result) { + if(result) { const status = getBinaryNodeChild(result, 'status') return { status: status?.content!.toString(), @@ -205,7 +205,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } /** update the profile picture for yourself or a group */ - const updateProfilePicture = async (jid: string, content: WAMediaUpload) => { + const updateProfilePicture = async(jid: string, content: WAMediaUpload) => { const { img } = await generateProfilePicture(content) await query({ tag: 'iq', @@ -225,7 +225,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } /** remove the profile picture for yourself or a group */ - const removeProfilePicture = async (jid: string) => { + const removeProfilePicture = async(jid: string) => { await query({ tag: 'iq', attrs: { @@ -237,7 +237,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } /** update the profile status for yourself */ - const updateProfileStatus = async (status: string) => { + const updateProfileStatus = async(status: string) => { await query({ tag: 'iq', attrs: { @@ -255,11 +255,11 @@ export const makeChatsSocket = (config: SocketConfig) => { }) } - const updateProfileName = async (name: string) => { + const updateProfileName = async(name: string) => { await chatModify({ pushNameSetting: name }, '') } - const fetchBlocklist = async () => { + const fetchBlocklist = async() => { const result = await query({ tag: 'iq', attrs: { @@ -274,7 +274,7 @@ export const makeChatsSocket = (config: SocketConfig) => { .map(n => n.attrs.jid) } - const updateBlockStatus = async (jid: string, action: 'block' | 'unblock') => { + const updateBlockStatus = async(jid: string, action: 'block' | 'unblock') => { await query({ tag: 'iq', attrs: { @@ -294,7 +294,7 @@ export const makeChatsSocket = (config: SocketConfig) => { }) } - const getBusinessProfile = async (jid: string): Promise => { + const getBusinessProfile = async(jid: string): Promise => { const results = await query({ tag: 'iq', attrs: { @@ -314,7 +314,7 @@ export const makeChatsSocket = (config: SocketConfig) => { const profileNode = getBinaryNodeChild(results, 'business_profile') const profiles = getBinaryNodeChild(profileNode, 'profile') - if (profiles) { + if(profiles) { const address = getBinaryNodeChild(profiles, 'address') const description = getBinaryNodeChild(profiles, 'description') const website = getBinaryNodeChild(profiles, 'website') @@ -340,7 +340,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } } - const updateAccountSyncTimestamp = async (fromTimestamp: number | string) => { + const updateAccountSyncTimestamp = async(fromTimestamp: number | string) => { logger.info({ fromTimestamp }, 'requesting account sync') await sendNode({ tag: 'iq', @@ -376,30 +376,30 @@ export const makeChatsSocket = (config: SocketConfig) => { } } - const resyncAppState = ev.createBufferedFunction(async (collections: readonly WAPatchName[], isInitialSync: boolean) => { + const resyncAppState = ev.createBufferedFunction(async(collections: readonly WAPatchName[], isInitialSync: boolean) => { // we use this to determine which events to fire // otherwise when we resync from scratch -- all notifications will fire const initialVersionMap: { [T in WAPatchName]?: number } = {} const globalMutationMap: ChatMutationMap = {} await authState.keys.transaction( - async () => { + 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: { [T in WAPatchName]?: number } = {} // 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) { + while(collectionsToHandle.size) { const states = {} as { [T in WAPatchName]: LTHashState } const nodes: BinaryNode[] = [] - for (const name of collectionsToHandle) { + 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') { + if(state) { + if(typeof initialVersionMap[name] === 'undefined') { initialVersionMap[name] = state.version } } else { @@ -439,11 +439,11 @@ export const makeChatsSocket = (config: SocketConfig) => { // extract from binary node const decoded = await extractSyncdPatches(result, config?.options) - for (const key in decoded) { + for(const key in decoded) { const name = key as WAPatchName const { patches, hasMorePatches, snapshot } = decoded[name] try { - if (snapshot) { + if(snapshot) { const { state: newState, mutationMap } = await decodeSyncdSnapshot( name, snapshot, @@ -460,7 +460,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } // only process if there are syncd patches - if (patches.length) { + if(patches.length) { const { state: newState, mutationMap } = await decodePatches( name, patches, @@ -480,12 +480,12 @@ export const makeChatsSocket = (config: SocketConfig) => { Object.assign(globalMutationMap, mutationMap) } - if (hasMorePatches) { + if(hasMorePatches) { logger.info(`${name} has more patches...`) } else { // collection is done with sync collectionsToHandle.delete(name) } - } catch (error) { + } catch(error) { // if retry attempts overshoot // or key not found const isIrrecoverableError = attemptsMap[name]! >= MAX_SYNC_ATTEMPTS @@ -499,7 +499,7 @@ export const makeChatsSocket = (config: SocketConfig) => { // increment number of retries attemptsMap[name] = (attemptsMap[name] || 0) + 1 - if (isIrrecoverableError) { + if(isIrrecoverableError) { // stop retrying collectionsToHandle.delete(name) } @@ -510,7 +510,7 @@ export const makeChatsSocket = (config: SocketConfig) => { ) const { onMutation } = newAppStateChunkHandler(isInitialSync) - for (const key in globalMutationMap) { + for(const key in globalMutationMap) { onMutation(globalMutationMap[key]) } }) @@ -520,7 +520,7 @@ export const makeChatsSocket = (config: SocketConfig) => { * type = "preview" for a low res picture * type = "image for the high res picture" */ - const profilePictureUrl = async (jid: string, type: 'preview' | 'image' = 'preview', timeoutMs?: number) => { + const profilePictureUrl = async(jid: string, type: 'preview' | 'image' = 'preview', timeoutMs?: number) => { jid = jidNormalizedUser(jid) const result = await query({ tag: 'iq', @@ -537,10 +537,10 @@ export const makeChatsSocket = (config: SocketConfig) => { return child?.attrs?.url } - const sendPresenceUpdate = async (type: WAPresence, toJid?: string) => { + const sendPresenceUpdate = async(type: WAPresence, toJid?: string) => { const me = authState.creds.me! - if (type === 'available' || type === 'unavailable') { - if (!me!.name) { + if(type === 'available' || type === 'unavailable') { + if(!me!.name) { logger.warn('no name present, ignoring presence update request...') return } @@ -600,23 +600,23 @@ export const makeChatsSocket = (config: SocketConfig) => { const jid = attrs.from const participant = attrs.participant || attrs.from - if (shouldIgnoreJid(jid)) { + if(shouldIgnoreJid(jid)) { return } - if (tag === 'presence') { + if(tag === 'presence') { presence = { lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available', lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined } - } else if (Array.isArray(content)) { + } else if(Array.isArray(content)) { const [firstChild] = content let type = firstChild.tag as WAPresence - if (type === 'paused') { + if(type === 'paused') { type = 'available' } - if (firstChild.attrs?.media === 'audio') { + if(firstChild.attrs?.media === 'audio') { type = 'recording' } @@ -625,15 +625,15 @@ export const makeChatsSocket = (config: SocketConfig) => { logger.error({ tag, attrs, content }, 'recv invalid presence node') } - if (presence) { + if(presence) { ev.emit('presence.update', { id: jid, presences: { [participant]: presence } }) } } - const appPatch = async (patchCreate: WAPatchCreate) => { + const appPatch = async(patchCreate: WAPatchCreate) => { const name = patchCreate.type const myAppStateKeyId = authState.creds.myAppStateKeyId - if (!myAppStateKeyId) { + if(!myAppStateKeyId) { throw new Boom('App state key not present!', { statusCode: 400 }) } @@ -641,9 +641,9 @@ export const makeChatsSocket = (config: SocketConfig) => { let encodeResult: { patch: proto.ISyncdPatch, state: LTHashState } await processingMutex.mutex( - async () => { + async() => { await authState.keys.transaction( - async () => { + async() => { logger.debug({ patch: patchCreate }, 'applying app patch') await resyncAppState([name], false) @@ -698,7 +698,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } ) - if (config.emitOwnEvents) { + if(config.emitOwnEvents) { const { onMutation } = newAppStateChunkHandler(false) const { mutationMap } = await decodePatches( name, @@ -709,14 +709,14 @@ export const makeChatsSocket = (config: SocketConfig) => { undefined, logger, ) - for (const key in mutationMap) { + for(const key in mutationMap) { onMutation(mutationMap[key]) } } } /** sending abt props may fix QR scan fail if server expects */ - const fetchAbt = async () => { + const fetchAbt = async() => { const abtNode = await query({ tag: 'iq', attrs: { @@ -732,7 +732,7 @@ export const makeChatsSocket = (config: SocketConfig) => { const propsNode = getBinaryNodeChild(abtNode, 'props') let props: { [_: string]: string } = {} - if (propsNode) { + if(propsNode) { props = reduceBinaryNodeToDictionary(propsNode, 'prop') } @@ -742,7 +742,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } /** sending non-abt props may fix QR scan fail if server expects */ - const fetchProps = async () => { + const fetchProps = async() => { const resultNode = await query({ tag: 'iq', attrs: { @@ -758,7 +758,7 @@ export const makeChatsSocket = (config: SocketConfig) => { const propsNode = getBinaryNodeChild(resultNode, 'props') let props: { [_: string]: string } = {} - if (propsNode) { + if(propsNode) { props = reduceBinaryNodeToDictionary(propsNode, 'prop') } @@ -827,7 +827,7 @@ export const makeChatsSocket = (config: SocketConfig) => { * queries need to be fired on connection open * help ensure parity with WA Web * */ - const executeInitQueries = async () => { + const executeInitQueries = async() => { await Promise.all([ fetchAbt(), fetchProps(), @@ -836,19 +836,19 @@ export const makeChatsSocket = (config: SocketConfig) => { ]) } - const upsertMessage = ev.createBufferedFunction(async (msg: WAMessage, type: MessageUpsertType) => { + const upsertMessage = ev.createBufferedFunction(async(msg: WAMessage, type: MessageUpsertType) => { ev.emit('messages.upsert', { messages: [msg], type }) - if (!!msg.pushName) { + if(!!msg.pushName) { let jid = msg.key.fromMe ? authState.creds.me!.id : (msg.key.participant || msg.key.remoteJid) jid = jidNormalizedUser(jid!) - if (!msg.key.fromMe) { + 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) { + if(msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) { ev.emit('creds.update', { me: { ...authState.creds.me!, name: msg.pushName! } }) } } @@ -861,14 +861,14 @@ export const makeChatsSocket = (config: SocketConfig) => { ) : false - if (historyMsg && !authState.creds.myAppStateKeyId) { + if(historyMsg && !authState.creds.myAppStateKeyId) { logger.warn('skipping app state sync, as myAppStateKeyId is not set') pendingAppStateSync = true } await Promise.all([ - (async () => { - if ( + (async() => { + if( historyMsg && authState.creds.myAppStateKeyId ) { @@ -890,7 +890,7 @@ export const makeChatsSocket = (config: SocketConfig) => { ) ]) - if ( + if( msg.message?.protocolMessage?.appStateSyncKeyShare && pendingAppStateSync ) { @@ -899,14 +899,14 @@ export const makeChatsSocket = (config: SocketConfig) => { } async function doAppStateSync() { - if (!authState.creds.accountSyncCounter) { + if(!authState.creds.accountSyncCounter) { logger.info('doing initial app state sync') await resyncAppState(ALL_WA_PATCH_NAMES, true) const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1 ev.emit('creds.update', { accountSyncCounter }) - if (needToFlushWithAppStateSync) { + if(needToFlushWithAppStateSync) { logger.debug('flushing with app state sync') ev.flush() } @@ -917,31 +917,31 @@ export const makeChatsSocket = (config: SocketConfig) => { ws.on('CB:presence', handlePresenceUpdate) ws.on('CB:chatstate', handlePresenceUpdate) - ws.on('CB:ib,,dirty', async (node: BinaryNode) => { + ws.on('CB:ib,,dirty', async(node: BinaryNode) => { const { attrs } = getBinaryNodeChild(node, 'dirty')! const type = attrs.type switch (type) { - case 'account_sync': - if (attrs.timestamp) { - let { lastAccountSyncTimestamp } = authState.creds - if (lastAccountSyncTimestamp) { - await updateAccountSyncTimestamp(lastAccountSyncTimestamp) - } - - lastAccountSyncTimestamp = +attrs.timestamp - ev.emit('creds.update', { lastAccountSyncTimestamp }) + case 'account_sync': + if(attrs.timestamp) { + let { lastAccountSyncTimestamp } = authState.creds + if(lastAccountSyncTimestamp) { + await updateAccountSyncTimestamp(lastAccountSyncTimestamp) } - break - default: - logger.info({ node }, 'received unknown sync') - break + lastAccountSyncTimestamp = +attrs.timestamp + ev.emit('creds.update', { lastAccountSyncTimestamp }) + } + + break + default: + logger.info({ node }, 'received unknown sync') + break } }) ev.on('connection.update', ({ connection, receivedPendingNotifications }) => { - if (connection === 'open') { - if (fireInitQueries) { + if(connection === 'open') { + if(fireInitQueries) { executeInitQueries() .catch( error => onUnexpectedError(error, 'init queries') @@ -954,11 +954,11 @@ export const makeChatsSocket = (config: SocketConfig) => { ) } - if (receivedPendingNotifications) { + if(receivedPendingNotifications) { // if we don't have the app state key // we keep buffering events until we finally have // the key and can sync the messages - if (!authState.creds?.myAppStateKeyId) { + if(!authState.creds?.myAppStateKeyId) { ev.buffer() needToFlushWithAppStateSync = true } diff --git a/src/Store/make-in-memory-store.ts b/src/Store/make-in-memory-store.ts index 5787b24..d35ad2e 100644 --- a/src/Store/make-in-memory-store.ts +++ b/src/Store/make-in-memory-store.ts @@ -91,7 +91,7 @@ export default ( const labelAssociations = new KeyedDB(labelAssociationKey, labelAssociationKey.key) const assertMessageList = (jid: string) => { - if (!messages[jid]) { + if(!messages[jid]) { messages[jid] = makeMessagesDictionary() } @@ -100,7 +100,7 @@ export default ( const contactsUpsert = (newContacts: Contact[]) => { const oldContacts = new Set(Object.keys(contacts)) - for (const contact of newContacts) { + for(const contact of newContacts) { oldContacts.delete(contact.id) contacts[contact.id] = Object.assign( contacts[contact.id] || {}, @@ -112,7 +112,7 @@ export default ( } const labelsUpsert = (newLabels: Label[]) => { - for (const label of newLabels) { + for(const label of newLabels) { labels.upsertById(label.id, label) } } @@ -134,10 +134,10 @@ export default ( messages: newMessages, isLatest }) => { - if (isLatest) { + if(isLatest) { chats.clear() - for (const id in messages) { + for(const id in messages) { delete messages[id] } } @@ -146,13 +146,13 @@ export default ( logger.debug({ chatsAdded }, 'synced chats') const oldContacts = contactsUpsert(newContacts) - for (const jid of oldContacts) { + for(const jid of oldContacts) { delete contacts[jid] } logger.debug({ deletedContacts: oldContacts.size, newContacts }, 'synced contacts') - for (const msg of newMessages) { + for(const msg of newMessages) { const jid = msg.key.remoteJid! const list = assertMessageList(jid) list.upsert(msg, 'prepend') @@ -162,8 +162,8 @@ export default ( }) ev.on('contacts.update', updates => { - for (const update of updates) { - if (contacts[update.id!]) { + for(const update of updates) { + if(contacts[update.id!]) { Object.assign(contacts[update.id!], update) } else { logger.debug({ update }, 'got update for non-existant contact') @@ -174,28 +174,28 @@ export default ( chats.upsert(...newChats) }) ev.on('chats.update', updates => { - for (let update of updates) { + for(let update of updates) { const result = chats.update(update.id!, chat => { - if (update.unreadCount! > 0) { + if(update.unreadCount! > 0) { update = { ...update } update.unreadCount = (chat.unreadCount || 0) + update.unreadCount! } Object.assign(chat, update) }) - if (!result) { + if(!result) { logger.debug({ update }, 'got update for non-existant chat') } } }) ev.on('labels.edit', (label: Label) => { - if (label.deleted) { + if(label.deleted) { return labels.deleteById(label.id) } // WhatsApp can store only up to 20 labels - if (labels.count() < 20) { + if(labels.count() < 20) { return labels.upsertById(label.id, label) } @@ -204,14 +204,14 @@ export default ( ev.on('labels.association', ({ type, association }) => { switch (type) { - case 'add': - labelAssociations.upsert(association) - break - case 'remove': - labelAssociations.delete(association) - break - default: - console.error(`unknown operation type [${type}]`) + case 'add': + labelAssociations.upsert(association) + break + case 'remove': + labelAssociations.delete(association) + break + default: + console.error(`unknown operation type [${type}]`) } }) @@ -220,52 +220,52 @@ export default ( Object.assign(presences[id], update) }) ev.on('chats.delete', deletions => { - for (const item of deletions) { + for(const item of deletions) { chats.deleteById(item) } }) ev.on('messages.upsert', ({ messages: newMessages, type }) => { switch (type) { - case 'append': - case 'notify': - for (const msg of newMessages) { - const jid = jidNormalizedUser(msg.key.remoteJid!) - const list = assertMessageList(jid) - list.upsert(msg, 'append') + case 'append': + case 'notify': + for(const msg of newMessages) { + const jid = jidNormalizedUser(msg.key.remoteJid!) + const list = assertMessageList(jid) + list.upsert(msg, 'append') - if (type === 'notify') { - if (!chats.get(jid)) { - ev.emit('chats.upsert', [ - { - id: jid, - conversationTimestamp: toNumber(msg.messageTimestamp), - unreadCount: 1 - } - ]) - } + if(type === 'notify') { + if(!chats.get(jid)) { + ev.emit('chats.upsert', [ + { + id: jid, + conversationTimestamp: toNumber(msg.messageTimestamp), + unreadCount: 1 + } + ]) } } + } - break + break } }) ev.on('messages.update', updates => { - for (const { update, key } of updates) { + for(const { update, key } of updates) { const list = assertMessageList(key.remoteJid!) const result = list.updateAssign(key.id!, update) - if (!result) { + if(!result) { logger.debug({ update }, 'got update for non-existent message') } } }) ev.on('messages.delete', item => { - if ('all' in item) { + if('all' in item) { const list = messages[item.jid] list?.clear() } else { const jid = item.keys[0].remoteJid! const list = messages[jid] - if (list) { + if(list) { const idSet = new Set(item.keys.map(k => k.id)) list.filter(m => !idSet.has(m.key.id)) } @@ -273,9 +273,9 @@ export default ( }) ev.on('groups.update', updates => { - for (const update of updates) { + for(const update of updates) { const id = update.id! - if (groupMetadata[id]) { + if(groupMetadata[id]) { Object.assign(groupMetadata[id], update) } else { logger.debug({ update }, 'got update for non-existant group metadata') @@ -285,42 +285,42 @@ export default ( ev.on('group-participants.update', ({ id, participants, action }) => { const metadata = groupMetadata[id] - if (metadata) { + if(metadata) { switch (action) { - case 'add': - metadata.participants.push(...participants.map(id => ({ id, isAdmin: false, isSuperAdmin: false }))) - break - case 'demote': - case 'promote': - for (const participant of metadata.participants) { - if (participants.includes(participant.id)) { - participant.isAdmin = action === 'promote' - } + case 'add': + metadata.participants.push(...participants.map(id => ({ id, isAdmin: false, isSuperAdmin: false }))) + break + case 'demote': + case 'promote': + for(const participant of metadata.participants) { + if(participants.includes(participant.id)) { + participant.isAdmin = action === 'promote' } + } - break - case 'remove': - metadata.participants = metadata.participants.filter(p => !participants.includes(p.id)) - break + break + case 'remove': + metadata.participants = metadata.participants.filter(p => !participants.includes(p.id)) + break } } }) ev.on('message-receipt.update', updates => { - for (const { key, receipt } of updates) { + for(const { key, receipt } of updates) { const obj = messages[key.remoteJid!] const msg = obj?.get(key.id!) - if (msg) { + if(msg) { updateMessageWithReceipt(msg, receipt) } } }) ev.on('messages.reaction', (reactions) => { - for (const { key, reaction } of reactions) { + for(const { key, reaction } of reactions) { const obj = messages[key.remoteJid!] const msg = obj?.get(key.id!) - if (msg) { + if(msg) { updateMessageWithReaction(msg, reaction) } } @@ -335,20 +335,14 @@ export default ( labelAssociations }) - const fromJSON = (json: { - chats: Chat[], - contacts: { [id: string]: Contact }, - messages: { [id: string]: WAMessage[] }, - labels: { [labelId: string]: Label }, - labelAssociations: LabelAssociation[] - }) => { + const fromJSON = (json: {chats: Chat[], contacts: { [id: string]: Contact }, messages: { [id: string]: WAMessage[] }, labels: { [labelId: string]: Label }, labelAssociations: LabelAssociation[]}) => { chats.upsert(...json.chats) labelAssociations.upsert(...json.labelAssociations || []) contactsUpsert(Object.values(json.contacts)) labelsUpsert(Object.values(json.labels || {})) - for (const jid in json.messages) { + for(const jid in json.messages) { const list = assertMessageList(jid) - for (const msg of json.messages[jid]) { + for(const msg of json.messages[jid]) { list.upsert(proto.WebMessageInfo.fromObject(msg), 'append') } } @@ -366,15 +360,15 @@ export default ( labelAssociations, bind, /** loads messages from the store, if not found -- uses the legacy connection */ - loadMessages: async (jid: string, count: number, cursor: WAMessageCursor) => { + loadMessages: async(jid: string, count: number, cursor: WAMessageCursor) => { const list = assertMessageList(jid) const mode = !cursor || 'before' in cursor ? 'before' : 'after' const cursorKey = !!cursor ? ('before' in cursor ? cursor.before : cursor.after) : undefined const cursorValue = cursorKey ? list.get(cursorKey.id!) : undefined let messages: WAMessage[] - if (list && mode === 'before' && (!cursorKey || cursorValue)) { - if (cursorValue) { + if(list && mode === 'before' && (!cursorKey || cursorValue)) { + if(cursorValue) { const msgIdx = list.array.findIndex(m => m.key.id === cursorKey?.id) messages = list.array.slice(0, msgIdx) } else { @@ -382,7 +376,7 @@ export default ( } const diff = count - messages.length - if (diff < 0) { + if(diff < 0) { messages = messages.slice(-count) // get the last X messages } } else { @@ -423,27 +417,27 @@ export default ( return associations.map(({ labelId }) => labelId) }, - loadMessage: async (jid: string, id: string) => messages[jid]?.get(id), - mostRecentMessage: async (jid: string) => { + loadMessage: async(jid: string, id: string) => messages[jid]?.get(id), + mostRecentMessage: async(jid: string) => { const message: WAMessage | undefined = messages[jid]?.array.slice(-1)[0] return message }, - fetchImageUrl: async (jid: string, sock: WASocket | undefined) => { + fetchImageUrl: async(jid: string, sock: WASocket | undefined) => { const contact = contacts[jid] - if (!contact) { + if(!contact) { return sock?.profilePictureUrl(jid) } - if (typeof contact.imgUrl === 'undefined') { + if(typeof contact.imgUrl === 'undefined') { contact.imgUrl = await sock?.profilePictureUrl(jid) } return contact.imgUrl }, - fetchGroupMetadata: async (jid: string, sock: WASocket | undefined) => { - if (!groupMetadata[jid]) { + fetchGroupMetadata: async(jid: string, sock: WASocket | undefined) => { + if(!groupMetadata[jid]) { const metadata = await sock?.groupMetadata(jid) - if (metadata) { + if(metadata) { groupMetadata[jid] = metadata } } @@ -460,7 +454,7 @@ export default ( // return groupMetadata[jid] // }, - fetchMessageReceipts: async ({ remoteJid, id }: WAMessageKey) => { + fetchMessageReceipts: async({ remoteJid, id }: WAMessageKey) => { const list = messages[remoteJid!] const msg = list?.get(id!) return msg?.userReceipt @@ -475,7 +469,7 @@ export default ( readFromFile: (path: string) => { // require fs here so that in case "fs" is not available -- the app does not crash const { readFileSync, existsSync } = require('fs') - if (existsSync(path)) { + if(existsSync(path)) { logger.debug({ path }, 'reading from file') const jsonStr = readFileSync(path, { encoding: 'utf-8' }) const json = JSON.parse(jsonStr) diff --git a/src/Store/object-repository.ts b/src/Store/object-repository.ts index 9cd0ec1..adf2924 100644 --- a/src/Store/object-repository.ts +++ b/src/Store/object-repository.ts @@ -1,31 +1,32 @@ export class ObjectRepository { - readonly entityMap: Map + readonly entityMap: Map - constructor(entities: Record = {}) { - this.entityMap = new Map(Object.entries(entities)) - } + constructor(entities: Record = {}) { + this.entityMap = new Map(Object.entries(entities)) + } - findById(id: string) { - return this.entityMap.get(id) - } + findById(id: string) { + return this.entityMap.get(id) + } - findAll() { - return Array.from(this.entityMap.values()) - } + findAll() { + return Array.from(this.entityMap.values()) + } - upsertById(id: string, entity: T) { - return this.entityMap.set(id, { ...entity }) - } + upsertById(id: string, entity: T) { + return this.entityMap.set(id, { ...entity }) + } - deleteById(id: string) { - return this.entityMap.delete(id) - } + deleteById(id: string) { + return this.entityMap.delete(id) + } - count() { - return this.entityMap.size - } + count() { + return this.entityMap.size + } + + toJSON() { + return this.findAll() + } - toJSON() { - return this.findAll() - } } \ No newline at end of file diff --git a/src/Types/Chat.ts b/src/Types/Chat.ts index e3dcec8..4077da8 100644 --- a/src/Types/Chat.ts +++ b/src/Types/Chat.ts @@ -15,13 +15,7 @@ export type WAReadReceiptsValue = 'all' | 'none' /** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */ export type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused' -export const ALL_WA_PATCH_NAMES = [ - 'critical_block', - 'critical_unblock_low', - 'regular_high', - 'regular_low', - 'regular' -] as const +export const ALL_WA_PATCH_NAMES = ['critical_block', 'critical_unblock_low', 'regular_high', 'regular_low', 'regular'] as const export type WAPatchName = typeof ALL_WA_PATCH_NAMES[number] diff --git a/src/Types/LabelAssociation.ts b/src/Types/LabelAssociation.ts index 38c1c6d..8c17cf5 100644 --- a/src/Types/LabelAssociation.ts +++ b/src/Types/LabelAssociation.ts @@ -1,35 +1,35 @@ /** Association type */ export enum LabelAssociationType { - Chat = 'label_jid', - Message = 'label_message' - } - - export type LabelAssociationTypes = `${LabelAssociationType}` - - /** Association for chat */ - export interface ChatLabelAssociation { - type: LabelAssociationType.Chat - chatId: string - labelId: string - } - - /** Association for message */ - export interface MessageLabelAssociation { - type: LabelAssociationType.Message - chatId: string - messageId: string - labelId: string - } - - export type LabelAssociation = ChatLabelAssociation | MessageLabelAssociation - - /** Body for add/remove chat label association action */ - export interface ChatLabelAssociationActionBody { - labelId: string - } - - /** body for add/remove message label association action */ - export interface MessageLabelAssociationActionBody { - labelId: string - messageId: string - } \ No newline at end of file + Chat = 'label_jid', + Message = 'label_message' +} + +export type LabelAssociationTypes = `${LabelAssociationType}` + +/** Association for chat */ +export interface ChatLabelAssociation { + type: LabelAssociationType.Chat + chatId: string + labelId: string +} + +/** Association for message */ +export interface MessageLabelAssociation { + type: LabelAssociationType.Message + chatId: string + messageId: string + labelId: string +} + +export type LabelAssociation = ChatLabelAssociation | MessageLabelAssociation + +/** Body for add/remove chat label association action */ +export interface ChatLabelAssociationActionBody { + labelId: string +} + +/** body for add/remove message label association action */ +export interface MessageLabelAssociationActionBody { + labelId: string + messageId: string +} \ No newline at end of file diff --git a/src/Utils/chat-utils.ts b/src/Utils/chat-utils.ts index 2c21ea6..99765e2 100644 --- a/src/Utils/chat-utils.ts +++ b/src/Utils/chat-utils.ts @@ -29,12 +29,12 @@ const generateMac = (operation: proto.SyncdMutation.SyncdOperation, data: Buffer const getKeyData = () => { let r: number switch (operation) { - case proto.SyncdMutation.SyncdOperation.SET: - r = 0x01 - break - case proto.SyncdMutation.SyncdOperation.REMOVE: - r = 0x02 - break + case proto.SyncdMutation.SyncdOperation.SET: + r = 0x01 + break + case proto.SyncdMutation.SyncdOperation.REMOVE: + r = 0x02 + break } const buff = Buffer.from([r]) @@ -69,8 +69,8 @@ const makeLtHashGenerator = ({ indexValueMap, hash }: Pick { const indexMacBase64 = Buffer.from(indexMac).toString('base64') const prevOp = indexValueMap[indexMacBase64] - if (operation === proto.SyncdMutation.SyncdOperation.REMOVE) { - if (!prevOp) { + if(operation === proto.SyncdMutation.SyncdOperation.REMOVE) { + if(!prevOp) { throw new Boom('tried remove, but no previous op', { data: { indexMac, valueMac } }) } @@ -82,7 +82,7 @@ const makeLtHashGenerator = ({ indexValueMap, hash }: Pick ({ version: 0, hash: Buffer.alloc(128), indexValueMap: {} }) -export const encodeSyncdPatch = async ( +export const encodeSyncdPatch = async( { type, index, syncAction, apiVersion, operation }: WAPatchCreate, myAppStateKeyId: string, state: LTHashState, getAppStateSyncKey: FetchAppStateSyncKey ) => { const key = !!myAppStateKeyId ? await getAppStateSyncKey(myAppStateKeyId) : undefined - if (!key) { + if(!key) { throw new Boom(`myAppStateKey ("${myAppStateKeyId}") not present`, { statusCode: 404 }) } @@ -185,7 +185,7 @@ export const encodeSyncdPatch = async ( return { patch, state } } -export const decodeSyncdMutations = async ( +export const decodeSyncdMutations = async( msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[], initialState: LTHashState, getAppStateSyncKey: FetchAppStateSyncKey, @@ -196,7 +196,7 @@ export const decodeSyncdMutations = async ( // indexKey used to HMAC sign record.index.blob // valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32] // the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId - for (const msgMutation of msgMutations!) { + for(const msgMutation of msgMutations!) { // if it's a syncdmutation, get the operation property // otherwise, if it's only a record -- it'll be a SET mutation const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdOperation.SET @@ -206,9 +206,9 @@ export const decodeSyncdMutations = async ( const content = Buffer.from(record.value!.blob!) const encContent = content.slice(0, -32) const ogValueMac = content.slice(-32) - if (validateMacs) { + if(validateMacs) { const contentHmac = generateMac(operation!, encContent, record.keyId!.id!, key.valueMacKey) - if (Buffer.compare(contentHmac, ogValueMac) !== 0) { + if(Buffer.compare(contentHmac, ogValueMac) !== 0) { throw new Boom('HMAC content verification failed') } } @@ -216,9 +216,9 @@ export const decodeSyncdMutations = async ( const result = aesDecrypt(encContent, key.valueEncryptionKey) const syncAction = proto.SyncActionData.decode(result) - if (validateMacs) { + if(validateMacs) { const hmac = hmacSign(syncAction.index, key.indexKey) - if (Buffer.compare(hmac, record.index!.blob!) !== 0) { + if(Buffer.compare(hmac, record.index!.blob!) !== 0) { throw new Boom('HMAC index verification failed') } } @@ -238,7 +238,7 @@ export const decodeSyncdMutations = async ( async function getKey(keyId: Uint8Array) { const base64Key = Buffer.from(keyId!).toString('base64') const keyEnc = await getAppStateSyncKey(base64Key) - if (!keyEnc) { + if(!keyEnc) { throw new Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 404, data: { msgMutations } }) } @@ -246,7 +246,7 @@ export const decodeSyncdMutations = async ( } } -export const decodeSyncdPatch = async ( +export const decodeSyncdPatch = async( msg: proto.ISyncdPatch, name: WAPatchName, initialState: LTHashState, @@ -254,10 +254,10 @@ export const decodeSyncdPatch = async ( onMutation: (mutation: ChatMutation) => void, validateMacs: boolean ) => { - if (validateMacs) { + if(validateMacs) { const base64Key = Buffer.from(msg.keyId!.id!).toString('base64') const mainKeyObj = await getAppStateSyncKey(base64Key) - if (!mainKeyObj) { + if(!mainKeyObj) { throw new Boom(`failed to find key "${base64Key}" to decode patch`, { statusCode: 404, data: { msg } }) } @@ -265,7 +265,7 @@ export const decodeSyncdPatch = async ( const mutationmacs = msg.mutations!.map(mutation => mutation.record!.value!.blob!.slice(-32)) const patchMac = generatePatchMac(msg.snapshotMac!, mutationmacs, toNumber(msg.version!.version!), name, mainKey.patchMacKey) - if (Buffer.compare(patchMac, msg.patchMac!) !== 0) { + if(Buffer.compare(patchMac, msg.patchMac!) !== 0) { throw new Boom('Invalid patch mac') } } @@ -274,7 +274,7 @@ export const decodeSyncdPatch = async ( return result } -export const extractSyncdPatches = async ( +export const extractSyncdPatches = async( result: BinaryNode, options: AxiosRequestConfig ) => { @@ -296,8 +296,8 @@ export const extractSyncdPatches = async ( const hasMorePatches = collectionNode.attrs.has_more_patches === 'true' let snapshot: proto.ISyncdSnapshot | undefined = undefined - if (snapshotNode && !!snapshotNode.content) { - if (!Buffer.isBuffer(snapshotNode)) { + if(snapshotNode && !!snapshotNode.content) { + if(!Buffer.isBuffer(snapshotNode)) { snapshotNode.content = Buffer.from(Object.values(snapshotNode.content)) } @@ -308,14 +308,14 @@ export const extractSyncdPatches = async ( snapshot = proto.SyncdSnapshot.decode(data) } - for (let { content } of patches) { - if (content) { - if (!Buffer.isBuffer(content)) { + for(let { content } of patches) { + if(content) { + if(!Buffer.isBuffer(content)) { content = Buffer.from(Object.values(content)) } const syncd = proto.SyncdPatch.decode(content! as Uint8Array) - if (!syncd.version) { + if(!syncd.version) { syncd.version = { version: +collectionNode.attrs.version + 1 } } @@ -332,7 +332,7 @@ export const extractSyncdPatches = async ( } -export const downloadExternalBlob = async ( +export const downloadExternalBlob = async( blob: proto.IExternalBlobReference, options: AxiosRequestConfig ) => { @@ -345,7 +345,7 @@ export const downloadExternalBlob = async ( return Buffer.concat(bufferArray) } -export const downloadExternalPatch = async ( +export const downloadExternalPatch = async( blob: proto.IExternalBlobReference, options: AxiosRequestConfig ) => { @@ -354,7 +354,7 @@ export const downloadExternalPatch = async ( return syncData } -export const decodeSyncdSnapshot = async ( +export const decodeSyncdSnapshot = async( name: WAPatchName, snapshot: proto.ISyncdSnapshot, getAppStateSyncKey: FetchAppStateSyncKey, @@ -383,16 +383,16 @@ export const decodeSyncdSnapshot = async ( newState.hash = hash newState.indexValueMap = indexValueMap - if (validateMacs) { + if(validateMacs) { const base64Key = Buffer.from(snapshot.keyId!.id!).toString('base64') const keyEnc = await getAppStateSyncKey(base64Key) - if (!keyEnc) { + if(!keyEnc) { throw new Boom(`failed to find key "${base64Key}" to decode mutation`) } const result = mutationKeys(keyEnc.keyData!) const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey) - if (Buffer.compare(snapshot.mac!, computedSnapshotMac) !== 0) { + if(Buffer.compare(snapshot.mac!, computedSnapshotMac) !== 0) { throw new Boom(`failed to verify LTHash at ${newState.version} of ${name} from snapshot`) } } @@ -403,7 +403,7 @@ export const decodeSyncdSnapshot = async ( } } -export const decodePatches = async ( +export const decodePatches = async( name: WAPatchName, syncds: proto.ISyncdPatch[], initial: LTHashState, @@ -420,10 +420,10 @@ export const decodePatches = async ( const mutationMap: ChatMutationMap = {} - for (let i = 0; i < syncds.length; i++) { + for(let i = 0; i < syncds.length; i++) { const syncd = syncds[i] const { version, keyId, snapshotMac } = syncd - if (syncd.externalMutations) { + if(syncd.externalMutations) { logger?.trace({ name, version }, 'downloading external patch') const ref = await downloadExternalPatch(syncd.externalMutations, options) logger?.debug({ name, version, mutations: ref.mutations.length }, 'downloaded external patch') @@ -452,16 +452,16 @@ export const decodePatches = async ( newState.hash = decodeResult.hash newState.indexValueMap = decodeResult.indexValueMap - if (validateMacs) { + if(validateMacs) { const base64Key = Buffer.from(keyId!.id!).toString('base64') const keyEnc = await getAppStateSyncKey(base64Key) - if (!keyEnc) { + if(!keyEnc) { throw new Boom(`failed to find key "${base64Key}" to decode mutation`) } const result = mutationKeys(keyEnc.keyData!) const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey) - if (Buffer.compare(snapshotMac!, computedSnapshotMac) !== 0) { + if(Buffer.compare(snapshotMac!, computedSnapshotMac) !== 0) { throw new Boom(`failed to verify LTHash at ${newState.version} of ${name}`) } } @@ -480,25 +480,25 @@ export const chatModificationToAppPatch = ( const OP = proto.SyncdMutation.SyncdOperation const getMessageRange = (lastMessages: LastMessageList) => { let messageRange: proto.SyncActionValue.ISyncActionMessageRange - if (Array.isArray(lastMessages)) { + if(Array.isArray(lastMessages)) { const lastMsg = lastMessages[lastMessages.length - 1] messageRange = { lastMessageTimestamp: lastMsg?.messageTimestamp, messages: lastMessages?.length ? lastMessages.map( m => { - if (!m.key?.id || !m.key?.remoteJid) { + if(!m.key?.id || !m.key?.remoteJid) { throw new Boom('Incomplete key', { statusCode: 400, data: m }) } - if (isJidGroup(m.key.remoteJid) && !m.key.fromMe && !m.key.participant) { + if(isJidGroup(m.key.remoteJid) && !m.key.fromMe && !m.key.participant) { throw new Boom('Expected not from me message to have participant', { statusCode: 400, data: m }) } - if (!m.messageTimestamp || !toNumber(m.messageTimestamp)) { + if(!m.messageTimestamp || !toNumber(m.messageTimestamp)) { throw new Boom('Missing timestamp in last message list', { statusCode: 400, data: m }) } - if (m.key.participant) { + if(m.key.participant) { m.key.participant = jidNormalizedUser(m.key.participant) } @@ -514,7 +514,7 @@ export const chatModificationToAppPatch = ( } let patch: WAPatchCreate - if ('mute' in mod) { + if('mute' in mod) { patch = { syncAction: { muteAction: { @@ -527,7 +527,7 @@ export const chatModificationToAppPatch = ( apiVersion: 2, operation: OP.SET } - } else if ('archive' in mod) { + } else if('archive' in mod) { patch = { syncAction: { archiveChatAction: { @@ -540,7 +540,7 @@ export const chatModificationToAppPatch = ( apiVersion: 3, operation: OP.SET } - } else if ('markRead' in mod) { + } else if('markRead' in mod) { patch = { syncAction: { markChatAsReadAction: { @@ -553,8 +553,8 @@ export const chatModificationToAppPatch = ( apiVersion: 3, operation: OP.SET } - } else if ('clear' in mod) { - if (mod.clear === 'all') { + } else if('clear' in mod) { + if(mod.clear === 'all') { throw new Boom('not supported') } else { const key = mod.clear.messages[0] @@ -571,7 +571,7 @@ export const chatModificationToAppPatch = ( operation: OP.SET } } - } else if ('pin' in mod) { + } else if('pin' in mod) { patch = { syncAction: { pinAction: { @@ -583,7 +583,7 @@ export const chatModificationToAppPatch = ( apiVersion: 5, operation: OP.SET } - } else if ('delete' in mod) { + } else if('delete' in mod) { patch = { syncAction: { deleteChatAction: { @@ -595,7 +595,7 @@ export const chatModificationToAppPatch = ( apiVersion: 6, operation: OP.SET } - } else if ('pushNameSetting' in mod) { + } else if('pushNameSetting' in mod) { patch = { syncAction: { pushNameSetting: { @@ -607,7 +607,7 @@ export const chatModificationToAppPatch = ( apiVersion: 1, operation: OP.SET, } - } else if ('addChatLabel' in mod) { + } else if('addChatLabel' in mod) { patch = { syncAction: { labelAssociationAction: { @@ -619,7 +619,7 @@ export const chatModificationToAppPatch = ( apiVersion: 3, operation: OP.SET, } - } else if ('removeChatLabel' in mod) { + } else if('removeChatLabel' in mod) { patch = { syncAction: { labelAssociationAction: { @@ -631,7 +631,7 @@ export const chatModificationToAppPatch = ( apiVersion: 3, operation: OP.SET, } - } else if ('addMessageLabel' in mod) { + } else if('addMessageLabel' in mod) { patch = { syncAction: { labelAssociationAction: { @@ -650,7 +650,7 @@ export const chatModificationToAppPatch = ( apiVersion: 3, operation: OP.SET, } - } else if ('removeMessageLabel' in mod) { + } else if('removeMessageLabel' in mod) { patch = { syncAction: { labelAssociationAction: { @@ -695,7 +695,7 @@ export const processSyncAction = ( index: [type, id, msgId, fromMe] } = syncAction - if (action?.muteAction) { + if(action?.muteAction) { ev.emit( 'chats.update', [ @@ -708,7 +708,7 @@ export const processSyncAction = ( } ] ) - } else if (action?.archiveChatAction || type === 'archive' || type === 'unarchive') { + } else if(action?.archiveChatAction || type === 'archive' || type === 'unarchive') { // okay so we've to do some annoying computation here // when we're initially syncing the app state // there are a few cases we need to handle @@ -737,7 +737,7 @@ export const processSyncAction = ( archived: isArchived, conditional: getChatUpdateConditional(id, msgRange) }]) - } else if (action?.markChatAsReadAction) { + } else if(action?.markChatAsReadAction) { const markReadAction = action.markChatAsReadAction // basically we don't need to fire an "read" update if the chat is being marked as read // because the chat is read by default @@ -749,7 +749,7 @@ export const processSyncAction = ( unreadCount: isNullUpdate ? null : !!markReadAction?.read ? 0 : -1, conditional: getChatUpdateConditional(id, markReadAction?.messageRange) }]) - } else if (action?.deleteMessageForMeAction || type === 'deleteMessageForMe') { + } else if(action?.deleteMessageForMeAction || type === 'deleteMessageForMe') { ev.emit('messages.delete', { keys: [ { @@ -759,30 +759,30 @@ export const processSyncAction = ( } ] }) - } else if (action?.contactAction) { + } else if(action?.contactAction) { ev.emit('contacts.upsert', [{ id, name: action.contactAction!.fullName! }]) - } else if (action?.pushNameSetting) { + } else if(action?.pushNameSetting) { const name = action?.pushNameSetting?.name - if (name && me?.name !== name) { + if(name && me?.name !== name) { ev.emit('creds.update', { me: { ...me, name } }) } - } else if (action?.pinAction) { + } else if(action?.pinAction) { ev.emit('chats.update', [{ id, pinned: action.pinAction?.pinned ? toNumber(action.timestamp!) : null, conditional: getChatUpdateConditional(id, undefined) }]) - } else if (action?.unarchiveChatsSetting) { + } else if(action?.unarchiveChatsSetting) { const unarchiveChats = !!action.unarchiveChatsSetting.unarchiveChats ev.emit('creds.update', { accountSettings: { unarchiveChats } }) logger?.info(`archive setting updated => '${action.unarchiveChatsSetting.unarchiveChats}'`) - if (accountSettings) { + if(accountSettings) { accountSettings.unarchiveChats = unarchiveChats } - } else if (action?.starAction || type === 'star') { + } else if(action?.starAction || type === 'star') { let starred = action?.starAction?.starred - if (typeof starred !== 'boolean') { + if(typeof starred !== 'boolean') { starred = syncAction.index[syncAction.index.length - 1] === '1' } @@ -792,11 +792,11 @@ export const processSyncAction = ( update: { starred } } ]) - } else if (action?.deleteChatAction || type === 'deleteChat') { - if (!isInitialSync) { + } else if(action?.deleteChatAction || type === 'deleteChat') { + if(!isInitialSync) { ev.emit('chats.delete', [id]) } - } else if (action?.labelEditAction) { + } else if(action?.labelEditAction) { const { name, color, deleted, predefinedId } = action.labelEditAction! ev.emit('labels.edit', { @@ -806,7 +806,7 @@ export const processSyncAction = ( deleted: deleted!, predefinedId: predefinedId ? String(predefinedId) : undefined }) - } else if (action?.labelAssociationAction) { + } else if(action?.labelAssociationAction) { ev.emit('labels.association', { type: action.labelAssociationAction.labeled ? 'add' @@ -832,7 +832,7 @@ export const processSyncAction = ( return isInitialSync ? (data) => { const chat = data.historySets.chats[id] || data.chatUpserts[id] - if (chat) { + if(chat) { return msgRange ? isValidPatchBasedOnMessageRange(chat, msgRange) : true } }