From bbf5bc96cca79bc31f8dd1aac4a5d54f49225e07 Mon Sep 17 00:00:00 2001 From: Diego Araujo Date: Sun, 14 Nov 2021 23:41:39 -0300 Subject: [PATCH] Fix retry receipt, fix assertingPreKeys, uploadPreKeys to 30, generate QR timeout and update example (#833) * update example * fix assertingPreKeys, uploadPreKeys to 30, generate QR timeout, sendRetryReceipt fix * update example, change clear qr to event connection.update * update example * firstQR flag * change checkConnection qr * update example * remove semis * update example --- Example/example.ts | 37 ++++++++++++++------------- src/Socket/messages-recv.ts | 13 +++++++--- src/Socket/socket.ts | 51 ++++++++++++++++++++++++++++--------- 3 files changed, 69 insertions(+), 32 deletions(-) diff --git a/Example/example.ts b/Example/example.ts index f863d5a..c0aadeb 100644 --- a/Example/example.ts +++ b/Example/example.ts @@ -32,9 +32,10 @@ import makeWASocket, { WASocket, AuthenticationState, DisconnectReason, AnyMessa JSON.stringify(state, BufferJSON.replacer, 2) ) } + // start a connection const startSock = () => { - const sock = makeWASocket({ + let sock = makeWASocket({ logger: P({ level: 'trace' }), printQRInTerminal: true, auth: loadState() @@ -50,15 +51,32 @@ import makeWASocket, { WASocket, AuthenticationState, DisconnectReason, AnyMessa } }) + sock.ev.on('messages.update', m => console.log(m)) sock.ev.on('presence.update', m => console.log(m)) sock.ev.on('chats.update', m => console.log(m)) sock.ev.on('contacts.update', m => console.log(m)) + + sock.ev.on('connection.update', (update) => { + const { connection, lastDisconnect } = update + if(connection === 'close') { + // reconnect if not logged out + if((lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) { + sock = startSock() + } else { + console.log('connection closed') + } + } + console.log('connection update', update) + }) + // listen for when the auth state is updated + // it is imperative you save this data, it affects the signing keys you need to have conversations + sock.ev.on('auth-state.update', () => saveState()) + return sock } const sendMessageWTyping = async(msg: AnyMessageContent, jid: string) => { - await sock.presenceSubscribe(jid) await delay(500) @@ -71,19 +89,4 @@ import makeWASocket, { WASocket, AuthenticationState, DisconnectReason, AnyMessa } sock = startSock() - sock.ev.on('connection.update', (update) => { - const { connection, lastDisconnect } = update - if(connection === 'close') { - // reconnect if not logged out - if((lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) { - sock = startSock() - } else { - console.log('connection closed') - } - } - console.log('connection update', update) - }) - // listen for when the auth state is updated - // it is imperative you save this data, it affects the signing keys you need to have conversations - sock.ev.on('auth-state.update', () => saveState()) })() \ No newline at end of file diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index 46b7279..1112caa 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -38,8 +38,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { await sendNode(stanza) } + const retries = new Map() const sendRetryRequest = async(node: BinaryNode) => { - const retryCount = +(node.attrs.retryCount || 0) + 1 + if (retries.has(node.attrs.id) && retries.get(node.attrs.id)! >= 5) { + retries.delete(node.attrs.id) + return + } + const retryCount = retries.get(node.attrs.id) || 1 + retries.set(node.attrs.id, retryCount + 1) + const isGroup = !!node.attrs.participant const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds @@ -81,7 +88,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { if(retryCount > 1) { const exec = generateSignalPubKey(Buffer.from(KEY_BUNDLE_TYPE)).slice(0, 1); - (node.content! as BinaryNode[]).push({ + (receipt.content! as BinaryNode[]).push({ tag: 'keys', attrs: { }, content: [ @@ -93,7 +100,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { ] }) } - await sendNode(node) + await sendNode(receipt) logger.info({ msgId: node.attrs.id, retryCount }, 'sent retry receipt') diff --git a/src/Socket/socket.ts b/src/Socket/socket.ts index ddedd35..2a1443c 100644 --- a/src/Socket/socket.ts +++ b/src/Socket/socket.ts @@ -50,6 +50,7 @@ export const makeSocket = ({ let lastDateRecv: Date let epoch = 0 let keepAliveReq: NodeJS.Timeout + let qrTimer: NodeJS.Timeout const uqTagId = `${randomBytes(1).toString('hex')[0]}.${randomBytes(1).toString('hex')[0]}-` const generateMessageTag = () => `${uqTagId}${epoch++}` @@ -174,10 +175,7 @@ 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, range) - const preKeys = await getPreKeys(authState.keys, preKeysRange[0], preKeysRange[1]) - await execute(preKeys) - creds.serverHasPreKeys = true creds.nextPreKeyId = Math.max(lastPreKeyId+1, creds.nextPreKeyId) creds.firstUnuploadedPreKeyId = Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId+1) @@ -185,11 +183,14 @@ export const makeSocket = ({ Object.keys(newPreKeys).map(k => authState.keys.setPreKey(+k, newPreKeys[+k])) ) + const preKeys = await getPreKeys(authState.keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1]) + await execute(preKeys) + ev.emit('auth-state.update', authState) } /** generates and uploads a set of pre-keys */ const uploadPreKeys = async() => { - await assertingPreKeys(50, async preKeys => { + await assertingPreKeys(30, async preKeys => { const node: BinaryNode = { tag: 'iq', attrs: { @@ -397,7 +398,7 @@ export const makeSocket = ({ }) // QR gen ws.on('CB:iq,type:set,pair-device', async (stanza: BinaryNode) => { - const postQR = async() => { + const postQR = async(qr: string) => { if(printQRInTerminal) { const QR = await import('qrcode-terminal').catch(err => { logger.error('add `qrcode-terminal` as a dependency to auto-print QR') @@ -405,8 +406,7 @@ export const makeSocket = ({ QR?.generate(qr, { small: true }) } } - - const refs = ((stanza.content[0] as BinaryNode).content as BinaryNode[]).map(n => n.content as string) + const iq: BinaryNode = { tag: 'iq', attrs: { @@ -415,14 +415,41 @@ export const makeSocket = ({ id: stanza.attrs.id, } } - const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64'); + await sendNode(iq) + const refs = ((stanza.content[0] as BinaryNode).content as BinaryNode[]).map(n => n.content as string) + const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64') const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64') const advB64 = creds.advSecretKey - const qr = [refs[0], noiseKeyB64, identityKeyB64, advB64].join(','); - ev.emit('connection.update', { qr }) - await postQR() - await sendNode(iq) + let firstQR = true + const genPairQR = () => { + const ms = firstQR ? 60000 : 20000 + firstQR = false + + const ref = refs.shift() + if(!ref) { + end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.restartRequired })) + return + } + + const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',') + + ev.emit('connection.update', { qr }) + postQR(qr) + + qrTimer = setTimeout(genPairQR, ms) + } + + genPairQR() + + const checkConnection = ({ connection }: ConnectionState) => { + if(connection === 'open' || connection === 'close') { + clearTimeout(qrTimer) + ev.off('connection.update', checkConnection) + } + } + + ev.on('connection.update', checkConnection) }) // device paired for the first time // if device pairs successfully, the server asks to restart the connection