Idle timeout

This commit is contained in:
Adhiraj
2020-09-18 16:22:57 +05:30
parent 7eb454b3dd
commit b7de86afce
4 changed files with 26 additions and 10 deletions

View File

@@ -45,6 +45,7 @@ export class WAConnection extends EventEmitter {
regenerateQRIntervalMs = 30*1000 regenerateQRIntervalMs = 30*1000
connectOptions: WAConnectOptions = { connectOptions: WAConnectOptions = {
timeoutMs: 60*1000, timeoutMs: 60*1000,
maxIdleTimeMs: 10*1000,
waitForChats: true, waitForChats: true,
maxRetries: 5, maxRetries: 5,
connectCooldownMs: 2250, connectCooldownMs: 2250,
@@ -80,6 +81,7 @@ export class WAConnection extends EventEmitter {
protected lastDisconnectReason: DisconnectReason protected lastDisconnectReason: DisconnectReason
protected mediaConn: MediaConnInfo protected mediaConn: MediaConnInfo
protected debounceTimeout: NodeJS.Timeout
constructor () { constructor () {
super () super ()
@@ -101,7 +103,10 @@ export class WAConnection extends EventEmitter {
error !== DisconnectReason.invalidSession // do not reconnect if credentials have been invalidated error !== DisconnectReason.invalidSession // do not reconnect if credentials have been invalidated
this.closeInternal(error, willReconnect) this.closeInternal(error, willReconnect)
willReconnect && this.connect () willReconnect && (
this.connect ()
.catch(err => {}) // prevent unhandled exeception
)
} }
/** /**
* base 64 encode the authentication credentials and return them * base 64 encode the authentication credentials and return them
@@ -316,6 +321,7 @@ export class WAConnection extends EventEmitter {
this.qrTimeout && clearTimeout (this.qrTimeout) this.qrTimeout && clearTimeout (this.qrTimeout)
this.keepAliveReq && clearInterval(this.keepAliveReq) this.keepAliveReq && clearInterval(this.keepAliveReq)
this.debounceTimeout && clearTimeout (this.debounceTimeout)
this.state = 'close' this.state = 'close'
this.msgCount = 0 this.msgCount = 0

View File

@@ -6,7 +6,7 @@ import { MessageLogLevel, WAMetric, WAFlag, BaileysError, Presence, WAUser } fro
export class WAConnection extends Base { export class WAConnection extends Base {
/** Authenticate the connection */ /** Authenticate the connection */
protected async authenticate (reconnect?: string) { protected async authenticate (onConnectionValidated: () => void, reconnect?: string) {
// if no auth info is present, that is, a new session has to be established // if no auth info is present, that is, a new session has to be established
// generate a client ID // generate a client ID
if (!this.authInfo?.clientID) { if (!this.authInfo?.clientID) {
@@ -56,9 +56,9 @@ export class WAConnection extends Base {
} }
const validationJSON = (await Promise.all (initQueries)).slice(-1)[0] // get the last result const validationJSON = (await Promise.all (initQueries)).slice(-1)[0] // get the last result
this.user = await this.validateNewConnection(validationJSON[1]) // validate the connection this.user = await this.validateNewConnection(validationJSON[1]) // validate the connection
onConnectionValidated ()
this.log('validated connection successfully', MessageLogLevel.info) this.log('validated connection successfully', MessageLogLevel.info)
const response = await this.query({ json: ['query', 'ProfilePicThumb', this.user.jid], waitForOpen: false, expect200: false }) const response = await this.query({ json: ['query', 'ProfilePicThumb', this.user.jid], waitForOpen: false, expect200: false })

View File

@@ -68,13 +68,18 @@ export class WAConnection extends Base {
let cancel: () => void let cancel: () => void
const task = Utils.promiseTimeout(timeoutMs, (resolve, reject) => { const task = Utils.promiseTimeout(timeoutMs, (resolve, reject) => {
let task: Promise<any> = Promise.resolve () let task: Promise<any> = Promise.resolve ()
const checkIdleTime = () => {
this.debounceTimeout && clearTimeout (this.debounceTimeout)
this.debounceTimeout = setTimeout (() => rejectSafe (TimedOutError()), this.connectOptions.maxIdleTimeMs)
}
const debouncedTimeout = () => this.connectOptions.maxIdleTimeMs && this.conn.addEventListener ('message', checkIdleTime)
// add wait for chats promise if required // add wait for chats promise if required
if (typeof options?.waitForChats === 'undefined' ? true : options?.waitForChats) { if (typeof options?.waitForChats === 'undefined' ? true : options?.waitForChats) {
const {waitForChats, cancelChats} = this.receiveChatsAndContacts() const {waitForChats, cancelChats} = this.receiveChatsAndContacts()
task = waitForChats task = waitForChats
cancel = cancelChats cancel = cancelChats
} }
// determine whether reconnect should be used or not // determine whether reconnect should be used or not
const shouldUseReconnect = this.lastDisconnectReason !== DisconnectReason.replaced && const shouldUseReconnect = this.lastDisconnectReason !== DisconnectReason.replaced &&
this.lastDisconnectReason !== DisconnectReason.unknown && this.lastDisconnectReason !== DisconnectReason.unknown &&
@@ -83,16 +88,17 @@ export class WAConnection extends Base {
const reconnectID = shouldUseReconnect ? this.user.jid.replace ('@s.whatsapp.net', '@c.us') : null const reconnectID = shouldUseReconnect ? this.user.jid.replace ('@s.whatsapp.net', '@c.us') : null
this.conn = new WS(WS_URL, null, { origin: DEFAULT_ORIGIN, timeout: timeoutMs, agent: options.agent }) this.conn = new WS(WS_URL, null, { origin: DEFAULT_ORIGIN, timeout: this.connectOptions.maxIdleTimeMs, agent: options.agent })
this.conn.on('message', data => this.onMessageRecieved(data as any)) this.conn.addEventListener('message', ({data}) => this.onMessageRecieved(data as any))
this.conn.on ('open', async () => { this.conn.on ('open', async () => {
this.log(`connected to WhatsApp Web server, authenticating via ${reconnectID ? 'reconnect' : 'takeover'}`, MessageLogLevel.info) this.log(`connected to WhatsApp Web server, authenticating via ${reconnectID ? 'reconnect' : 'takeover'}`, MessageLogLevel.info)
try { try {
task = Promise.all ([ task = Promise.all ([
task, task,
this.authenticate (reconnectID) // debounce the timeout once validated
this.authenticate (debouncedTimeout, reconnectID)
.then ( .then (
() => { () => {
this.conn this.conn
@@ -102,12 +108,14 @@ export class WAConnection extends Base {
) )
]) ])
const [result] = await task const [result] = await task
this.conn.removeEventListener ('message', checkIdleTime)
resolve (result) resolve (result)
} catch (error) { } catch (error) {
reject (error) reject (error)
} }
}) })
const rejectSafe = error => { const rejectSafe = error => {
task = task.catch (() => {}) task = task.catch (() => {})
reject (error) reject (error)
@@ -138,7 +146,7 @@ export class WAConnection extends Base {
const result = connect () const result = connect ()
cancellations.push (result.cancel) cancellations.push (result.cancel)
const final = await result.promise const final = await result.promise
return final return final
} catch (error) { } catch (error) {
this.endConnection () this.endConnection ()

View File

@@ -67,6 +67,8 @@ export enum ReconnectMode {
export type WAConnectOptions = { export type WAConnectOptions = {
/** timeout after which the connect attempt will fail, set to null for default timeout value */ /** timeout after which the connect attempt will fail, set to null for default timeout value */
timeoutMs?: number timeoutMs?: number
/** */
maxIdleTimeMs?: number
/** maximum attempts to connect */ /** maximum attempts to connect */
maxRetries?: number maxRetries?: number
/** should the chats be waited for */ /** should the chats be waited for */