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 qrTimeout: NodeJS.Timeout
|
||||||
protected phoneCheck: NodeJS.Timeout
|
protected phoneCheck: NodeJS.Timeout
|
||||||
|
|
||||||
|
protected lastDisconnectReason: DisconnectReason
|
||||||
protected cancelledReconnect = false
|
protected cancelledReconnect = false
|
||||||
protected cancelReconnect: () => void
|
protected cancelReconnect: () => void
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
super ()
|
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 =
|
const willReconnect =
|
||||||
(this.autoReconnect === ReconnectMode.onAllErrors ||
|
(this.autoReconnect === ReconnectMode.onAllErrors ||
|
||||||
(this.autoReconnect === ReconnectMode.onConnectionLost && (error !== 'replaced'))) &&
|
(this.autoReconnect === ReconnectMode.onConnectionLost && (error !== 'replaced'))) &&
|
||||||
error !== 'invalid_session'
|
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)
|
this.closeInternal(error, willReconnect)
|
||||||
|
|
||||||
willReconnect && !this.cancelReconnect && this.reconnectLoop ()
|
willReconnect && !this.cancelReconnect && this.reconnectLoop ()
|
||||||
@@ -215,6 +216,11 @@ export class WAConnection extends EventEmitter {
|
|||||||
|
|
||||||
const response = await this.waitForMessage(tag, json, timeoutMs)
|
const response = await this.waitForMessage(tag, json, timeoutMs)
|
||||||
if (expect200 && response.status && Math.floor(+response.status / 100) !== 2) {
|
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})
|
throw new BaileysError(`Unexpected status code in '${json[0] || 'generic query'}': ${response.status}`, {query: json})
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
@@ -282,11 +288,7 @@ export class WAConnection extends EventEmitter {
|
|||||||
/** Close the connection to WhatsApp Web */
|
/** Close the connection to WhatsApp Web */
|
||||||
close () {
|
close () {
|
||||||
this.closeInternal ('intentional')
|
this.closeInternal ('intentional')
|
||||||
|
|
||||||
this.cancelReconnect && this.cancelReconnect ()
|
this.cancelReconnect && this.cancelReconnect ()
|
||||||
|
|
||||||
this.pendingRequests.forEach (({reject}) => reject(new Error('close')))
|
|
||||||
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)
|
||||||
@@ -298,6 +300,12 @@ export class WAConnection extends EventEmitter {
|
|||||||
this.conn?.close()
|
this.conn?.close()
|
||||||
this.conn = null
|
this.conn = null
|
||||||
this.phoneConnected = false
|
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 => {
|
Object.keys(this.callbacks).forEach(key => {
|
||||||
if (!key.includes('function:')) {
|
if (!key.includes('function:')) {
|
||||||
@@ -307,6 +315,7 @@ 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
|
// reconnecting if the timeout is active for the reconnect loop
|
||||||
this.emit ('close', { reason, isReconnecting: this.cancelReconnect || isReconnecting})
|
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 {
|
export class WAConnection extends Base {
|
||||||
|
|
||||||
/** Authenticate the connection */
|
/** 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
|
// 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) {
|
||||||
@@ -27,8 +27,9 @@ export class WAConnection extends Base {
|
|||||||
this.authInfo?.clientToken,
|
this.authInfo?.clientToken,
|
||||||
this.authInfo?.serverToken,
|
this.authInfo?.serverToken,
|
||||||
this.authInfo?.clientID,
|
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.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
|
return this.generateKeysForAuth(json.ref) // generate keys which will in turn be the QR
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export class WAConnection extends Base {
|
|||||||
ws
|
ws
|
||||||
.then (conn => this.conn = conn)
|
.then (conn => this.conn = conn)
|
||||||
.then (() => this.conn.on('message', data => this.onMessageRecieved(data as any)))
|
.then (() => this.conn.on('message', data => this.onMessageRecieved(data as any)))
|
||||||
.then (() => this.log('connected to WhatsApp Web server, authenticating...', MessageLogLevel.info))
|
.then (() => this.log(`connected to WhatsApp Web server, authenticating via ${options.reconnectID ? 'reconnect' : 'takeover'}`, MessageLogLevel.info))
|
||||||
.then (() => this.authenticate())
|
.then (() => this.authenticate(options?.reconnectID))
|
||||||
.then (() => {
|
.then (() => {
|
||||||
this.startKeepAliveRequest()
|
this.startKeepAliveRequest()
|
||||||
this.conn.removeAllListeners ('error')
|
this.conn.removeAllListeners ('error')
|
||||||
@@ -261,7 +261,8 @@ export class WAConnection extends Base {
|
|||||||
|
|
||||||
await delay
|
await delay
|
||||||
try {
|
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
|
this.cancelReconnect = null
|
||||||
break
|
break
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -18,14 +18,18 @@ export class WAConnection extends Base {
|
|||||||
* @param jid the ID of the person/group who you are updating
|
* @param jid the ID of the person/group who you are updating
|
||||||
* @param type your presence
|
* @param type your presence
|
||||||
*/
|
*/
|
||||||
async updatePresence(jid: string | null, type: Presence) {
|
updatePresence = (jid: string | null, type: Presence) =>
|
||||||
const json = [
|
this.query(
|
||||||
'action',
|
{
|
||||||
{ epoch: this.msgCount.toString(), type: 'set' },
|
json: [
|
||||||
[['presence', { type: type, to: jid }, null]],
|
'action',
|
||||||
]
|
{ epoch: this.msgCount.toString(), type: 'set' },
|
||||||
return this.query({json, binaryTags: [WAMetric.group, WAFlag.acknowledge]}) as Promise<{ status: number }>
|
[['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 */
|
/** Request an update on the presence of a user */
|
||||||
requestPresenceUpdate = async (jid: string) => this.query({json: ['action', 'presence', 'subscribe', jid]})
|
requestPresenceUpdate = async (jid: string) => this.query({json: ['action', 'presence', 'subscribe', jid]})
|
||||||
/** Query the status of the person (see groupMetadata() for groups) */
|
/** Query the status of the person (see groupMetadata() for groups) */
|
||||||
|
|||||||
@@ -62,10 +62,12 @@ export type WAConnectOptions = {
|
|||||||
waitForChats?: boolean
|
waitForChats?: boolean
|
||||||
/** retry on network errors while connecting */
|
/** retry on network errors while connecting */
|
||||||
retryOnNetworkErrors?: boolean
|
retryOnNetworkErrors?: boolean
|
||||||
|
/** use the 'reconnect' tag to reconnect instead of the 'takeover' tag */
|
||||||
|
reconnectID?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WAConnectionState = 'open' | 'connecting' | 'close'
|
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 {
|
export enum MessageLogLevel {
|
||||||
none=0,
|
none=0,
|
||||||
info=1,
|
info=1,
|
||||||
|
|||||||
Reference in New Issue
Block a user