mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Added reconnect mechanism
This commit is contained in:
@@ -65,20 +65,21 @@ export class WAConnection extends EventEmitter {
|
||||
protected qrTimeout: NodeJS.Timeout
|
||||
protected phoneCheck: NodeJS.Timeout
|
||||
|
||||
protected lastDisconnectReason: DisconnectReason
|
||||
protected cancelledReconnect = false
|
||||
protected cancelReconnect: () => void
|
||||
|
||||
constructor () {
|
||||
super ()
|
||||
this.registerCallback (['Cmd', 'type:disconnect'], json => this.unexpectedDisconnect(json[1].kind))
|
||||
this.registerCallback (['Cmd', 'type:disconnect'], json => this.unexpectedDisconnect(json[1].kind || 'unknown'))
|
||||
}
|
||||
async unexpectedDisconnect (error?: DisconnectReason) {
|
||||
async unexpectedDisconnect (error: DisconnectReason) {
|
||||
const willReconnect =
|
||||
(this.autoReconnect === ReconnectMode.onAllErrors ||
|
||||
(this.autoReconnect === ReconnectMode.onConnectionLost && (error !== 'replaced'))) &&
|
||||
error !== 'invalid_session'
|
||||
|
||||
this.log (`got disconnected, reason ${error || 'unknown'}${willReconnect ? ', reconnecting in a few seconds...' : ''}`, MessageLogLevel.info)
|
||||
this.log (`got disconnected, reason ${error}${willReconnect ? ', reconnecting in a few seconds...' : ''}`, MessageLogLevel.info)
|
||||
this.closeInternal(error, willReconnect)
|
||||
|
||||
willReconnect && !this.cancelReconnect && this.reconnectLoop ()
|
||||
@@ -215,6 +216,11 @@ export class WAConnection extends EventEmitter {
|
||||
|
||||
const response = await this.waitForMessage(tag, json, timeoutMs)
|
||||
if (expect200 && response.status && Math.floor(+response.status / 100) !== 2) {
|
||||
if (response.status >= 500) {
|
||||
this.unexpectedDisconnect ('bad_session')
|
||||
const response = await this.query ({json, binaryTags, tag, timeoutMs, expect200, waitForOpen})
|
||||
return response
|
||||
}
|
||||
throw new BaileysError(`Unexpected status code in '${json[0] || 'generic query'}': ${response.status}`, {query: json})
|
||||
}
|
||||
return response
|
||||
@@ -282,11 +288,7 @@ export class WAConnection extends EventEmitter {
|
||||
/** Close the connection to WhatsApp Web */
|
||||
close () {
|
||||
this.closeInternal ('intentional')
|
||||
|
||||
this.cancelReconnect && this.cancelReconnect ()
|
||||
|
||||
this.pendingRequests.forEach (({reject}) => reject(new Error('close')))
|
||||
this.pendingRequests = []
|
||||
}
|
||||
protected closeInternal (reason?: DisconnectReason, isReconnecting: boolean=false) {
|
||||
this.qrTimeout && clearTimeout (this.qrTimeout)
|
||||
@@ -298,6 +300,12 @@ export class WAConnection extends EventEmitter {
|
||||
this.conn?.close()
|
||||
this.conn = null
|
||||
this.phoneConnected = false
|
||||
this.lastDisconnectReason = reason
|
||||
|
||||
if (reason === 'invalid_session' || reason === 'intentional') {
|
||||
this.pendingRequests.forEach (({reject}) => reject(new Error('close')))
|
||||
this.pendingRequests = []
|
||||
}
|
||||
|
||||
Object.keys(this.callbacks).forEach(key => {
|
||||
if (!key.includes('function:')) {
|
||||
@@ -307,6 +315,7 @@ export class WAConnection extends EventEmitter {
|
||||
}
|
||||
})
|
||||
if (this.keepAliveReq) clearInterval(this.keepAliveReq)
|
||||
|
||||
// reconnecting if the timeout is active for the reconnect loop
|
||||
this.emit ('close', { reason, isReconnecting: this.cancelReconnect || isReconnecting})
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { MessageLogLevel, WAMetric, WAFlag, BaileysError, Presence } from './Con
|
||||
export class WAConnection extends Base {
|
||||
|
||||
/** Authenticate the connection */
|
||||
protected async authenticate() {
|
||||
protected async authenticate (reconnect?: string) {
|
||||
// if no auth info is present, that is, a new session has to be established
|
||||
// generate a client ID
|
||||
if (!this.authInfo?.clientID) {
|
||||
@@ -27,8 +27,9 @@ export class WAConnection extends Base {
|
||||
this.authInfo?.clientToken,
|
||||
this.authInfo?.serverToken,
|
||||
this.authInfo?.clientID,
|
||||
'takeover',
|
||||
]
|
||||
if (reconnect) json.push(...['reconnect', reconnect.replace('@s.whatsapp.net', '@c.us')])
|
||||
else json.push ('takeover')
|
||||
return this.query({ json, tag: 's1', waitForOpen: false }) // wait for response with tag "s1"
|
||||
}
|
||||
return this.generateKeysForAuth(json.ref) // generate keys which will in turn be the QR
|
||||
|
||||
@@ -20,8 +20,8 @@ export class WAConnection extends Base {
|
||||
ws
|
||||
.then (conn => this.conn = conn)
|
||||
.then (() => this.conn.on('message', data => this.onMessageRecieved(data as any)))
|
||||
.then (() => this.log('connected to WhatsApp Web server, authenticating...', MessageLogLevel.info))
|
||||
.then (() => this.authenticate())
|
||||
.then (() => this.log(`connected to WhatsApp Web server, authenticating via ${options.reconnectID ? 'reconnect' : 'takeover'}`, MessageLogLevel.info))
|
||||
.then (() => this.authenticate(options?.reconnectID))
|
||||
.then (() => {
|
||||
this.startKeepAliveRequest()
|
||||
this.conn.removeAllListeners ('error')
|
||||
@@ -261,7 +261,8 @@ export class WAConnection extends Base {
|
||||
|
||||
await delay
|
||||
try {
|
||||
await this.connect ({ timeoutMs: 30000, retryOnNetworkErrors: true })
|
||||
const reconnectID = this.lastDisconnectReason !== 'replaced' && this.user ? this.user.id.replace ('@s.whatsapp.net', '@c.us') : null
|
||||
await this.connect ({ timeoutMs: 30000, retryOnNetworkErrors: true, reconnectID })
|
||||
this.cancelReconnect = null
|
||||
break
|
||||
} catch (error) {
|
||||
|
||||
@@ -18,14 +18,18 @@ export class WAConnection extends Base {
|
||||
* @param jid the ID of the person/group who you are updating
|
||||
* @param type your presence
|
||||
*/
|
||||
async updatePresence(jid: string | null, type: Presence) {
|
||||
const json = [
|
||||
'action',
|
||||
{ epoch: this.msgCount.toString(), type: 'set' },
|
||||
[['presence', { type: type, to: jid }, null]],
|
||||
]
|
||||
return this.query({json, binaryTags: [WAMetric.group, WAFlag.acknowledge]}) as Promise<{ status: number }>
|
||||
}
|
||||
updatePresence = (jid: string | null, type: Presence) =>
|
||||
this.query(
|
||||
{
|
||||
json: [
|
||||
'action',
|
||||
{ epoch: this.msgCount.toString(), type: 'set' },
|
||||
[['presence', { type: type, to: jid }, null]],
|
||||
],
|
||||
binaryTags: [WAMetric.group, WAFlag.acknowledge],
|
||||
expect200: true
|
||||
}
|
||||
) as Promise<{status: number}>
|
||||
/** Request an update on the presence of a user */
|
||||
requestPresenceUpdate = async (jid: string) => this.query({json: ['action', 'presence', 'subscribe', jid]})
|
||||
/** Query the status of the person (see groupMetadata() for groups) */
|
||||
|
||||
@@ -62,10 +62,12 @@ export type WAConnectOptions = {
|
||||
waitForChats?: boolean
|
||||
/** retry on network errors while connecting */
|
||||
retryOnNetworkErrors?: boolean
|
||||
/** use the 'reconnect' tag to reconnect instead of the 'takeover' tag */
|
||||
reconnectID?: string
|
||||
}
|
||||
|
||||
export type WAConnectionState = 'open' | 'connecting' | 'close'
|
||||
export type DisconnectReason = 'close' | 'lost' | 'replaced' | 'intentional' | 'invalid_session'
|
||||
export type DisconnectReason = 'close' | 'lost' | 'replaced' | 'intentional' | 'invalid_session' | 'unknown' | 'bad_session'
|
||||
export enum MessageLogLevel {
|
||||
none=0,
|
||||
info=1,
|
||||
|
||||
Reference in New Issue
Block a user