From de7d1002a915475d7c5d9591ea232b6abea531b3 Mon Sep 17 00:00:00 2001 From: Adhiraj Singh Date: Tue, 1 Mar 2022 16:32:14 +0530 Subject: [PATCH] lint: stricter linting rules --- package.json | 16 +++- src/Defaults/index.ts | 2 +- src/LegacySocket/business.ts | 12 +-- src/LegacySocket/chats.ts | 110 ++++++++++++------------- src/LegacySocket/groups.ts | 34 ++++---- src/LegacySocket/messages.ts | 118 +++++++++++++-------------- src/LegacySocket/socket.ts | 52 ++++++------ src/Socket/chats.ts | 60 +++++++------- src/Socket/groups.ts | 10 +-- src/Socket/messages-recv.ts | 50 ++++++------ src/Socket/messages-send.ts | 72 ++++++++-------- src/Socket/socket.ts | 44 +++++----- src/Store/make-in-memory-store.ts | 26 +++--- src/Store/make-ordered-dictionary.ts | 4 +- src/Tests/test.media-download.ts | 8 +- src/Types/Auth.ts | 6 +- src/Types/Chat.ts | 12 +-- src/Types/Events.ts | 10 +-- src/Types/Message.ts | 18 ++-- src/Types/Product.ts | 6 +- src/Types/Socket.ts | 2 +- src/Types/State.ts | 2 +- src/Types/index.ts | 10 +-- src/Utils/auth-utils.ts | 12 +-- src/Utils/chat-utils.ts | 34 ++++---- src/Utils/crypto.ts | 6 +- src/Utils/decode-wa-message.ts | 16 ++-- src/Utils/generics.ts | 22 ++--- src/Utils/legacy-msgs.ts | 26 +++--- src/Utils/lt-hash.ts | 2 +- src/Utils/messages-media.ts | 64 +++++++-------- src/Utils/messages.ts | 72 ++++++++-------- src/Utils/noise-handler.ts | 28 +++---- src/Utils/signal.ts | 8 +- src/Utils/validate-connection.ts | 10 +-- src/WABinary/Legacy/index.ts | 18 ++-- src/WABinary/jid-utils.ts | 2 +- src/WABinary/types.ts | 10 +-- yarn.lock | 46 +++++------ 39 files changed, 534 insertions(+), 526 deletions(-) diff --git a/package.json b/package.json index 21157ac..bea36df 100644 --- a/package.json +++ b/package.json @@ -52,10 +52,18 @@ "sharp": "^0.29.3" }, "peerDependenciesMeta": { - "@adiwajshing/keyed-db": { "optional": true }, - "jimp": { "optional": true }, - "qrcode-terminal": { "optional": true }, - "sharp": { "optional": true } + "@adiwajshing/keyed-db": { + "optional": true + }, + "jimp": { + "optional": true + }, + "qrcode-terminal": { + "optional": true + }, + "sharp": { + "optional": true + } }, "files": [ "lib/*", diff --git a/src/Defaults/index.ts b/src/Defaults/index.ts index ab7f737..06ea237 100644 --- a/src/Defaults/index.ts +++ b/src/Defaults/index.ts @@ -10,7 +10,7 @@ export const DEF_CALLBACK_PREFIX = 'CB:' export const DEF_TAG_PREFIX = 'TAG:' export const PHONE_CONNECTION_CB = 'CB:Pong' -export const WA_DEFAULT_EPHEMERAL = 7*24*60*60 +export const WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60 export const NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0' export const NOISE_WA_HEADER = new Uint8Array([87, 65, 5, 2]) // last is "DICT_VERSION" diff --git a/src/LegacySocket/business.ts b/src/LegacySocket/business.ts index e061f1b..9a06aba 100644 --- a/src/LegacySocket/business.ts +++ b/src/LegacySocket/business.ts @@ -49,7 +49,7 @@ const makeBusinessSocket = (config: LegacySocketConfig) => { mapProductCreate(product) ] }) - + return mapProduct(result.data.product) } @@ -80,7 +80,7 @@ const makeBusinessSocket = (config: LegacySocketConfig) => { { product_id: productId, ...mapProductCreate( - { ...update, originCountryCode: undefined }, + { ...update, originCountryCode: undefined }, false ) } @@ -89,7 +89,7 @@ const makeBusinessSocket = (config: LegacySocketConfig) => { return mapProduct(result.data.product) } - + // maps product create to send to WA const mapProductCreate = (product: ProductCreate, mapCompliance = true) => { const result: any = { @@ -107,10 +107,10 @@ const makeBusinessSocket = (config: LegacySocketConfig) => { } if(mapCompliance) { Object.assign(result, { - compliance_category: product.originCountryCode - ? undefined : + compliance_category: product.originCountryCode + ? undefined : 'COUNTRY_ORIGIN_EXEMPT', - compliance_info: product.originCountryCode + compliance_info: product.originCountryCode ? { country_code_origin: product.originCountryCode } : undefined }) diff --git a/src/LegacySocket/chats.ts b/src/LegacySocket/chats.ts index e6c87c0..c4c497e 100644 --- a/src/LegacySocket/chats.ts +++ b/src/LegacySocket/chats.ts @@ -6,12 +6,12 @@ import makeAuthSocket from './auth' const makeChatsSocket = (config: LegacySocketConfig) => { const { logger } = config const sock = makeAuthSocket(config) - const { - ev, + const { + ev, ws: socketEvents, currentEpoch, setQuery, - query, + query, sendNode, state } = sock @@ -29,9 +29,9 @@ const makeChatsSocket = (config: LegacySocketConfig) => { ) const profilePictureUrl = async(jid: string, timeoutMs?: number) => { - const response = await query({ - json: ['query', 'ProfilePicThumb', jid], - expect200: false, + const response = await query({ + json: ['query', 'ProfilePicThumb', jid], + expect200: false, requiresPhoneConnection: false, timeoutMs }) @@ -71,11 +71,11 @@ const makeChatsSocket = (config: LegacySocketConfig) => { case 'unstar': const starred = updateType === 'star' const updates: WAMessageUpdate[] = (node.content as BinaryNode[]).map( - ({ attrs }) => ({ + ({ attrs }) => ({ key: { - remoteJid: jid, - id: attrs.index, - fromMe: attrs.owner === 'true' + remoteJid: jid, + id: attrs.index, + fromMe: attrs.owner === 'true' }, update: { starred } }) @@ -93,13 +93,13 @@ const makeChatsSocket = (config: LegacySocketConfig) => { default: logger.warn({ node }, 'received unrecognized chat update') break - } + } } const applyingPresenceUpdate = (update: BinaryNode['attrs']): BaileysEventMap['presence.update'] => { const id = jidNormalizedUser(update.id) const participant = jidNormalizedUser(update.participant || update.id) - + const presence: PresenceData = { lastSeen: update.t ? +update.t : undefined, lastKnownPresence: update.type as WAPresence @@ -110,21 +110,21 @@ const makeChatsSocket = (config: LegacySocketConfig) => { const chatRead = async(fromMessage: WAMessageKey, count: number) => { await setQuery ( [ - { + { tag: 'read', - attrs: { - jid: fromMessage.remoteJid, - count: count.toString(), - index: fromMessage.id, + attrs: { + jid: fromMessage.remoteJid, + count: count.toString(), + index: fromMessage.id, owner: fromMessage.fromMe ? 'true' : 'false' } } - ], + ], [ WAMetric.read, WAFlag.ignore ] ) if(config.emitOwnEvents) { ev.emit('chats.update', [{ id: fromMessage.remoteJid, unreadCount: count < 0 ? -1 : 0 }]) - } + } } ev.on('connection.update', async({ connection }) => { @@ -143,7 +143,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => { binaryTag: [ WAMetric.queryStatus, WAFlag.ignore ] }), sendNode({ - json: { tag: 'query', attrs: { type: 'quick_reply', epoch: '1' } }, + json: { tag: 'query', attrs: { type: 'quick_reply', epoch: '1' } }, binaryTag: [ WAMetric.queryQuickReply, WAFlag.ignore ] }), sendNode({ @@ -152,21 +152,21 @@ const makeChatsSocket = (config: LegacySocketConfig) => { }), sendNode({ json: { tag: 'query', attrs: { type: 'emoji', epoch: '1' } }, - binaryTag: [ WAMetric.queryEmoji, WAFlag.ignore ] + binaryTag: [ WAMetric.queryEmoji, WAFlag.ignore ] }), sendNode({ - json: { - tag: 'action', + json: { + tag: 'action', attrs: { type: 'set', epoch: '1' }, content: [ { tag: 'presence', attrs: { type: 'available' } } ] - }, + }, binaryTag: [ WAMetric.presence, WAFlag.available ] }) ]) chatsDebounceTimeout.start() - + logger.debug('sent init queries') } catch(error) { logger.error(`error in sending init queries: ${error}`) @@ -246,16 +246,16 @@ const makeChatsSocket = (config: LegacySocketConfig) => { ev.emit('chats.update', [update]) } - }) + }) socketEvents.on('CB:Cmd,type:picture', async json => { json = json[1] const id = jidNormalizedUser(json.jid) const imgUrl = await profilePictureUrl(id).catch(() => '') - + ev.emit('contacts.update', [ { id, imgUrl } ]) }) - + // chat archive, pin etc. socketEvents.on('CB:action,,chat', ({ content }: BinaryNode) => { if(Array.isArray(content)) { @@ -269,7 +269,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => { const user = json.content[0].attrs if(user.id) { user.id = jidNormalizedUser(user.id) - + //ev.emit('contacts.upsert', [user]) } else { logger.warn({ json }, 'recv unknown action') @@ -303,7 +303,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => { * Modify a given chat (archive, pin etc.) * @param jid the ID of the person/group you are modifiying */ - chatModify: async(modification: ChatModification, jid: string, chatInfo: Pick, timestampNow?: number) => { + chatModify: async(modification: ChatModification, jid: string, chatInfo: Pick, timestampNow?: number) => { const chatAttrs: BinaryNode['attrs'] = { jid: jid } let data: BinaryNode[] | undefined = undefined @@ -327,10 +327,10 @@ const makeChatsSocket = (config: LegacySocketConfig) => { } } else if('clear' in modification) { chatAttrs.type = 'clear' - chatAttrs.modify_tag = Math.round(Math.random()*1000000).toString() + chatAttrs.modify_tag = Math.round(Math.random() * 1000000).toString() if(modification.clear !== 'all') { data = modification.clear.messages.map(({ id, fromMe }) => ( - { + { tag: 'item', attrs: { owner: (!!fromMe).toString(), index: id } } @@ -339,20 +339,20 @@ const makeChatsSocket = (config: LegacySocketConfig) => { } else if('star' in modification) { chatAttrs.type = modification.star.star ? 'star' : 'unstar' data = modification.star.messages.map(({ id, fromMe }) => ( - { + { tag: 'item', attrs: { owner: (!!fromMe).toString(), index: id } } )) } else if('markRead' in modification) { - const indexKey = modification.lastMessages[modification.lastMessages.length-1].key + const indexKey = modification.lastMessages[modification.lastMessages.length - 1].key return chatRead(indexKey, modification.markRead ? 0 : -1) } else if('delete' in modification) { chatAttrs.type = 'delete' } if('lastMessages' in modification) { - const indexKey = modification.lastMessages[modification.lastMessages.length-1].key + const indexKey = modification.lastMessages[modification.lastMessages.length - 1].key if(indexKey) { chatAttrs.index = indexKey.id chatAttrs.owner = indexKey.fromMe ? 'true' : 'false' @@ -368,20 +368,20 @@ const makeChatsSocket = (config: LegacySocketConfig) => { return response }, - /** + /** * Query whether a given number is registered on WhatsApp * @param str phone number/jid you want to check for * @returns undefined if the number doesn't exists, otherwise the correctly formatted jid */ onWhatsApp: async(str: string) => { const { status, jid, biz } = await query({ - json: ['query', 'exist', str], + json: ['query', 'exist', str], requiresPhoneConnection: false }) if(status === 200) { - return { - exists: true, - jid: jidNormalizedUser(jid), + return { + exists: true, + jid: jidNormalizedUser(jid), isBusiness: biz as boolean } } @@ -394,20 +394,20 @@ const makeChatsSocket = (config: LegacySocketConfig) => { sendPresenceUpdate: (type: WAPresence, jid: string | undefined) => ( sendNode({ binaryTag: [WAMetric.presence, WAFlag[type]], // weird stuff WA does - json: { + json: { tag: 'action', attrs: { epoch: currentEpoch().toString(), type: 'set' }, content: [ - { - tag: 'presence', + { + tag: 'presence', attrs: { type: type, to: jid } } ] } }) ), - /** - * Request updates on the presence of a user + /** + * Request updates on the presence of a user * this returns nothing, you'll receive updates in chats.update event * */ presenceSubscribe: async(jid: string) => ( @@ -421,7 +421,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => { setStatus: async(status: string) => { const response = await setQuery( [ - { + { tag: 'status', attrs: {}, content: Buffer.from (status, 'utf-8') @@ -444,7 +444,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => { updateProfileName: async(name: string) => { const response = (await setQuery( [ - { + { tag: 'profile', attrs: { name } } @@ -463,14 +463,14 @@ const makeChatsSocket = (config: LegacySocketConfig) => { }, /** * Update the profile picture - * @param jid - * @param img + * @param jid + * @param img */ async updateProfilePicture(jid: string, img: Buffer) { jid = jidNormalizedUser (jid) const data = { img: Buffer.from([]), preview: Buffer.from([]) } //await generateProfilePicture(img) TODO const tag = this.generateMessageTag () - const query: BinaryNode = { + const query: BinaryNode = { tag: 'picture', attrs: { jid: jid, id: tag, type: 'set' }, content: [ @@ -481,7 +481,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => { const user = state.legacy?.user const { eurl } = await this.setQuery ([query], [WAMetric.picture, 136], tag) as { eurl: string, status: number } - + if(config.emitOwnEvents) { if(jid === user.id) { user.imgUrl = eurl @@ -502,7 +502,7 @@ const makeChatsSocket = (config: LegacySocketConfig) => { * @param type type of operation */ blockUser: async(jid: string, type: 'add' | 'remove' = 'add') => { - const json = { + const json = { tag: 'block', attrs: { type }, content: [ { tag: 'user', attrs: { jid } } ] @@ -522,12 +522,12 @@ const makeChatsSocket = (config: LegacySocketConfig) => { const { profiles: [{ profile, - wid + wid }] } = await query({ json: [ - 'query', 'businessProfile', - [ { 'wid': jid.replace('@s.whatsapp.net', '@c.us') } ], + 'query', 'businessProfile', + [ { 'wid': jid.replace('@s.whatsapp.net', '@c.us') } ], 84 ], expect200: true, diff --git a/src/LegacySocket/groups.ts b/src/LegacySocket/groups.ts index 1c5a6a3..d8a012c 100644 --- a/src/LegacySocket/groups.ts +++ b/src/LegacySocket/groups.ts @@ -6,8 +6,8 @@ import makeMessagesSocket from './messages' const makeGroupsSocket = (config: LegacySocketConfig) => { const { logger } = config const sock = makeMessagesSocket(config) - const { - ev, + const { + ev, ws: socketEvents, query, generateMessageTag, @@ -29,10 +29,10 @@ const makeGroupsSocket = (config: LegacySocketConfig) => { jid: jid, subject: subject, }, - content: participants ? + content: participants ? participants.map(jid => ( { tag: 'participant', attrs: { jid } } - )) : + )) : additionalNodes } ], [WAMetric.group, 136], tag) @@ -42,9 +42,9 @@ const makeGroupsSocket = (config: LegacySocketConfig) => { /** Get the metadata of the group from WA */ const groupMetadataFull = async(jid: string) => { const metadata = await query({ - json: ['query', 'GroupMetadata', jid], + json: ['query', 'GroupMetadata', jid], expect200: true - }) + }) const meta: GroupMetadata = { id: metadata.id, @@ -69,10 +69,10 @@ const makeGroupsSocket = (config: LegacySocketConfig) => { const groupMetadataMinimal = async(jid: string) => { const { attrs, content }:BinaryNode = await query({ json: { - tag: 'query', + tag: 'query', attrs: { type: 'group', jid: jid, epoch: currentEpoch().toString() } }, - binaryTag: [WAMetric.group, WAFlag.ignore], + binaryTag: [WAMetric.group, WAFlag.ignore], expect200: true }) const participants: GroupParticipant[] = [] @@ -102,7 +102,7 @@ const makeGroupsSocket = (config: LegacySocketConfig) => { } return meta } - + socketEvents.on('CB:Chat,cmd:action', (json: BinaryNode) => { /*const data = json[1].data if (data) { @@ -138,7 +138,7 @@ const makeGroupsSocket = (config: LegacySocketConfig) => { result = await groupMetadataFull(jid) } - return result + return result }, /** * Create a group @@ -219,16 +219,16 @@ const makeGroupsSocket = (config: LegacySocketConfig) => { return jids }, /** Query broadcast list info */ - getBroadcastListInfo: async(jid: string) => { + getBroadcastListInfo: async(jid: string) => { interface WABroadcastListInfo { status: number name: string recipients?: {id: string}[] } - - const result = await query({ - json: ['query', 'contact', jid], - expect200: true, + + const result = await query({ + json: ['query', 'contact', jid], + expect200: true, requiresPhoneConnection: true }) as WABroadcastListInfo @@ -245,8 +245,8 @@ const makeGroupsSocket = (config: LegacySocketConfig) => { }, groupInviteCode: async(jid: string) => { const response = await sock.query({ - json: ['query', 'inviteCode', jid], - expect200: true, + json: ['query', 'inviteCode', jid], + expect200: true, requiresPhoneConnection: false }) return response.code as string diff --git a/src/LegacySocket/messages.ts b/src/LegacySocket/messages.ts index 2316ddd..67d8bcb 100644 --- a/src/LegacySocket/messages.ts +++ b/src/LegacySocket/messages.ts @@ -15,8 +15,8 @@ const STATUS_MAP = { const makeMessagesSocket = (config: LegacySocketConfig) => { const { logger } = config const sock = makeChatsSocket(config) - const { - ev, + const { + ev, ws: socketEvents, query, generateMessageTag, @@ -28,10 +28,10 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { let mediaConn: Promise const refreshMediaConn = async(forceGet = false) => { const media = await mediaConn - if(!media || forceGet || (new Date().getTime()-media.fetchDate.getTime()) > media.ttl*1000) { + if(!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) { mediaConn = (async() => { const { media_conn } = await query({ - json: ['query', 'mediaConn'], + json: ['query', 'mediaConn'], requiresPhoneConnection: false, expect200: true }) @@ -40,12 +40,12 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { })() } - return mediaConn + return mediaConn } const fetchMessagesFromWA = async( - jid: string, - count: number, + jid: string, + count: number, cursor?: WAMessageCursor ) => { let key: WAMessageKey @@ -66,8 +66,8 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { owner: key?.fromMe === false ? 'false' : 'true', } }, - binaryTag: [WAMetric.queryMessages, WAFlag.ignore], - expect200: false, + binaryTag: [WAMetric.queryMessages, WAFlag.ignore], + expect200: false, requiresPhoneConnection: true }) if(Array.isArray(content)) { @@ -78,27 +78,27 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { } const updateMediaMessage = async(message: WAMessage) => { - const content = message.message?.audioMessage || message.message?.videoMessage || message.message?.imageMessage || message.message?.stickerMessage || message.message?.documentMessage + const content = message.message?.audioMessage || message.message?.videoMessage || message.message?.imageMessage || message.message?.stickerMessage || message.message?.documentMessage if(!content) { throw new Boom( - `given message ${message.key.id} is not a media message`, + `given message ${message.key.id} is not a media message`, { statusCode: 400, data: message } ) } - + const response: BinaryNode = await query ({ json: { tag: 'query', attrs: { - type: 'media', - index: message.key.id, - owner: message.key.fromMe ? 'true' : 'false', - jid: message.key.remoteJid, + type: 'media', + index: message.key.id, + owner: message.key.fromMe ? 'true' : 'false', + jid: message.key.remoteJid, epoch: currentEpoch().toString() } - }, - binaryTag: [WAMetric.queryMedia, WAFlag.ignore], - expect200: true, + }, + binaryTag: [WAMetric.queryMedia, WAFlag.ignore], + expect200: true, requiresPhoneConnection: true }) const attrs = response.attrs @@ -112,14 +112,14 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { const onMessage = (message: WAMessage, type: MessageUpdateType) => { const jid = message.key.remoteJid! // store chat updates in this - const chatUpdate: Partial = { + const chatUpdate: Partial = { id: jid, } const emitGroupUpdate = (update: Partial) => { ev.emit('groups.update', [ { id: jid, ...update } ]) } - + if(message.message) { chatUpdate.conversationTimestamp = +toNumber(message.messageTimestamp) // add to count if the message isn't from me & there exists a message @@ -128,7 +128,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { const participant = jidNormalizedUser(message.participant || jid) ev.emit( - 'presence.update', + 'presence.update', { id: jid, presences: { [participant]: { lastKnownPresence: 'available' } } @@ -144,11 +144,11 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { case proto.ProtocolMessage.ProtocolMessageType.REVOKE: const key = protocolMessage.key const messageStubType = WAMessageStubType.REVOKE - ev.emit('messages.update', [ - { + ev.emit('messages.update', [ + { // the key of the deleted message is updated - update: { message: null, key: message.key, messageStubType }, - key + update: { message: null, key: message.key, messageStubType }, + key } ]) return @@ -166,7 +166,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { } } - // check if the message is an action + // check if the message is an action if(message.messageStubType) { const { user } = state.legacy! //let actor = jidNormalizedUser (message.participant) @@ -234,14 +234,14 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { const response: BinaryNode = await query({ json: { tag: 'query', - attrs: { - type: 'url', - url: text, - epoch: currentEpoch().toString() + attrs: { + type: 'url', + url: text, + epoch: currentEpoch().toString() } - }, - binaryTag: [26, WAFlag.ignore], - expect200: true, + }, + binaryTag: [26, WAFlag.ignore], + expect200: true, requiresPhoneConnection: false }) const urlInfo = { ...response.attrs } as any as WAUrlInfo @@ -258,9 +258,9 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { tag: 'action', attrs: { epoch: currentEpoch().toString(), type: 'relay' }, content: [ - { - tag: 'message', - attrs: {}, + { + tag: 'message', + attrs: {}, content: proto.WebMessageInfo.encode(message).finish() } ] @@ -272,9 +272,9 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { message.status = WAMessageStatus.PENDING const promise = query({ - json, - binaryTag: [WAMetric.message, flag], - tag: mID, + json, + binaryTag: [WAMetric.message, flag], + tag: mID, expect200: true, requiresPhoneConnection: true }) @@ -308,7 +308,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { socketEvents.on('CB:action,add:last', json => messagesUpdate(json, true)) socketEvents.on('CB:action,add:unread', json => messagesUpdate(json, false)) socketEvents.on('CB:action,add:before', json => messagesUpdate(json, false)) - + // new messages socketEvents.on('CB:action,add:relay,message', (node: BinaryNode) => { const msgs = getBinaryNodeMessages(node) @@ -316,7 +316,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { onMessage(msg, 'notify') } }) - // If a message has been updated + // If a message has been updated // usually called when a video message gets its upload url, or live locations or ciphertext message gets fixed socketEvents.on ('CB:action,add:update,message', (node: BinaryNode) => { const msgs = getBinaryNodeMessages(node) @@ -369,7 +369,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { return } - const keyPartial = { + const keyPartial = { remoteJid: jidNormalizedUser(attributes.to), fromMe: areJidsSameUser(attributes.from, state.legacy?.user?.id || ''), } @@ -413,13 +413,13 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { json: { tag: 'query', attrs: { - type: 'message_info', - index: messageID, - jid: jid, + type: 'message_info', + index: messageID, + jid: jid, epoch: currentEpoch().toString() } - }, - binaryTag: [WAMetric.queryRead, WAFlag.ignore], + }, + binaryTag: [WAMetric.queryRead, WAFlag.ignore], expect200: true, requiresPhoneConnection: true }) @@ -427,7 +427,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { if(Array.isArray(content)) { for(const { tag, content: innerData } of content) { const [{ attrs }] = (innerData as BinaryNode[]) - + const jid = jidNormalizedUser(attrs.jid) const recp = info[jid] || { userJid: jid } const date = +attrs.t @@ -465,14 +465,14 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { return stream } - + try { const result = await downloadMediaMessage() return result } catch(error) { if(error.message.includes('404')) { // media needs to be updated logger.info (`updating media of message: ${message.key.id}`) - + await updateMediaMessage(message) const result = await downloadMediaMessage() @@ -508,13 +508,13 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { page: page.toString(), jid: inJid } - }, - binaryTag: [24, WAFlag.ignore], + }, + binaryTag: [24, WAFlag.ignore], expect200: true }) // encrypt and send off - return { - last: node.attrs?.last === 'true', + return { + last: node.attrs?.last === 'true', messages: getBinaryNodeMessages(node) } }, @@ -531,7 +531,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { isJidGroup(jid) ) { const { disappearingMessagesInChat } = content - const value = typeof disappearingMessagesInChat === 'boolean' ? + const value = typeof disappearingMessagesInChat === 'boolean' ? (disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) : disappearingMessagesInChat const tag = generateMessageTag(true) @@ -539,8 +539,8 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { { tag: 'group', attrs: { id: tag, jid, type: 'prop', author: userJid }, - content: [ - { tag: 'ephemeral', attrs: { value: value.toString() } } + content: [ + { tag: 'ephemeral', attrs: { value: value.toString() } } ] } ]) @@ -557,7 +557,7 @@ const makeMessagesSocket = (config: LegacySocketConfig) => { ...options, } ) - + await relayMessage(msg, { waitForAck: options.waitForAck }) return msg } diff --git a/src/LegacySocket/socket.ts b/src/LegacySocket/socket.ts index 333ca99..18f0024 100644 --- a/src/LegacySocket/socket.ts +++ b/src/LegacySocket/socket.ts @@ -14,13 +14,13 @@ import { BinaryNode, encodeBinaryNodeLegacy } from '../WABinary' * - query phone connection */ export const makeSocket = ({ - waWebSocketUrl, - connectTimeoutMs, - phoneResponseTimeMs, - logger, - agent, + waWebSocketUrl, + connectTimeoutMs, + phoneResponseTimeMs, + logger, + agent, keepAliveIntervalMs, - expectResponseTimeout, + expectResponseTimeout, }: LegacySocketConfig) => { // for generating tags const referenceDateSeconds = unixTimestampSeconds(new Date()) @@ -53,7 +53,7 @@ export const makeSocket = ({ const sendPromise = promisify(ws.send) /** generate message tag and increment epoch */ const generateMessageTag = (longTag: boolean = false) => { - const tag = `${longTag ? referenceDateSeconds : (referenceDateSeconds%1000)}.--${epoch}` + const tag = `${longTag ? referenceDateSeconds : (referenceDateSeconds % 1000)}.--${epoch}` epoch += 1 // increment message count, it makes the 'epoch' field when sending binary messages return tag } @@ -66,7 +66,7 @@ export const makeSocket = ({ return sendPromise.call(ws, data) as Promise } - /** + /** * Send a message to the WA servers * @returns the tag attached in the message * */ @@ -121,7 +121,7 @@ export const makeSocket = ({ if(ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) { try { - ws.close() + ws.close() } catch{ } } @@ -150,7 +150,7 @@ export const makeSocket = ({ return } //if (this.shouldLogMessages) this.messageLog.push ({ tag: messageTag, json: JSON.stringify(json), fromMe: false }) - + if(logger.level === 'trace') { logger.trace({ tag: messageTag, fromMe: false, json }, 'communication') } @@ -213,7 +213,7 @@ export const makeSocket = ({ phoneConnectionChanged(false) }, phoneResponseTimeMs) - } + } } const clearPhoneCheckInterval = () => { @@ -254,12 +254,12 @@ export const makeSocket = ({ } cancelToken = () => onErr(new Boom('Cancelled', { statusCode: 500 })) - + if(requiresPhoneConnection) { startPhoneCheckInterval() cancelPhoneChecker = exitQueryIfResponseNotExpected(tag, onErr) } - + ws.on(`TAG:${tag}`, onRecv) ws.on('ws-close', onErr) // if the socket closes, you'll never receive the message }, @@ -268,13 +268,13 @@ export const makeSocket = ({ } finally { requiresPhoneConnection && clearPhoneCheckInterval() cancelPhoneChecker && cancelPhoneChecker() - + ws.off(`TAG:${tag}`, onRecv) ws.off('ws-close', onErr) // if the socket closes, you'll never receive the message } })(), cancelToken: () => { - cancelToken() + cancelToken() } } } @@ -300,7 +300,7 @@ export const makeSocket = ({ // throw back the error throw error } - + const response = await promise const responseStatusCode = +(response.status ? response.status : 200) // default status // read here: http://getstatuscode.com/599 @@ -308,10 +308,10 @@ export const makeSocket = ({ end(new Boom('WA server overloaded', { statusCode: 599, data: { query: json, response } })) } - if(expect200 && Math.floor(responseStatusCode/100) !== 2) { + if(expect200 && Math.floor(responseStatusCode / 100) !== 2) { const message = STATUS_CODES[responseStatusCode] || 'unknown' throw new Boom( - `Unexpected status in '${Array.isArray(json) ? json[0] : (json?.tag || 'query')}': ${message}(${responseStatusCode})`, + `Unexpected status in '${Array.isArray(json) ? json[0] : (json?.tag || 'query')}': ${message}(${responseStatusCode})`, { data: { query: json, response }, statusCode: response.status } ) } @@ -330,7 +330,7 @@ export const makeSocket = ({ check if it's been a suspicious amount of time since the server responded with our last seen it could be that the network is down */ - if(diff > keepAliveIntervalMs+5000) { + if(diff > keepAliveIntervalMs + 5000) { end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost })) } else if(ws.readyState === ws.OPEN) { sendRawMessage('?,,') // if its all good, send a keep alive request @@ -394,7 +394,7 @@ export const makeSocket = ({ } end(new Boom( - `Connection terminated by server: "${kind || 'unknown'}"`, + `Connection terminated by server: "${kind || 'unknown'}"`, { statusCode: reason } )) }) @@ -417,12 +417,12 @@ export const makeSocket = ({ content: nodes } - return query({ - json, - binaryTag, - tag, - expect200: true, - requiresPhoneConnection: true + return query({ + json, + binaryTag, + tag, + expect200: true, + requiresPhoneConnection: true }) as Promise<{ status: number }> }, currentEpoch: () => epoch, diff --git a/src/Socket/chats.ts b/src/Socket/chats.ts index a268d99..31933f8 100644 --- a/src/Socket/chats.ts +++ b/src/Socket/chats.ts @@ -11,7 +11,7 @@ const MAX_SYNC_ATTEMPTS = 5 export const makeChatsSocket = (config: SocketConfig) => { const { logger } = config const sock = makeMessagesSocket(config) - const { + const { ev, ws, authState, @@ -95,7 +95,7 @@ export const makeChatsSocket = (config: SocketConfig) => { const fetchStatus = async(jid: string) => { const [result] = await interactiveQuery( - [{ tag: 'user', attrs: { jid } }], + [{ tag: 'user', attrs: { jid } }], { tag: 'status', attrs: { } } ) if(result) { @@ -130,8 +130,8 @@ export const makeChatsSocket = (config: SocketConfig) => { const result = await query({ tag: 'iq', attrs: { - xmlns: 'blocklist', - to: S_WHATSAPP_NET, + xmlns: 'blocklist', + to: S_WHATSAPP_NET, type: 'get' } }) @@ -223,7 +223,7 @@ export const makeChatsSocket = (config: SocketConfig) => { attrs: { type: 'account_sync', timestamp: fromTimestamp.toString(), - } + } } ] }) @@ -238,7 +238,7 @@ export const makeChatsSocket = (config: SocketConfig) => { 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 + // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from const attemptsMap = { } as { [T in WAPatchName]: number | undefined } // keep executing till all collections are done // sometimes a single patch request will not return all the patches (God knows why) @@ -265,9 +265,9 @@ export const makeChatsSocket = (config: SocketConfig) => { nodes.push({ tag: 'collection', - attrs: { - name, - version: state.version.toString(), + attrs: { + name, + version: state.version.toString(), // return snapshot if being synced from scratch return_snapshot: (!state.version).toString() } @@ -289,7 +289,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } ] }) - + const decoded = await extractSyncdPatches(result) // extract from binary node for(const key in decoded) { const name = key as WAPatchName @@ -298,7 +298,7 @@ export const makeChatsSocket = (config: SocketConfig) => { if(snapshot) { const { state: newState, mutations } = await decodeSyncdSnapshot(name, snapshot, getAppStateSyncKey, initialVersionMap[name]) states[name] = newState - + logger.info(`restored state of ${name} from snapshot to v${newState.version} with ${mutations.length} mutations`) await authState.keys.set({ 'app-state-sync-version': { [name]: newState } }) @@ -309,14 +309,14 @@ export const makeChatsSocket = (config: SocketConfig) => { // only process if there are syncd patches if(patches.length) { const { newMutations, state: newState } = await decodePatches(name, patches, states[name], getAppStateSyncKey, initialVersionMap[name]) - + await authState.keys.set({ 'app-state-sync-version': { [name]: newState } }) - + logger.info(`synced ${name} to v${newState.version}`) if(newMutations.length) { logger.trace({ newMutations, name }, 'recv new mutations') } - + appStateChunk.totalMutations.push(...newMutations) } @@ -431,17 +431,17 @@ export const makeChatsSocket = (config: SocketConfig) => { } const resyncMainAppState = async() => { - + logger.debug('resyncing main app state') - + await ( mutationMutex.mutex( - () => resyncAppState([ - 'critical_block', - 'critical_unblock_low', - 'regular_high', - 'regular_low', - 'regular' + () => resyncAppState([ + 'critical_block', + 'critical_unblock_low', + 'regular_high', + 'regular_low', + 'regular' ]) ) .catch(err => ( @@ -458,7 +458,7 @@ export const makeChatsSocket = (config: SocketConfig) => { for(const { syncAction: { value: action }, index: [_, id, msgId, fromMe] } of actions) { const update: Partial = { id } if(action?.muteAction) { - update.mute = action.muteAction?.muted ? + update.mute = action.muteAction?.muted ? toNumber(action.muteAction!.muteEndTimestamp!) : undefined } else if(action?.archiveChatAction) { @@ -546,7 +546,7 @@ export const makeChatsSocket = (config: SocketConfig) => { tag: 'collection', attrs: { name, - version: (state.version-1).toString(), + version: (state.version - 1).toString(), return_snapshot: 'false' }, content: [ @@ -562,9 +562,9 @@ export const makeChatsSocket = (config: SocketConfig) => { ] } await query(node) - + await authState.keys.set({ 'app-state-sync-version': { [name]: state } }) - + if(config.emitOwnEvents) { const result = await decodePatches(name, [{ ...patch, version: { version: state.version }, }], initial, getAppStateSyncKey) processSyncActions(result.newMutations) @@ -589,7 +589,7 @@ export const makeChatsSocket = (config: SocketConfig) => { }) const propsNode = getBinaryNodeChild(abtNode, 'props') - + let props: { [_: string]: string } = { } if(propsNode) { props = reduceBinaryNodeToDictionary(propsNode, 'prop') @@ -616,7 +616,7 @@ export const makeChatsSocket = (config: SocketConfig) => { }) const propsNode = getBinaryNodeChild(resultNode, 'props') - + let props: { [_: string]: string } = { } if(propsNode) { props = reduceBinaryNodeToDictionary(propsNode, 'prop') @@ -651,7 +651,7 @@ export const makeChatsSocket = (config: SocketConfig) => { } lastAccountSyncTimestamp = +attrs.timestamp - ev.emit('creds.update', { lastAccountSyncTimestamp }) + ev.emit('creds.update', { lastAccountSyncTimestamp }) break default: logger.info({ node }, 'received unknown sync') @@ -683,7 +683,7 @@ export const makeChatsSocket = (config: SocketConfig) => { }) return { - ...sock, + ...sock, appPatch, sendPresenceUpdate, presenceSubscribe, diff --git a/src/Socket/groups.ts b/src/Socket/groups.ts index b6e8097..7531707 100644 --- a/src/Socket/groups.ts +++ b/src/Socket/groups.ts @@ -21,15 +21,15 @@ export const makeGroupsSocket = (config: SocketConfig) => { const groupMetadata = async(jid: string) => { const result = await groupQuery( - jid, - 'get', + jid, + 'get', [ { tag: 'query', attrs: { request: 'interactive' } } ] ) return extractGroupMetadata(result) } return { - ...sock, + ...sock, groupMetadata, groupCreate: async(subject: string, participants: string[]) => { const key = generateMessageID() @@ -86,7 +86,7 @@ export const makeGroupsSocket = (config: SocketConfig) => { action: ParticipantAction ) => { const result = await groupQuery( - jid, + jid, 'set', participants.map( jid => ({ @@ -135,7 +135,7 @@ export const makeGroupsSocket = (config: SocketConfig) => { return result.attrs.jid }, groupToggleEphemeral: async(jid: string, ephemeralExpiration: number) => { - const content: BinaryNode = ephemeralExpiration ? + const content: BinaryNode = ephemeralExpiration ? { tag: 'ephemeral', attrs: { ephemeral: ephemeralExpiration.toString() } } : { tag: 'not_ephemeral', attrs: { } } await groupQuery(jid, 'set', [content]) diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index ac73ae0..6c8bd8a 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -19,14 +19,14 @@ const getStatusFromReceiptType = (type: string | undefined) => { if(typeof type === 'undefined') { return proto.WebMessageInfo.WebMessageInfoStatus.DELIVERY_ACK } - + return status } export const makeMessagesRecvSocket = (config: SocketConfig) => { const { logger } = config const sock = makeChatsSocket(config) - const { + const { ev, authState, ws, @@ -74,11 +74,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { return } - msgRetryMap[msgId] = retryCount+1 + msgRetryMap[msgId] = retryCount + 1 const isGroup = !!node.attrs.participant const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds - + const deviceIdentity = proto.ADVSignedDeviceIdentity.encode(account).finish() await assertingPreKeys(1, async preKeys => { const [keyId] = Object.keys(preKeys) @@ -93,14 +93,14 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { to: isGroup ? node.attrs.from : jidEncode(decFrom!.user, 's.whatsapp.net', decFrom!.device, 0) }, content: [ - { - tag: 'retry', - attrs: { - count: retryCount.toString(), + { + tag: 'retry', + attrs: { + count: retryCount.toString(), id: node.attrs.id, t: node.attrs.t, v: '1' - } + } }, { tag: 'registration', @@ -145,10 +145,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { switch (protocolMsg.type) { case proto.ProtocolMessage.ProtocolMessageType.HISTORY_SYNC_NOTIFICATION: const histNotification = protocolMsg!.historySyncNotification - + logger.info({ histNotification, id: message.key.id }, 'got history notification') const { chats, contacts, messages, isLatest } = await downloadAndProcessHistorySyncNotification(histNotification, historyCache) - + const meJid = authState.creds.me!.id await sendNode({ tag: 'receipt', @@ -178,15 +178,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { let newAppStateSyncKeyId = '' for(const { keyData, keyId } of keys) { const strKeyId = Buffer.from(keyId.keyId!).toString('base64') - + logger.info({ strKeyId }, 'injecting new app state sync key') await authState.keys.set({ 'app-state-sync-key': { [strKeyId]: keyData } }) - + newAppStateSyncKeyId = strKeyId } - + ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId }) - + resyncMainAppState() } else { [ @@ -197,12 +197,12 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { break case proto.ProtocolMessage.ProtocolMessageType.REVOKE: ev.emit('messages.update', [ - { + { key: { ...message.key, id: protocolMsg.key!.id - }, - update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key } + }, + update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key } } ]) break @@ -299,7 +299,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const participants = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid) if( - participants.length === 1 && + participants.length === 1 && // if recv. "remove" message and sender removed themselves // mark as left areJidsSameUser(participants[0], node.attrs.participant) && @@ -324,7 +324,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { result.messageStubType = WAMessageStubType.GROUP_CHANGE_RESTRICT result.messageStubParameters = [ (child.tag === 'locked') ? 'on' : 'off' ] break - + } } else { switch (child.tag) { @@ -354,7 +354,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // message failed to decrypt if(msg.messageStubType === proto.WebMessageInfo.WebMessageInfoStubType.CIPHERTEXT) { logger.error( - { msgId: msg.key.id, params: msg.messageStubParameters }, + { msgId: msg.key.id, params: msg.messageStubParameters }, 'failure in decrypting message' ) retryMutex.mutex( @@ -366,7 +366,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { await sendReceipt(msg.key.remoteJid!, msg.key.participant, [msg.key.id!], undefined) logger.debug({ msg: msg.key }, 'sent delivery receipt') } - + msg.key.remoteJid = jidNormalizedUser(msg.key.remoteJid!) ev.emit('messages.upsert', { messages: [msg], type: stanza.attrs.offline ? 'append' : 'notify' }) } @@ -470,7 +470,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { ) } else { ev.emit( - 'messages.update', + 'messages.update', ids.map(id => ({ key: { ...key, id }, update: { status } @@ -521,7 +521,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { ...(msg.key || {}) } msg.messageTimestamp = +node.attrs.t - + const fullMsg = proto.WebMessageInfo.fromObject(msg) ev.emit('messages.upsert', { messages: [fullMsg], type: 'append' }) } @@ -549,7 +549,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { 'p-' + chat.id!, () => processMessage(msg, chat) ) - + if(!!msg.message && !msg.message!.protocolMessage) { chat.conversationTimestamp = toNumber(msg.messageTimestamp) if(!msg.key.fromMe) { diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index e6b0cfd..14aca4b 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -10,7 +10,7 @@ import { makeGroupsSocket } from './groups' export const makeMessagesSocket = (config: SocketConfig) => { const { logger } = config const sock = makeGroupsSocket(config) - const { + const { ev, authState, query, @@ -31,8 +31,8 @@ export const makeMessagesSocket = (config: SocketConfig) => { const { content } = await query({ tag: 'iq', attrs: { - xmlns: 'privacy', - to: S_WHATSAPP_NET, + xmlns: 'privacy', + to: S_WHATSAPP_NET, type: 'get' }, content: [ @@ -48,7 +48,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { let mediaConn: Promise const refreshMediaConn = async(forceGet = false) => { const media = await mediaConn - if(!media || forceGet || (new Date().getTime()-media.fetchDate.getTime()) > media.ttl*1000) { + if(!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) { mediaConn = (async() => { const result = await query({ tag: 'iq', @@ -77,7 +77,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { } /** - * generic send receipt function + * generic send receipt function * used for receipts of phone call, read, delivery etc. * */ const sendReceipt = async(jid: string, participant: string | undefined, messageIds: string[], type: 'read' | 'read-self' | undefined) => { @@ -124,7 +124,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { const getUSyncDevices = async(jids: string[], ignoreZeroDevices: boolean) => { const deviceResults: JidWithDevice[] = [] - + const users: BinaryNode[] = [] jids = Array.from(new Set(jids)) for(let jid of jids) { @@ -162,8 +162,8 @@ export const makeMessagesSocket = (config: SocketConfig) => { tag: 'query', attrs: { }, content: [ - { - tag: 'devices', + { + tag: 'devices', attrs: { version: '2' } } ] @@ -194,7 +194,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { const assertSessions = async(jids: string[], force: boolean) => { let jidsRequiringFetch: string[] = [] if(force) { - jidsRequiringFetch = jids + jidsRequiringFetch = jids } else { const addrs = jids.map(jid => jidToSignalProtocolAddress(jid).toString()) const sessions = await authState.keys.get('session', addrs) @@ -240,11 +240,11 @@ export const makeMessagesSocket = (config: SocketConfig) => { if(authState.keys.isInTransaction()) { await authState.keys.prefetch( - 'session', + 'session', jids.map(jid => jidToSignalProtocolAddress(jid).toString()) ) } - + const nodes = await Promise.all( jids.map( async jid => { @@ -266,8 +266,8 @@ export const makeMessagesSocket = (config: SocketConfig) => { } const relayMessage = async( - jid: string, - message: proto.IMessage, + jid: string, + message: proto.IMessage, { messageId: msgId, participant, additionalAttributes, cachedGroupMetadata }: MessageRelayOptions ) => { const meId = authState.creds.me!.id @@ -293,10 +293,10 @@ export const makeMessagesSocket = (config: SocketConfig) => { async() => { if(isGroup) { const { ciphertext, senderKeyDistributionMessageKey } = await encryptSenderKeyMsgSignalProto(destinationJid, encodedMsg, meId, authState) - + const [groupData, senderKeyMap] = await Promise.all([ (async() => { - let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined + let groupData = cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined if(!groupData) { groupData = await groupMetadata(jid) } @@ -308,13 +308,13 @@ export const makeMessagesSocket = (config: SocketConfig) => { return result[jid] || { } })() ]) - + if(!participant) { const participantsList = groupData.participants.map(p => p.id) const additionalDevices = await getUSyncDevices(participantsList, false) devices.push(...additionalDevices) } - + const senderKeyJids: string[] = [] // ensure a connection is established with every device for(const { user, device } of devices) { @@ -330,44 +330,44 @@ export const makeMessagesSocket = (config: SocketConfig) => { // if there are, we re-send the senderkey if(senderKeyJids.length) { logger.debug({ senderKeyJids }, 'sending new sender key') - + const encSenderKeyMsg = encodeWAMessage({ senderKeyDistributionMessage: { axolotlSenderKeyDistributionMessage: senderKeyDistributionMessageKey, groupId: destinationJid } }) - + participants.push( ...(await createParticipantNodes(senderKeyJids, encSenderKeyMsg)) ) } - + binaryNodeContent.push({ tag: 'enc', attrs: { v: '2', type: 'skmsg' }, content: ciphertext }) - + await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } }) } else { const { user: meUser } = jidDecode(meId) - + const encodedMeMsg = encodeWAMessage({ deviceSentMessage: { destinationJid, message } }) - + if(!participant) { devices.push({ user }) devices.push({ user: meUser }) - + const additionalDevices = await getUSyncDevices([ meId, jid ], true) devices.push(...additionalDevices) } - + const meJids: string[] = [] const otherJids: string[] = [] for(const { user, device } of devices) { @@ -379,7 +379,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { otherJids.push(jid) } } - + const [meNodes, otherNodes] = await Promise.all([ createParticipantNodes(meJids, encodedMeMsg), createParticipantNodes(otherJids, encodedMsg) @@ -387,7 +387,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { participants.push(...meNodes) participants.push(...otherNodes) } - + if(participants.length) { binaryNodeContent.push({ tag: 'participants', @@ -395,7 +395,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { content: participants }) } - + const stanza: BinaryNode = { tag: 'message', attrs: { @@ -406,32 +406,32 @@ export const makeMessagesSocket = (config: SocketConfig) => { }, content: binaryNodeContent } - + const shouldHaveIdentity = !!participants.find( participant => (participant.content! as BinaryNode[]).find(n => n.attrs.type === 'pkmsg') ) - + if(shouldHaveIdentity) { (stanza.content as BinaryNode[]).push({ tag: 'device-identity', attrs: { }, content: proto.ADVSignedDeviceIdentity.encode(authState.creds.account).finish() }) - + logger.debug({ jid }, 'adding device identity') } - + logger.debug({ msgId }, `sending message to ${participants.length} devices`) - + await sendNode(stanza) } ) return msgId - } + } const waUploadToServer = getWAUploadToServer(config, refreshMediaConn) - + return { ...sock, assertSessions, @@ -454,7 +454,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { isJidGroup(jid) ) { const { disappearingMessagesInChat } = content - const value = typeof disappearingMessagesInChat === 'boolean' ? + const value = typeof disappearingMessagesInChat === 'boolean' ? (disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) : disappearingMessagesInChat await groupToggleEphemeral(jid, value) diff --git a/src/Socket/socket.ts b/src/Socket/socket.ts index d31df02..44cf7ab 100644 --- a/src/Socket/socket.ts +++ b/src/Socket/socket.ts @@ -16,10 +16,10 @@ import { assertNodeErrorFree, BinaryNode, encodeBinaryNode, getBinaryNodeChild, * - query phone connection */ export const makeSocket = ({ - waWebSocketUrl, - connectTimeoutMs, - logger, - agent, + waWebSocketUrl, + connectTimeoutMs, + logger, + agent, keepAliveIntervalMs, version, browser, @@ -58,7 +58,7 @@ export const makeSocket = ({ } const { creds } = authState - + let lastDateRecv: Date let epoch = 0 let keepAliveReq: NodeJS.Timeout @@ -129,7 +129,7 @@ export const makeSocket = ({ onErr = err => { reject(err || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })) } - + ws.on(`TAG:${msgId}`, onRecv) ws.on('close', onErr) // if the socket closes, you'll never receive the message ws.off('error', onErr) @@ -203,10 +203,10 @@ export const makeSocket = ({ /** get some pre-keys and do something with them */ const assertingPreKeys = async(range: number, execute: (keys: { [_: number]: any }) => Promise) => { const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(authState.creds, range) - + const update: Partial = { - nextPreKeyId: Math.max(lastPreKeyId+1, creds.nextPreKeyId), - firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId+1) + nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId), + firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1) } if(!creds.serverHasPreKeys) { update.serverHasPreKeys = true @@ -255,7 +255,7 @@ export const makeSocket = ({ if(logger.level === 'trace') { logger.trace({ msgId, fromMe: false, frame }, 'communication') } - + let anyTriggered = false /* Check if this is a response to a message we sent */ anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) @@ -263,7 +263,7 @@ export const makeSocket = ({ const l0 = frame.tag const l1 = frame.attrs || { } const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : '' - + Object.keys(l1).forEach(key => { anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered @@ -272,7 +272,7 @@ export const makeSocket = ({ anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered anyTriggered = ws.emit('frame', frame) || anyTriggered - + if(!anyTriggered && logger.level === 'debug') { logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv') } @@ -293,16 +293,16 @@ export const makeSocket = ({ if(ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) { try { - ws.close() + ws.close() } catch{ } } - ev.emit('connection.update', { - connection: 'close', + ev.emit('connection.update', { + connection: 'close', lastDisconnect: { error, date: new Date() - } + } }) ev.removeAllListeners('connection.update') } @@ -343,7 +343,7 @@ export const makeSocket = ({ check if it's been a suspicious amount of time since the server responded with our last seen it could be that the network is down */ - if(diff > keepAliveIntervalMs+5000) { + if(diff > keepAliveIntervalMs + 5000) { end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost })) } else if(ws.readyState === ws.OPEN) { // if its all good, send a keep alive request @@ -422,8 +422,8 @@ export const makeSocket = ({ }) // QR gen ws.on('CB:iq,type:set,pair-device', async(stanza: BinaryNode) => { - const iq: BinaryNode = { - tag: 'iq', + const iq: BinaryNode = { + tag: 'iq', attrs: { to: S_WHATSAPP_NET, type: 'result', @@ -446,7 +446,7 @@ export const makeSocket = ({ } const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',') - + ev.emit('connection.update', { qr }) qrTimer = setTimeout(genPairQR, qrMs) @@ -498,10 +498,10 @@ export const makeSocket = ({ ev.emit('connection.update', { connection: 'open' }) }) - + ws.on('CB:ib,,offline', (node: BinaryNode) => { const child = getBinaryNodeChild(node, 'offline') - const offlineCount = +child.attrs.count + const offlineCount = +child.attrs.count logger.info(`got ${offlineCount} offline messages/notifications`) diff --git a/src/Store/make-in-memory-store.ts b/src/Store/make-in-memory-store.ts index 2820030..45c17a1 100644 --- a/src/Store/make-in-memory-store.ts +++ b/src/Store/make-in-memory-store.ts @@ -14,7 +14,7 @@ type LegacyWASocket = ReturnType type AnyWASocket = ReturnType export const waChatKey = (pin: boolean) => ({ - key: (c: Chat) => (pin ? (c.pin ? '1' : '0') : '') + (c.archive ? '0' : '1') + (c.conversationTimestamp ? c.conversationTimestamp.toString(16).padStart(8, '0') :'') + c.id, + key: (c: Chat) => (pin ? (c.pin ? '1' : '0') : '') + (c.archive ? '0' : '1') + (c.conversationTimestamp ? c.conversationTimestamp.toString(16).padStart(8, '0') : '') + c.id, compare: (k1: string, k2: string) => k2.localeCompare (k1) }) @@ -33,7 +33,7 @@ export default ( logger = logger || DEFAULT_CONNECTION_CONFIG.logger.child({ stream: 'in-mem-store' }) chatKey = chatKey || waChatKey(true) const KeyedDB = require('@adiwajshing/keyed-db').default as new (...args: any[]) => KeyedDB - + const chats = new KeyedDB(chatKey, c => c.id) const messages: { [_: string]: ReturnType } = { } const contacts: { [_: string]: Contact } = { } @@ -54,7 +54,7 @@ export default ( for(const contact of newContacts) { oldContacts.delete(contact.id) contacts[contact.id] = Object.assign( - contacts[contact.id] || {}, + contacts[contact.id] || {}, contact ) } @@ -63,7 +63,7 @@ export default ( } /** - * binds to a BaileysEventEmitter. + * binds to a BaileysEventEmitter. * It listens to all events and constructs a state that you can query accurate data from. * Eg. can use the store to fetch chats, contacts, messages etc. * @param ev typically the event emitter from the socket connection @@ -76,7 +76,7 @@ export default ( if(isLatest) { chats.clear() } - + const chatsAdded = chats.insertIfAbsent(...newChats).length logger.debug({ chatsAdded }, 'synced chats') }) @@ -150,12 +150,12 @@ export default ( if(type === 'notify') { if(!chats.get(jid)) { - ev.emit('chats.upsert', [ - { - id: jid, - conversationTimestamp: toNumber(msg.messageTimestamp), - unreadCount: 1 - } + ev.emit('chats.upsert', [ + { + id: jid, + conversationTimestamp: toNumber(msg.messageTimestamp), + unreadCount: 1 + } ]) } } @@ -268,7 +268,7 @@ export default ( 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) { @@ -286,7 +286,7 @@ export default ( const cursor = { before: fMessage?.key || cursorKey } const extra = await retrieve (diff, cursor) // add to DB - for(let i = extra.length-1; i >= 0;i--) { + for(let i = extra.length - 1; i >= 0;i--) { list.upsert(extra[i], 'prepend') } diff --git a/src/Store/make-ordered-dictionary.ts b/src/Store/make-ordered-dictionary.ts index b66d760..e312320 100644 --- a/src/Store/make-ordered-dictionary.ts +++ b/src/Store/make-ordered-dictionary.ts @@ -29,7 +29,7 @@ function makeOrderedDictionary(idGetter: (item: T) => string) { dict[id] = item } } - + const remove = (item: T) => { const id = idGetter(item) const idx = array.findIndex(i => idGetter(i) === id) @@ -62,7 +62,7 @@ function makeOrderedDictionary(idGetter: (item: T) => string) { clear: () => { array.splice(0, array.length) Object.keys(dict).forEach(key => { - delete dict[key] + delete dict[key] }) }, filter: (contain: (item: T) => boolean) => { diff --git a/src/Tests/test.media-download.ts b/src/Tests/test.media-download.ts index 81e7b4a..0e96d25 100644 --- a/src/Tests/test.media-download.ts +++ b/src/Tests/test.media-download.ts @@ -39,7 +39,7 @@ describe('Media Download Tests', () => { it('should download a full encrypted media correctly', async() => { for(const { type, message, plaintext } of TEST_VECTORS) { const readPipe = await downloadContentFromMessage(message, type) - + let buffer = Buffer.alloc(0) for await (const read of readPipe) { buffer = Buffer.concat([ buffer, read ]) @@ -53,13 +53,13 @@ describe('Media Download Tests', () => { for(const { type, message, plaintext } of TEST_VECTORS) { // check all edge cases const ranges = [ - { startByte: 51, endByte: plaintext.length-100 }, // random numbers + { startByte: 51, endByte: plaintext.length - 100 }, // random numbers { startByte: 1024, endByte: 2038 }, // larger random multiples of 16 - { startByte: 1, endByte: plaintext.length-1 } // borders + { startByte: 1, endByte: plaintext.length - 1 } // borders ] for(const range of ranges) { const readPipe = await downloadContentFromMessage(message, type, range) - + let buffer = Buffer.alloc(0) for await (const read of readPipe) { buffer = Buffer.concat([ buffer, read ]) diff --git a/src/Types/Auth.ts b/src/Types/Auth.ts index 30cc1ce..5c28533 100644 --- a/src/Types/Auth.ts +++ b/src/Types/Auth.ts @@ -13,10 +13,10 @@ export type SignalIdentity = { identifierKey: Uint8Array } -export type LTHashState = { +export type LTHashState = { version: number hash: Buffer - indexValueMap: { + indexValueMap: { [indexMacBase64: string]: { valueMac: Uint8Array | Buffer } } } @@ -30,7 +30,7 @@ export type SignalCreds = { export type AuthenticationCreds = SignalCreds & { readonly noiseKey: KeyPair readonly advSecretKey: string - + me?: Contact account?: proto.IADVSignedDeviceIdentity signalIdentities?: SignalIdentity[] diff --git a/src/Types/Chat.ts b/src/Types/Chat.ts index 3b98d84..edba11d 100644 --- a/src/Types/Chat.ts +++ b/src/Types/Chat.ts @@ -10,7 +10,7 @@ export interface PresenceData { lastSeen?: number } -export type ChatMutation = { +export type ChatMutation = { syncAction: proto.ISyncActionData index: string[] } @@ -32,14 +32,14 @@ export type Chat = Omit & { pin?: number | null archive?: boolean } -/** +/** * the last messages in a chat, sorted reverse-chronologically * for MD modifications, the last message in the array must be the last message recv in the chat * */ export type LastMessageList = Pick[] -export type ChatModification = - { +export type ChatModification = + { archive: boolean lastMessages: LastMessageList } | @@ -54,11 +54,11 @@ export type ChatModification = clear: 'all' | { messages: {id: string, fromMe?: boolean}[] } } | { - star: { + star: { messages: { id: string, fromMe?: boolean }[], star: boolean } - } | + } | { markRead: boolean lastMessages: LastMessageList diff --git a/src/Types/Events.ts b/src/Types/Events.ts index 889040d..26b0ecc 100644 --- a/src/Types/Events.ts +++ b/src/Types/Events.ts @@ -24,15 +24,15 @@ export type BaileysEventMap = { /** delete chats with given ID */ 'chats.delete': string[] /** presence of contact in a chat updated */ - 'presence.update': { id: string, presences: { [participant: string]: PresenceData } } + 'presence.update': { id: string, presences: { [participant: string]: PresenceData } } 'contacts.upsert': Contact[] - 'contacts.update': Partial[] - + 'contacts.update': Partial[] + 'messages.delete': { keys: WAMessageKey[] } | { jid: string, all: true } 'messages.update': WAMessageUpdate[] - /** - * add/update the given messages. If they were received while the connection was online, + /** + * add/update the given messages. If they were received while the connection was online, * the update will have type: "notify" * */ 'messages.upsert': { messages: WAMessage[], type: MessageUpdateType } diff --git a/src/Types/Message.ts b/src/Types/Message.ts index 5cbf760..47f4d5f 100644 --- a/src/Types/Message.ts +++ b/src/Types/Message.ts @@ -25,7 +25,7 @@ export type MessageType = keyof proto.Message export type DownloadableMessage = { mediaKey?: Uint8Array, directPath?: string, url?: string } export type MediaConnInfo = { - auth: string + auth: string ttl: number hosts: { hostname: string, maxContentLengthBytes: number }[] fetchDate: Date @@ -77,7 +77,7 @@ export type AnyMediaMessageContent = ( image: WAMediaUpload caption?: string jpegThumbnail?: string - } & Mentionable & Buttonable & Templatable & WithDimensions) | + } & Mentionable & Buttonable & Templatable & WithDimensions) | ({ video: WAMediaUpload caption?: string @@ -95,22 +95,22 @@ export type AnyMediaMessageContent = ( document: WAMediaUpload mimetype: string fileName?: string - } & Buttonable & Templatable)) & + } & Buttonable & Templatable)) & { mimetype?: string } export type AnyRegularMessageContent = ( ({ text: string - } - & Mentionable & Buttonable & Templatable & Listable) | - AnyMediaMessageContent | + } + & Mentionable & Buttonable & Templatable & Listable) | + AnyMediaMessageContent | { contacts: { displayName?: string contacts: proto.IContactMessage[] } - } | - { + } | + { location: WALocationMessage } ) & ViewOnce @@ -120,7 +120,7 @@ export type AnyMessageContent = AnyRegularMessageContent | { force?: boolean } | { delete: WAMessageKey -} | { +} | { disappearingMessagesInChat: boolean | number } diff --git a/src/Types/Product.ts b/src/Types/Product.ts index 7c15c80..c6d25ec 100644 --- a/src/Types/Product.ts +++ b/src/Types/Product.ts @@ -1,8 +1,8 @@ export type CatalogResult = { - data: { - paging: { cursors: { before: string, after: string } }, - data: any[] + data: { + paging: { cursors: { before: string, after: string } }, + data: any[] } } diff --git a/src/Types/Socket.ts b/src/Types/Socket.ts index aee0639..66468d2 100644 --- a/src/Types/Socket.ts +++ b/src/Types/Socket.ts @@ -12,7 +12,7 @@ export type CommonSocketConfig = { /** provide an auth state object to maintain the auth state */ auth?: T /** the WS url to connect to WA */ - waWebSocketUrl: string | URL + waWebSocketUrl: string | URL /** Fails the connection if the socket times out in this interval */ connectTimeoutMs: number /** Default timeout for queries, undefined for no timeout */ diff --git a/src/Types/State.ts b/src/Types/State.ts index 5774c50..4b6866a 100644 --- a/src/Types/State.ts +++ b/src/Types/State.ts @@ -21,5 +21,5 @@ export type ConnectionState = { phoneConnected: boolean user?: Contact } - + } \ No newline at end of file diff --git a/src/Types/index.ts b/src/Types/index.ts index 624721e..d646b70 100644 --- a/src/Types/index.ts +++ b/src/Types/index.ts @@ -19,8 +19,8 @@ export type SocketConfig = CommonSocketConfig & { userDevicesCache?: NodeCache /** map to store the retry counts for failed messages */ msgRetryCounterMap?: { [msgId: string]: number } - /** - * fetch a message from your store + /** + * fetch a message from your store * implement this so that messages failed to send (solves the "this message can take a while" issue) can be retried * */ getMessage: (key: proto.IMessageKey) => Promise @@ -53,15 +53,15 @@ export type WABusinessHoursConfig = { export type WABusinessProfile = { description: string email: string - business_hours: { + business_hours: { timezone?: string - config?: WABusinessHoursConfig[] + config?: WABusinessHoursConfig[] business_config?: WABusinessHoursConfig[] } website: string[] categories: { id: string - localized_display_name: string + localized_display_name: string }[] wid?: string } diff --git a/src/Utils/auth-utils.ts b/src/Utils/auth-utils.ts index 9013859..2cd6722 100644 --- a/src/Utils/auth-utils.ts +++ b/src/Utils/auth-utils.ts @@ -128,10 +128,10 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta JSON.stringify({ creds, keys }, BufferJSON.replacer, 2) ) } - + if(existsSync(filename)) { const result = JSON.parse( - readFileSync(filename, { encoding: 'utf-8' }), + readFileSync(filename, { encoding: 'utf-8' }), BufferJSON.reviver ) creds = result.creds @@ -141,8 +141,8 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta keys = { } } - return { - state: { + return { + state: { creds, keys: { get: (type, ids) => { @@ -172,7 +172,7 @@ export const useSingleFileAuthState = (filename: string, logger?: Logger): { sta saveState() } } - }, - saveState + }, + saveState } } \ No newline at end of file diff --git a/src/Utils/chat-utils.ts b/src/Utils/chat-utils.ts index 1ae606c..0b6b521 100644 --- a/src/Utils/chat-utils.ts +++ b/src/Utils/chat-utils.ts @@ -5,7 +5,7 @@ import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildren } from '../WABina import { aesDecrypt, aesEncrypt, hkdf, hmacSign } from './crypto' import { toNumber } from './generics' import { LT_HASH_ANTI_TAMPERING } from './lt-hash' -import { downloadContentFromMessage, } from './messages-media' +import { downloadContentFromMessage, } from './messages-media' type FetchAppStateSyncKey = (keyId: string) => Promise | proto.IAppStateSyncKeyData @@ -39,7 +39,7 @@ const generateMac = (operation: proto.SyncdMutation.SyncdMutationSyncdOperation, const keyData = getKeyData() const last = Buffer.alloc(8) // 8 bytes - last.set([ keyData.length ], last.length-1) + last.set([ keyData.length ], last.length - 1) const total = Buffer.concat([ keyData, data, last ]) const hmac = hmacSign(total, key, 'sha512') @@ -180,7 +180,7 @@ export const encodeSyncdPatch = async( } export const decodeSyncdMutations = async( - msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[], + msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[], initialState: LTHashState, getAppStateSyncKey: FetchAppStateSyncKey, validateMacs: boolean @@ -214,7 +214,7 @@ export const decodeSyncdMutations = async( // otherwise, if it's only a record -- it'll be a SET mutation const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdMutationSyncdOperation.SET const record = ('record' in msgMutation && !!msgMutation.record) ? msgMutation.record : msgMutation as proto.ISyncdRecord - + const key = await getKey(record.keyId!.id!) const content = Buffer.from(record.value!.blob!) const encContent = content.slice(0, -32) @@ -236,12 +236,12 @@ export const decodeSyncdMutations = async( } } - const indexStr = Buffer.from(syncAction.index).toString() + const indexStr = Buffer.from(syncAction.index).toString() mutations.push({ syncAction, index: JSON.parse(indexStr), }) - ltGenerator.mix({ + ltGenerator.mix({ indexMac: record.index!.blob!, valueMac: ogValueMac, operation: operation @@ -252,7 +252,7 @@ export const decodeSyncdMutations = async( } export const decodeSyncdPatch = async( - msg: proto.ISyncdPatch, + msg: proto.ISyncdPatch, name: WAPatchName, initialState: LTHashState, getAppStateSyncKey: FetchAppStateSyncKey, @@ -263,11 +263,11 @@ export const decodeSyncdPatch = async( const mainKeyObj = await getAppStateSyncKey(base64Key) const mainKey = mutationKeys(mainKeyObj.keyData!) 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) { throw new Boom('Invalid patch mac') - } + } } const result = await decodeSyncdMutations(msg!.mutations!, initialState, getAppStateSyncKey, validateMacs) @@ -277,7 +277,7 @@ export const decodeSyncdPatch = async( export const extractSyncdPatches = async(result: BinaryNode) => { const syncNode = getBinaryNodeChild(result, 'sync') const collectionNodes = getBinaryNodeChildren(syncNode, 'collection') - + const final = { } as { [T in WAPatchName]: { patches: proto.ISyncdPatch[], hasMorePatches: boolean, snapshot?: proto.ISyncdSnapshot } } await Promise.all( collectionNodes.map( @@ -291,7 +291,7 @@ export const extractSyncdPatches = async(result: BinaryNode) => { const name = collectionNode.attrs.name as WAPatchName const hasMorePatches = collectionNode.attrs.has_more_patches === 'true' - + let snapshot: proto.ISyncdSnapshot | undefined = undefined if(snapshotNode && !!snapshotNode.content) { if(!Buffer.isBuffer(snapshotNode)) { @@ -313,13 +313,13 @@ export const extractSyncdPatches = async(result: BinaryNode) => { const syncd = proto.SyncdPatch.decode(content! as Uint8Array) if(!syncd.version) { - syncd.version = { version: +collectionNode.attrs.version+1 } + syncd.version = { version: +collectionNode.attrs.version + 1 } } syncds.push(syncd) } } - + final[name] = { patches: syncds, hasMorePatches, snapshot } } ) @@ -354,7 +354,7 @@ export const decodeSyncdSnapshot = async( ) => { const newState = newLTHashState() newState.version = toNumber(snapshot.version!.version!) - + const { hash, indexValueMap, mutations } = await decodeSyncdMutations(snapshot.records!, newState, getAppStateSyncKey, validateMacs) newState.hash = hash newState.indexValueMap = indexValueMap @@ -408,7 +408,7 @@ export const decodePatches = async( } const patchVersion = toNumber(version.version!) - + newState.version = patchVersion const decodeResult = await decodeSyncdPatch(syncd, name, newState, getAppStateSyncKey, validateMacs) @@ -418,7 +418,7 @@ export const decodePatches = async( if(typeof minimumVersionNumber === 'undefined' || patchVersion > minimumVersionNumber) { successfulMutations.push(...decodeResult.mutations) } - + if(validateMacs) { const base64Key = Buffer.from(keyId!.id!).toString('base64') const keyEnc = await getAppStateSyncKey(base64Key) @@ -450,7 +450,7 @@ export const chatModificationToAppPatch = ( throw new Boom('Expected last message to be not from me', { statusCode: 400 }) } - const lastMsg = lastMessages[lastMessages.length-1] + const lastMsg = lastMessages[lastMessages.length - 1] if(lastMsg.key.fromMe) { throw new Boom('Expected last message in array to be not from me', { statusCode: 400 }) } diff --git a/src/Utils/crypto.ts b/src/Utils/crypto.ts index 81b4e74..85b8bd1 100644 --- a/src/Utils/crypto.ts +++ b/src/Utils/crypto.ts @@ -29,7 +29,7 @@ export const signedKeyPair = (keyPair: KeyPair, keyId: number) => { pubKey.set(signKeys.public, 1) const signature = Curve.sign(keyPair.private, pubKey) - + return { keyPair: signKeys, signature, keyId } } @@ -78,10 +78,10 @@ export function hkdf(buffer: Uint8Array, expandedLength: number, { info, salt }: let prev = Buffer.from([]) const buffers = [] const num_blocks = Math.ceil(expandedLength / hashLength) - + const infoBuff = Buffer.from(info || []) - for(var i=0; i { - let decryptables = 0 + let decryptables = 0 if(Array.isArray(stanza.content)) { for(const { tag, attrs, content } of stanza.content) { if(tag !== 'enc') { continue } - + if(!(content instanceof Uint8Array)) { continue - } + } decryptables += 1 - + let msgBuffer: Buffer - + try { const e2eType = attrs.type switch (e2eType) { @@ -110,13 +110,13 @@ export const decodeMessageStanza = (stanza: BinaryNode, auth: AuthenticationStat msgBuffer = await decryptSignalProto(user, e2eType, content as Buffer, auth) break } - + let msg: proto.IMessage = proto.Message.decode(unpadRandomMax16(msgBuffer)) msg = msg.deviceSentMessage?.message || msg if(msg.senderKeyDistributionMessage) { await processSenderKeyMessage(author, msg.senderKeyDistributionMessage, auth) } - + if(fullMessage.message) { Object.assign(fullMessage.message, msg) } else { diff --git a/src/Utils/generics.ts b/src/Utils/generics.ts index a3380f5..b027663 100644 --- a/src/Utils/generics.ts +++ b/src/Utils/generics.ts @@ -38,7 +38,7 @@ export const BufferJSON = { } return value - } + } } export const writeRandomPadMax16 = (e: Binary) => { @@ -47,7 +47,7 @@ export const writeRandomPadMax16 = (e: Binary) => { e.writeUint8(t) } } - + var t = randomBytes(1) r(e, 1 + (15 & t[0])) return e @@ -58,12 +58,12 @@ export const unpadRandomMax16 = (e: Uint8Array | Buffer) => { if(0 === t.length) { throw new Error('unpadPkcs7 given empty bytes') } - + var r = t[t.length - 1] if(r > t.length) { throw new Error(`unpad given ${t.length} bytes, but pad is ${r}`) } - + return new Uint8Array(t.buffer, t.byteOffset, t.length - r) } @@ -88,7 +88,7 @@ export const encodeInt = (e: number, t: number) => { return a } -export const encodeBigEndian = (e: number, t=4) => { +export const encodeBigEndian = (e: number, t = 4) => { let r = e const a = new Uint8Array(t) for(let i = t - 1; i >= 0; i--) { @@ -121,7 +121,7 @@ export function shallowChanges (old: T, current: T, { lookForDeletedKeys }: { } /** unix timestamp of a date in seconds */ -export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime()/1000) +export const unixTimestampSeconds = (date: Date = new Date()) => Math.floor(date.getTime() / 1000) export type DebouncedTimeout = ReturnType @@ -175,7 +175,7 @@ export async function promiseTimeout(ms: number, promise: (resolve: (v?: T)=> const stack = new Error().stack // Create a promise that rejects in milliseconds - const { delay, cancel } = delayCancellable (ms) + const { delay, cancel } = delayCancellable (ms) const p = new Promise ((resolve, reject) => { delay .then(() => reject( @@ -186,8 +186,8 @@ export async function promiseTimeout(ms: number, promise: (resolve: (v?: T)=> } }) )) - .catch (err => reject(err)) - + .catch (err => reject(err)) + promise (resolve, reject) }) .finally (cancel) @@ -202,7 +202,7 @@ export const bindWaitForConnectionUpdate = (ev: CommonBaileysEventEmitter) let listener: (item: Partial) => void await ( promiseTimeout( - timeoutMs, + timeoutMs, (resolve, reject) => { listener = (update) => { if(check(update)) { @@ -236,7 +236,7 @@ export const printQRIfNecessaryListener = (ev: CommonBaileysEventEmitter, l /** * utility that fetches latest baileys version from the master branch. - * Use to ensure your WA connection is always on the latest version + * Use to ensure your WA connection is always on the latest version */ export const fetchLatestBaileysVersion = async() => { const URL = 'https://raw.githubusercontent.com/adiwajshing/Baileys/master/src/Defaults/baileys-version.json' diff --git a/src/Utils/legacy-msgs.ts b/src/Utils/legacy-msgs.ts index 3c874da..c728e94 100644 --- a/src/Utils/legacy-msgs.ts +++ b/src/Utils/legacy-msgs.ts @@ -10,21 +10,21 @@ export const newLegacyAuthCreds = () => ({ }) as LegacyAuthenticationCreds export const decodeWAMessage = ( - message: Buffer | string, - auth: { macKey: Buffer, encKey: Buffer }, - fromMe: boolean=false + message: Buffer | string, + auth: { macKey: Buffer, encKey: Buffer }, + fromMe: boolean = false ) => { let commaIndex = message.indexOf(',') // all whatsapp messages have a tag and a comma, followed by the actual message if(commaIndex < 0) { throw new Boom('invalid message', { data: message }) } // if there was no comma, then this message must be not be valid - - if(message[commaIndex+1] === ',') { + + if(message[commaIndex + 1] === ',') { commaIndex += 1 } - let data = message.slice(commaIndex+1, message.length) - + let data = message.slice(commaIndex + 1, message.length) + // get the message tag. // If a query was done, the server will respond with the same message tag we sent the query with const messageTag: string = message.slice(0, commaIndex).toString() @@ -43,7 +43,7 @@ export const decodeWAMessage = ( throw new Boom('recieved encrypted buffer when auth creds unavailable', { data: message, statusCode: DisconnectReason.badSession }) } - /* + /* If the data recieved was not a JSON, then it must be an encrypted message. Such a message can only be decrypted if we're connected successfully to the servers & have encryption keys */ @@ -51,11 +51,11 @@ export const decodeWAMessage = ( tags = [data[0], data[1]] data = data.slice(2, data.length) } - + const checksum = data.slice(0, 32) // the first 32 bytes of the buffer are the HMAC sign of the message data = data.slice(32, data.length) // the actual message const computedChecksum = hmacSign(data, macKey) // compute the sign of the message we recieved using our macKey - + if(checksum.equals(computedChecksum)) { // the checksum the server sent, must match the one we computed for the message to be valid const decrypted = aesDecrypt(data, encKey) // decrypt using AES @@ -73,7 +73,7 @@ export const decodeWAMessage = ( }) } } - } + } } return [messageTag, json, tags] as const @@ -85,7 +85,7 @@ export const decodeWAMessage = ( * @param json */ export const validateNewConnection = ( - json: { [_: string]: any }, + json: { [_: string]: any }, auth: LegacyAuthenticationCreds, curveKeys: CurveKeyPair ) => { @@ -164,7 +164,7 @@ export const useSingleFileLegacyAuthState = (file: string) => { if(existsSync(file)) { state = JSON.parse( - readFileSync(file, { encoding: 'utf-8' }), + readFileSync(file, { encoding: 'utf-8' }), BufferJSON.reviver ) if(typeof state.encKey === 'string') { diff --git a/src/Utils/lt-hash.ts b/src/Utils/lt-hash.ts index bc9b057..fa38484 100644 --- a/src/Utils/lt-hash.ts +++ b/src/Utils/lt-hash.ts @@ -2,7 +2,7 @@ import { hkdf } from './crypto' /** * LT Hash is a summation based hash algorithm that maintains the integrity of a piece of data - * over a series of mutations. You can add/remove mutations and it'll return a hash equal to + * over a series of mutations. You can add/remove mutations and it'll return a hash equal to * if the same series of mutations was made sequentially. */ diff --git a/src/Utils/messages-media.ts b/src/Utils/messages-media.ts index fab4930..d9b88d5 100644 --- a/src/Utils/messages-media.ts +++ b/src/Utils/messages-media.ts @@ -54,7 +54,7 @@ export const hkdfInfoKey = (type: MediaType) => { if(type === 'md-app-state') { str = 'App State' } - + const hkdfInfo = str[0].toUpperCase() + str.slice(1) return `WhatsApp ${hkdfInfo} Keys` } @@ -145,7 +145,7 @@ export const generateProfilePicture = async(mediaUpload: WAMediaUpload) => { .resize(640, 640, RESIZE_BILINEAR) .getBufferAsync(MIME_JPEG) } - + return { img: await img, } @@ -207,8 +207,8 @@ export const getStream = async(item: WAMediaUpload) => { /** generates a thumbnail for a given media, if required */ export async function generateThumbnail( - file: string, - mediaType: 'video' | 'image', + file: string, + mediaType: 'video' | 'image', options: { logger?: Logger } @@ -229,7 +229,7 @@ export async function generateThumbnail( options.logger?.debug('could not generate video thumb: ' + err) } } - + return thumbnail } @@ -238,9 +238,9 @@ export const getHttpStream = async(url: string | URL, options: AxiosRequestConfi const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' }) return fetched.data as Readable } - + export const encryptedStream = async( - media: WAMediaUpload, + media: WAMediaUpload, mediaType: MediaType, saveOriginalFileIfRequired = true, logger?: Logger @@ -266,7 +266,7 @@ export const encryptedStream = async( writeStream = createWriteStream(bodyPath) didSaveToTmpPath = true } - + let fileLength = 0 const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv) let hmac = Crypto.createHmac('sha256', macKey).update(iv) @@ -278,7 +278,7 @@ export const encryptedStream = async( hmac = hmac.update(buff) encWriteStream.push(buff) } - + try { for await (const data of stream) { fileLength += data.length @@ -293,21 +293,21 @@ export const encryptedStream = async( } onChunk(aes.final()) - + const mac = hmac.digest().slice(0, 10) sha256Enc = sha256Enc.update(mac) - + const fileSha256 = sha256Plain.digest() const fileEncSha256 = sha256Enc.digest() - + encWriteStream.push(mac) encWriteStream.push(null) - + writeStream && writeStream.end() stream.destroy() logger?.debug('encrypted data successfully') - + return { mediaKey, encWriteStream, @@ -356,14 +356,14 @@ export const downloadContentFromMessage = async( if(startByte) { const chunk = toSmallestChunkSize(startByte || 0) if(chunk) { - startChunk = chunk-AES_CHUNK_SIZE + startChunk = chunk - AES_CHUNK_SIZE bytesFetched = chunk firstBlockIsIV = true } } - const endChunk = endByte ? toSmallestChunkSize(endByte || 0)+AES_CHUNK_SIZE : undefined + const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined const headers: { [_: string]: string } = { Origin: DEFAULT_ORIGIN, @@ -377,7 +377,7 @@ export const downloadContentFromMessage = async( // download the message const fetched = await getHttpStream( - downloadUrl, + downloadUrl, { headers, maxBodyLength: Infinity, @@ -392,11 +392,11 @@ export const downloadContentFromMessage = async( const pushBytes = (bytes: Buffer, push: (bytes: Buffer) => void) => { if(startByte || endByte) { - const start = bytesFetched >= startByte ? undefined : Math.max(startByte-bytesFetched, 0) - const end = bytesFetched+bytes.length < endByte ? undefined : Math.max(endByte-bytesFetched, 0) - + const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0) + const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0) + push(bytes.slice(start, end)) - + bytesFetched += bytes.length } else { push(bytes) @@ -406,7 +406,7 @@ export const downloadContentFromMessage = async( const output = new Transform({ transform(chunk, _, callback) { let data = Buffer.concat([remainingBytes, chunk]) - + const decryptLength = toSmallestChunkSize(data.length) remainingBytes = data.slice(decryptLength) data = data.slice(0, decryptLength) @@ -424,7 +424,7 @@ export const downloadContentFromMessage = async( if(endByte) { aes.setAutoPadding(false) } - + } try { @@ -432,7 +432,7 @@ export const downloadContentFromMessage = async( callback() } catch(error) { callback(error) - } + } }, final(callback) { try { @@ -451,14 +451,14 @@ export const downloadContentFromMessage = async( * @param message the media message you want to decode */ export async function decryptMediaMessageBuffer(message: WAMessageContent): Promise { - /* + /* One can infer media type from the key in the message it is usually written as [mediaType]Message. Eg. imageMessage, audioMessage etc. */ const type = Object.keys(message)[0] as MessageType if( !type || - type === 'conversation' || + type === 'conversation' || type === 'extendedTextMessage' ) { throw new Boom(`no media message for "${type}"`, { statusCode: 400 }) @@ -492,8 +492,8 @@ export function extensionForMediaMessage(message: WAMessageContent) { const type = Object.keys(message)[0] as MessageType let extension: string if( - type === 'locationMessage' || - type === 'liveLocationMessage' || + type === 'locationMessage' || + type === 'liveLocationMessage' || type === 'productMessage' ) { extension = '.jpeg' @@ -539,8 +539,8 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C const body = await axios.post( url, reqBody, - { - headers: { + { + headers: { 'Content-Type': 'application/octet-stream', 'Origin': DEFAULT_ORIGIN }, @@ -552,7 +552,7 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C } ) result = body.data - + if(result?.url || result?.directPath) { urls = { mediaUrl: result.url, @@ -568,7 +568,7 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger }: C result = error.response?.data } - const isLast = hostname === hosts[uploadInfo.hosts.length-1]?.hostname + const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`) } } diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts index 02a19a6..899c465 100644 --- a/src/Utils/messages.ts +++ b/src/Utils/messages.ts @@ -2,22 +2,22 @@ import { Boom } from '@hapi/boom' import { promises as fs } from 'fs' import { proto } from '../../WAProto' import { MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults' -import { - AnyMediaMessageContent, - AnyMessageContent, - MediaGenerationOptions, - MediaType, - MessageContentGenerationOptions, - MessageGenerationOptions, +import { + AnyMediaMessageContent, + AnyMessageContent, + MediaGenerationOptions, + MediaType, + MessageContentGenerationOptions, + MessageGenerationOptions, MessageGenerationOptionsFromContent, - MessageType, + MessageType, MessageUserReceipt, - WAMediaUpload, - WAMessage, - WAMessageContent, + WAMediaUpload, + WAMessage, + WAMessageContent, WAMessageStatus, - WAProto, - WATextMessage + WAProto, + WATextMessage } from '../Types' import { generateMessageID, unixTimestampSeconds } from './generics' import { encryptedStream, generateThumbnail, getAudioDuration } from './messages-media' @@ -54,7 +54,7 @@ const MessageTypeProto = { const ButtonType = proto.ButtonsMessage.ButtonsMessageHeaderType export const prepareWAMessageMedia = async( - message: AnyMediaMessageContent, + message: AnyMediaMessageContent, options: MediaGenerationOptions ) => { const logger = options.logger @@ -66,15 +66,15 @@ export const prepareWAMessageMedia = async( } } - const uploadData: MediaUploadData = { + const uploadData: MediaUploadData = { ...message, media: message[mediaType] } delete uploadData[mediaType] // check if cacheable + generate cache key - const cacheableKey = typeof uploadData.media === 'object' && - ('url' in uploadData.media) && - !!uploadData.media.url && + const cacheableKey = typeof uploadData.media === 'object' && + ('url' in uploadData.media) && + !!uploadData.media.url && !!options.mediaCache && ( // generate the key mediaType + ':' + uploadData.media.url!.toString() @@ -93,7 +93,7 @@ export const prepareWAMessageMedia = async( const mediaBuff: Buffer = options.mediaCache!.get(cacheableKey) if(mediaBuff) { logger?.debug({ cacheableKey }, 'got media cache hit') - + const obj = WAProto.Message.decode(mediaBuff) const key = `${mediaType}Message` @@ -105,7 +105,7 @@ export const prepareWAMessageMedia = async( } const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined' - const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && + const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && (typeof uploadData['jpegThumbnail'] === 'undefined') const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation const { @@ -118,7 +118,7 @@ export const prepareWAMessageMedia = async( didSaveToTmpPath } = await encryptedStream(uploadData.media, mediaType, requiresOriginalForSomeProcessing) // url safe Base64 encode the SHA256 hash of the body - const fileEncSha256B64 = encodeURIComponent( + const fileEncSha256B64 = encodeURIComponent( fileEncSha256.toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') @@ -239,7 +239,7 @@ export const generateForwardMessageContent = ( } export const generateWAMessageContent = async( - message: AnyMessageContent, + message: AnyMessageContent, options: MessageContentGenerationOptions ) => { let m: WAMessageContent = {} @@ -256,7 +256,7 @@ export const generateWAMessageContent = async( extContent.previewType = 0 } catch(error) { // ignore if fails options.logger?.warn({ trace: error.stack }, 'url generation failed') - } + } } m.extendedTextMessage = extContent @@ -265,7 +265,7 @@ export const generateWAMessageContent = async( if(!contactLen) { throw new Boom('require atleast 1 contact', { statusCode: 400 }) } - + if(contactLen === 1) { m.contactMessage = WAProto.ContactMessage.fromObject(message.contacts.contacts[0]) } else { @@ -284,7 +284,7 @@ export const generateWAMessageContent = async( message.force ) } else if('disappearingMessagesInChat' in message) { - const exp = typeof message.disappearingMessagesInChat === 'boolean' ? + const exp = typeof message.disappearingMessagesInChat === 'boolean' ? (message.disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) : message.disappearingMessagesInChat m = prepareDisappearingMessageSettingContent(exp) @@ -309,7 +309,7 @@ export const generateWAMessageContent = async( const type = Object.keys(m)[0].replace('Message', '').toUpperCase() buttonsMessage.headerType = ButtonType[type] - + Object.assign(buttonsMessage, m) } @@ -324,7 +324,7 @@ export const generateWAMessageContent = async( hydratedButtons: message.templateButtons } } - + if('text' in message) { templateMessage.hydratedTemplate.hydratedContentText = message.text } else { @@ -332,7 +332,7 @@ export const generateWAMessageContent = async( if('caption' in message) { templateMessage.hydratedTemplate.hydratedContentText = message.caption } - + Object.assign(templateMessage.hydratedTemplate, m) } @@ -342,7 +342,7 @@ export const generateWAMessageContent = async( m = { templateMessage } } - + if('sections' in message && !!message.sections) { const listMessage: proto.IListMessage = { sections: message.sections, @@ -370,8 +370,8 @@ export const generateWAMessageContent = async( } export const generateWAMessageFromContent = ( - jid: string, - message: WAMessageContent, + jid: string, + message: WAMessageContent, options: MessageGenerationOptionsFromContent ) => { if(!options.timestamp) { @@ -389,7 +389,7 @@ export const generateWAMessageFromContent = ( message[key].contextInfo.participant = participant message[key].contextInfo.stanzaId = quoted.key.id message[key].contextInfo.quotedMessage = quoted.message - + // if a participant is quoted, then it must be a group // hence, remoteJid of group must also be entered if(quoted.key.participant || quoted.participant) { @@ -403,7 +403,7 @@ export const generateWAMessageFromContent = ( // and it's not a protocol message -- delete, toggle disappear message key !== 'protocolMessage' && // already not converted to disappearing message - key !== 'ephemeralMessage' + key !== 'ephemeralMessage' ) { message[key].contextInfo = { ...(message[key].contextInfo || {}), @@ -478,10 +478,10 @@ export const extractMessageContent = (content: WAMessageContent | undefined | nu return { conversation: 'contentText' in msg ? msg.contentText : ('hydratedContentText' in msg ? msg.hydratedContentText : '') } } } - - content = content?.ephemeralMessage?.message || + + content = content?.ephemeralMessage?.message || content?.viewOnceMessage?.message || - content || + content || undefined if(content?.buttonsMessage) { diff --git a/src/Utils/noise-handler.ts b/src/Utils/noise-handler.ts index 9df96ce..f57509f 100644 --- a/src/Utils/noise-handler.ts +++ b/src/Utils/noise-handler.ts @@ -30,7 +30,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key const result = Buffer.concat([cipher.update(plaintext), cipher.final(), cipher.getAuthTag()]) writeCounter += 1 - + authenticate(result) return result } @@ -42,8 +42,8 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key const cipher = createDecipheriv('aes-256-gcm', decKey, iv) // decrypt additional adata const tagLength = 128 >> 3 - const enc = ciphertext.slice(0, ciphertext.length-tagLength) - const tag = ciphertext.slice(ciphertext.length-tagLength) + const enc = ciphertext.slice(0, ciphertext.length - tagLength) + const tag = ciphertext.slice(ciphertext.length - tagLength) // set additional data cipher.setAAD(hash) cipher.setAuthTag(tag) @@ -55,7 +55,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key } else { writeCounter += 1 } - + authenticate(ciphertext) return result } @@ -83,7 +83,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key writeCounter = 0 isFinished = true } - + const data = Binary.build(NOISE_MODE).readBuffer() let hash = Buffer.from(data.byteLength === 32 ? data : sha256(Buffer.from(data))) let salt = hash @@ -109,19 +109,19 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key processHandshake: ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => { authenticate(serverHello!.ephemeral!) mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral!)) - + const decStaticContent = decrypt(serverHello!.static!) mixIntoKey(Curve.sharedKey(privateKey, decStaticContent)) - + const certDecoded = decrypt(serverHello!.payload!) const { details: certDetails, signature: certSignature } = proto.NoiseCertificate.decode(certDecoded) - + const { key: certKey } = proto.NoiseCertificateDetails.decode(certDetails) - + if(Buffer.compare(decStaticContent, certKey) !== 0) { throw new Boom('certification match failed', { statusCode: 400 }) } - + const keyEnc = encrypt(noiseKey.public) mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello!.ephemeral!)) @@ -135,16 +135,16 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key const introSize = sentIntro ? 0 : NOISE_WA_HEADER.length outBinary.ensureAdditionalCapacity(introSize + 3 + data.byteLength) - + if(!sentIntro) { outBinary.writeByteArray(NOISE_WA_HEADER) sentIntro = true } - + outBinary.writeUint8(data.byteLength >> 16) outBinary.writeUint16(65535 & data.byteLength) outBinary.write(data) - + const bytes = outBinary.readByteArray() return bytes as Uint8Array }, @@ -175,5 +175,5 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key inBinary.peek(peekSize) } - } + } } \ No newline at end of file diff --git a/src/Utils/signal.ts b/src/Utils/signal.ts index 7b0dfa2..6b87319 100644 --- a/src/Utils/signal.ts +++ b/src/Utils/signal.ts @@ -27,7 +27,7 @@ export const createSignalIdentity = ( wid: string, accountSignatureKey: Uint8Array ): SignalIdentity => { - return { + return { identifier: { name: wid, deviceId: 0 }, identifierKey: generateSignalPubKey(accountSignatureKey) } @@ -145,7 +145,7 @@ export const decryptGroupSignalProto = (group: string, user: string, msg: Buffer export const processSenderKeyMessage = async( authorJid: string, - item: proto.ISenderKeyDistributionMessage, + item: proto.ISenderKeyDistributionMessage, auth: SignalAuthState ) => { const builder = new GroupSessionBuilder(signalStorage(auth)) @@ -171,7 +171,7 @@ export const decryptSignalProto = async(user: string, type: 'pkmsg' | 'msg', msg break case 'msg': result = await session.decryptWhisperMessage(msg) - break + break } return result @@ -231,7 +231,7 @@ export const parseAndInjectE2ESessions = async(node: BinaryNode, auth: SignalAut const identity = getBinaryNodeChildBuffer(node, 'identity') const jid = node.attrs.jid const registrationId = getBinaryNodeChildUInt(node, 'registration', 4) - + const device = { registrationId, identityKey: generateSignalPubKey(identity), diff --git a/src/Utils/validate-connection.ts b/src/Utils/validate-connection.ts index 8656493..0b978af 100644 --- a/src/Utils/validate-connection.ts +++ b/src/Utils/validate-connection.ts @@ -130,8 +130,8 @@ export const configureSuccessfulPairing = ( id: msgId, }, content: [ - { - tag: 'pair-device-sign', + { + tag: 'pair-device-sign', attrs: { }, content: [ { tag: 'device-identity', attrs: { 'key-index': `${keyIndex}` }, content: accountEnc } @@ -141,9 +141,9 @@ export const configureSuccessfulPairing = ( } const authUpdate: Partial = { - account, - me: { id: jid, verifiedName }, - signalIdentities: [...(signalIdentities || []), identity] + account, + me: { id: jid, verifiedName }, + signalIdentities: [...(signalIdentities || []), identity] } return { creds: authUpdate, diff --git a/src/WABinary/Legacy/index.ts b/src/WABinary/Legacy/index.ts index b73cf28..79dcaf7 100644 --- a/src/WABinary/Legacy/index.ts +++ b/src/WABinary/Legacy/index.ts @@ -14,7 +14,7 @@ export const isLegacyBinaryNode = (buffer: Buffer) => { } function decode(buffer: Buffer, indexRef: { index: number }): BinaryNode { - + const checkEOS = (length: number) => { if(indexRef.index + length > buffer.length) { throw new Error('end of stream') @@ -201,7 +201,7 @@ function decode(buffer: Buffer, indexRef: { index: number }): BinaryNode { throw new Error('invalid node') } // read the attributes in - + const attributesLength = (listSize - 1) >> 1 for(let i = 0; i < attributesLength; i++) { const key = readString(readByte()) @@ -243,10 +243,10 @@ function decode(buffer: Buffer, indexRef: { index: number }): BinaryNode { } const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => { - + const pushByte = (value: number) => buffer.push(value & 0xff) - const pushInt = (value: number, n: number, littleEndian=false) => { + const pushInt = (value: number, n: number, littleEndian = false) => { for(let i = 0; i < n; i++) { const curShift = littleEndian ? i : n - 1 - i buffer.push((value >> (curShift * 8)) & 0xff) @@ -263,7 +263,7 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => { if(length >= 4294967296) { throw new Error('string too large to encode: ' + length) } - + if(length >= 1 << 20) { pushByte(Tags.BINARY_32) pushInt(length, 4) // 32 bit integer @@ -294,7 +294,7 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => { if(token === 'c.us') { token = 's.whatsapp.net' } - + const tokenIndex = SingleByteTokens.indexOf(token) if(!i && token === 's.whatsapp.net') { writeToken(tokenIndex) @@ -341,14 +341,14 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => { typeof attrs[k] !== 'undefined' && attrs[k] !== null )) - writeListStart(2*validAttributes.length + 1 + (typeof content !== 'undefined' && content !== null ? 1 : 0)) + writeListStart(2 * validAttributes.length + 1 + (typeof content !== 'undefined' && content !== null ? 1 : 0)) writeString(tag) validAttributes.forEach((key) => { if(typeof attrs[key] === 'string') { writeString(key) writeString(attrs[key]) - } + } }) if(typeof content === 'string') { @@ -364,7 +364,7 @@ const encode = ({ tag, attrs, content }: BinaryNode, buffer: number[] = []) => { } } } else if(typeof content === 'undefined' || content === null) { - + } else { throw new Error(`invalid children for header "${tag}": ${content} (${typeof content})`) } diff --git a/src/WABinary/jid-utils.ts b/src/WABinary/jid-utils.ts index 266482f..50ad837 100644 --- a/src/WABinary/jid-utils.ts +++ b/src/WABinary/jid-utils.ts @@ -21,7 +21,7 @@ export const jidDecode = (jid: string) => { return undefined } - const server = jid.slice(sepIdx+1) + const server = jid.slice(sepIdx + 1) const userCombined = jid.slice(0, sepIdx) const [userAgent, device] = userCombined.split(':') diff --git a/src/WABinary/types.ts b/src/WABinary/types.ts index 66714f9..a617bdf 100644 --- a/src/WABinary/types.ts +++ b/src/WABinary/types.ts @@ -1,8 +1,8 @@ -/** - * the binary node WA uses internally for communication - * - * this is manipulated soley as an object and it does not have any functions. - * This is done for easy serialization, to prevent running into issues with prototypes & +/** + * the binary node WA uses internally for communication + * + * this is manipulated soley as an object and it does not have any functions. + * This is done for easy serialization, to prevent running into issues with prototypes & * to maintain functional code structure * */ export type BinaryNode = { diff --git a/yarn.lock b/yarn.lock index 84fae75..bbdeb57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,7 +4,7 @@ "@adiwajshing/eslint-config@git+https://github.com/adiwajshing/eslint-config": version "1.0.0" - resolved "git+https://github.com/adiwajshing/eslint-config#db16c7427bd6dcf8fba20e0aaa526724e46c83aa" + resolved "git+https://github.com/adiwajshing/eslint-config#ee2b90dba10bc161d85745be59217efa10bc1eb3" dependencies: "@typescript-eslint/eslint-plugin" "^4.33.0" "@typescript-eslint/parser" "^4.33.0" @@ -1800,14 +1800,14 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@^4.1.0, debug@^4.1.1: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" -debug@^4.0.1: +debug@^4.0.1, debug@^4.3.1: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -2033,21 +2033,21 @@ escodegen@^2.0.0: source-map "~0.6.1" eslint-plugin-react@^7.26.1: - version "7.28.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz#8f3ff450677571a659ce76efc6d80b6a525adbdf" - integrity sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw== + version "7.29.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.2.tgz#2d4da69d30d0a736efd30890dc6826f3e91f3f7c" + integrity sha512-ypEBTKOy5liFQXZWMchJ3LN0JX1uPI6n7MN7OPHKacqXAxq5gYC30TdO7wqGYQyxD1OrzpobdHC3hDmlRWDg9w== dependencies: array-includes "^3.1.4" array.prototype.flatmap "^1.2.5" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.0.4" + minimatch "^3.1.2" object.entries "^1.1.5" object.fromentries "^2.0.5" object.hasown "^1.1.0" object.values "^1.1.5" - prop-types "^15.7.2" + prop-types "^15.8.1" resolve "^2.0.0-next.3" semver "^6.3.0" string.prototype.matchall "^4.0.6" @@ -2180,16 +2180,11 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.1.0, estraverse@^5.3.0: +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2680,9 +2675,9 @@ is-ci@^3.0.0: ci-info "^3.1.1" is-core-module@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" - integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -3544,10 +3539,10 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@^3.0.4, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" @@ -3873,11 +3868,16 @@ phin@^2.9.1: resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== -picomatch@^2.0.4, picomatch@^2.2.3: +picomatch@^2.0.4: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picomatch@^2.2.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pino-abstract-transport@v0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" @@ -4010,7 +4010,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==