feat: errorStack

This commit is contained in:
pk5ls20 2024-11-13 16:52:03 +08:00
parent f07941685b
commit 75866b435e
No known key found for this signature in database
GPG Key ID: 6370ED7A169F493A
11 changed files with 102 additions and 35 deletions

View File

@ -148,6 +148,7 @@ export class LogWrapper {
} else if (this.consoleLogEnabled) { } else if (this.consoleLogEnabled) {
this.logger.log(level, message); this.logger.log(level, message);
} else if (this.fileLogEnabled) { } else if (this.fileLogEnabled) {
// eslint-disable-next-line no-control-regex
this.logger.log(level, message.replace(/\x1B[@-_][0-?]*[ -/]*[@-~]/g, '')); this.logger.log(level, message.replace(/\x1B[@-_][0-?]*[ -/]*[@-~]/g, ''));
} }
} }
@ -226,7 +227,7 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
? ?
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) : rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})` `未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
}]`; }]`;
} }
if (element.picElement) { if (element.picElement) {
@ -277,4 +278,4 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
} }
return tokens.join(' '); return tokens.join(' ');
} }

View File

@ -19,34 +19,46 @@ export class NTQQPacketApi {
core: NapCatCore; core: NapCatCore;
logger: LogWrapper; logger: LogWrapper;
qqVersion: string | undefined; qqVersion: string | undefined;
pkt: PacketClientSession; pkt!: PacketClientSession;
errStack: string[] = [];
constructor(context: InstanceContext, core: NapCatCore) { constructor(context: InstanceContext, core: NapCatCore) {
this.context = context; this.context = context;
this.core = core; this.core = core;
this.logger = core.context.logger; this.logger = core.context.logger;
this.pkt = new PacketClientSession(core);
this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion()) this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion())
.then() .then()
.catch(this.core.context.logger.logError.bind(this.core.context.logger)); .catch((err) => {
this.logger.logError.bind(this.core.context.logger);
this.errStack.push(err);
});
} }
get available(): boolean { get available(): boolean {
return this.pkt?.available; return this.pkt?.available ?? false;
}
get clientLogStack() {
return this.pkt?.clientLogStack + '\n' + this.errStack.join('\n');
} }
async InitSendPacket(qqVer: string) { async InitSendPacket(qqVer: string) {
this.qqVersion = qqVer; this.qqVersion = qqVer;
const table = typedOffset[qqVer + '-' + os.arch()]; const table = typedOffset[qqVer + '-' + os.arch()];
if (!table) { if (!table) {
this.logger.logError(`[Core] [Packet] PacketBackend 不支持当前QQ版本架构${qqVer}-${os.arch()} const err = `[Core] [Packet] PacketBackend 不支持当前QQ版本架构${qqVer}-${os.arch()}
https://github.com/NapNeko/NapCatQQ/releases/tag/v${napCatVersion} 配置正确的QQ版本`); https://github.com/NapNeko/NapCatQQ/releases/tag/v${napCatVersion} 配置正确的QQ版本`;
this.logger.logError(err);
this.errStack.push(err);
return false; return false;
} }
if (this.core.configLoader.configData.packetBackend === 'disable') { if (this.core.configLoader.configData.packetBackend === 'disable') {
this.logger.logWarn('[Core] [Packet] 已禁用PacketBackendNapCat.Packet将不会加载'); const err = '[Core] [Packet] 已禁用PacketBackendNapCat.Packet将不会加载';
this.logger.logError(err);
this.errStack.push(err);
return false; return false;
} }
this.pkt = new PacketClientSession(this.core);
await this.pkt.init(process.pid, table.recv, table.send); await this.pkt.init(process.pid, table.recv, table.send);
return true; return true;
} }

View File

@ -2,6 +2,7 @@ import { LRUCache } from "@/common/lru-cache";
import crypto, { createHash } from "crypto"; import crypto, { createHash } from "crypto";
import { PacketContext } from "@/core/packet/context/packetContext"; import { PacketContext } from "@/core/packet/context/packetContext";
import { OidbPacket, PacketHexStr } from "@/core/packet/transformer/base"; import { OidbPacket, PacketHexStr } from "@/core/packet/transformer/base";
import { LogStack } from "@/core/packet/context/clientContext";
export interface RecvPacket { export interface RecvPacket {
type: string, // 仅recv type: string, // 仅recv
@ -24,13 +25,16 @@ function randText(len: number): string {
return text; return text;
} }
export abstract class IPacketClient { export abstract class IPacketClient {
protected readonly context: PacketContext; protected readonly context: PacketContext;
protected readonly cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback protected readonly cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
logStack: LogStack;
available: boolean = false; available: boolean = false;
protected constructor(context: PacketContext) { protected constructor(context: PacketContext, logStack: LogStack) {
this.context = context; this.context = context;
this.logStack = logStack;
} }
abstract check(): boolean; abstract check(): boolean;

View File

@ -6,6 +6,7 @@ import { IPacketClient } from "@/core/packet/client/baseClient";
import { constants } from "node:os"; import { constants } from "node:os";
import { LRUCache } from "@/common/lru-cache"; import { LRUCache } from "@/common/lru-cache";
import { PacketContext } from "@/core/packet/context/packetContext"; import { PacketContext } from "@/core/packet/context/packetContext";
import { LogStack } from "@/core/packet/context/clientContext";
// 0 send 1 recv // 0 send 1 recv
export interface NativePacketExportType { export interface NativePacketExportType {
@ -18,19 +19,19 @@ export class NativePacketClient extends IPacketClient {
private MoeHooExport: { exports: NativePacketExportType } = { exports: {} }; private MoeHooExport: { exports: NativePacketExportType } = { exports: {} };
private sendEvent = new LRUCache<number, string>(500); // seq->trace_id private sendEvent = new LRUCache<number, string>(500); // seq->trace_id
constructor(context: PacketContext) { constructor(context: PacketContext, logStack: LogStack) {
super(context); super(context, logStack);
} }
check(): boolean { check(): boolean {
const platform = process.platform + '.' + process.arch; const platform = process.platform + '.' + process.arch;
if (!this.supportedPlatforms.includes(platform)) { if (!this.supportedPlatforms.includes(platform)) {
this.context.logger.warn(`不支持的平台: ${platform}`); this.logStack.pushLogWarn(`NativePacketClient: 不支持的平台: ${platform}`);
return false; return false;
} }
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node'); const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node');
if (!fs.existsSync(moehoo_path)) { if (!fs.existsSync(moehoo_path)) {
this.context.logger.warn(`[Core] [Packet:Native] 缺失运行时文件: ${moehoo_path}`); this.logStack.pushLogWarn(`NativePacketClient: 缺失运行时文件: ${moehoo_path}`);
return false; return false;
} }
return true; return true;

View File

@ -1,6 +1,7 @@
import { Data, WebSocket } from "ws"; import { Data, WebSocket } from "ws";
import { IPacketClient, RecvPacket } from "@/core/packet/client/baseClient"; import { IPacketClient, RecvPacket } from "@/core/packet/client/baseClient";
import { PacketContext } from "@/core/packet/context/packetContext"; import { PacketContext } from "@/core/packet/context/packetContext";
import { LogStack } from "@/core/packet/context/clientContext";
export class wsPacketClient extends IPacketClient { export class wsPacketClient extends IPacketClient {
private websocket: WebSocket | null = null; private websocket: WebSocket | null = null;
@ -12,8 +13,8 @@ export class wsPacketClient extends IPacketClient {
private isInitialized: boolean = false; private isInitialized: boolean = false;
private initPayload: { pid: number, recv: string, send: string } | null = null; private initPayload: { pid: number, recv: string, send: string } | null = null;
constructor(context: PacketContext) { constructor(context: PacketContext, logStack: LogStack) {
super(context); super(context, logStack);
this.clientUrl = this.context.napcore.config.packetServer this.clientUrl = this.context.napcore.config.packetServer
? this.clientUrlWrap(this.context.napcore.config.packetServer) ? this.clientUrlWrap(this.context.napcore.config.packetServer)
: this.clientUrlWrap('127.0.0.1:8083'); : this.clientUrlWrap('127.0.0.1:8083');
@ -21,7 +22,7 @@ export class wsPacketClient extends IPacketClient {
check(): boolean { check(): boolean {
if (!this.context.napcore.config.packetServer) { if (!this.context.napcore.config.packetServer) {
this.context.logger.warn(`wsPacketClient 未配置服务器地址`); this.logStack.pushLogWarn(`wsPacketClient 未配置服务器地址`);
return false; return false;
} }
return true; return true;
@ -41,7 +42,7 @@ export class wsPacketClient extends IPacketClient {
trace_id trace_id
})); }));
} else { } else {
this.context.logger.warn(`WebSocket 未连接,无法发送命令: ${cmd}`); this.logStack.pushLogWarn(`WebSocket 未连接,无法发送命令: ${cmd}`);
} }
} }
@ -52,11 +53,11 @@ export class wsPacketClient extends IPacketClient {
return; return;
} catch (error) { } catch (error) {
this.reconnectAttempts++; this.reconnectAttempts++;
this.context.logger.warn(`${this.reconnectAttempts}/${this.maxReconnectAttempts} 次尝试重连失败!`); this.logStack.pushLogWarn(`${this.reconnectAttempts}/${this.maxReconnectAttempts} 次尝试重连失败!`);
await this.delay(5000); await this.delay(5000);
} }
} }
this.context.logger.error(`wsPacketClient 在 ${this.clientUrl} 达到最大重连次数 (${this.maxReconnectAttempts})`); this.logStack.pushLogError(`wsPacketClient 在 ${this.clientUrl} 达到最大重连次数 (${this.maxReconnectAttempts})`);
throw new Error(`无法连接到 WebSocket 服务器:${this.clientUrl}`); throw new Error(`无法连接到 WebSocket 服务器:${this.clientUrl}`);
} }

View File

@ -12,6 +12,10 @@ export class PacketClientSession {
return this.context.client.init(pid, recv, send); return this.context.client.init(pid, recv, send);
} }
get clientLogStack() {
return this.context.client.clientLogStack;
}
get available() { get available() {
return this.context.client.available; return this.context.client.available;
} }

View File

@ -3,22 +3,61 @@ import { IPacketClient } from "@/core/packet/client/baseClient";
import { NativePacketClient } from "@/core/packet/client/nativeClient"; import { NativePacketClient } from "@/core/packet/client/nativeClient";
import { wsPacketClient } from "@/core/packet/client/wsClient"; import { wsPacketClient } from "@/core/packet/client/wsClient";
import { OidbPacket } from "@/core/packet/transformer/base"; import { OidbPacket } from "@/core/packet/transformer/base";
import { PacketLogger } from "@/core/packet/context/loggerContext";
type clientPriority = { type clientPriority = {
[key: number]: (context: PacketContext) => IPacketClient; [key: number]: (context: PacketContext, logStack: LogStack) => IPacketClient;
} }
const clientPriority: clientPriority = { const clientPriority: clientPriority = {
10: (context: PacketContext) => new NativePacketClient(context), 10: (context: PacketContext, logStack: LogStack) => new NativePacketClient(context, logStack),
1: (context: PacketContext) => new wsPacketClient(context), 1: (context: PacketContext, logStack: LogStack) => new wsPacketClient(context, logStack),
}; };
export class LogStack {
private stack: string[] = [];
private logger: PacketLogger;
constructor(logger: PacketLogger) {
this.logger = logger;
}
push(msg: string) {
this.stack.push(msg);
}
pushLogInfo(msg: string) {
this.logger.info(msg);
this.stack.push(`${new Date().toISOString()} [INFO] ${msg}`);
}
pushLogWarn(msg: string) {
this.logger.warn(msg);
this.stack.push(`${new Date().toISOString()} [WARN] ${msg}`);
}
pushLogError(msg: string) {
this.logger.error(msg);
this.stack.push(`${new Date().toISOString()} [ERROR] ${msg}`);
}
clear() {
this.stack = [];
}
content() {
return this.stack.join('\n');
}
}
export class PacketClientContext { export class PacketClientContext {
private readonly _client: IPacketClient;
private readonly context: PacketContext; private readonly context: PacketContext;
private readonly logStack: LogStack;
private readonly _client: IPacketClient;
constructor(context: PacketContext) { constructor(context: PacketContext) {
this.context = context; this.context = context;
this.logStack = new LogStack(context.logger);
this._client = this.newClient(); this._client = this.newClient();
} }
@ -26,13 +65,17 @@ export class PacketClientContext {
return this._client.available; return this._client.available;
} }
get clientLogStack(): string {
return this._client.logStack.content();
}
async init(pid: number, recv: string, send: string): Promise<void> { async init(pid: number, recv: string, send: string): Promise<void> {
await this._client.init(pid, recv, send); await this._client.init(pid, recv, send);
} }
async sendOidbPacket(pkt: OidbPacket, rsp = false): Promise<Buffer> { async sendOidbPacket<T extends boolean = false>(pkt: OidbPacket, rsp?: T): Promise<T extends true ? Buffer : void> {
const raw = await this._client.sendOidbPacket(pkt, rsp); const raw = await this._client.sendOidbPacket(pkt, rsp);
return Buffer.from(raw.hex_data, "hex"); return (rsp ? Buffer.from(raw.hex_data, "hex") : undefined) as T extends true ? Buffer : void;
} }
private newClient(): IPacketClient { private newClient(): IPacketClient {
@ -41,11 +84,11 @@ export class PacketClientContext {
switch (prefer) { switch (prefer) {
case "native": case "native":
this.context.logger.info("使用指定的 NativePacketClient 作为后端"); this.context.logger.info("使用指定的 NativePacketClient 作为后端");
client = new NativePacketClient(this.context); client = new NativePacketClient(this.context, this.logStack);
break; break;
case "frida": case "frida":
this.context.logger.info("[Core] [Packet] 使用指定的 FridaPacketClient 作为后端"); this.context.logger.info("[Core] [Packet] 使用指定的 FridaPacketClient 作为后端");
client = new wsPacketClient(this.context); client = new wsPacketClient(this.context, this.logStack);
break; break;
case "auto": case "auto":
case undefined: case undefined:
@ -64,7 +107,7 @@ export class PacketClientContext {
private judgeClient(): IPacketClient { private judgeClient(): IPacketClient {
const sortedClients = Object.entries(clientPriority) const sortedClients = Object.entries(clientPriority)
.map(([priority, clientFactory]) => { .map(([priority, clientFactory]) => {
const client = clientFactory(this.context); const client = clientFactory(this.context, this.logStack);
const score = +priority * +client.check(); const score = +priority * +client.check();
return { client, score }; return { client, score };
}) })

View File

@ -4,7 +4,7 @@ import {
MiniAppRawData, MiniAppRawData,
MiniAppReqCustomParams, MiniAppReqCustomParams,
MiniAppReqTemplateParams MiniAppReqTemplateParams
} from "@/core/packet/client/entities/miniApp"; } from "@/core/packet/entities/miniApp";
type MiniAppTemplateNameList = "bili" | "weibo"; type MiniAppTemplateNameList = "bili" | "weibo";

View File

@ -10,7 +10,8 @@ export abstract class GetPacketStatusDepends<PT, RT> extends BaseAction<PT, RT>
// TODO: add error stack? // TODO: add error stack?
return { return {
valid: false, valid: false,
message: "packetBackend不可用请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置", message: "packetBackend不可用请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置" +
"错误堆栈信息:" + this.core.apis.PacketApi.clientLogStack,
}; };
} }
return await super.check(payload); return await super.check(payload);

View File

@ -83,7 +83,7 @@ export class NapCatOneBot11Adapter {
async registerNative(core: NapCatCore, context: InstanceContext) { async registerNative(core: NapCatCore, context: InstanceContext) {
try { try {
this.nativeCore = new Native(context.pathWrapper.binaryPath); this.nativeCore = new Native(context.pathWrapper.binaryPath);
if (!this.nativeCore.inited) throw new Error('Native Not Init'); // if (!this.nativeCore.inited) throw new Error('Native Not Init');
this.nativeCore.registerRecallCallback(async (hex: string) => { this.nativeCore.registerRecallCallback(async (hex: string) => {
try { try {
// TODO: refactor! // TODO: refactor!
@ -346,10 +346,10 @@ export class NapCatOneBot11Adapter {
} }
}; };
msgListener.onKickedOffLine = async (kick) => { msgListener.onKickedOffLine = async (kick) => {
let event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc); const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc);
this.networkManager.emitEvent(event) this.networkManager.emitEvent(event)
.catch(e => this.context.logger.logError.bind(this.context.logger)('处理Bot掉线失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理Bot掉线失败', e));
} };
this.context.session.getMsgService().addKernelMsgListener( this.context.session.getMsgService().addKernelMsgListener(
proxiedListenerOf(msgListener, this.context.logger), proxiedListenerOf(msgListener, this.context.logger),
); );

View File

@ -232,7 +232,7 @@ export async function NCoreInitShell() {
logger.log(`可用于快速登录的 QQ\n${historyLoginList logger.log(`可用于快速登录的 QQ\n${historyLoginList
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`) .map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
.join('\n') .join('\n')
}`); }`);
} }
loginService.getQRCodePicture(); loginService.getQRCodePicture();
} }