Refactor packet client and update message history actions

Replaced LRUCache with Map for callback and event management in packet clients, and standardized callback hash usage. Updated GetFriendMsgHistory and GetGroupMsgHistory actions to use snake_case for payload keys. Modified OneBotMsgApi to support disabling URL retrieval for ptt elements via a new parameter.
This commit is contained in:
手瓜一十雪 2025-09-02 22:24:53 +08:00
parent 6200097f7c
commit 4d200de6b7
6 changed files with 40 additions and 41 deletions

View File

@ -1,4 +1,3 @@
import { LRUCache } from '@/common/lru-cache';
import crypto, { createHash } from 'crypto';
import { OidbPacket, PacketHexStr } from '@/core/packet/transformer/base';
import { LogStack } from '@/core/packet/context/clientContext';
@ -7,7 +6,6 @@ import { PacketLogger } from '@/core/packet/context/loggerContext';
export interface RecvPacket {
type: string, // 仅recv
trace_id_md5?: string,
data: RecvPacketData
}
@ -30,7 +28,7 @@ function randText(len: number): string {
export abstract class IPacketClient {
protected readonly napcore: NapCoreContext;
protected readonly logger: PacketLogger;
protected readonly cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
protected readonly cb = new Map<string, (json: RecvPacketData) => Promise<any> | any>(); // hash-type callback
logStack: LogStack;
available: boolean = false;
@ -44,24 +42,21 @@ export abstract class IPacketClient {
abstract init(pid: number, recv: string, send: string): Promise<void>;
abstract sendCommandImpl(cmd: string, data: string, trace_id: string): void;
abstract sendCommandImpl(cmd: string, data: string, hash: string, timeout: number): void;
private async registerCallback(trace_id: string, type: string, callback: (json: RecvPacketData) => Promise<void>): Promise<void> {
this.cb.put(createHash('md5').update(trace_id).digest('hex') + type, callback);
}
private async sendCommand(cmd: string, data: string, trace_id: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => {
private async sendCommand(cmd: string, data: string, trace_data: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => {
}): Promise<RecvPacketData> {
return new Promise<RecvPacketData>((resolve, reject) => {
if (!this.available) {
reject(new Error('packetBackend 当前不可用!'));
}
let hash = createHash('md5').update(trace_data).digest('hex');
const timeoutHandle = setTimeout(() => {
reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with trace_id ${trace_id}`));
this.cb.delete(hash + 'send');
this.cb.delete(hash + 'recv');
reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with hash ${hash}`));
}, timeout);
this.registerCallback(trace_id, 'send', async (json: RecvPacketData) => {
this.cb.set(hash + 'send', async (json: RecvPacketData) => {
sendcb(json);
if (!rsp) {
clearTimeout(timeoutHandle);
@ -70,21 +65,20 @@ export abstract class IPacketClient {
});
if (rsp) {
this.registerCallback(trace_id, 'recv', async (json: RecvPacketData) => {
this.cb.set(hash + 'recv', async (json: RecvPacketData) => {
clearTimeout(timeoutHandle);
resolve(json);
});
}
this.sendCommandImpl(cmd, data, trace_id);
this.sendCommandImpl(cmd, data, hash, timeout);
});
}
async sendPacket(cmd: string, data: PacketHexStr, rsp = false, timeout = 20000): Promise<RecvPacketData> {
const md5 = crypto.createHash('md5').update(data).digest('hex');
const trace_id = (randText(4) + md5 + data).slice(0, data.length / 2);
return this.sendCommand(cmd, data, trace_id, rsp, timeout, async () => {
await this.napcore.sendSsoCmdReqByContend(cmd, trace_id);
const trace_data = (randText(4) + md5 + data).slice(0, data.length / 2);// trace_data
return this.sendCommand(cmd, data, trace_data, rsp, timeout, async () => {
await this.napcore.sendSsoCmdReqByContend(cmd, trace_data);
});
}

View File

@ -4,7 +4,6 @@ import { fileURLToPath } from 'url';
import fs from 'fs';
import { IPacketClient } from '@/core/packet/client/baseClient';
import { constants } from 'node:os';
import { LRUCache } from '@/common/lru-cache';
import { LogStack } from '@/core/packet/context/clientContext';
import { NapCoreContext } from '@/core/packet/context/napCoreContext';
import { PacketLogger } from '@/core/packet/context/loggerContext';
@ -18,7 +17,8 @@ export interface NativePacketExportType {
export class NativePacketClient extends IPacketClient {
private readonly supportedPlatforms = ['win32.x64', 'linux.x64', 'linux.arm64', 'darwin.x64', 'darwin.arm64'];
private readonly MoeHooExport: { exports: NativePacketExportType } = { exports: {} };
private readonly sendEvent = new LRUCache<number, string>(500); // seq->trace_id
private readonly sendEvent = new Map<number, string>(); // seq - hash
private readonly timeEvent = new Map<string, NodeJS.Timeout>(); // hash - timeout
constructor(napCore: NapCoreContext, logger: PacketLogger, logStack: LogStack) {
super(napCore, logger, logStack);
@ -45,25 +45,31 @@ export class NativePacketClient extends IPacketClient {
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
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')) {
const hash = createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex');
if (type === 0 && this.cb.get(hash + 'recv')) {
//此时为send 提取seq
this.sendEvent.put(seq, trace_id);
this.sendEvent.set(seq, hash);
setTimeout(() => {
this.sendEvent.delete(seq);
this.timeEvent.delete(hash);
}, +(this.timeEvent.get(hash) ?? 20000));
//正式send完成 无recv v
//均无异常 v
}
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);
const hash = this.sendEvent.get(seq);
const callback = this.cb.get(hash + 'recv');
callback?.({ seq, cmd, hex_data });
}
}, this.napcore.config.o3HookMode == 1);
this.available = true;
}
sendCommandImpl(cmd: string, data: string, trace_id: string): void {
const trace_id_md5 = createHash('md5').update(trace_id).digest('hex');
this.MoeHooExport.exports.SendPacket?.(cmd, data, trace_id_md5);
this.cb.get(trace_id_md5 + 'send')?.({ seq: 0, cmd, hex_data: '' });
sendCommandImpl(cmd: string, data: string, hash: string, timeout: number): void {
this.timeEvent.set(hash, setTimeout(() => {
this.timeEvent.delete(hash);//考虑情况为正式send都没进
}, timeout));
this.MoeHooExport.exports.SendPacket?.(cmd, data, hash);
this.cb.get(hash + 'send')?.({ seq: 0, cmd, hex_data: '' });
}
}

View File

@ -15,8 +15,8 @@ const SchemaData = Type.Object({
message_seq: Type.Optional(Type.String()),
count: Type.Number({ default: 20 }),
reverseOrder: Type.Boolean({ default: false }),
disableGetUrl: Type.Boolean({ default: false }),
parseMultMsg: Type.Boolean({ default: true })
disable_get_url: Type.Boolean({ default: false }),
parse_mult_msg: Type.Boolean({ default: true })
});
@ -43,7 +43,7 @@ export default class GetFriendMsgHistory extends OneBotAction<Payload, Response>
}));
//烘焙消息
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat, payload.parseMultMsg, payload.disableGetUrl)))
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat, payload.parse_mult_msg, payload.disable_get_url)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}

View File

@ -15,8 +15,8 @@ const SchemaData = Type.Object({
message_seq: Type.Optional(Type.String()),
count: Type.Number({ default: 20 }),
reverseOrder: Type.Boolean({ default: false }),
disableGetUrl: Type.Boolean({ default: false }),
parseMultMsg: Type.Boolean({ default: true }),
disable_get_url: Type.Boolean({ default: false }),
parse_mult_msg: Type.Boolean({ default: true }),
});
@ -41,7 +41,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends OneBotAction<Payload, Re
}));
//烘焙消息
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat, payload.parseMultMsg, payload.disableGetUrl)))
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat, payload.parse_mult_msg, payload.disable_get_url)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}

View File

@ -444,7 +444,7 @@ export class OneBotMsgApi {
};
},
pttElement: async (element, msg, elementWrapper) => {
pttElement: async (element, msg, elementWrapper, { disableGetUrl }) => {
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
@ -452,7 +452,7 @@ export class OneBotMsgApi {
};
const fileCode = FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, '', element.fileName);
let pttUrl = '';
if (this.core.apis.PacketApi.packetStatus) {
if (this.core.apis.PacketApi.packetStatus && !disableGetUrl) {
try {
pttUrl = await registerResource(
'ptt-url-get',

View File

@ -62,7 +62,6 @@ export class NapCatOneBot11Adapter {
networkManager: OB11NetworkManager;
actions: ActionMap;
private readonly bootTime = Date.now() / 1000;
//recallMsgCache = new LRUCache<string, boolean>(100);
recallEventCache = new Map<string, any>();
constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
this.core = core;