Reconnect fixes

This commit is contained in:
Adhiraj
2020-08-19 16:06:20 +05:30
parent 95d2567e76
commit a6c8fee814
6 changed files with 100 additions and 14 deletions

View File

@@ -124,6 +124,8 @@ conn.regenerateQRIntervalMs = 20000 // QR regen every 20 seconds
Baileys now uses the EventEmitter syntax for events. Baileys now uses the EventEmitter syntax for events.
They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.
Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.
``` ts ``` ts
/** when the connection has opened successfully */ /** when the connection has opened successfully */

View File

@@ -1,7 +1,8 @@
import * as assert from 'assert' import * as assert from 'assert'
import {WAConnection} from '../WAConnection/WAConnection' import {WAConnection} from '../WAConnection/WAConnection'
import { AuthenticationCredentialsBase64, BaileysError, MessageLogLevel } from '../WAConnection/Constants' import { AuthenticationCredentialsBase64, BaileysError, MessageLogLevel, ReconnectMode } from '../WAConnection/Constants'
import { delay, promiseTimeout } from '../WAConnection/Utils' import { delay, promiseTimeout } from '../WAConnection/Utils'
import { close } from 'fs'
describe('QR Generation', () => { describe('QR Generation', () => {
it('should generate QR', async () => { it('should generate QR', async () => {
@@ -72,7 +73,84 @@ describe('Test Connect', () => {
.finally (() => conn.close()) .finally (() => conn.close())
}) })
}) })
describe ('Pending Requests', async () => { describe ('Reconnects', () => {
it ('should disconnect & reconnect phone', async () => {
const conn = new WAConnection ()
await conn.loadAuthInfo('./auth_info.json').connect ()
assert.equal (conn.phoneConnected, true)
try {
const waitForEvent = expect => new Promise (resolve => {
conn.on ('connection-phone-change', ({connected}) => {
assert.equal (connected, expect)
conn.removeAllListeners ('connection-phone-change')
resolve ()
})
})
console.log ('disconnect your phone from the internet')
await waitForEvent (false)
console.log ('reconnect your phone to the internet')
await waitForEvent (true)
} finally {
conn.close ()
}
})
it ('should reconnect connection', async () => {
const conn = new WAConnection ()
conn.autoReconnect = ReconnectMode.onConnectionLost
await conn.loadAuthInfo('./auth_info.json').connect ()
assert.equal (conn.phoneConnected, true)
try {
const closeConn = () => conn['conn']?.terminate ()
const task = new Promise (resolve => {
let closes = 0
conn.on ('closed', ({reason, isReconnecting}) => {
console.log (`closed: ${reason}`)
assert.ok (reason)
assert.ok (isReconnecting)
closes += 1
// let it fail reconnect a few times
if (closes > 4) {
console.log ('here')
conn.removeAllListeners ('closed')
conn.removeAllListeners ('connecting')
resolve ()
}
})
conn.on ('connecting', () => {
// close again
delay (500).then (closeConn)
})
})
closeConn ()
await task
await new Promise (resolve => {
conn.on ('open', () => {
conn.removeAllListeners ('open')
resolve ()
})
})
conn.close ()
conn.on ('connecting', () => assert.fail('should not connect'))
await delay (2000)
} finally {
conn.removeAllListeners ('connecting')
conn.removeAllListeners ('closed')
conn.removeAllListeners ('open')
conn.close ()
}
})
})
describe ('Pending Requests', () => {
it('should queue requests when closed', async () => { it('should queue requests when closed', async () => {
const conn = new WAConnection () const conn = new WAConnection ()
conn.pendingRequestTimeoutMs = null conn.pendingRequestTimeoutMs = null

View File

@@ -73,12 +73,12 @@ export class WAConnection extends EventEmitter {
this.registerCallback (['Cmd', 'type:disconnect'], json => this.unexpectedDisconnect(json[1].kind)) this.registerCallback (['Cmd', 'type:disconnect'], json => this.unexpectedDisconnect(json[1].kind))
} }
async unexpectedDisconnect (error?: DisconnectReason) { async unexpectedDisconnect (error?: DisconnectReason) {
const willReconnect = this.autoReconnect === ReconnectMode.onAllErrors || (this.autoReconnect === ReconnectMode.onConnectionLost && (error === 'lost' || error === 'closed')) const willReconnect = this.autoReconnect === ReconnectMode.onAllErrors || (this.autoReconnect === ReconnectMode.onConnectionLost && (error !== 'replaced'))
this.log (`got disconnected, reason ${error || 'unknown'}${willReconnect ? ', reconnecting in a few seconds...' : ''}`, MessageLogLevel.info) this.log (`got disconnected, reason ${error || 'unknown'}${willReconnect ? ', reconnecting in a few seconds...' : ''}`, MessageLogLevel.info)
this.closeInternal(error, willReconnect) this.closeInternal(error, willReconnect)
willReconnect && this.reconnectLoop ()
willReconnect && !this.cancelReconnect && this.reconnectLoop ()
} }
/** /**
* base 64 encode the authentication credentials and return them * base 64 encode the authentication credentials and return them
@@ -285,7 +285,7 @@ export class WAConnection extends EventEmitter {
this.pendingRequests.forEach (({reject}) => reject(new Error('closed'))) this.pendingRequests.forEach (({reject}) => reject(new Error('closed')))
this.pendingRequests = [] this.pendingRequests = []
} }
protected closeInternal (reason?: DisconnectReason, isReconnecting: boolean = false) { protected closeInternal (reason?: DisconnectReason, isReconnecting: boolean=false) {
this.qrTimeout && clearTimeout (this.qrTimeout) this.qrTimeout && clearTimeout (this.qrTimeout)
this.phoneCheck && clearTimeout (this.phoneCheck) this.phoneCheck && clearTimeout (this.phoneCheck)
@@ -303,8 +303,8 @@ export class WAConnection extends EventEmitter {
} }
}) })
if (this.keepAliveReq) clearInterval(this.keepAliveReq) if (this.keepAliveReq) clearInterval(this.keepAliveReq)
// reconnecting if the timeout is active for the reconnect loop
this.emit ('closed', { reason, isReconnecting }) this.emit ('closed', { reason, isReconnecting: this.cancelReconnect || isReconnecting })
} }
protected async reconnectLoop () { protected async reconnectLoop () {

View File

@@ -28,6 +28,7 @@ export class WAConnection extends Base {
this.startKeepAliveRequest() this.startKeepAliveRequest()
this.conn.removeAllListeners ('error') this.conn.removeAllListeners ('error')
this.conn.removeAllListeners ('close')
this.conn.on ('close', () => this.unexpectedDisconnect ('closed')) this.conn.on ('close', () => this.unexpectedDisconnect ('closed'))
this.state = 'open' this.state = 'open'
@@ -37,7 +38,8 @@ export class WAConnection extends Base {
}) })
this.conn.on('message', m => this.onMessageRecieved(m)) this.conn.on('message', m => this.onMessageRecieved(m))
// if there was an error in the WebSocket // if there was an error in the WebSocket
this.conn.on('error', error => { this.closeInternal(error.message as any); reject(error) }) this.conn.on('error', reject)
this.conn.on('close', () => reject(new Error('closed')))
}) })
try { try {
@@ -77,7 +79,6 @@ export class WAConnection extends Base {
let receivedMessages = false let receivedMessages = false
let convoResolve: () => void let convoResolve: () => void
this.log('waiting for chats & contacts', MessageLogLevel.info) // wait for the message with chats
const waitForConvos = () => const waitForConvos = () =>
Utils.promiseTimeout(timeoutMs, resolve => { Utils.promiseTimeout(timeoutMs, resolve => {
convoResolve = () => { convoResolve = () => {
@@ -91,7 +92,7 @@ export class WAConnection extends Base {
} }
const chatUpdate = json => { const chatUpdate = json => {
receivedMessages = true receivedMessages = true
const isLast = json[1].last || (json[1].add === 'last' && stopAfterMostRecentMessage) const isLast = json[1].last || stopAfterMostRecentMessage
const messages = json[2] as WANode[] const messages = json[2] as WANode[]
if (messages) { if (messages) {
@@ -133,6 +134,8 @@ export class WAConnection extends Base {
this.deregisterCallback(['response', 'type:chat']) this.deregisterCallback(['response', 'type:chat'])
this.log ('received chats list', MessageLogLevel.info)
if (this.chats.all().length > 0) waitForConvos().then (resolve) if (this.chats.all().length > 0) waitForConvos().then (resolve)
else resolve () else resolve ()
}) })
@@ -157,6 +160,8 @@ export class WAConnection extends Base {
resolve () resolve ()
this.deregisterCallback(['response', 'type:contacts']) this.deregisterCallback(['response', 'type:contacts'])
this.log ('received contacts list', MessageLogLevel.info)
}) })
}) })
) )
@@ -259,6 +264,7 @@ export class WAConnection extends Base {
try { try {
await this.connect () await this.connect ()
this.cancelReconnect = null this.cancelReconnect = null
break
} catch (error) { } catch (error) {
this.log (`error in reconnecting: ${error}, reconnecting...`, MessageLogLevel.info) this.log (`error in reconnecting: ${error}, reconnecting...`, MessageLogLevel.info)
} }

View File

@@ -261,7 +261,7 @@ export class WAConnection extends Base {
} }
protected registerPhoneConnectionPoll () { protected registerPhoneConnectionPoll () {
this.phoneCheck = setInterval (() => { this.phoneCheck = setInterval (() => {
this.checkPhoneConnection (7500) // 7500 ms for timeout this.checkPhoneConnection (5000) // 5000 ms for timeout
.then (connected => { .then (connected => {
if (this.phoneConnected != connected) { if (this.phoneConnected != connected) {
this.emit ('connection-phone-change', {connected}) this.emit ('connection-phone-change', {connected})
@@ -269,7 +269,7 @@ export class WAConnection extends Base {
this.phoneConnected = connected this.phoneConnected = connected
}) })
.catch (error => this.log(`error in getting phone connection: ${error}`, MessageLogLevel.info)) .catch (error => this.log(`error in getting phone connection: ${error}`, MessageLogLevel.info))
}, 20000) }, 15000)
} }
// Add all event types // Add all event types

View File

@@ -134,7 +134,7 @@ export class WAConnection extends Base {
/** Get the invite link of the given group */ /** Get the invite link of the given group */
async groupInviteCode(jid: string) { async groupInviteCode(jid: string) {
const json = ['query', 'inviteCode', jid] const json = ['query', 'inviteCode', jid]
const response = await this.query({json}) const response = await this.query({json, expect200: true})
return response.code as string return response.code as string
} }
} }