mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
refactor: express-> hono
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
"ajv": "^8.13.0",
|
"ajv": "^8.13.0",
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"commander": "^13.0.0",
|
"commander": "^13.0.0",
|
||||||
|
"compressing": "^1.10.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"esbuild": "0.25.0",
|
"esbuild": "0.25.0",
|
||||||
"eslint": "^9.14.0",
|
"eslint": "^9.14.0",
|
||||||
@@ -54,14 +55,15 @@
|
|||||||
"image-size": "^1.1.1",
|
"image-size": "^1.1.1",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
|
"napcat.protobuf": "^1.1.3",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.13.0",
|
"typescript-eslint": "^8.13.0",
|
||||||
"vite": "^6.0.1",
|
"vite": "^6.0.1",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-tsconfig-paths": "^5.1.0",
|
"vite-tsconfig-paths": "^5.1.0",
|
||||||
"napcat.protobuf": "^1.1.3",
|
"hono": "^4.7.2",
|
||||||
"winston": "^3.17.0",
|
"@hono/node-server": "^1.13.8",
|
||||||
"compressing": "^1.10.1"
|
"winston": "^3.17.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ffmpeg.wasm/core-mt": "^0.13.2",
|
"@ffmpeg.wasm/core-mt": "^0.13.2",
|
||||||
|
@@ -1,33 +1,36 @@
|
|||||||
import { OB11EmitEventContent } from './index';
|
import { OB11EmitEventContent } from './index';
|
||||||
import { Request, Response } from 'express';
|
|
||||||
import { OB11HttpServerAdapter } from './http-server';
|
import { OB11HttpServerAdapter } from './http-server';
|
||||||
|
import { Context } from 'hono';
|
||||||
|
import { SSEStreamingApi, streamSSE } from 'hono/streaming';
|
||||||
|
import { Mutex } from 'async-mutex';
|
||||||
|
|
||||||
export class OB11HttpSSEServerAdapter extends OB11HttpServerAdapter {
|
export class OB11HttpSSEServerAdapter extends OB11HttpServerAdapter {
|
||||||
private sseClients: Response[] = [];
|
private sseClients: { context: Context; stream: SSEStreamingApi }[] = [];
|
||||||
|
private mutex = new Mutex();
|
||||||
override async handleRequest(req: Request, res: Response) {
|
override async httpApiRequest(c: Context): Promise<any> {
|
||||||
if (req.path === '/_events') {
|
if (c.req.path === '/_events') {
|
||||||
this.createSseSupport(req, res);
|
return await this.createSseSupport(c);
|
||||||
} else {
|
} else {
|
||||||
super.httpApiRequest(req, res);
|
return super.httpApiRequest(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSseSupport(req: Request, res: Response) {
|
private async createSseSupport(c: Context) {
|
||||||
res.setHeader('Content-Type', 'text/event-stream');
|
return streamSSE(c, async (stream) => {
|
||||||
res.setHeader('Cache-Control', 'no-cache');
|
this.mutex.runExclusive(async () => {
|
||||||
res.setHeader('Connection', 'keep-alive');
|
this.sseClients.push({ context: c, stream });
|
||||||
res.flushHeaders();
|
stream.onAbort(() => {
|
||||||
|
this.sseClients = this.sseClients.filter(({ stream: s }) => s !== stream);
|
||||||
this.sseClients.push(res);
|
|
||||||
req.on('close', () => {
|
|
||||||
this.sseClients = this.sseClients.filter((client) => client !== res);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override onEvent<T extends OB11EmitEventContent>(event: T) {
|
override onEvent<T extends OB11EmitEventContent>(event: T) {
|
||||||
this.sseClients.forEach((res) => {
|
this.mutex.runExclusive(async () => {
|
||||||
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
this.sseClients.forEach(({ stream }) => {
|
||||||
|
stream.writeSSE({ data: JSON.stringify(event) });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,19 +1,17 @@
|
|||||||
import { OB11EmitEventContent, OB11NetworkReloadType } from './index';
|
import { OB11EmitEventContent, OB11NetworkReloadType } from './index';
|
||||||
import express, { Express, NextFunction, Request, Response } from 'express';
|
import { Context, Hono, Next } from 'hono';
|
||||||
import http from 'http';
|
|
||||||
import { NapCatCore } from '@/core';
|
import { NapCatCore } from '@/core';
|
||||||
import { OB11Response } from '@/onebot/action/OneBotAction';
|
import { OB11Response } from '@/onebot/action/OneBotAction';
|
||||||
import { ActionMap } from '@/onebot/action';
|
import { ActionMap } from '@/onebot/action';
|
||||||
import cors from 'cors';
|
import { cors } from 'hono/cors';
|
||||||
import { HttpServerConfig } from '@/onebot/config/config';
|
import { HttpServerConfig } from '@/onebot/config/config';
|
||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
import { IOB11NetworkAdapter } from '@/onebot/network/adapter';
|
import { IOB11NetworkAdapter } from '@/onebot/network/adapter';
|
||||||
import json5 from 'json5';
|
import { serve } from '@hono/node-server';
|
||||||
import { isFinished } from 'on-finished';
|
|
||||||
import typeis from 'type-is';
|
|
||||||
export class OB11HttpServerAdapter extends IOB11NetworkAdapter<HttpServerConfig> {
|
export class OB11HttpServerAdapter extends IOB11NetworkAdapter<HttpServerConfig> {
|
||||||
private app: Express | undefined;
|
private app: Hono | undefined;
|
||||||
private server: http.Server | undefined;
|
private server: ReturnType<typeof serve> | undefined;
|
||||||
|
|
||||||
constructor(name: string, config: HttpServerConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap) {
|
constructor(name: string, config: HttpServerConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap) {
|
||||||
super(name, config, core, obContext, actions);
|
super(name, config, core, obContext, actions);
|
||||||
@@ -30,14 +28,11 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter<HttpServerConfig>
|
|||||||
this.core.context.logger.logError('Cannot open a closed HTTP server');
|
this.core.context.logger.logError('Cannot open a closed HTTP server');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.isEnable) {
|
|
||||||
this.initializeServer();
|
this.initializeServer();
|
||||||
this.isEnable = true;
|
this.isEnable = true;
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.core.context.logger.logError(`[OneBot] [HTTP Server Adapter] Boot Error: ${e}`);
|
this.core.context.logger.logError(`[OneBot] [HTTP Server Adapter] Boot Error: ${e}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async close() {
|
async close() {
|
||||||
@@ -46,99 +41,61 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter<HttpServerConfig>
|
|||||||
this.app = undefined;
|
this.app = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private initializeServer() {
|
private initializeServer() {
|
||||||
this.app = express();
|
this.app = new Hono();
|
||||||
this.server = http.createServer(this.app);
|
|
||||||
|
|
||||||
this.app.use(cors());
|
this.app.use(cors());
|
||||||
this.app.use(express.urlencoded({ extended: true, limit: '5000mb' }));
|
this.app.use(async (c, next) => this.authorize(this.config.token, c, next));
|
||||||
|
this.app.use(async (c) => {
|
||||||
this.app.use((req, res, next) => {
|
await this.handleRequest(c);
|
||||||
if (isFinished(req)) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!typeis.hasBody(req)) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 兼容处理没有带content-type的请求
|
|
||||||
req.headers['content-type'] = 'application/json';
|
|
||||||
let rawData = '';
|
|
||||||
req.on('data', (chunk) => {
|
|
||||||
rawData += chunk;
|
|
||||||
});
|
});
|
||||||
req.on('end', () => {
|
this.server = serve({
|
||||||
try {
|
fetch: this.app.fetch,
|
||||||
req.body = { ...json5.parse(rawData || '{}'), ...req.body };
|
port: this.config.port,
|
||||||
next();
|
|
||||||
} catch {
|
|
||||||
return res.status(400).send('Invalid JSON');
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
req.on('error', () => {
|
|
||||||
return res.status(400).send('Invalid JSON');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//@ts-expect-error authorize
|
|
||||||
this.app.use((req, res, next) => this.authorize(this.config.token, req, res, next));
|
|
||||||
this.app.use(async (req, res) => {
|
|
||||||
await this.handleRequest(req, res);
|
|
||||||
});
|
|
||||||
this.server.listen(this.config.port, () => {
|
|
||||||
this.core.context.logger.log(`[OneBot] [HTTP Server Adapter] Start On Port ${this.config.port}`);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private authorize(token: string | undefined, req: Request, res: Response, next: NextFunction) {
|
private authorize(token: string | undefined, c: Context, next: Next) {
|
||||||
if (!token || token.length == 0) return next();//客户端未设置密钥
|
if (!token || token.length === 0) return next(); // 客户端未设置密钥
|
||||||
const HeaderClientToken = req.headers.authorization?.split('Bearer ').pop() || '';
|
const headerClientToken = c.req.header('authorization')?.split('Bearer ').pop() || '';
|
||||||
const QueryClientToken = req.query['access_token'];
|
const queryClientToken = c.req.query('access_token');
|
||||||
const ClientToken = typeof (QueryClientToken) === 'string' && QueryClientToken !== '' ? QueryClientToken : HeaderClientToken;
|
const clientToken = typeof queryClientToken === 'string' && queryClientToken !== '' ? queryClientToken : headerClientToken;
|
||||||
if (ClientToken === token) {
|
if (clientToken === token) {
|
||||||
return next();
|
return next();
|
||||||
} else {
|
|
||||||
return res.status(403).send(JSON.stringify({ message: 'token verify failed!' }));
|
|
||||||
}
|
}
|
||||||
|
c.status(403);
|
||||||
|
c.json({ message: 'token verify failed!' });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async httpApiRequest(req: Request, res: Response) {
|
async httpApiRequest(c: Context) {
|
||||||
let payload = req.body;
|
const payload = await c.req.json();
|
||||||
if (req.method == 'get') {
|
if (c.req.path === '' || c.req.path === '/') {
|
||||||
payload = req.query;
|
|
||||||
} else if (req.query) {
|
|
||||||
payload = { ...req.body, ...req.query };
|
|
||||||
}
|
|
||||||
if (req.path === '' || req.path === '/') {
|
|
||||||
const hello = OB11Response.ok({});
|
const hello = OB11Response.ok({});
|
||||||
hello.message = 'NapCat4 Is Running';
|
hello.message = 'NapCat4 Is Running';
|
||||||
return res.json(hello);
|
return c.json(hello);
|
||||||
}
|
}
|
||||||
const actionName = req.path.split('/')[1];
|
const actionName = c.req.path.split('/')[1];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const action = this.actions.get(actionName as any);
|
const action = this.actions.get(actionName as any);
|
||||||
if (action) {
|
if (action) {
|
||||||
try {
|
try {
|
||||||
const result = await action.handle(payload, this.name, this.config);
|
const result = await action.handle(payload, this.name, this.config);
|
||||||
return res.json(result);
|
return c.json(result);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return res.json(OB11Response.error((error as Error)?.stack?.toString() || (error as Error)?.message || 'Error Handle', 200));
|
return c.json(OB11Response.error((error as Error)?.stack?.toString() || (error as Error)?.message || 'Error Handle', 200));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return res.json(OB11Response.error('不支持的Api ' + actionName, 200));
|
return c.json(OB11Response.error('不支持的Api ' + actionName, 200));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleRequest(req: Request, res: Response) {
|
async handleRequest(c: Context) {
|
||||||
if (!this.isEnable) {
|
if (!this.isEnable) {
|
||||||
this.core.context.logger.log('[OneBot] [HTTP Server Adapter] Server is closed');
|
this.core.context.logger.log('[OneBot] [HTTP Server Adapter] Server is closed');
|
||||||
res.json(OB11Response.error('Server is closed', 200));
|
c.json(OB11Response.error('Server is closed', 200));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.httpApiRequest(req, res);
|
await this.httpApiRequest(c);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async reload(newConfig: HttpServerConfig) {
|
async reload(newConfig: HttpServerConfig) {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { OB11EmitEventContent, OB11NetworkReloadType } from '@/onebot/network/index';
|
import { OB11EmitEventContent, OB11NetworkReloadType } from '@/onebot/network/index';
|
||||||
import { RawData, WebSocket } from 'ws';
|
import { RawData } from 'ws';
|
||||||
import { OB11HeartbeatEvent } from '@/onebot/event/meta/OB11HeartbeatEvent';
|
import { OB11HeartbeatEvent } from '@/onebot/event/meta/OB11HeartbeatEvent';
|
||||||
import { NapCatCore } from '@/core';
|
import { NapCatCore } from '@/core';
|
||||||
import { ActionName } from '@/onebot/action/router';
|
import { ActionName } from '@/onebot/action/router';
|
||||||
@@ -10,10 +10,12 @@ import { WebsocketClientConfig } from '@/onebot/config/config';
|
|||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
import { IOB11NetworkAdapter } from '@/onebot/network/adapter';
|
import { IOB11NetworkAdapter } from '@/onebot/network/adapter';
|
||||||
import json5 from 'json5';
|
import json5 from 'json5';
|
||||||
|
import { hc } from 'hono/client';
|
||||||
|
|
||||||
export class OB11WebSocketClientAdapter extends IOB11NetworkAdapter<WebsocketClientConfig> {
|
export class OB11WebSocketClientAdapter extends IOB11NetworkAdapter<WebsocketClientConfig> {
|
||||||
private connection: WebSocket | null = null;
|
private connection: WebSocket | null = null;
|
||||||
private heartbeatRef: NodeJS.Timeout | null = null;
|
private heartbeatRef: NodeJS.Timeout | null = null;
|
||||||
|
private client = hc(this.config.url);
|
||||||
|
|
||||||
constructor(name: string, config: WebsocketClientConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap) {
|
constructor(name: string, config: WebsocketClientConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap) {
|
||||||
super(name, config, core, obContext, actions);
|
super(name, config, core, obContext, actions);
|
||||||
@@ -65,37 +67,23 @@ export class OB11WebSocketClientAdapter extends IOB11NetworkAdapter<WebsocketCli
|
|||||||
private async tryConnect() {
|
private async tryConnect() {
|
||||||
if (!this.connection && this.isEnable) {
|
if (!this.connection && this.isEnable) {
|
||||||
let isClosedByError = false;
|
let isClosedByError = false;
|
||||||
|
let wsClientX = this.client['ws']?.$ws(0);
|
||||||
|
if (!wsClientX) throw new Error('WebSocket Client Error');
|
||||||
|
this.connection = wsClientX;
|
||||||
|
|
||||||
this.connection = new WebSocket(this.config.url, {
|
this.connection.addEventListener('open', () => {
|
||||||
maxPayload: 1024 * 1024 * 1024,
|
|
||||||
handshakeTimeout: 2000,
|
|
||||||
perMessageDeflate: false,
|
|
||||||
headers: {
|
|
||||||
'X-Self-ID': this.core.selfInfo.uin,
|
|
||||||
'Authorization': `Bearer ${this.config.token}`,
|
|
||||||
'x-client-role': 'Universal', // 为koishi adpter适配
|
|
||||||
'User-Agent': 'OneBot/11',
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
this.connection.on('ping', () => {
|
|
||||||
this.connection?.pong();
|
|
||||||
});
|
|
||||||
this.connection.on('pong', () => {
|
|
||||||
//this.logger.logDebug('[OneBot] [WebSocket Client] 收到pong');
|
|
||||||
});
|
|
||||||
this.connection.on('open', () => {
|
|
||||||
try {
|
try {
|
||||||
this.connectEvent(this.core);
|
this.connectEvent(this.core);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.logError('[OneBot] [WebSocket Client] 发送连接生命周期失败', e);
|
this.logger.logError('[OneBot] [WebSocket Client] 发送连接生命周期失败', e);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.connection.addEventListener('message', (event) => {
|
||||||
|
this.handleMessage(event.data);
|
||||||
});
|
});
|
||||||
this.connection.on('message', (data) => {
|
|
||||||
this.handleMessage(data);
|
this.connection.addEventListener('close', () => {
|
||||||
});
|
|
||||||
this.connection.once('close', () => {
|
|
||||||
if (!isClosedByError) {
|
if (!isClosedByError) {
|
||||||
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接意外关闭`);
|
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接意外关闭`);
|
||||||
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
||||||
@@ -105,7 +93,8 @@ export class OB11WebSocketClientAdapter extends IOB11NetworkAdapter<WebsocketCli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.connection.on('error', (err) => {
|
|
||||||
|
this.connection.addEventListener('error', (err) => {
|
||||||
isClosedByError = true;
|
isClosedByError = true;
|
||||||
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接错误`, err);
|
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接错误`, err);
|
||||||
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
||||||
@@ -124,6 +113,7 @@ export class OB11WebSocketClientAdapter extends IOB11NetworkAdapter<WebsocketCli
|
|||||||
this.logger.logError('[OneBot] [WebSocket Client] 发送生命周期失败', e);
|
this.logger.logError('[OneBot] [WebSocket Client] 发送生命周期失败', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleMessage(message: RawData) {
|
private async handleMessage(message: RawData) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
let receiveData: { action: typeof ActionName[keyof typeof ActionName], params?: any, echo?: any } = { action: ActionName.Unknown, params: {} };
|
let receiveData: { action: typeof ActionName[keyof typeof ActionName], params?: any, echo?: any } = { action: ActionName.Unknown, params: {} };
|
||||||
@@ -148,6 +138,7 @@ export class OB11WebSocketClientAdapter extends IOB11NetworkAdapter<WebsocketCli
|
|||||||
const retdata = await action.websocketHandle(receiveData.params, echo ?? '', this.name, this.config);
|
const retdata = await action.websocketHandle(receiveData.params, echo ?? '', this.name, this.config);
|
||||||
this.checkStateAndReply<unknown>({ ...retdata });
|
this.checkStateAndReply<unknown>({ ...retdata });
|
||||||
}
|
}
|
||||||
|
|
||||||
async reload(newConfig: WebsocketClientConfig) {
|
async reload(newConfig: WebsocketClientConfig) {
|
||||||
const wasEnabled = this.isEnable;
|
const wasEnabled = this.isEnable;
|
||||||
const oldUrl = this.config.url;
|
const oldUrl = this.config.url;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { OB11EmitEventContent, OB11NetworkReloadType } from './index';
|
import { OB11EmitEventContent, OB11NetworkReloadType } from './index';
|
||||||
import urlParse from 'url';
|
import { Hono } from 'hono';
|
||||||
import { RawData, WebSocket, WebSocketServer } from 'ws';
|
import { RawData, WebSocket, WebSocketServer } from 'ws';
|
||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import { OB11Response } from '@/onebot/action/OneBotAction';
|
import { OB11Response } from '@/onebot/action/OneBotAction';
|
||||||
@@ -12,9 +12,12 @@ import { LifeCycleSubType, OB11LifeCycleEvent } from '@/onebot/event/meta/OB11Li
|
|||||||
import { WebsocketServerConfig } from '@/onebot/config/config';
|
import { WebsocketServerConfig } from '@/onebot/config/config';
|
||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
import { IOB11NetworkAdapter } from '@/onebot/network/adapter';
|
import { IOB11NetworkAdapter } from '@/onebot/network/adapter';
|
||||||
|
import { serve } from '@hono/node-server';
|
||||||
import json5 from 'json5';
|
import json5 from 'json5';
|
||||||
|
|
||||||
export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketServerConfig> {
|
export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketServerConfig> {
|
||||||
|
private app: Hono | undefined;
|
||||||
|
private server: ReturnType<typeof serve> | undefined;
|
||||||
wsServer?: WebSocketServer;
|
wsServer?: WebSocketServer;
|
||||||
wsClients: WebSocket[] = [];
|
wsClients: WebSocket[] = [];
|
||||||
wsClientsMutex = new Mutex();
|
wsClientsMutex = new Mutex();
|
||||||
@@ -25,14 +28,25 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
|||||||
name: string, config: WebsocketServerConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap
|
name: string, config: WebsocketServerConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap
|
||||||
) {
|
) {
|
||||||
super(name, config, core, obContext, actions);
|
super(name, config, core, obContext, actions);
|
||||||
this.wsServer = new WebSocketServer({
|
|
||||||
port: this.config.port,
|
|
||||||
host: this.config.host === '0.0.0.0' ? '' : this.config.host,
|
|
||||||
maxPayload: 1024 * 1024 * 1024,
|
|
||||||
});
|
|
||||||
this.createServer(this.wsServer);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initializeServer() {
|
||||||
|
this.app = new Hono();
|
||||||
|
this.server = serve({
|
||||||
|
fetch: this.app.fetch,
|
||||||
|
port: this.config.port,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.wsServer = new WebSocketServer({ noServer: true });
|
||||||
|
this.server.on('upgrade', (request, socket, head) => {
|
||||||
|
this.wsServer?.handleUpgrade(request, socket, head, (ws) => {
|
||||||
|
this.wsServer?.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.createServer(this.wsServer);
|
||||||
|
}
|
||||||
|
|
||||||
createServer(newServer: WebSocketServer) {
|
createServer(newServer: WebSocketServer) {
|
||||||
newServer.on('connection', async (wsClient, wsReq) => {
|
newServer.on('connection', async (wsClient, wsReq) => {
|
||||||
if (!this.isEnable) {
|
if (!this.isEnable) {
|
||||||
@@ -78,6 +92,7 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
|||||||
});
|
});
|
||||||
}).on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Server Error:', err.message));
|
}).on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Server Error:', err.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
connectEvent(core: NapCatCore, wsClient: WebSocket) {
|
connectEvent(core: NapCatCore, wsClient: WebSocket) {
|
||||||
try {
|
try {
|
||||||
this.checkStateAndReply<unknown>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient);
|
this.checkStateAndReply<unknown>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient);
|
||||||
@@ -99,25 +114,22 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
|||||||
this.logger.logError('[OneBot] [WebSocket Server] Cannot open a opened WebSocket server');
|
this.logger.logError('[OneBot] [WebSocket Server] Cannot open a opened WebSocket server');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const addressInfo = this.wsServer?.address();
|
this.initializeServer();
|
||||||
this.logger.log('[OneBot] [WebSocket Server] Server Started', typeof (addressInfo) === 'string' ? addressInfo : addressInfo?.address + ':' + addressInfo?.port);
|
|
||||||
|
|
||||||
this.isEnable = true;
|
this.isEnable = true;
|
||||||
if (this.config.heartInterval > 0) {
|
if (this.config.heartInterval > 0) {
|
||||||
this.registerHeartBeat();
|
this.registerHeartBeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async close() {
|
async close() {
|
||||||
this.isEnable = false;
|
this.isEnable = false;
|
||||||
|
this.server?.close();
|
||||||
this.wsServer?.close((err) => {
|
this.wsServer?.close((err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.logger.logError('[OneBot] [WebSocket Server] Error closing server:', err.message);
|
this.logger.logError('[OneBot] [WebSocket Server] Error closing server:', err.message);
|
||||||
} else {
|
} else {
|
||||||
this.logger.log('[OneBot] [WebSocket Server] Server Closed');
|
this.logger.log('[OneBot] [WebSocket Server] Server Closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
if (this.heartbeatIntervalId) {
|
if (this.heartbeatIntervalId) {
|
||||||
clearInterval(this.heartbeatIntervalId);
|
clearInterval(this.heartbeatIntervalId);
|
||||||
@@ -146,9 +158,9 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
|||||||
|
|
||||||
private authorize(token: string | undefined, wsClient: WebSocket, wsReq: IncomingMessage) {
|
private authorize(token: string | undefined, wsClient: WebSocket, wsReq: IncomingMessage) {
|
||||||
if (!token || token.length == 0) return;//客户端未设置密钥
|
if (!token || token.length == 0) return;//客户端未设置密钥
|
||||||
const QueryClientToken = urlParse.parse(wsReq?.url || '', true).query['access_token'];
|
const QueryClientToken = new URL(wsReq.url || '', `http://${wsReq.headers.host}`).searchParams.get('access_token');
|
||||||
const HeaderClientToken = wsReq.headers.authorization?.split('Bearer ').pop() || '';
|
const HeaderClientToken = wsReq.headers.authorization?.split('Bearer ').pop() || '';
|
||||||
const ClientToken = typeof (QueryClientToken) === 'string' && QueryClientToken !== '' ? QueryClientToken : HeaderClientToken;
|
const ClientToken = QueryClientToken || HeaderClientToken;
|
||||||
if (ClientToken === token) {
|
if (ClientToken === token) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -203,12 +215,7 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
|||||||
|
|
||||||
if (oldPort !== newConfig.port || oldHost !== newConfig.host) {
|
if (oldPort !== newConfig.port || oldHost !== newConfig.host) {
|
||||||
this.close();
|
this.close();
|
||||||
this.wsServer = new WebSocketServer({
|
this.initializeServer();
|
||||||
port: newConfig.port,
|
|
||||||
host: newConfig.host === '0.0.0.0' ? '' : newConfig.host,
|
|
||||||
maxPayload: 1024 * 1024 * 1024,
|
|
||||||
});
|
|
||||||
this.createServer(this.wsServer);
|
|
||||||
if (newConfig.enable) {
|
if (newConfig.enable) {
|
||||||
this.open();
|
this.open();
|
||||||
}
|
}
|
||||||
@@ -229,4 +236,3 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
|||||||
return OB11NetworkReloadType.Normal;
|
return OB11NetworkReloadType.Normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user