mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
feat: errorStack
This commit is contained in:
parent
f07941685b
commit
75866b435e
@ -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, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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] 已禁用PacketBackend,NapCat.Packet将不会加载!');
|
const err = '[Core] [Packet] 已禁用PacketBackend,NapCat.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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 };
|
||||||
})
|
})
|
||||||
|
@ -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";
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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),
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user