diff --git a/package.json b/package.json index 446b9961..95af5bdc 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "devDependencies": { "@babel/preset-typescript": "^7.24.7", "@log4js-node/log4js-api": "^1.0.2", + "@napneko/nap-proto-core": "^0.0.2", "@protobuf-ts/runtime": "^2.9.4", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/src/common/lru-cache.ts b/src/common/lru-cache.ts index 0e537b6a..39c879f0 100644 --- a/src/common/lru-cache.ts +++ b/src/common/lru-cache.ts @@ -1,6 +1,6 @@ export class LRUCache { private capacity: number; - private cache: Map; + public cache: Map; constructor(capacity: number) { this.capacity = capacity; diff --git a/src/core/apis/packet.ts b/src/core/apis/packet.ts index f112da87..beb47362 100644 --- a/src/core/apis/packet.ts +++ b/src/core/apis/packet.ts @@ -2,10 +2,9 @@ import * as crypto from 'crypto'; import * as os from 'os'; import { ChatType, InstanceContext, NapCatCore } from '..'; import offset from '@/core/external/offset.json'; -import { PacketClient, RecvPacketData } from '@/core/packet/client'; import { PacketSession } from "@/core/packet/session"; import { OidbPacket, PacketHexStr } from "@/core/packet/packer"; -import { NapProtoEncodeStructType, NapProtoDecodeStructType, NapProtoMsg } from '@/core/packet/proto/NapProto'; +import { NapProtoMsg, NapProtoEncodeStructType, NapProtoDecodeStructType } from "@napneko/nap-proto-core"; import { OidbSvcTrpcTcp0X9067_202_Rsp_Body } from '@/core/packet/proto/oidb/Oidb.0x9067_202'; import { OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp } from '@/core/packet/proto/oidb/OidbBase'; import { OidbSvcTrpcTcp0XFE1_2RSP } from '@/core/packet/proto/oidb/Oidb.0XFE1_2'; @@ -25,6 +24,7 @@ import { AIVoiceChatType, AIVoiceItemList } from "@/core/packet/entities/aiChat" import { OidbSvcTrpcTcp0X929B_0Resp, OidbSvcTrpcTcp0X929D_0Resp } from "@/core/packet/proto/oidb/Oidb.0x929"; import { IndexNode, MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq"; import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp"; +import { RecvPacketData } from "@/core/packet/client/client"; interface OffsetType { @@ -40,7 +40,6 @@ export class NTQQPacketApi { context: InstanceContext; core: NapCatCore; logger: LogWrapper; - serverUrl: string | undefined; qqVersion: string | undefined; packetSession: PacketSession | undefined; @@ -49,32 +48,27 @@ export class NTQQPacketApi { this.core = core; this.logger = core.context.logger; this.packetSession = undefined; - const config = this.core.configLoader.configData; - if (config && config.packetServer && config.packetServer.length > 0) { - const serverUrl = this.core.configLoader.configData.packetServer ?? '127.0.0.1:8086'; - this.InitSendPacket(serverUrl, this.context.basicInfoWrapper.getFullQQVesion()) - .then() - .catch(this.core.context.logger.logError.bind(this.core.context.logger)); - } else { - this.core.context.logger.logWarn('PacketServer未配置,NapCat.Packet将不会加载!'); - } + this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion()) + .then() + .catch(this.core.context.logger.logError.bind(this.core.context.logger)); } get available(): boolean { return this.packetSession?.client.available ?? false; } - async InitSendPacket(serverUrl: string, qqversion: string) { - this.serverUrl = serverUrl; + async InitSendPacket(qqversion: string) { this.qqVersion = qqversion; - const offsetTable: OffsetType = offset; - const table = offsetTable[qqversion + '-' + os.arch()]; + const table = typedOffset[qqversion + '-' + os.arch()]; if (!table) { - this.logger.logError('PacketServer Offset table not found for QQVersion: ', qqversion + '-' + os.arch()); + this.logger.logError('[Core] [Packet] PacketServer Offset table not found for QQVersion: ', qqversion + '-' + os.arch()); return false; } - const url = 'ws://' + this.serverUrl + '/ws'; - this.packetSession = new PacketSession(this.core.context.logger, new PacketClient(url, this.core)); + if (this.core.configLoader.configData.packetBackend === 'disable') { + this.logger.logWarn('[Core] [Packet] 已禁用Packet后端,NapCat.Packet将不会加载!'); + return false; + } + this.packetSession = new PacketSession(this.core); const cb = () => { if (this.packetSession && this.packetSession.client) { this.packetSession.client.init(process.pid, table.recv, table.send).then().catch(this.logger.logError.bind(this.logger)); diff --git a/src/core/external/napcat.json b/src/core/external/napcat.json index 92432677..6abfe1e6 100644 --- a/src/core/external/napcat.json +++ b/src/core/external/napcat.json @@ -3,5 +3,6 @@ "consoleLog": true, "fileLogLevel": "debug", "consoleLogLevel": "info", + "packetBackend": "auto", "packetServer": "" -} \ No newline at end of file +} diff --git a/src/core/packet/client.ts b/src/core/packet/client.ts deleted file mode 100644 index 49400798..00000000 --- a/src/core/packet/client.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { LogWrapper } from "@/common/log"; -import { LRUCache } from "@/common/lru-cache"; -import WebSocket, { Data } from "ws"; -import crypto, { createHash } from "crypto"; -import { NapCatCore } from "@/core"; -import { OidbPacket, PacketHexStr } from "@/core/packet/packer"; - -export interface RecvPacket { - type: string, // 仅recv - trace_id_md5?: string, - data: RecvPacketData -} - -export interface RecvPacketData { - seq: number - cmd: string - hex_data: string -} - -export class PacketClient { - private websocket: WebSocket | undefined; - private isConnected: boolean = false; - private reconnectAttempts: number = 0; - private readonly maxReconnectAttempts: number = 60;//现在暂时不可配置 - private readonly cb = new LRUCache Promise>(500); // trace_id-type callback - private readonly clientUrl: string = ''; - readonly napCatCore: NapCatCore; - private readonly logger: LogWrapper; - - constructor(url: string, core: NapCatCore) { - this.clientUrl = url; - this.napCatCore = core; - this.logger = core.context.logger; - } - - get available(): boolean { - return this.isConnected && this.websocket !== undefined; - } - - private randText(len: number) { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < len; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; - } - - connect(cb: any): Promise { - return new Promise((resolve, reject) => { - //this.logger.log.bind(this.logger)(`[Core] [Packet Server] Attempting to connect to ${this.clientUrl}`); - this.websocket = new WebSocket(this.clientUrl); - this.websocket.on('error', (err) => { }/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/); - - this.websocket.onopen = () => { - this.isConnected = true; - this.reconnectAttempts = 0; - this.logger.log.bind(this.logger)(`[Core] [Packet Server] 已连接到 ${this.clientUrl}`); - cb(); - resolve(); - }; - - this.websocket.onerror = (error) => { - //this.logger.logError.bind(this.logger)(`WebSocket error: ${error}`); - reject(new Error(`${error.message}`)); - }; - - this.websocket.onmessage = (event) => { - // const message = JSON.parse(event.data.toString()); - // console.log("Received message:", message); - this.handleMessage(event.data).then().catch(); - }; - - this.websocket.onclose = () => { - this.isConnected = false; - //this.logger.logWarn.bind(this.logger)(`[Core] [Packet Server] Disconnected from ${this.clientUrl}`); - this.attemptReconnect(cb); - }; - }); - } - - private attemptReconnect(cb: any): void { - try { - if (this.reconnectAttempts < this.maxReconnectAttempts) { - this.reconnectAttempts++; - setTimeout(() => { - this.connect(cb).catch((error) => { - this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 尝试重连失败:${error.message}`); - }); - }, 5000 * this.reconnectAttempts); - } else { - this.logger.logError.bind(this.logger)(`[Core] [Packet Server] ${this.clientUrl} 已达到最大重连次数!`); - } - } catch (error: any) { - this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 重连时出错: ${error.message}`); - } - } - - private async registerCallback(trace_id: string, type: string, callback: (json: RecvPacketData) => Promise): Promise { - this.cb.put(createHash('md5').update(trace_id).digest('hex') + type, callback); - } - - async init(pid: number, recv: string, send: string): Promise { - if (!this.isConnected || !this.websocket) { - throw new Error("WebSocket is not connected"); - } - const initMessage = { - action: 'init', - pid: pid, - recv: recv, - send: send - }; - this.websocket.send(JSON.stringify(initMessage)); - } - - private async sendCommand(cmd: string, data: string, trace_id: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => { - }): Promise { - return new Promise((resolve, reject) => { - if (!this.isConnected || !this.websocket) { - throw new Error("WebSocket is not connected"); - } - const commandMessage = { - action: 'send', - cmd: cmd, - data: data, - trace_id: trace_id - }; - this.websocket.send(JSON.stringify(commandMessage)); - if (rsp) { - this.registerCallback(trace_id, 'recv', async (json: RecvPacketData) => { - clearTimeout(timeoutHandle); - resolve(json); - }); - } - this.registerCallback(trace_id, 'send', async (json: RecvPacketData) => { - sendcb(json); - if (!rsp) { - clearTimeout(timeoutHandle); - resolve(json); - } - }); - const timeoutHandle = setTimeout(() => { - reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with trace_id ${trace_id}`)); - }, timeout); - }); - } - - private async handleMessage(message: Data): Promise { - try { - const json: RecvPacket = JSON.parse(message.toString()); - const trace_id_md5 = json.trace_id_md5; - const action = json?.type ?? 'init'; - const event = this.cb.get(trace_id_md5 + action); - if (event) { - await event(json.data); - } - //console.log("Received message:", json); - } catch (error) { - this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`); - } - } - - async sendPacket(cmd: string, data: PacketHexStr, rsp = false): Promise { - // wtfk tx - // 校验失败和异常 可能返回undefined - return new Promise((resolve, reject) => { - if (!this.available) { - this.logger.logError('NapCat.Packet 未初始化!'); - return undefined; - } - const md5 = crypto.createHash('md5').update(data).digest('hex'); - const trace_id = (this.randText(4) + md5 + data).slice(0, data.length / 2); - this.sendCommand(cmd, data, trace_id, rsp, 20000, async () => { - // await sleep(10); - await this.napCatCore.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id); - }).then((res) => resolve(res)).catch((e: Error) => reject(e)); - }); - } - - async sendOidbPacket(pkt: OidbPacket, rsp = false): Promise { - return this.sendPacket(pkt.cmd, pkt.data, rsp); - } -} diff --git a/src/core/packet/native.ts b/src/core/packet/client/client.ts similarity index 53% rename from src/core/packet/native.ts rename to src/core/packet/client/client.ts index d5e16cb2..c8aac93e 100644 --- a/src/core/packet/native.ts +++ b/src/core/packet/client/client.ts @@ -1,35 +1,33 @@ -import { LogWrapper } from "@/common/log"; import { LRUCache } from "@/common/lru-cache"; -import crypto, { createHash } from "crypto"; import { NapCatCore } from "@/core"; +import { LogWrapper } from "@/common/log"; +import crypto, { createHash } from "crypto"; import { OidbPacket, PacketHexStr } from "@/core/packet/packer"; -import path, { dirname } from "path"; -import { fileURLToPath } from "url"; -import fs from "fs"; -import { constants } from "os"; -import { console } from "inspector"; +import { NapCatConfig } from "@/core/helper/config"; -export interface RecvPacketData { - seq: number; - cmd: string; - hex_data: string; +export interface RecvPacket { + type: string, // 仅recv + trace_id_md5?: string, + data: RecvPacketData } -export class NativePacketClient { - private isInit: boolean = false; - private readonly cb = new LRUCache Promise>(500); // trace_id-type callback - readonly napCatCore: NapCatCore; - private readonly logger: LogWrapper; - private supportedPlatforms = ['win32.x64']; - private MoeHooExport: any = { exports: {} }; +export interface RecvPacketData { + seq: number + cmd: string + hex_data: string +} - constructor(core: NapCatCore) { +export abstract class PacketClient { + readonly napCatCore: NapCatCore; + protected readonly logger: LogWrapper; + protected readonly cb = new LRUCache Promise>(500); // trace_id-type callback + protected isAvailable: boolean = false; + protected config: NapCatConfig; + + protected constructor(core: NapCatCore) { this.napCatCore = core; this.logger = core.context.logger; - } - - get available(): boolean { - return this.isInit; + this.config = core.configLoader.configData; } private randText(len: number): string { @@ -41,55 +39,32 @@ export class NativePacketClient { return text; } + get available(): boolean { + return this.isAvailable; + } + + abstract check(core: NapCatCore): boolean; + + abstract init(pid: number, recv: string, send: string): Promise; + + abstract connect(cb: () => void): Promise; + + abstract sendCommandImpl(cmd: string, data: string, trace_id: string): void; + private async registerCallback(trace_id: string, type: string, callback: (json: RecvPacketData) => Promise): Promise { this.cb.put(createHash('md5').update(trace_id).digest('hex') + type, callback); + console.log(this.cb.cache); } - async init(pid: number, recv: string, send: string): Promise { - const platform = process.platform + '.' + process.arch; - if (!this.supportedPlatforms.includes(platform)) { - throw new Error(`Unsupported platform: ${platform}`); - } - - const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/moehoo.' + platform + '.node'); - if (!fs.existsSync(moehoo_path)) { - throw new Error(`moehoo.${platform}.node not found`); - } - - process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY); - this.MoeHooExport.exports.InitHook(pid, recv, send, (type: number, uin: string, seq: number, cmd: string, hex_data: string) => { - const callback = this.cb.get(createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex') + (type === 0 ? 'send' : 'recv')); - if (callback) { - callback({ seq, cmd, hex_data }); - } else { - this.logger.logError(`Callback not found for hex_data: ${hex_data}`); - } - console.log('type:', type, 'uin:', uin, 'seq:', seq, 'cmd:', cmd, 'hex_data:', hex_data); - }); - } - - private async sendCommand( - cmd: string, - data: string, - trace_id: string, - rsp: boolean = false, - timeout: number = 20000, - sendcb: (json: RecvPacketData) => void = () => { } - ): Promise { + private async sendCommand(cmd: string, data: string, trace_id: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => { + }): Promise { return new Promise((resolve, reject) => { - if (!this.available) { - throw new Error("MoeHoo is not Init"); - } - - this.MoeHooExport.exports.SendPacket(cmd, data, crypto.createHash('md5').update(trace_id).digest('hex')); - if (rsp) { this.registerCallback(trace_id, 'recv', async (json: RecvPacketData) => { clearTimeout(timeoutHandle); resolve(json); }); } - this.registerCallback(trace_id, 'send', async (json: RecvPacketData) => { sendcb(json); if (!rsp) { @@ -97,7 +72,7 @@ export class NativePacketClient { resolve(json); } }); - + this.sendCommandImpl(cmd, data, trace_id); const timeoutHandle = setTimeout(() => { reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with trace_id ${trace_id}`)); }, timeout); @@ -115,6 +90,7 @@ export class NativePacketClient { const trace_id = (this.randText(4) + md5 + data).slice(0, data.length / 2); this.sendCommand(cmd, data, trace_id, rsp, 20000, async () => { + //console.log('sendPacket:', cmd, data, trace_id); await this.napCatCore.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id); }).then((res) => resolve(res)).catch((e: Error) => reject(e)); }); @@ -123,4 +99,4 @@ export class NativePacketClient { async sendOidbPacket(pkt: OidbPacket, rsp = false): Promise { return this.sendPacket(pkt.cmd, pkt.data, rsp); } -} \ No newline at end of file +} diff --git a/src/core/packet/client/nativeClient.ts b/src/core/packet/client/nativeClient.ts new file mode 100644 index 00000000..a0efae6e --- /dev/null +++ b/src/core/packet/client/nativeClient.ts @@ -0,0 +1,82 @@ +import crypto, { createHash } from "crypto"; +import { NapCatCore } from "@/core"; +import path, { dirname } from "path"; +import { fileURLToPath } from "url"; +import fs from "fs"; +import { PacketClient } from "@/core/packet/client/client"; +import { constants } from "node:os"; +import { LRUCache } from "@/common/lru-cache"; +//0 send 1recv +export interface NativePacketExportType { + InitHook?: (recv: string, send: string, callback: (type: number, uin: string, cmd: string, seq: number, hex_data: string) => void) => boolean; + SendPacket?: (cmd: string, data: string, trace_id: string) => void; +} +export class NativePacketClient extends PacketClient { + private readonly supportedPlatforms = ['win32.x64']; + private MoeHooExport: { exports: NativePacketExportType } = { exports: {} }; + private sendEvent = new LRUCache(500);//seq->trace_id + constructor(core: NapCatCore) { + super(core); + } + + get available(): boolean { + return this.isAvailable; + } + + check(): boolean { + const platform = process.platform + '.' + process.arch; + if (!this.supportedPlatforms.includes(platform)) { + this.logger.logWarn(`[Core] [Packet:Native] 不支持的平台: ${platform}`); + return false; + } + const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node'); + if (!fs.existsSync(moehoo_path)) { + this.logger.logWarn(`[Core] [Packet:Native] 缺失运行时文件: ${moehoo_path}`); + return false; + } + return true; + } + + async init(pid: number, recv: string, send: string): Promise { + const platform = process.platform + '.' + process.arch; + const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node'); + process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY); + console.log('MoeHooExport:', this.MoeHooExport); + console.log('recv:', recv, 'send:',); + this.MoeHooExport.exports.InitHook?.(send, recv, (type: number, uin: string, cmd: string, seq: number, hex_data: string) => { + const trace_id = createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex'); + if (type === 0 && this.cb.get(trace_id + 'recv')) { + //此时为send 提取seq + this.sendEvent.put(seq, trace_id); + } + if (type === 1 && this.sendEvent.get(seq)) { + //此时为recv 调用callback + const trace_id = this.sendEvent.get(seq); + const callback = this.cb.get(trace_id + 'recv'); + console.log('callback:', callback, trace_id); + callback?.({ seq, cmd, hex_data }); + } + + // const callback = this.cb.get(createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex') + (type === 0 ? 'send' : 'recv')); + // if (callback) { + // callback({ seq, cmd, hex_data }); + // } else { + // this.logger.logError(`Callback not found for hex_data: ${hex_data}`); + // } + //console.log('type:', type, 'cmd:', cmd, 'trace_id:', trace_id); + }); + this.isAvailable = true; + } + + sendCommandImpl(cmd: string, data: string, trace_id: string): void { + const trace_id_md5 = createHash('md5').update(trace_id).digest('hex'); + //console.log('sendCommandImpl:', cmd, data, trace_id_md5); + this.MoeHooExport.exports.SendPacket?.(cmd, data, trace_id_md5); + this.cb.get(trace_id_md5 + 'send')?.({ seq: 0, cmd, hex_data: '' }); + } + + connect(cb: () => void): Promise { + cb(); + return Promise.resolve(); + } +} diff --git a/src/core/packet/client/wsClient.ts b/src/core/packet/client/wsClient.ts new file mode 100644 index 00000000..0111018a --- /dev/null +++ b/src/core/packet/client/wsClient.ts @@ -0,0 +1,112 @@ +import { Data, WebSocket } from "ws"; +import { NapCatCore } from "@/core"; +import { PacketClient, RecvPacket } from "@/core/packet/client/client"; + +export class wsPacketClient extends PacketClient { + private websocket: WebSocket | undefined; + private reconnectAttempts: number = 0; + private readonly maxReconnectAttempts: number = 60; // 现在暂时不可配置 + private readonly clientUrl: string | null = null; + private clientUrlWrap: (url: string) => string = (url: string) => `ws://${url}/ws`; + + constructor(core: NapCatCore) { + super(core); + this.clientUrl = this.config.packetServer ? this.clientUrlWrap( this.config.packetServer) : null; + } + + check(): boolean { + if (!this.clientUrl) { + this.logger.logWarn(`[Core] [Packet:Server] 未配置服务器地址`); + return false; + } + return true; + } + + connect(cb: () => void): Promise { + return new Promise((resolve, reject) => { + //this.logger.log.bind(this.logger)(`[Core] [Packet Server] Attempting to connect to ${this.clientUrl}`); + this.websocket = new WebSocket(this.clientUrl!); + this.websocket.on('error', (err) => { }/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/); + + this.websocket.onopen = () => { + this.isAvailable = true; + this.reconnectAttempts = 0; + this.logger.log.bind(this.logger)(`[Core] [Packet:Server] 已连接到 ${this.clientUrl}`); + cb(); + resolve(); + }; + + this.websocket.onerror = (error) => { + //this.logger.logError.bind(this.logger)(`WebSocket error: ${error}`); + reject(new Error(`${error.message}`)); + }; + + this.websocket.onmessage = (event) => { + // const message = JSON.parse(event.data.toString()); + // console.log("Received message:", message); + this.handleMessage(event.data).then().catch(); + }; + + this.websocket.onclose = () => { + this.isAvailable = false; + //this.logger.logWarn.bind(this.logger)(`[Core] [Packet Server] Disconnected from ${this.clientUrl}`); + this.attemptReconnect(cb); + }; + }); + } + + private attemptReconnect(cb: any): void { + try { + if (this.reconnectAttempts < this.maxReconnectAttempts) { + this.reconnectAttempts++; + setTimeout(() => { + this.connect(cb).catch((error) => { + this.logger.logError.bind(this.logger)(`[Core] [Packet:Server] 尝试重连失败:${error.message}`); + }); + }, 5000 * this.reconnectAttempts); + } else { + this.logger.logError.bind(this.logger)(`[Core] [Packet:Server] ${this.clientUrl} 已达到最大重连次数!`); + } + } catch (error: any) { + this.logger.logError.bind(this.logger)(`[Core] [Packet:Server] 重连时出错: ${error.message}`); + } + } + + async init(pid: number, recv: string, send: string): Promise { + if (!this.isAvailable || !this.websocket) { + throw new Error("WebSocket is not connected"); + } + const initMessage = { + action: 'init', + pid: pid, + recv: recv, + send: send + }; + this.websocket.send(JSON.stringify(initMessage)); + } + + sendCommandImpl(cmd: string, data: string, trace_id: string) : void { + const commandMessage = { + action: 'send', + cmd: cmd, + data: data, + trace_id: trace_id + }; + this.websocket!.send(JSON.stringify(commandMessage)); + } + + private async handleMessage(message: Data): Promise { + try { + const json: RecvPacket = JSON.parse(message.toString()); + const trace_id_md5 = json.trace_id_md5; + const action = json?.type ?? 'init'; + const event = this.cb.get(trace_id_md5 + action); + if (event) { + await event(json.data); + } + //console.log("Received message:", json); + } catch (error) { + this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`); + } + } +} diff --git a/src/core/packet/highway/session.ts b/src/core/packet/highway/session.ts index 997c5aa9..8cc534d3 100644 --- a/src/core/packet/highway/session.ts +++ b/src/core/packet/highway/session.ts @@ -1,9 +1,8 @@ import * as fs from "node:fs"; import { ChatType, Peer } from "@/core"; import { LogWrapper } from "@/common/log"; -import { PacketClient } from "@/core/packet/client"; import { PacketPacker } from "@/core/packet/packer"; -import { NapProtoMsg } from "@/core/packet/proto/NapProto"; +import { NapProtoMsg } from "@napneko/nap-proto-core"; import { HttpConn0x6ff_501Response } from "@/core/packet/proto/action/action"; import { PacketHighwayClient } from "@/core/packet/highway/client"; import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp"; @@ -19,6 +18,7 @@ import { int32ip2str, oidbIpv4s2HighwayIpv4s } from "@/core/packet/highway/utils import { calculateSha1, calculateSha1StreamBytes, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash"; import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6"; import { OidbSvcTrpcTcp0XE37_800Response, OidbSvcTrpcTcp0XE37Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800"; +import { PacketClient } from "@/core/packet/client/client"; export const BlockSize = 1024 * 1024; @@ -59,7 +59,7 @@ export class PacketHighwaySession { private async checkAvailable() { if (!this.packetClient.available) { - throw new Error('packetServer不可用,请参照文档 https://napneko.github.io/config/advanced 检查packetServer状态或进行配置'); + throw new Error('packetBackend不可用,请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置!'); } if (this.sig.sigSession === null || this.sig.sessionKey === null) { this.logger.logWarn('[Highway] sigSession or sessionKey not available!'); diff --git a/src/core/packet/highway/uploader.ts b/src/core/packet/highway/uploader.ts index 49ee5fe7..34822df4 100644 --- a/src/core/packet/highway/uploader.ts +++ b/src/core/packet/highway/uploader.ts @@ -4,7 +4,7 @@ import * as http from "node:http"; import * as stream from "node:stream"; import { LogWrapper } from "@/common/log"; import * as tea from "@/core/packet/utils/crypto/tea"; -import { NapProtoMsg } from "@/core/packet/proto/NapProto"; +import { NapProtoMsg } from "@napneko/nap-proto-core"; import { ReqDataHighwayHead, RespDataHighwayHead } from "@/core/packet/proto/highway/highway"; import { BlockSize } from "@/core/packet/highway/session"; import { PacketHighwayTrans } from "@/core/packet/highway/client"; diff --git a/src/core/packet/highway/utils.ts b/src/core/packet/highway/utils.ts index e786afbd..383155d3 100644 --- a/src/core/packet/highway/utils.ts +++ b/src/core/packet/highway/utils.ts @@ -1,4 +1,4 @@ -import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto"; +import { NapProtoEncodeStructType } from "@napneko/nap-proto-core"; import { IPv4 } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp"; import { NTHighwayIPv4 } from "@/core/packet/proto/highway/highway"; diff --git a/src/core/packet/message/builder.ts b/src/core/packet/message/builder.ts index 63f92593..12fa374b 100644 --- a/src/core/packet/message/builder.ts +++ b/src/core/packet/message/builder.ts @@ -1,6 +1,6 @@ import * as crypto from "crypto"; import { PushMsgBody } from "@/core/packet/proto/message/message"; -import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto"; +import { NapProtoEncodeStructType } from "@napneko/nap-proto-core"; import { LogWrapper } from "@/common/log"; import { PacketMsg, PacketSendMsgElement } from "@/core/packet/message/message"; import { IPacketMsgElement, PacketMsgTextElement } from "@/core/packet/message/element"; diff --git a/src/core/packet/message/element.ts b/src/core/packet/message/element.ts index 6086fb31..9deb598d 100644 --- a/src/core/packet/message/element.ts +++ b/src/core/packet/message/element.ts @@ -1,5 +1,5 @@ import * as zlib from "node:zlib"; -import { NapProtoEncodeStructType, NapProtoMsg } from "@/core/packet/proto/NapProto"; +import { NapProtoEncodeStructType, NapProtoMsg } from "@napneko/nap-proto-core"; import { CustomFace, Elem, diff --git a/src/core/packet/packer.ts b/src/core/packet/packer.ts index 848742a4..039cf11e 100644 --- a/src/core/packet/packer.ts +++ b/src/core/packet/packer.ts @@ -1,7 +1,7 @@ import * as zlib from "node:zlib"; import * as crypto from "node:crypto"; import { computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash"; -import { NapProtoEncodeStructType, NapProtoMsg } from "@/core/packet/proto/NapProto"; +import { NapProtoEncodeStructType, NapProtoMsg } from "@napneko/nap-proto-core"; import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase"; import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202"; import { OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body } from "@/core/packet/proto/oidb/Oidb.0x8FC_2"; @@ -22,7 +22,6 @@ import { PacketMsg } from "@/core/packet/message/message"; import { OidbSvcTrpcTcp0x6D6 } from "@/core/packet/proto/oidb/Oidb.0x6D6"; import { OidbSvcTrpcTcp0XE37_1200 } from "@/core/packet/proto/oidb/Oidb.0xE37_1200"; import { PacketMsgConverter } from "@/core/packet/message/converter"; -import { PacketClient } from "@/core/packet/client"; import { OidbSvcTrpcTcp0XE37_1700 } from "@/core/packet/proto/oidb/Oidb.0xE37_1700"; import { OidbSvcTrpcTcp0XE37_800 } from "@/core/packet/proto/oidb/Oidb.0XE37_800"; import { OidbSvcTrpcTcp0XEB7 } from "./proto/oidb/Oidb.0xEB7"; @@ -30,6 +29,7 @@ import { MiniAppReqParams } from "@/core/packet/entities/miniApp"; import { MiniAppAdaptShareInfoReq } from "@/core/packet/proto/action/miniAppAdaptShareInfo"; import { AIVoiceChatType } from "@/core/packet/entities/aiChat"; import { OidbSvcTrpcTcp0X929B_0, OidbSvcTrpcTcp0X929D_0 } from "@/core/packet/proto/oidb/Oidb.0x929"; +import { PacketClient } from "@/core/packet/client/client"; export type PacketHexStr = string & { readonly hexNya: unique symbol }; diff --git a/src/core/packet/proto/NapProto.ts b/src/core/packet/proto/NapProto.ts deleted file mode 100644 index 06593487..00000000 --- a/src/core/packet/proto/NapProto.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { MessageType, PartialMessage, RepeatType, ScalarType } from '@protobuf-ts/runtime'; -import { PartialFieldInfo } from "@protobuf-ts/runtime/build/types/reflection-info"; - -type LowerCamelCase = CamelCaseHelper; - -type CamelCaseHelper< - S extends string, - CapNext extends boolean, - IsFirstChar extends boolean -> = S extends `${infer F}${infer R}` - ? F extends '_' - ? CamelCaseHelper - : F extends `${number}` - ? `${F}${CamelCaseHelper}` - : CapNext extends true - ? `${Uppercase}${CamelCaseHelper}` - : IsFirstChar extends true - ? `${Lowercase}${CamelCaseHelper}` - : `${F}${CamelCaseHelper}` - : ''; - -type ScalarTypeToTsType = - T extends ScalarType.DOUBLE | ScalarType.FLOAT | ScalarType.INT32 | ScalarType.FIXED32 | ScalarType.UINT32 | ScalarType.SFIXED32 | ScalarType.SINT32 ? number : - T extends ScalarType.INT64 | ScalarType.UINT64 | ScalarType.FIXED64 | ScalarType.SFIXED64 | ScalarType.SINT64 ? bigint : - T extends ScalarType.BOOL ? boolean : - T extends ScalarType.STRING ? string : - T extends ScalarType.BYTES ? Uint8Array : - never; - -interface BaseProtoFieldType { - kind: 'scalar' | 'message'; - no: number; - type: T; - optional: O; - repeat: R; -} - -interface ScalarProtoFieldType extends BaseProtoFieldType { - kind: 'scalar'; -} - -interface MessageProtoFieldType ProtoMessageType, O extends boolean, R extends O extends true ? false : boolean> extends BaseProtoFieldType { - kind: 'message'; -} - -type ProtoFieldType = - | ScalarProtoFieldType - | MessageProtoFieldType<() => ProtoMessageType, boolean, boolean>; - -type ProtoMessageType = { - [key: string]: ProtoFieldType; -}; - -export function ProtoField(no: number, type: T, optional?: O, repeat?: R): ScalarProtoFieldType; -export function ProtoField ProtoMessageType, O extends boolean = false, R extends O extends true ? false : boolean = false>(no: number, type: T, optional?: O, repeat?: R): MessageProtoFieldType; -export function ProtoField(no: number, type: ScalarType | (() => ProtoMessageType), optional?: boolean, repeat?: boolean): ProtoFieldType { - if (typeof type === 'function') { - return { kind: 'message', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false }; - } else { - return { kind: 'scalar', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false }; - } -} - -type ProtoFieldReturnType = NonNullable extends ScalarProtoFieldType - ? ScalarTypeToTsType - : T extends NonNullable> - ? NonNullable, E>> - : never; - -type RequiredFieldsBaseType = { - [K in keyof T as T[K] extends { optional: true } ? never : LowerCamelCase]: - T[K] extends { repeat: true } - ? ProtoFieldReturnType[] - : ProtoFieldReturnType -} - -type OptionalFieldsBaseType = { - [K in keyof T as T[K] extends { optional: true } ? LowerCamelCase : never]?: - T[K] extends { repeat: true } - ? ProtoFieldReturnType[] - : ProtoFieldReturnType -} - -type RequiredFieldsType = E extends true ? Partial> : RequiredFieldsBaseType; - -type OptionalFieldsType = E extends true ? Partial> : OptionalFieldsBaseType; - -type NapProtoStructType = RequiredFieldsType & OptionalFieldsType; - -export type NapProtoEncodeStructType = NapProtoStructType; - -export type NapProtoDecodeStructType = NapProtoStructType; - -class NapProtoRealMsg { - private readonly _field: PartialFieldInfo[]; - private readonly _proto_msg: MessageType>; - private static cache = new WeakMap>(); - - private constructor(fields: T) { - this._field = Object.keys(fields).map(key => { - const field = fields[key]; - if (field.kind === 'scalar') { - const repeatType = field.repeat - ? [ScalarType.STRING, ScalarType.BYTES].includes(field.type) - ? RepeatType.UNPACKED - : RepeatType.PACKED - : RepeatType.NO; - return { - no: field.no, - name: key, - kind: 'scalar', - T: field.type, - opt: field.optional, - repeat: repeatType, - }; - } else if (field.kind === 'message') { - return { - no: field.no, - name: key, - kind: 'message', - repeat: field.repeat ? RepeatType.PACKED : RepeatType.NO, - T: () => NapProtoRealMsg.getInstance(field.type())._proto_msg, - }; - } - }) as PartialFieldInfo[]; - this._proto_msg = new MessageType>('nya', this._field); - } - - static getInstance(fields: T): NapProtoRealMsg { - let instance = this.cache.get(fields); - if (!instance) { - instance = new NapProtoRealMsg(fields); - this.cache.set(fields, instance); - } - return instance; - } - - encode(data: NapProtoEncodeStructType): Uint8Array { - return this._proto_msg.toBinary(this._proto_msg.create(data as PartialMessage>)); - } - - decode(data: Uint8Array): NapProtoDecodeStructType { - return this._proto_msg.fromBinary(data) as NapProtoDecodeStructType; - } -} - -export class NapProtoMsg { - private realMsg: NapProtoRealMsg; - - constructor(fields: T) { - this.realMsg = NapProtoRealMsg.getInstance(fields); - } - - encode(data: NapProtoEncodeStructType): Uint8Array { - return this.realMsg.encode(data); - } - - decode(data: Uint8Array): NapProtoDecodeStructType { - return this.realMsg.decode(data); - } -} diff --git a/src/core/packet/proto/action/action.ts b/src/core/packet/proto/action/action.ts index af6b45d6..78452bbc 100644 --- a/src/core/packet/proto/action/action.ts +++ b/src/core/packet/proto/action/action.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { ContentHead, MessageBody, MessageControl, RoutingHead } from "@/core/packet/proto/message/message"; export const FaceRoamRequest = { diff --git a/src/core/packet/proto/action/miniAppAdaptShareInfo.ts b/src/core/packet/proto/action/miniAppAdaptShareInfo.ts index 76bb2829..0c137253 100644 --- a/src/core/packet/proto/action/miniAppAdaptShareInfo.ts +++ b/src/core/packet/proto/action/miniAppAdaptShareInfo.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const MiniAppAdaptShareInfoReq = { appId: ProtoField(2, ScalarType.STRING), diff --git a/src/core/packet/proto/highway/highway.ts b/src/core/packet/proto/highway/highway.ts index f69b9e4b..1e96074d 100644 --- a/src/core/packet/proto/highway/highway.ts +++ b/src/core/packet/proto/highway/highway.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { MsgInfo, MsgInfoBody } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq"; export const DataHighwayHead = { diff --git a/src/core/packet/proto/message/action.ts b/src/core/packet/proto/message/action.ts index 29d7945f..df26d42f 100644 --- a/src/core/packet/proto/message/action.ts +++ b/src/core/packet/proto/message/action.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { PushMsgBody } from "@/core/packet/proto/message/message"; export const LongMsgResult = { diff --git a/src/core/packet/proto/message/c2c.ts b/src/core/packet/proto/message/c2c.ts index 2a93e5eb..6db5b646 100644 --- a/src/core/packet/proto/message/c2c.ts +++ b/src/core/packet/proto/message/c2c.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const C2C = { uin: ProtoField(1, ScalarType.UINT32, true), diff --git a/src/core/packet/proto/message/component.ts b/src/core/packet/proto/message/component.ts index 0981a73e..04d0468d 100644 --- a/src/core/packet/proto/message/component.ts +++ b/src/core/packet/proto/message/component.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { Elem } from "@/core/packet/proto/message/element"; export const Attr = { diff --git a/src/core/packet/proto/message/element.ts b/src/core/packet/proto/message/element.ts index 3ed728e3..ed6a237f 100644 --- a/src/core/packet/proto/message/element.ts +++ b/src/core/packet/proto/message/element.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const Elem = { text: ProtoField(1, () => Text, true), diff --git a/src/core/packet/proto/message/group.ts b/src/core/packet/proto/message/group.ts index 3416f1e5..2c8ef617 100644 --- a/src/core/packet/proto/message/group.ts +++ b/src/core/packet/proto/message/group.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const GroupRecallMsg = { type: ProtoField(1, ScalarType.UINT32), diff --git a/src/core/packet/proto/message/message.ts b/src/core/packet/proto/message/message.ts index 5203f285..11a43665 100644 --- a/src/core/packet/proto/message/message.ts +++ b/src/core/packet/proto/message/message.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { ForwardHead, Grp, GrpTmp, ResponseForward, ResponseGrp, Trans0X211, WPATmp } from "@/core/packet/proto/message/routing"; import { RichText } from "@/core/packet/proto/message/component"; import { C2C } from "@/core/packet/proto/message/c2c"; diff --git a/src/core/packet/proto/message/notify.ts b/src/core/packet/proto/message/notify.ts index e739e151..efedfd45 100644 --- a/src/core/packet/proto/message/notify.ts +++ b/src/core/packet/proto/message/notify.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const FriendRecall = { info: ProtoField(1, () => FriendRecallInfo), diff --git a/src/core/packet/proto/message/routing.ts b/src/core/packet/proto/message/routing.ts index 7de44012..b8d539b0 100644 --- a/src/core/packet/proto/message/routing.ts +++ b/src/core/packet/proto/message/routing.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const ForwardHead = { field1: ProtoField(1, ScalarType.UINT32, true), diff --git a/src/core/packet/proto/oidb/Oidb.0XE37_800.ts b/src/core/packet/proto/oidb/Oidb.0XE37_800.ts index dc708071..a8418e76 100644 --- a/src/core/packet/proto/oidb/Oidb.0XE37_800.ts +++ b/src/core/packet/proto/oidb/Oidb.0XE37_800.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { OidbSvcTrpcTcp0XE37_800_1200Metadata } from "@/core/packet/proto/oidb/Oidb.0xE37_1200"; export const OidbSvcTrpcTcp0XE37_800 = { diff --git a/src/core/packet/proto/oidb/Oidb.0XFE1_2.ts b/src/core/packet/proto/oidb/Oidb.0XFE1_2.ts index 7c6b37be..13d7f428 100644 --- a/src/core/packet/proto/oidb/Oidb.0XFE1_2.ts +++ b/src/core/packet/proto/oidb/Oidb.0XFE1_2.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const OidbSvcTrpcTcp0XFE1_2 = { uin: ProtoField(1, ScalarType.UINT32), diff --git a/src/core/packet/proto/oidb/Oidb.0x6D6.ts b/src/core/packet/proto/oidb/Oidb.0x6D6.ts index be16da98..2824035a 100644 --- a/src/core/packet/proto/oidb/Oidb.0x6D6.ts +++ b/src/core/packet/proto/oidb/Oidb.0x6D6.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const OidbSvcTrpcTcp0x6D6 = { file: ProtoField(1, () => OidbSvcTrpcTcp0x6D6Upload, true), diff --git a/src/core/packet/proto/oidb/Oidb.0x8FC_2.ts b/src/core/packet/proto/oidb/Oidb.0x8FC_2.ts index 3721ee8b..c39cdbbe 100644 --- a/src/core/packet/proto/oidb/Oidb.0x8FC_2.ts +++ b/src/core/packet/proto/oidb/Oidb.0x8FC_2.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; //设置群头衔 OidbSvcTrpcTcp.0x8fc_2 @@ -13,4 +13,4 @@ export const OidbSvcTrpcTcp0X8FC_2_Body = { export const OidbSvcTrpcTcp0X8FC_2 = { groupUin: ProtoField(1, ScalarType.UINT32), body: ProtoField(3, ScalarType.BYTES), -}; \ No newline at end of file +}; diff --git a/src/core/packet/proto/oidb/Oidb.0x9067_202.ts b/src/core/packet/proto/oidb/Oidb.0x9067_202.ts index 2ee31688..c72fceef 100644 --- a/src/core/packet/proto/oidb/Oidb.0x9067_202.ts +++ b/src/core/packet/proto/oidb/Oidb.0x9067_202.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { MultiMediaReqHead } from "./common/Ntv2.RichMediaReq"; //Req diff --git a/src/core/packet/proto/oidb/Oidb.0x929.ts b/src/core/packet/proto/oidb/Oidb.0x929.ts index cc04e64b..b401d13d 100644 --- a/src/core/packet/proto/oidb/Oidb.0x929.ts +++ b/src/core/packet/proto/oidb/Oidb.0x929.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq"; export const OidbSvcTrpcTcp0X929D_0 = { diff --git a/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts b/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts index ba2b0f16..969b36f2 100644 --- a/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts +++ b/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const OidbSvcTrpcTcp0XE37_1200 = { subCommand: ProtoField(1, ScalarType.UINT32, true), diff --git a/src/core/packet/proto/oidb/Oidb.0xE37_1700.ts b/src/core/packet/proto/oidb/Oidb.0xE37_1700.ts index 046dff8f..4c800f4e 100644 --- a/src/core/packet/proto/oidb/Oidb.0xE37_1700.ts +++ b/src/core/packet/proto/oidb/Oidb.0xE37_1700.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const OidbSvcTrpcTcp0XE37_1700 = { command: ProtoField(1, ScalarType.UINT32, true), diff --git a/src/core/packet/proto/oidb/Oidb.0xEB7.ts b/src/core/packet/proto/oidb/Oidb.0xEB7.ts index 27cf8cee..947a2268 100644 --- a/src/core/packet/proto/oidb/Oidb.0xEB7.ts +++ b/src/core/packet/proto/oidb/Oidb.0xEB7.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const OidbSvcTrpcTcp0XEB7_Body = { uin: ProtoField(1, ScalarType.STRING), @@ -9,4 +9,4 @@ export const OidbSvcTrpcTcp0XEB7_Body = { export const OidbSvcTrpcTcp0XEB7 = { body: ProtoField(2, () => OidbSvcTrpcTcp0XEB7_Body), -}; \ No newline at end of file +}; diff --git a/src/core/packet/proto/oidb/Oidb.0xED3_1.ts b/src/core/packet/proto/oidb/Oidb.0xED3_1.ts index 5e644c50..c8d6af21 100644 --- a/src/core/packet/proto/oidb/Oidb.0xED3_1.ts +++ b/src/core/packet/proto/oidb/Oidb.0xED3_1.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; // Send Poke export const OidbSvcTrpcTcp0XED3_1 = { diff --git a/src/core/packet/proto/oidb/OidbBase.ts b/src/core/packet/proto/oidb/OidbBase.ts index 49e9e5cd..fe1d1fab 100644 --- a/src/core/packet/proto/oidb/OidbBase.ts +++ b/src/core/packet/proto/oidb/OidbBase.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const OidbSvcTrpcTcpBase = { command: ProtoField(1, ScalarType.UINT32), diff --git a/src/core/packet/proto/oidb/common/Ntv2.RichMediaReq.ts b/src/core/packet/proto/oidb/common/Ntv2.RichMediaReq.ts index 2f117520..e279de3d 100644 --- a/src/core/packet/proto/oidb/common/Ntv2.RichMediaReq.ts +++ b/src/core/packet/proto/oidb/common/Ntv2.RichMediaReq.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; export const NTV2RichMediaReq = { ReqHead: ProtoField(1, () => MultiMediaReqHead), diff --git a/src/core/packet/proto/oidb/common/Ntv2.RichMediaResp.ts b/src/core/packet/proto/oidb/common/Ntv2.RichMediaResp.ts index e1d16556..fe7ac659 100644 --- a/src/core/packet/proto/oidb/common/Ntv2.RichMediaResp.ts +++ b/src/core/packet/proto/oidb/common/Ntv2.RichMediaResp.ts @@ -1,5 +1,5 @@ import { ScalarType } from "@protobuf-ts/runtime"; -import { ProtoField } from "../../NapProto"; +import { ProtoField } from "@napneko/nap-proto-core"; import { CommonHead, MsgInfo, PicUrlExtInfo, VideoExtInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq"; export const NTV2RichMediaResp = { diff --git a/src/core/packet/session.ts b/src/core/packet/session.ts index 7e636281..3413bc3b 100644 --- a/src/core/packet/session.ts +++ b/src/core/packet/session.ts @@ -1,7 +1,19 @@ -import { PacketClient } from "@/core/packet/client"; import { PacketHighwaySession } from "@/core/packet/highway/session"; import { LogWrapper } from "@/common/log"; import { PacketPacker } from "@/core/packet/packer"; +import { PacketClient } from "@/core/packet/client/client"; +import { NativePacketClient } from "@/core/packet/client/nativeClient"; +import { wsPacketClient } from "@/core/packet/client/wsClient"; +import { NapCatCore } from "@/core"; + +type clientPriority = { + [key: number]: (core: NapCatCore) => PacketClient; +} + +const clientPriority: clientPriority = { + 10: (core: NapCatCore) => new NativePacketClient(core), + 1: (core: NapCatCore) => new wsPacketClient(core), +}; export class PacketSession { readonly logger: LogWrapper; @@ -9,10 +21,42 @@ export class PacketSession { readonly packer: PacketPacker; readonly highwaySession: PacketHighwaySession; - constructor(logger: LogWrapper, client: PacketClient) { - this.logger = logger; - this.client = client; + constructor(core: NapCatCore) { + this.logger = core.context.logger; + this.client = this.newClient(core); this.packer = new PacketPacker(this.logger, this.client); this.highwaySession = new PacketHighwaySession(this.logger, this.client, this.packer); } + + private newClient(core: NapCatCore): PacketClient { + const prefer = core.configLoader.configData.packetBackend; + switch (prefer) { + case "native": + return new NativePacketClient(core); + case "frida": + return new wsPacketClient(core); + case "auto": + case undefined: + return this.judgeClient(core); + default: + throw new Error(`[Core] [Packet] 未知的Packet后端类型 ${prefer},请检查配置文件!`); + } + } + + private judgeClient(core: NapCatCore): PacketClient { + const sortedClients = Object.entries(clientPriority) + .map(([priority, clientFactory]) => { + const client = clientFactory(core); + const score = +priority * +client.check(core); + return { client, score }; + }) + .filter(({ score }) => score > 0) + .sort((a, b) => b.score - a.score); + const selectedClient = sortedClients[0]?.client; + if (!selectedClient) { + throw new Error("[Core] [Packet] 无可用的后端,NapCat.Packet将不会加载!"); + } + this.logger.log(`[Core] [Packet] 自动选择 ${selectedClient.constructor.name} 作为后端`); + return selectedClient; + } } diff --git a/src/native/packet/MoeHoo.win32.x64.node b/src/native/packet/MoeHoo.win32.x64.node new file mode 100644 index 00000000..ab1701c0 Binary files /dev/null and b/src/native/packet/MoeHoo.win32.x64.node differ diff --git a/src/onebot/action/packet/GetPacketStatus.ts b/src/onebot/action/packet/GetPacketStatus.ts index 265e7126..2c611135 100644 --- a/src/onebot/action/packet/GetPacketStatus.ts +++ b/src/onebot/action/packet/GetPacketStatus.ts @@ -9,7 +9,7 @@ export abstract class GetPacketStatusDepends extends BaseAction if (!this.core.apis.PacketApi.available) { return { valid: false, - message: "packetServer不可用,请参照文档 https://napneko.github.io/config/advanced 检查packetServer状态或进行配置!", + message: "packetBackend不可用,请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置!", }; } return await super.check(payload); diff --git a/vite.config.ts b/vite.config.ts index d52133c2..b74c214c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -26,6 +26,7 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [ targets: [ { src: './manifest.json', dest: 'dist' }, { src: './src/core/external/napcat.json', dest: 'dist/config/' }, + { src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, { src: './static/', dest: 'dist/static/', flatten: false }, { src: './src/onebot/config/onebot11.json', dest: 'dist/config/' }, { src: './src/framework/liteloader.cjs', dest: 'dist' }, @@ -42,6 +43,7 @@ const ShellBaseConfigPlugin: PluginOption[] = [ cp({ targets: [ { src: './src/native/external', dest: 'dist/native', flatten: false }, + { src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, { src: './static/', dest: 'dist/static/', flatten: false }, { src: './src/core/external/napcat.json', dest: 'dist/config/' }, { src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },