diff --git a/package.json b/package.json index b598bd9e..7acf7d70 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@types/jest": "^29.5.12", "@types/node": "^22.0.0", "@types/qrcode-terminal": "^0.12.2", - "@types/ws": "^8.5.10", + "@types/ws": "^8.5.12", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", "eslint": "^8.57.0", @@ -46,6 +46,7 @@ }, "dependencies": { "ajv": "^8.13.0", + "async-mutex": "^0.5.0", "chalk": "^5.3.0", "commander": "^12.1.0", "cors": "^2.8.5", @@ -59,6 +60,6 @@ "qrcode-terminal": "^0.12.0", "silk-wasm": "^3.6.1", "strtok3": "8.0.1", - "ws": "^8.16.0" + "ws": "^8.18.0" } } diff --git a/src/onebot/action/extends/FetchEmojioLike.ts b/src/onebot/action/extends/FetchEmojiLike.ts similarity index 80% rename from src/onebot/action/extends/FetchEmojioLike.ts rename to src/onebot/action/extends/FetchEmojiLike.ts index 4ae82c28..f61cc5e5 100644 --- a/src/onebot/action/extends/FetchEmojioLike.ts +++ b/src/onebot/action/extends/FetchEmojiLike.ts @@ -3,6 +3,7 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; import { MessageUnique } from '@/common/utils/MessageUnique'; + const SchemaData = { type: 'object', properties: { @@ -18,15 +19,14 @@ const SchemaData = { type Payload = FromSchema; -export class FetchEmojioLike extends BaseAction { - actionName = ActionName.FetchEmojioLike; +export class FetchEmojiLike extends BaseAction { + actionName = ActionName.FetchEmojiLike; PayloadSchema = SchemaData; protected async _handle(payload: Payload) { const NTQQMsgApi = this.CoreContext.getApiContext().MsgApi; const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString())); if(!msgIdPeer) throw new Error('消息不存在'); const msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0]; - const ret = await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer,msg.msgSeq,payload.emojiId,payload.emojiType,payload.count); - return ret; + return await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer, msg.msgSeq, payload.emojiId, payload.emojiType, payload.count); } } diff --git a/src/onebot/action/index.ts b/src/onebot/action/index.ts index 0d80862f..aeba84ef 100644 --- a/src/onebot/action/index.ts +++ b/src/onebot/action/index.ts @@ -35,7 +35,6 @@ import GetImage from './file/GetImage'; import GetRecord from './file/GetRecord'; import { GoCQHTTPMarkMsgAsRead, MarkAllMsgAsRead, MarkGroupMsgAsRead, MarkPrivateMsgAsRead } from './msg/MarkMsgAsRead'; import GoCQHTTPUploadGroupFile from './go-cqhttp/UploadGroupFile'; -import GetGroupAddRequest from '@/onebot/action/extends/GetGroupAddRequest'; import SetQQAvatar from '@/onebot/action/extends/SetQQAvatar'; import GoCQHTTPDownloadFile from './go-cqhttp/DownloadFile'; import GoCQHTTPGetGroupMsgHistory from './go-cqhttp/GetGroupMsgHistory'; @@ -73,13 +72,13 @@ import { GetProfileLike } from './extends/GetProfileLike'; import SetGroupHeader from './extends/SetGroupHeader'; import { FetchCustomFace } from './extends/FetchCustomFace'; import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile'; -import { FetchEmojioLike } from './extends/FetchEmojioLike'; +import { FetchEmojiLike } from './extends/FetchEmojiLike'; import { NapCatCore } from '@/core'; import { NapCatOneBot11Adapter } from '../main'; export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContext: NapCatCore) { const actionHandlers = [ - new FetchEmojioLike(onebotContext,coreContext), + new FetchEmojiLike(onebotContext,coreContext), new GetFile(onebotContext,coreContext), new SetSelfProfile(onebotContext,coreContext), new shareGroupEx(onebotContext,coreContext), diff --git a/src/onebot/action/types.ts b/src/onebot/action/types.ts index 408f933a..ce7d6f6d 100644 --- a/src/onebot/action/types.ts +++ b/src/onebot/action/types.ts @@ -104,5 +104,5 @@ export enum ActionName { FetchCustomFace = 'fetch_custom_face', GOCQHTTP_UploadPrivateFile = 'upload_private_file', TestApi01 = 'test_api_01', - FetchEmojioLike = 'fetch_emoji_like' + FetchEmojiLike = 'fetch_emoji_like' } diff --git a/src/onebot/network/index.ts b/src/onebot/network/index.ts new file mode 100644 index 00000000..673c4e4d --- /dev/null +++ b/src/onebot/network/index.ts @@ -0,0 +1,9 @@ +import BaseAction from '@/onebot/action/BaseAction'; +import { OB11BaseEvent } from '@/onebot/event/OB11BaseEvent'; + +export interface IOB11NetworkAdapter { + registerAction, P, R>(action: T): void; + onEvent(event: T): void; + open(): void | Promise; + close(): void | Promise; +} diff --git a/src/onebot/network/passive-websocket.ts b/src/onebot/network/passive-websocket.ts new file mode 100644 index 00000000..da321a61 --- /dev/null +++ b/src/onebot/network/passive-websocket.ts @@ -0,0 +1,72 @@ +import { IOB11NetworkAdapter } from './index'; +import { OB11BaseEvent } from '@/onebot/event/OB11BaseEvent'; +import BaseAction from '@/onebot/action/BaseAction'; +import { WebSocket, WebSocketServer } from 'ws'; +import { Mutex } from 'async-mutex'; + +export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter { + wsServer: WebSocketServer; + wsClients: WebSocket[] = []; + wsClientsMutex = new Mutex(); + isOpen: boolean = false; + hasBeenClosed: boolean = false; + + private actionMap: Map> = new Map(); + + constructor(ip: string, port: number, token: string) { + this.wsServer = new WebSocketServer({ port: port, host: ip }); + this.wsServer.on('connection', async (wsClient) => { + if (!this.isOpen) { + wsClient.close(); + return; + } + if (token) { + const incomingToken = wsClient.url.split('?')[1]?.split('=')[1]; + if (incomingToken !== token) { + wsClient.close(); + return; + } + } + wsClient.on('message', (message) => { + // TODO: extract action name and payload from the message, then call the corresponding action. + }); + wsClient.once('close', () => { + this.wsClientsMutex.runExclusive(async () => { + const index = this.wsClients.indexOf(wsClient); + if (index !== -1) { + this.wsClients.splice(index, 1); + } + }); + }); + await this.wsClientsMutex.runExclusive(async () => { + this.wsClients.push(wsClient); + }); + }); + } + + registerAction, P, R>(action: T) { + this.actionMap.set(action.actionName, action); + } + + onEvent(event: T) { + this.wsClientsMutex.runExclusive(async () => { + this.wsClients.forEach((wsClient) => { + // wsClient.send(JSON.stringify(event)); + // TODO: wrap the event, and send the wrapped to the client. + }); + }); + } + + open() { + if (this.hasBeenClosed) { + throw new Error('Cannot open a closed WebSocket server'); + } + this.isOpen = true; + } + + async close() { + this.isOpen = false; + this.hasBeenClosed = true; + this.wsServer.close(); + } +}