From 6785922379db8f70b86c97b9e1069f2d26577671 Mon Sep 17 00:00:00 2001 From: "Wesley F. Young" Date: Mon, 12 Aug 2024 02:12:09 +0800 Subject: [PATCH 1/2] fix: auto retry of active ws --- src/onebot/network/active-websocket.ts | 75 +++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/src/onebot/network/active-websocket.ts b/src/onebot/network/active-websocket.ts index a73b9f54..1b557846 100644 --- a/src/onebot/network/active-websocket.ts +++ b/src/onebot/network/active-websocket.ts @@ -1,7 +1,6 @@ import { IOB11NetworkAdapter, OB11EmitEventContent } from '@/onebot/network/index'; import { WebSocket } from 'ws'; import BaseAction from '@/onebot/action/BaseAction'; -import { sleep } from '@/common/utils/helper'; import { OB11HeartbeatEvent } from '../event/meta/OB11HeartbeatEvent'; import { NapCatCore } from '@/core'; import { NapCatOneBot11Adapter } from '../main'; @@ -19,7 +18,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { logger: LogWrapper; private connection: WebSocket | null = null; private actionMap: Map> = new Map(); - private heartbeatTimer: NodeJS.Timeout | null = null; + private heartbeatRef: NodeJS.Timeout | null = null; private readonly token: string; constructor(url: string, reconnectIntervalInMillis: number, heartbeatInterval: number, token:string, coreContext: NapCatCore, onebotContext: NapCatOneBot11Adapter) { @@ -50,6 +49,11 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { if (this.connection) { return; } + this.heartbeatRef = setInterval(() => { + if (this.connection && this.connection.readyState === WebSocket.OPEN) { + this.connection.send(JSON.stringify(new OB11HeartbeatEvent(this.coreContext, this.heartbeatInterval, this.coreContext.selfInfo.online, true))); + } + }, this.heartbeatInterval); await this.tryConnect(); } @@ -64,19 +68,9 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { this.connection.close(); this.connection = null; } - if (this.heartbeatTimer) { - clearInterval(this.heartbeatTimer); - this.heartbeatTimer = null; - } - } - - private registerHeartBeat() { - if (this.connection) { - this.heartbeatTimer = setInterval(() => { - if (this.connection && this.connection.readyState === WebSocket.OPEN) { - this.connection.send(JSON.stringify(new OB11HeartbeatEvent(this.coreContext, this.heartbeatInterval, this.coreContext.selfInfo.online, true))); - } - }, this.heartbeatInterval); + if (this.heartbeatRef) { + clearInterval(this.heartbeatRef); + this.heartbeatRef = null; } } @@ -87,31 +81,40 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { } private async tryConnect() { - while (!this.connection && !this.isClosed) { - try { - this.connection = new WebSocket(this.url, { - headers: { - 'X-Self-ID': this.coreContext.selfInfo.uin, - 'Authorization': `Bearer ${this.token}`, - 'x-client-role': 'Universal', // koishi-adapter-onebot 需要这个字段 - 'User-Agent': 'OneBot/11', - } - }); - this.connection.on('message', (data) => { - this.handleMessage(data); - }); - this.connection.once('close', () => { + if (!this.connection && !this.isClosed) { + let isClosedByError = false; + + this.connection = new WebSocket(this.url, { + headers: { + 'X-Self-ID': this.coreContext.selfInfo.uin, + 'Authorization': `Bearer ${this.token}`, + 'x-client-role': 'Universal', // koishi-adapter-onebot 需要这个字段 + 'User-Agent': 'OneBot/11', + }, + + }); + this.connection.on('message', (data) => { + this.handleMessage(data); + }); + this.connection.once('close', () => { + if (!isClosedByError) { + this.logger.logError(`反向WebSocket (${this.url}) 连接意外关闭`); + this.logger.logError(`在 ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`); if (!this.isClosed) { this.connection = null; setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis); } - }); - this.registerHeartBeat(); - } catch (e) { - this.connection = null; - console.log('Failed to connect to the server, retrying in 5 seconds...'); - await sleep(5000); - } + } + }); + this.connection.on('error', (err) => { + isClosedByError = true; + this.logger.logError(`反向WebSocket (${this.url}) 连接错误`, err); + this.logger.logError(`在 ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`); + if (!this.isClosed) { + this.connection = null; + setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis); + } + }); } } From 1e5721d7d55c5905c0389be3865408b0d352ed5a Mon Sep 17 00:00:00 2001 From: "Wesley F. Young" Date: Mon, 12 Aug 2024 02:22:20 +0800 Subject: [PATCH 2/2] fix: sleep before loading --- src/onebot/main.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/onebot/main.ts b/src/onebot/main.ts index 78c2ccad..a1e6126a 100644 --- a/src/onebot/main.ts +++ b/src/onebot/main.ts @@ -33,6 +33,7 @@ import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest import { OB11GroupAdminNoticeEvent } from '@/onebot/event/notice/OB11GroupAdminNoticeEvent'; import { GroupDecreaseSubType, OB11GroupDecreaseEvent } from '@/onebot/event/notice/OB11GroupDecreaseEvent'; import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest'; +import { sleep } from '@/common/utils/helper'; //OneBot实现类 export class NapCatOneBot11Adapter { @@ -59,6 +60,15 @@ export class NapCatOneBot11Adapter { } async InitOneBot() { + // 这个延迟对个别 API 的初始化很重要 + // 例如,如果没有这个延迟,则 getGroupMembers 只能返回空列表 + // 反向(Active)WebSocket 在初始化后立即连接,如果 QQ 初始化没完成, + // 而连接的对方在初始化逻辑中用到这个 getGroupMembers, + // 则会导致 getGroupMembers 返回空列表,进而导致初始化失败 + // 初始化完成的标准尚不明确! + // TODO: 弄清楚初始化完成的标志,并试着用监听器回调解决 + await sleep(2500); + const NTQQUserApi = this.core.apis.UserApi; const selfInfo = this.core.selfInfo; const ob11Config = this.configLoader.configData;