Better phone connection detection

This commit is contained in:
Adhiraj Singh
2021-01-06 18:21:36 +05:30
parent b9ece77220
commit 39802eb449
4 changed files with 70 additions and 10 deletions

View File

@@ -372,6 +372,21 @@ describe ('Pending Requests', () => {
conn.close () conn.close ()
}) })
it('[MANUAL] should receive query response after phone disconnect', async () => {
const conn = makeConnection ()
await conn.loadAuthInfo('./auth_info.json').connect ()
console.log(`disconnect your phone from the internet!`)
await delay(5000)
const task = conn.loadMessages(testJid, 50)
setTimeout(() => console.log('reconnect your phone!'), 20_000)
const result = await task
assert.ok(result.messages[0])
assert.ok(!conn['phoneCheckInterval']) // should be undefined
conn.close ()
})
it('should re-execute query on connection closed error', async () => { it('should re-execute query on connection closed error', async () => {
const conn = makeConnection () const conn = makeConnection ()
//conn.pendingRequestTimeoutMs = 10_000 //conn.pendingRequestTimeoutMs = 10_000

View File

@@ -1,4 +1,4 @@
import { Presence, ChatModification, delay, newMessagesDB, WA_DEFAULT_EPHEMERAL, MessageType } from '../WAConnection/WAConnection' import { Presence, ChatModification, delay, newMessagesDB, WA_DEFAULT_EPHEMERAL, MessageType, WAMessage } from '../WAConnection/WAConnection'
import { promises as fs } from 'fs' import { promises as fs } from 'fs'
import * as assert from 'assert' import * as assert from 'assert'
import fetch from 'node-fetch' import fetch from 'node-fetch'
@@ -397,4 +397,20 @@ WAConnectionTest('Misc', conn => {
assert.strictEqual(conn.blocklist.length, blockedCount); assert.strictEqual(conn.blocklist.length, blockedCount);
await waitForEventRemoved await waitForEventRemoved
}) })
it('should exit an invalid query', async () => {
// try and send an already sent message
let msg: WAMessage
await conn.findMessage(testJid, 5, m => {
if(m.key.fromMe) {
msg = m
return true
}
})
try {
await conn.relayWAMessage(msg)
assert.fail('should not have sent')
} catch(error) {
assert.strictEqual(error.status, 422)
}
})
}) })

View File

@@ -180,16 +180,18 @@ export class WAConnection extends EventEmitter {
* @param timeoutMs timeout after which the promise will reject * @param timeoutMs timeout after which the promise will reject
*/ */
async waitForMessage(tag: string, requiresPhoneConnection: boolean, timeoutMs?: number) { async waitForMessage(tag: string, requiresPhoneConnection: boolean, timeoutMs?: number) {
if (requiresPhoneConnection) {
this.startPhoneCheckInterval ()
}
let onRecv: (json) => void let onRecv: (json) => void
let onErr: (err) => void let onErr: (err) => void
let cancelPhoneChecker: () => void
if (requiresPhoneConnection) {
this.startPhoneCheckInterval()
cancelPhoneChecker = this.exitQueryIfResponseNotExpected(tag, err => onErr(err))
}
try { try {
const result = await Utils.promiseTimeout(timeoutMs, const result = await Utils.promiseTimeout(timeoutMs,
(resolve, reject) => { (resolve, reject) => {
onRecv = resolve onRecv = resolve
onErr = ({reason}) => reject(new Error(reason)) onErr = ({ reason, status }) => reject(new BaileysError(reason, { status }))
this.on (`TAG:${tag}`, onRecv) this.on (`TAG:${tag}`, onRecv)
this.on ('ws-close', onErr) // if the socket closes, you'll never receive the message this.on ('ws-close', onErr) // if the socket closes, you'll never receive the message
}, },
@@ -199,6 +201,7 @@ export class WAConnection extends EventEmitter {
requiresPhoneConnection && this.clearPhoneCheckInterval() requiresPhoneConnection && this.clearPhoneCheckInterval()
this.off (`TAG:${tag}`, onRecv) this.off (`TAG:${tag}`, onRecv)
this.off (`ws-close`, onErr) this.off (`ws-close`, onErr)
cancelPhoneChecker && cancelPhoneChecker()
} }
} }
/** Generic function for action, set queries */ /** Generic function for action, set queries */
@@ -253,7 +256,12 @@ export class WAConnection extends EventEmitter {
// read here: http://getstatuscode.com/599 // read here: http://getstatuscode.com/599
if (error.status === 599) { if (error.status === 599) {
this.unexpectedDisconnect (DisconnectReason.badSession) this.unexpectedDisconnect (DisconnectReason.badSession)
} else if ((error.message === 'close' || error.message === 'lost') && waitForOpen && this.state !== 'close') { } else if (
(error.message === 'close' || error.message === 'lost') &&
waitForOpen &&
this.state !== 'close' &&
(this.pendingRequestTimeoutMs === null ||
this.pendingRequestTimeoutMs > 0)) {
// nothing here // nothing here
} else throw error } else throw error
@@ -262,6 +270,23 @@ export class WAConnection extends EventEmitter {
} }
} }
} }
protected exitQueryIfResponseNotExpected(tag: string, cancel: ({ reason, status }) => void) {
let timeout: NodeJS.Timeout
const listener = ({ connected }) => {
if(connected) {
timeout = setTimeout(() => {
this.logger.info({ tag }, `cancelling wait for message as a response is no longer expected from the phone`)
cancel({ reason: 'Not expecting a response', status: 422 })
}, 5_000)
this.off('connection-phone-change', listener)
}
}
this.on('connection-phone-change', listener)
return () => {
this.off('connection-phone-change', listener)
timeout && clearTimeout(timeout)
}
}
/** interval is started when a query takes too long to respond */ /** interval is started when a query takes too long to respond */
protected startPhoneCheckInterval () { protected startPhoneCheckInterval () {
this.phoneCheckListeners += 1 this.phoneCheckListeners += 1
@@ -272,9 +297,10 @@ export class WAConnection extends EventEmitter {
this.logger.info('checking phone connection...') this.logger.info('checking phone connection...')
this.sendAdminTest () this.sendAdminTest ()
if(this.phoneConnected !== false) {
this.phoneConnected = false this.phoneConnected = false
this.emit ('connection-phone-change', { connected: false }) this.emit ('connection-phone-change', { connected: false })
}
}, this.connectOptions.phoneResponseTime) }, this.connectOptions.phoneResponseTime)
} }

View File

@@ -265,7 +265,10 @@ export class WAConnection extends Base {
const response: WANode = await this.query({json, binaryTags: [24, WAFlag.ignore], expect200: true}) // encrypt and send off const response: WANode = await this.query({json, binaryTags: [24, WAFlag.ignore], expect200: true}) // encrypt and send off
const messages = response[2] ? response[2].map (row => row[2]) : [] const messages = response[2] ? response[2].map (row => row[2]) : []
return { last: response[1]['last'] === 'true', messages: messages as WAMessage[] } return {
last: response[1]['last'] === 'true',
messages: messages as WAMessage[]
}
} }
/** /**
* Delete a message in a chat for yourself * Delete a message in a chat for yourself