From f61f553e015599d4eb0fcaaaf9d742326899561f Mon Sep 17 00:00:00 2001 From: Adhiraj Singh Date: Sat, 18 Dec 2021 22:18:51 +0530 Subject: [PATCH] chore: more unification of legacy APIs 1. unify waitForConnectionUpdate 2. unify printing QR in terminal --- src/Defaults/index.ts | 1 - src/LegacySocket/auth.ts | 47 +++++----------------------------------- src/Socket/socket.ts | 43 +++++++----------------------------- src/Types/Legacy.ts | 2 -- src/Utils/generics.ts | 41 ++++++++++++++++++++++++++++++++++- 5 files changed, 54 insertions(+), 80 deletions(-) diff --git a/src/Defaults/index.ts b/src/Defaults/index.ts index dba9ff9..db81210 100644 --- a/src/Defaults/index.ts +++ b/src/Defaults/index.ts @@ -42,7 +42,6 @@ export const DEFAULT_LEGACY_CONNECTION_CONFIG: LegacySocketConfig = { waWebSocketUrl: 'wss://web.whatsapp.com/ws', phoneResponseTimeMs: 20_000, expectResponseTimeout: 60_000, - pendingRequestTimeoutMs: 60_000 } export const MEDIA_PATH_MAP: { [T in MediaType]: string } = { diff --git a/src/LegacySocket/auth.ts b/src/LegacySocket/auth.ts index 262a37c..4ccab6b 100644 --- a/src/LegacySocket/auth.ts +++ b/src/LegacySocket/auth.ts @@ -1,7 +1,7 @@ import { Boom } from '@hapi/boom' import EventEmitter from "events" -import { LegacyBaileysEventEmitter, BaileysEventMap, LegacySocketConfig, CurveKeyPair, WAInitResponse, ConnectionState, DisconnectReason, LegacyAuthenticationCreds } from "../Types" -import { newLegacyAuthCreds, promiseTimeout, computeChallengeResponse, validateNewConnection, Curve } from "../Utils" +import { LegacyBaileysEventEmitter, LegacySocketConfig, CurveKeyPair, WAInitResponse, ConnectionState, DisconnectReason } from "../Types" +import { newLegacyAuthCreds, bindWaitForConnectionUpdate, computeChallengeResponse, validateNewConnection, Curve, printQRIfNecessaryListener } from "../Utils" import { makeSocket } from "./socket" const makeAuthSocket = (config: LegacySocketConfig) => { @@ -10,7 +10,6 @@ const makeAuthSocket = (config: LegacySocketConfig) => { version, browser, connectTimeoutMs, - pendingRequestTimeoutMs, printQRInTerminal, auth: initialAuthInfo } = config @@ -74,34 +73,6 @@ const makeAuthSocket = (config: LegacySocketConfig) => { new Boom('Logged Out', { statusCode: DisconnectReason.loggedOut }) ) } - /** Waits for the connection to WA to open up */ - const waitForConnection = async(waitInfinitely: boolean = false) => { - if(state.connection === 'open') return - - let listener: (item: BaileysEventMap['connection.update']) => void - const timeout = waitInfinitely ? undefined : pendingRequestTimeoutMs - if(timeout < 0) { - throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }) - } - - await ( - promiseTimeout( - timeout, - (resolve, reject) => { - listener = ({ connection, lastDisconnect }) => { - if(connection === 'open') resolve() - else if(connection == 'close') { - reject(lastDisconnect.error || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })) - } - } - ev.on('connection.update', listener) - } - ) - .finally(() => ( - ev.off('connection.update', listener) - )) - ) - } const updateEncKeys = () => { // update the keys so we can decrypt traffic @@ -246,14 +217,7 @@ const makeAuthSocket = (config: LegacySocketConfig) => { }) if(printQRInTerminal) { - ev.on('connection.update', async({ qr }) => { - if(qr) { - const QR = await import('qrcode-terminal').catch(err => { - logger.error('QR code terminal not added as dependency') - }) - QR?.generate(qr, { small: true }) - } - }) + printQRIfNecessaryListener(ev, logger) } return { @@ -261,9 +225,10 @@ const makeAuthSocket = (config: LegacySocketConfig) => { state, authInfo, ev, - waitForConnection, canLogin, - logout + logout, + /** Waits for the connection to WA to reach a state */ + waitForConnectionUpdate: bindWaitForConnectionUpdate(ev) } } export default makeAuthSocket \ No newline at end of file diff --git a/src/Socket/socket.ts b/src/Socket/socket.ts index 059cb33..14a6e55 100644 --- a/src/Socket/socket.ts +++ b/src/Socket/socket.ts @@ -4,8 +4,8 @@ import { promisify } from "util" import WebSocket from "ws" import { randomBytes } from 'crypto' import { proto } from '../../WAProto' -import { DisconnectReason, SocketConfig, BaileysEventEmitter, ConnectionState, AuthenticationCreds } from "../Types" -import { Curve, generateRegistrationNode, configureSuccessfulPairing, generateLoginNode, encodeBigEndian, promiseTimeout, generateOrGetPreKeys, xmppSignedPreKey, xmppPreKey, getPreKeys, makeNoiseHandler, useSingleFileAuthState, addTransactionCapability } from "../Utils" +import { DisconnectReason, SocketConfig, BaileysEventEmitter, AuthenticationCreds } from "../Types" +import { Curve, generateRegistrationNode, configureSuccessfulPairing, generateLoginNode, encodeBigEndian, promiseTimeout, generateOrGetPreKeys, xmppSignedPreKey, xmppPreKey, getPreKeys, makeNoiseHandler, useSingleFileAuthState, addTransactionCapability, bindWaitForConnectionUpdate, printQRIfNecessaryListener } from "../Utils" import { DEFAULT_ORIGIN, DEF_TAG_PREFIX, DEF_CALLBACK_PREFIX, KEY_BUNDLE_TYPE } from "../Defaults" import { assertNodeErrorFree, BinaryNode, encodeBinaryNode, S_WHATSAPP_NET, getBinaryNodeChild } from '../WABinary' @@ -389,28 +389,6 @@ export const makeSocket = ({ end(new Boom('Intentional Logout', { statusCode: DisconnectReason.loggedOut })) } - /** Waits for the connection to WA to reach a state */ - const waitForConnectionUpdate = async(check: (u: Partial) => boolean, timeoutMs?: number) => { - let listener: (item: Partial) => void - await ( - promiseTimeout( - timeoutMs, - (resolve, reject) => { - listener = (update) => { - if(check(update)) { - resolve() - } else if(update.connection == 'close') { - reject(update.lastDisconnect?.error || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })) - } - } - ev.on('connection.update', listener) - } - ) - .finally(() => ( - ev.off('connection.update', listener) - )) - ) - } ws.on('message', onMessageRecieved) ws.on('open', validateConnection) @@ -422,15 +400,6 @@ export const makeSocket = ({ }) // QR gen ws.on('CB:iq,type:set,pair-device', async (stanza: BinaryNode) => { - 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') - }) - QR?.generate(qr, { small: true }) - } - } - const iq: BinaryNode = { tag: 'iq', attrs: { @@ -457,7 +426,6 @@ export const makeSocket = ({ const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',') ev.emit('connection.update', { qr }) - postQR(qr) qrTimer = setTimeout(genPairQR, qrMs) qrMs = 20_000 // shorter subsequent qrs @@ -539,6 +507,10 @@ export const makeSocket = ({ // update credentials when required ev.on('creds.update', update => Object.assign(creds, update)) + if(printQRInTerminal) { + printQRIfNecessaryListener(ev, logger) + } + return { type: 'md' as 'md', ws, @@ -560,7 +532,8 @@ export const makeSocket = ({ sendNode, logout, end, - waitForConnectionUpdate + /** Waits for the connection to WA to reach a state */ + waitForConnectionUpdate: bindWaitForConnectionUpdate(ev) } } export type Socket = ReturnType \ No newline at end of file diff --git a/src/Types/Legacy.ts b/src/Types/Legacy.ts index 688c122..c30e006 100644 --- a/src/Types/Legacy.ts +++ b/src/Types/Legacy.ts @@ -76,8 +76,6 @@ export type LegacySocketConfig = CommonSocketConfig & phoneResponseTimeMs: number /** max time for WA server to respond before error with 422 */ expectResponseTimeout: number - - pendingRequestTimeoutMs: number } export type LegacyBaileysEventEmitter = CommonBaileysEventEmitter \ No newline at end of file diff --git a/src/Utils/generics.ts b/src/Utils/generics.ts index 50f5065..50675af 100644 --- a/src/Utils/generics.ts +++ b/src/Utils/generics.ts @@ -1,7 +1,10 @@ import { Boom } from '@hapi/boom' import { randomBytes } from 'crypto' import { platform, release } from 'os' +import { Logger } from 'pino' +import { ConnectionState } from '..' import { proto } from '../../WAProto' +import { CommonBaileysEventEmitter, DisconnectReason } from '../Types' import { Binary } from '../WABinary' const PLATFORM_MAP = { @@ -176,4 +179,40 @@ export async function promiseTimeout(ms: number, promise: (resolve: (v?: T)=> return p as Promise } // generate a random ID to attach to a message -export const generateMessageID = () => 'BAE5' + randomBytes(6).toString('hex').toUpperCase() \ No newline at end of file +export const generateMessageID = () => 'BAE5' + randomBytes(6).toString('hex').toUpperCase() + +export const bindWaitForConnectionUpdate = (ev: CommonBaileysEventEmitter) => ( + async(check: (u: Partial) => boolean, timeoutMs?: number) => { + let listener: (item: Partial) => void + await ( + promiseTimeout( + timeoutMs, + (resolve, reject) => { + listener = (update) => { + if(check(update)) { + resolve() + } else if(update.connection == 'close') { + reject(update.lastDisconnect?.error || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })) + } + } + ev.on('connection.update', listener) + } + ) + .finally(() => ( + ev.off('connection.update', listener) + )) + ) + } +) + +export const printQRIfNecessaryListener = (ev: CommonBaileysEventEmitter, logger: Logger) => { + ev.on('connection.update', async({ qr }) => { + if(qr) { + const QR = await import('qrcode-terminal') + .catch(err => { + logger.error('QR code terminal not added as dependency') + }) + QR?.generate(qr, { small: true }) + } + }) +} \ No newline at end of file