UNPKG

neroxbailx

Version:

baileys whatsapp-api

194 lines (187 loc) 6.22 kB
"use strict" Object.defineProperty(exports, "__esModule", { value: true }) const lodash_1 = require("lodash") const Defaults_1 = require("../Defaults") const WABinary_1 = require("../WABinary") const crypto_1 = require("./crypto") const generics_1 = require("./generics") const createSignalIdentity = (wid, accountSignatureKey) => { return { identifier: { name: wid, deviceId: 0 }, identifierKey: crypto_1.generateSignalPubKey(accountSignatureKey) } } const getPreKeys = async ({ get }, min, limit) => { const idList = [] for (let id = min; id < limit; id++) { idList.push(id.toString()) } return get('pre-key', idList) } const generateOrGetPreKeys = (creds, range) => { const avaliable = creds.nextPreKeyId - creds.firstUnuploadedPreKeyId const remaining = range - avaliable const lastPreKeyId = creds.nextPreKeyId + remaining - 1 const newPreKeys = {} if (remaining > 0) { for (let i = creds.nextPreKeyId; i <= lastPreKeyId; i++) { newPreKeys[i] = crypto_1.Curve.generateKeyPair() } } return { newPreKeys, lastPreKeyId, preKeysRange: [creds.firstUnuploadedPreKeyId, range], } } const xmppSignedPreKey = (key) => ({ tag: 'skey', attrs: {}, content: [ { tag: 'id', attrs: {}, content: generics_1.encodeBigEndian(key.keyId, 3) }, { tag: 'value', attrs: {}, content: key.keyPair.public }, { tag: 'signature', attrs: {}, content: key.signature } ] }) const xmppPreKey = (pair, id) => ({ tag: 'key', attrs: {}, content: [ { tag: 'id', attrs: {}, content: generics_1.encodeBigEndian(id, 3) }, { tag: 'value', attrs: {}, content: pair.public } ] }) const parseAndInjectE2ESessions = async (node, repository) => { const extractKey = (key) => (key ? ({ keyId: WABinary_1.getBinaryNodeChildUInt(key, 'id', 3), publicKey: crypto_1.generateSignalPubKey(WABinary_1.getBinaryNodeChildBuffer(key, 'value')), signature: WABinary_1.getBinaryNodeChildBuffer(key, 'signature') }) : undefined) // Check if node has proper structure if (!node || !node.content || !Array.isArray(node.content)) { console.warn('parseAndInjectE2ESessions: Invalid node structure') return } const listNode = WABinary_1.getBinaryNodeChild(node, 'list') if (!listNode) { console.warn('parseAndInjectE2ESessions: No list node found') return } const nodes = WABinary_1.getBinaryNodeChildren(listNode, 'user') if (!nodes || nodes.length === 0) { console.warn('parseAndInjectE2ESessions: No user nodes found') return } for (const node of nodes) { try { WABinary_1.assertNodeErrorFree(node) } catch (error) { console.warn('parseAndInjectE2ESessions: Node error, skipping:', error.message) continue } } // Most of the work in repository.injectE2ESession is CPU intensive, not IO // So Promise.all doesn't really help here, // but blocks even loop if we're using it inside keys.transaction, and it makes it "sync" actually // This way we chunk it in smaller parts and between those parts we can yield to the event loop // It's rare case when you need to E2E sessions for so many users, but it's possible const chunkSize = 100 const chunks = lodash_1.chunk(nodes, chunkSize) for (const nodesChunk of chunks) { await Promise.all(nodesChunk.map(async (node) => { try { const signedKey = WABinary_1.getBinaryNodeChild(node, 'skey') const key = WABinary_1.getBinaryNodeChild(node, 'key') const identity = WABinary_1.getBinaryNodeChildBuffer(node, 'identity') const jid = node.attrs.jid const registrationId = WABinary_1.getBinaryNodeChildUInt(node, 'registration', 4) // Validate required fields if (!jid) { console.warn('parseAndInjectE2ESessions: Missing JID, skipping node') return } if (!identity || identity.length === 0) { console.warn('parseAndInjectE2ESessions: Missing identity for JID:', jid) return } await repository.injectE2ESession({ jid, session: { registrationId: registrationId || 0, identityKey: crypto_1.generateSignalPubKey(identity), signedPreKey: extractKey(signedKey), preKey: extractKey(key) } }) } catch (error) { console.warn('parseAndInjectE2ESessions: Error processing node:', error.message) // Continue with other nodes } })) } } const extractDeviceJids = (result, myJid, excludeZeroDevices) => { const { user: myUser, device: myDevice } = WABinary_1.jidDecode(myJid) const extracted = [] for (const userResult of result) { const { devices, id } = userResult const { user } = WABinary_1.jidDecode(id) const deviceList = devices?.deviceList if (Array.isArray(deviceList)) { for (const { id: device, keyIndex } of deviceList) { if ((!excludeZeroDevices || device !== 0) && // if zero devices are not-excluded, or device is non zero (myUser !== user || myDevice !== device) && // either different user or if me user, not this device (device === 0 || !!keyIndex) // ensure that "key-index" is specified for "non-zero" devices, produces a bad req otherwise ) { extracted.push({ user, device }) } } } } return extracted } /** * get the next N keys for upload or processing * @param count number of pre-keys to get or generate */ const getNextPreKeys = async ({ creds, keys }, count) => { const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(creds, count) const update = { nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId), firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1) } await keys.set({ 'pre-key': newPreKeys }) const preKeys = await getPreKeys(keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1]) return { update, preKeys } } const getNextPreKeysNode = async (state, count) => { const { creds } = state const { update, preKeys } = await getNextPreKeys(state, count) const node = { tag: 'iq', attrs: { xmlns: 'encrypt', type: 'set', to: WABinary_1.S_WHATSAPP_NET, }, content: [ { tag: 'registration', attrs: {}, content: generics_1.encodeBigEndian(creds.registrationId) }, { tag: 'type', attrs: {}, content: Defaults_1.KEY_BUNDLE_TYPE }, { tag: 'identity', attrs: {}, content: creds.signedIdentityKey.public }, { tag: 'list', attrs: {}, content: Object.keys(preKeys).map(k => xmppPreKey(preKeys[+k], +k)) }, xmppSignedPreKey(creds.signedPreKey) ] } return { update, node } } module.exports = { createSignalIdentity, getPreKeys, generateOrGetPreKeys, xmppSignedPreKey, xmppPreKey, parseAndInjectE2ESessions, extractDeviceJids, getNextPreKeys, getNextPreKeysNode }