chore: 进度提交

This commit is contained in:
手瓜一十雪 2024-08-08 17:51:15 +08:00
parent c417a95e1f
commit c70b2eaa30
16 changed files with 1139 additions and 701 deletions

View File

@ -0,0 +1,262 @@
import { NodeIQQNTWrapperSession } from "@/core/wrapper/wrapper";
import { randomUUID } from "crypto";
interface Internal_MapKey {
timeout: number;
createtime: number;
func: (...arg: any[]) => any;
checker: ((...args: any[]) => boolean) | undefined;
}
export class ListenerClassBase {
[key: string]: string;
}
export interface ListenerIBase {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new (listener: any): ListenerClassBase;
}
export class NTEventWrapper {
private ListenerMap: { [key: string]: ListenerIBase } | undefined; //ListenerName-Unique -> Listener构造函数
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
private ListenerManger: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
constructor() {}
createProxyDispatch(ListenerMainName: string) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const current = this;
return new Proxy(
{},
{
get(target: any, prop: any, receiver: any) {
// console.log('get', prop, typeof target[prop]);
if (typeof target[prop] === "undefined") {
// 如果方法不存在返回一个函数这个函数调用existentMethod
return (...args: any[]) => {
current.DispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then();
};
}
// 如果方法存在,正常返回
return Reflect.get(target, prop, receiver);
},
}
);
}
init({
ListenerMap,
WrapperSession,
}: {
ListenerMap: { [key: string]: typeof ListenerClassBase };
WrapperSession: NodeIQQNTWrapperSession;
}) {
this.ListenerMap = ListenerMap;
this.WrapperSession = WrapperSession;
}
CreatEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {
const eventNameArr = eventName.split("/");
type eventType = {
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>> };
};
if (eventNameArr.length > 1) {
const serviceName = "get" + eventNameArr[0].replace("NodeIKernel", "");
const eventName = eventNameArr[1];
//getNodeIKernelGroupListener,GroupService
//console.log('2', eventName);
const services = (this.WrapperSession as unknown as eventType)[serviceName]();
let event = services[eventName];
//重新绑定this
event = event.bind(services);
if (event) {
return event as T;
}
return undefined;
}
}
CreatListenerFunction<T>(listenerMainName: string, uniqueCode: string = ""): T {
const ListenerType = this.ListenerMap![listenerMainName];
let Listener = this.ListenerManger.get(listenerMainName + uniqueCode);
if (!Listener && ListenerType) {
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
const Service = "NodeIKernel" + ServiceSubName + "Service/addKernel" + ServiceSubName + "Listener";
const addfunc = this.CreatEventFunction<(listener: T) => number>(Service);
addfunc!(Listener as T);
//console.log(addfunc!(Listener as T));
this.ListenerManger.set(listenerMainName + uniqueCode, Listener);
}
return Listener as T;
}
//统一回调清理事件
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args);
this.EventTask.get(ListenerMainName)
?.get(ListenerSubName)
?.forEach((task, uuid) => {
//console.log(task.func, uuid, task.createtime, task.timeout);
if (task.createtime + task.timeout < Date.now()) {
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
return;
}
if (task.checker && task.checker(...args)) {
task.func(...args);
}
});
}
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any> | any>(
EventName = "",
timeout: number = 3000,
...args: Parameters<EventType>
) {
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
const EventFunc = this.CreatEventFunction<EventType>(EventName);
let complete = false;
const Timeouter = setTimeout(() => {
if (!complete) {
reject(new Error("NTEvent EventName:" + EventName + " timeout"));
}
}, timeout);
const retData = await EventFunc!(...args);
complete = true;
resolve(retData);
});
}
async RegisterListen<ListenerType extends (...args: any[]) => void>(
ListenerName = "",
waitTimes = 1,
timeout = 5000,
checker: (...args: Parameters<ListenerType>) => boolean
) {
return new Promise<Parameters<ListenerType>>((resolve, reject) => {
const ListenerNameList = ListenerName.split("/");
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const id = randomUUID();
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
const databack = () => {
if (complete == 0) {
reject(new Error(" ListenerName:" + ListenerName + " timeout"));
} else {
resolve(retData!);
}
};
const Timeouter = setTimeout(databack, timeout);
const eventCallbak = {
timeout: timeout,
createtime: Date.now(),
checker: checker,
func: (...args: Parameters<ListenerType>) => {
complete++;
retData = args;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
databack();
}
},
};
if (!this.EventTask.get(ListenerMainName)) {
this.EventTask.set(ListenerMainName, new Map());
}
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
}
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.CreatListenerFunction(ListenerMainName);
});
}
async CallNormalEvent<
EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void
>(
EventName = "",
ListenerName = "",
waitTimes = 1,
timeout: number = 3000,
checker: (...args: Parameters<ListenerType>) => boolean,
...args: Parameters<EventType>
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
async (resolve, reject) => {
const id = randomUUID();
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {};
const databack = () => {
if (complete == 0) {
reject(
new Error(
"Timeout: NTEvent EventName:" +
EventName +
" ListenerName:" +
ListenerName +
" EventRet:\n" +
JSON.stringify(retEvent, null, 4) +
"\n"
)
);
} else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
}
};
const ListenerNameList = ListenerName.split("/");
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const Timeouter = setTimeout(databack, timeout);
const eventCallbak = {
timeout: timeout,
createtime: Date.now(),
checker: checker,
func: (...args: any[]) => {
complete++;
//console.log('func', ...args);
retData = args as Parameters<ListenerType>;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
databack();
}
},
};
if (!this.EventTask.get(ListenerMainName)) {
this.EventTask.set(ListenerMainName, new Map());
}
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
}
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.CreatListenerFunction(ListenerMainName);
const EventFunc = this.CreatEventFunction<EventType>(EventName);
retEvent = await EventFunc!(...(args as any[]));
}
);
}
}
export const NTEventDispatch = new NTEventWrapper();
// 示例代码 快速创建事件
// let NTEvent = new NTEventWrapper();
// let TestEvent = NTEvent.CreatEventFunction<(force: boolean) => Promise<Number>>('NodeIKernelProfileLikeService/GetTest');
// if (TestEvent) {
// TestEvent(true);
// }
// 示例代码 快速创建监听Listener类
// let NTEvent = new NTEventWrapper();
// NTEvent.CreatListenerFunction<NodeIKernelMsgListener>('NodeIKernelMsgListener', 'core')
// 调用接口
//let NTEvent = new NTEventWrapper();
//let ret = await NTEvent.CallNormalEvent<(force: boolean) => Promise<Number>, (data1: string, data2: number) => void>('NodeIKernelProfileLikeService/GetTest', 'NodeIKernelMsgListener/onAddSendMsg', 1, 3000, true);
// 注册监听 解除监听
// NTEventDispatch.RigisterListener('NodeIKernelMsgListener/onAddSendMsg','core',cb);
// NTEventDispatch.UnRigisterListener('NodeIKernelMsgListener/onAddSendMsg','core');
// let GetTest = NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode);
// GetTest('test');
// always模式
// NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode,(...args:any[])=>{ console.log(args) });

View File

@ -0,0 +1,138 @@
import type { NodeIQQNTWrapperSession, WrapperNodeApi } from "@/core/wrapper/wrapper";
import { randomUUID } from "node:crypto";
import EventEmitter from "node:events";
export type ListenerClassBase = Record<string, string>;
export interface ListenerIBase {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: any): ListenerClassBase;
}
export class NTEventChannel extends EventEmitter {
private wrapperApi: WrapperNodeApi;
private wrapperSession: NodeIQQNTWrapperSession;
private listenerRefStorage = new Map<string, ListenerIBase>();
constructor(WrapperApi: WrapperNodeApi, WrapperSession: NodeIQQNTWrapperSession) {
super();
this.on('error', () => { });
this.wrapperApi = WrapperApi;
this.wrapperSession = WrapperSession;
}
dispatcherListener(ListenerEvent: string, ...args: any[]) {
this.emit(ListenerEvent, ...args);
}
createProxyDispatch(ListenerMainName: string) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const current = this;
return new Proxy({}, {
get(_target: any, prop: any, _receiver: any) {
return (...args: any[]) => {
current.dispatcherListener.apply(current, [ListenerMainName + '/' + prop, ...args]);
};
}
});
}
async getOrInitListener<T>(listenerMainName: string): Promise<T> {
const ListenerType = this.wrapperApi[listenerMainName];
//获取NTQQ 外部 Listener包装
if (!ListenerType) throw new Error('Init Listener not found');
let Listener = this.listenerRefStorage.get(listenerMainName);
//判断是否已创建 创建则跳过
if (!Listener && ListenerType) {
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
if (!Listener) throw new Error('Init Listener failed');
//实例化NTQQ Listener外包装
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
//添加Listener到NTQQ
addfunc!(Listener as T);
this.listenerRefStorage.set(listenerMainName, Listener);
//保存Listener实例
}
return Listener as T;
}
async createEventWithListener<EventType extends (...args: any) => any, ListenerType extends (...args: any) => any>
(
eventName: string,
listenerName: string,
waitTimes = 1,
timeout: number = 3000,
checker: (...args: Parameters<ListenerType>) => boolean,
...eventArg: Parameters<EventType>
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
const ListenerNameList = listenerName.split('/');
const ListenerMainName = ListenerNameList[0];
//const ListenerSubName = ListenerNameList[1];
this.getOrInitListener<ListenerType>(ListenerMainName);
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {};
const databack = () => {
if (complete == 0) {
reject(new Error('Timeout: NTEvent EventName:' + eventName + ' ListenerName:' + listenerName + ' EventRet:\n' + JSON.stringify(retEvent, null, 4) + '\n'));
} else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
}
};
const Timeouter = setTimeout(databack, timeout);
let callback = (...args: Parameters<ListenerType>) => {
if (checker(...args)) {
complete++;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
this.removeListener(listenerName, callback);
databack();
}
}
};
this.on(listenerName, callback);
const EventFunc = this.createEventFunction<EventType>(eventName);
retEvent = await EventFunc!(...(eventArg as any[]));
});
}
private createEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {
const eventNameArr = eventName.split('/');
type eventType = {
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>> }
}
if (eventNameArr.length > 1) {
const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', '');
const eventName = eventNameArr[1];
//getNodeIKernelGroupListener,GroupService
//console.log('2', eventName);
const services = (this.wrapperSession as unknown as eventType)[serviceName]();
let event = services[eventName]
//重新绑定this
.bind(services);
if (event) {
return event as T;
}
return undefined;
}
}
async callEvent<EventType extends (...args: any[]) => Promise<any> | any>(
EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
const EventFunc = this.createEventFunction<EventType>(EventName);
let complete = false;
const Timeouter = setTimeout(() => {
if (!complete) {
reject(new Error('NTEvent EventName:' + EventName + ' timeout'));
}
}, timeout);
const retData = await EventFunc!(...args);
complete = true;
resolve(retData);
});
}
}
//NTEvent2.0

View File

@ -0,0 +1,14 @@
import path, { dirname } from "path";
import { fileURLToPath } from "url";
export class NapCatPathWrapper {
binaryPath: string;
logsPath: string;
configPath: string;
constructor(mainPath: string = dirname(fileURLToPath(import.meta.url))) {
this.binaryPath = mainPath;
this.logsPath = path.join(this.binaryPath, "logs");
this.configPath = path.join(this.binaryPath, "config");
}
}

View File

@ -1,24 +0,0 @@
import path, { dirname } from "path";
import { fileURLToPath } from "url";
class NapCatPathWrapper {
NapCat_Main_Path: string | undefined;
NapCat_Logs_Path: string | undefined;
NapCat_Config_Path: string | undefined;
constructor() { }
Init(MainPath: string = dirname(fileURLToPath(import.meta.url))) {
this.NapCat_Main_Path = MainPath;
this.NapCat_Logs_Path = path.join(this.NapCat_Main_Path, "logs");
this.NapCat_Config_Path = path.join(this.NapCat_Main_Path, "config");
}
getScriptPath() {
return this.NapCat_Main_Path;
}
getLogsPath() {
return this.NapCat_Logs_Path;
}
getConfigPath() {
return this.NapCat_Config_Path;
}
}
export let NapCatPath: NapCatPathWrapper | undefined;

View File

@ -1,67 +1,78 @@
import path from 'node:path';
import fs from 'node:fs';
import { systemPlatform } from '@/common/utils/system';
import { getDefaultQQVersionConfigInfo, getQQVersionConfigPath } from './helper';
import AppidTable from '@/core/external/appid.json';
import { LogWrapper } from './log';
import path from "node:path";
import fs from "node:fs";
import { systemPlatform } from "@/common/utils/system";
import { getDefaultQQVersionConfigInfo, getQQVersionConfigPath } from "./helper";
import AppidTable from "@/core/external/appid.json";
import { LogWrapper } from "./log";
export class QQBasicInfoWrapper {
QQMainPath: string | undefined;
QQPackageInfoPath: string | undefined;
QQVersionConfigPath: string | undefined;
isQuickUpdate: boolean | undefined;
QQVersionConfig: QQVersionConfigType | undefined;
QQPackageInfo: QQPackageInfoType | undefined;
QQVersionAppid: string | undefined;
QQVersionQua: string | undefined;
context: { logger: LogWrapper; };
constructor(context: { logger: LogWrapper }) {
//基础目录获取
this.context = context;
this.QQMainPath = process.execPath;
this.QQPackageInfoPath = path.join(path.dirname(this.QQMainPath), 'resources', 'app', 'package.json');
this.QQVersionConfigPath = getQQVersionConfigPath(this.QQMainPath);
QQMainPath: string | undefined;
QQPackageInfoPath: string | undefined;
QQVersionConfigPath: string | undefined;
isQuickUpdate: boolean | undefined;
QQVersionConfig: QQVersionConfigType | undefined;
QQPackageInfo: QQPackageInfoType | undefined;
QQVersionAppid: string | undefined;
QQVersionQua: string | undefined;
context: { logger: LogWrapper };
//基础信息获取 无快更则启用默认模板填充
this.isQuickUpdate = !!this.QQVersionConfigPath;
this.QQVersionConfig = this.isQuickUpdate ? JSON.parse(fs.readFileSync(this.QQVersionConfigPath!).toString()) : getDefaultQQVersionConfigInfo();
this.QQPackageInfo = JSON.parse(fs.readFileSync(this.QQPackageInfoPath).toString());
let { appid: IQQVersionAppid, qua: IQQVersionQua } = this.getAppidV2();
this.QQVersionAppid = IQQVersionAppid;
this.QQVersionQua = IQQVersionQua;
}
constructor(context: { logger: LogWrapper }) {
//基础目录获取
this.context = context;
this.QQMainPath = process.execPath;
this.QQPackageInfoPath = path.join(path.dirname(this.QQMainPath), "resources", "app", "package.json");
this.QQVersionConfigPath = getQQVersionConfigPath(this.QQMainPath);
//基础函数
getQQBuildStr() {
return this.isQuickUpdate ? this.QQVersionConfig?.buildId : this.QQPackageInfo?.buildVersion;
}
getFullQQVesion() { return this.isQuickUpdate ? this.QQVersionConfig?.curVersion : this.QQPackageInfo?.version; }
requireMinNTQQBuild(buildStr: string) {
let currentBuild = parseInt(this.getQQBuildStr() || '0')
if (currentBuild == 0) throw new Error('QQBuildStr获取失败')
return currentBuild >= parseInt(buildStr);
}
//此方法不要直接使用
getQUAInternal() {
return systemPlatform === 'linux' ? `V1_LNX_NQ_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B` : `V1_WIN_NQ_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
}
getAppidV2(): { appid: string, qua: string } {
const appidTbale = AppidTable as unknown as QQAppidTableType;
try {
let fullVersion = this.getFullQQVesion();
if (!fullVersion) throw new Error('QQ版本获取失败');
const data = appidTbale[fullVersion];
if (data) {
return data;
}
//基础信息获取 无快更则启用默认模板填充
this.isQuickUpdate = !!this.QQVersionConfigPath;
this.QQVersionConfig = this.isQuickUpdate
? JSON.parse(fs.readFileSync(this.QQVersionConfigPath!).toString())
: getDefaultQQVersionConfigInfo();
this.QQPackageInfo = JSON.parse(fs.readFileSync(this.QQPackageInfoPath).toString());
let { appid: IQQVersionAppid, qua: IQQVersionQua } = this.getAppidV2();
this.QQVersionAppid = IQQVersionAppid;
this.QQVersionQua = IQQVersionQua;
}
catch (e) {
this.context.logger.log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
//基础函数
getQQBuildStr() {
return this.isQuickUpdate ? this.QQVersionConfig?.buildId : this.QQPackageInfo?.buildVersion;
}
getFullQQVesion() {
let version = this.isQuickUpdate ? this.QQVersionConfig?.curVersion : this.QQPackageInfo?.version;
if(!version) throw new Error("QQ版本获取失败");
return version;
}
requireMinNTQQBuild(buildStr: string) {
let currentBuild = parseInt(this.getQQBuildStr() || "0");
if (currentBuild == 0) throw new Error("QQBuildStr获取失败");
return currentBuild >= parseInt(buildStr);
}
//此方法不要直接使用
getQUAInternal() {
return systemPlatform === "linux"
? `V1_LNX_NQ_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`
: `V1_WIN_NQ_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
}
getAppidV2(): { appid: string; qua: string } {
const appidTbale = AppidTable as unknown as QQAppidTableType;
try {
let fullVersion = this.getFullQQVesion();
if (!fullVersion) throw new Error("QQ版本获取失败");
const data = appidTbale[fullVersion];
if (data) {
return data;
}
} catch (e) {
this.context.logger.log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
}
// 以下是兜底措施
this.context.logger.log(
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`
);
return { appid: systemPlatform === "linux" ? "537237950" : "537237765", qua: this.getQUAInternal() };
}
// 以下是兜底措施
this.context.logger.log(`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`);
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: this.getQUAInternal() };
}
}
export let QQBasicInfo: QQBasicInfoWrapper | undefined;
export let QQBasicInfo: QQBasicInfoWrapper | undefined;

View File

@ -1,390 +1,154 @@
import crypto from 'node:crypto';
import path from 'node:path';
import fs from 'fs';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import * as fsPromise from 'node:fs/promises';
import os from 'node:os';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import crypto from "node:crypto";
import path from "node:path";
import fs from "fs";
import * as fsPromise from "node:fs/promises";
import os from "node:os";
//下面这个类是用于将uid+msgid合并的类
export class UUIDConverter {
static encode(highStr: string, lowStr: string): string {
const high = BigInt(highStr);
const low = BigInt(lowStr);
const highHex = high.toString(16).padStart(16, '0');
const lowHex = low.toString(16).padStart(16, '0');
const combinedHex = highHex + lowHex;
const uuid = `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(12, 16)}-${combinedHex.substring(16, 20)}-${combinedHex.substring(20)}`;
return uuid;
}
static decode(uuid: string): { high: string, low: string } {
const hex = uuid.replace(/-/g, '');
const high = BigInt('0x' + hex.substring(0, 16));
const low = BigInt('0x' + hex.substring(16));
return { high: high.toString(), low: low.toString() };
}
static encode(highStr: string, lowStr: string): string {
const high = BigInt(highStr);
const low = BigInt(lowStr);
const highHex = high.toString(16).padStart(16, "0");
const lowHex = low.toString(16).padStart(16, "0");
const combinedHex = highHex + lowHex;
const uuid = `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(
12,
16
)}-${combinedHex.substring(16, 20)}-${combinedHex.substring(20)}`;
return uuid;
}
static decode(uuid: string): { high: string; low: string } {
const hex = uuid.replace(/-/g, "");
const high = BigInt("0x" + hex.substring(0, 16));
const low = BigInt("0x" + hex.substring(16));
return { high: high.toString(), low: low.toString() };
}
}
export function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function PromiseTimer<T>(promise: Promise<T>, ms: number): Promise<T> {
const timeoutPromise = new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error('PromiseTimer: Operation timed out')), ms)
);
return Promise.race([promise, timeoutPromise]);
const timeoutPromise = new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error("PromiseTimer: Operation timed out")), ms)
);
return Promise.race([promise, timeoutPromise]);
}
export async function runAllWithTimeout<T>(tasks: Promise<T>[], timeout: number): Promise<T[]> {
const wrappedTasks = tasks.map(task =>
PromiseTimer(task, timeout).then(
result => ({ status: 'fulfilled', value: result }),
error => ({ status: 'rejected', reason: error })
)
);
const results = await Promise.all(wrappedTasks);
return results
.filter(result => result.status === 'fulfilled')
.map(result => (result as { status: 'fulfilled'; value: T }).value);
const wrappedTasks = tasks.map((task) =>
PromiseTimer(task, timeout).then(
(result) => ({ status: "fulfilled", value: result }),
(error) => ({ status: "rejected", reason: error })
)
);
const results = await Promise.all(wrappedTasks);
return results
.filter((result) => result.status === "fulfilled")
.map((result) => (result as { status: "fulfilled"; value: T }).value);
}
export function getMd5(s: string) {
const h = crypto.createHash('md5');
h.update(s);
return h.digest('hex');
const h = crypto.createHash("md5");
h.update(s);
return h.digest("hex");
}
export function isNull(value: any) {
return value === undefined || value === null;
return value === undefined || value === null;
}
export function isNumeric(str: string) {
return /^\d+$/.test(str);
return /^\d+$/.test(str);
}
export function truncateString(obj: any, maxLength = 500) {
if (obj !== null && typeof obj === 'object') {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'string') {
// 如果是字符串且超过指定长度,则截断
if (obj[key].length > maxLength) {
obj[key] = obj[key].substring(0, maxLength) + '...';
}
} else if (typeof obj[key] === 'object') {
// 如果是对象或数组,则递归调用
truncateString(obj[key], maxLength);
}
});
}
return obj;
}
export function simpleDecorator(target: any, context: any) {
}
// export function CacheClassFunc(ttl: number = 3600 * 1000, customKey: string = '') {
// const cache = new Map<string, { expiry: number; value: any }>();
// return function CacheClassFuncDecorator(originalMethod: Function, context: ClassMethodDecoratorContext) {
// async function CacheClassFuncDecoratorInternal(this: any, ...args: any[]) {
// const key = `${customKey}${String(context.name)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
// const cachedValue = cache.get(key);
// if (cachedValue && cachedValue.expiry > Date.now()) {
// return cachedValue.value;
// }
// const result = originalMethod.call(this, ...args);
// cache.set(key, { expiry: Date.now() + ttl, value: result });
// return result;
// }
// return CacheClassFuncDecoratorInternal;
// }
// }
export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = '') {
//console.log('CacheClassFuncAsync', ttl, customKey);
function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
//console.log('logExecutionTime', target, methodName, descriptor);
const cache = new Map<string, { expiry: number; value: any }>();
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
cache.forEach((value, key) => {
if (value.expiry < Date.now()) {
cache.delete(key);
}
});
const cachedValue = cache.get(key);
if (cachedValue && cachedValue.expiry > Date.now()) {
return cachedValue.value;
}
// const start = Date.now();
const result = await originalMethod.apply(this, args);
// const end = Date.now();
// console.log(`Method ${methodName} executed in ${end - start} ms.`);
cache.set(key, { expiry: Date.now() + ttl, value: result });
return result;
};
}
return logExecutionTime;
}
export function CacheClassFuncAsyncExtend(ttl: number = 3600 * 1000, customKey: string = '', checker: any = (...data: any[]) => { return true; }) {
//console.log('CacheClassFuncAsync', ttl, customKey);
function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
//console.log('logExecutionTime', target, methodName, descriptor);
const cache = new Map<string, { expiry: number; value: any }>();
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
cache.forEach((value, key) => {
if (value.expiry < Date.now()) {
cache.delete(key);
}
});
const cachedValue = cache.get(key);
if (cachedValue && cachedValue.expiry > Date.now()) {
return cachedValue.value;
}
// const start = Date.now();
const result = await originalMethod.apply(this, args);
if (!checker(...args, result)) {
return result;//丢弃缓存
}
// const end = Date.now();
// console.log(`Method ${methodName} executed in ${end - start} ms.`);
cache.set(key, { expiry: Date.now() + ttl, value: result });
return result;
};
}
return logExecutionTime;
}
// export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = ''): any {
// const cache = new Map<string, { expiry: number; value: any }>();
// // 注意在JavaScript装饰器中我们通常不直接处理ClassMethodDecoratorContext这样的类型
// // 因为装饰器的参数通常是目标类(对于类装饰器)、属性名(对于属性装饰器)等。
// // 对于方法装饰器,我们关注的是方法本身及其描述符。
// // 但这里我们维持原逻辑,假设有一个自定义的处理上下文的方式。
// return function (originalMethod: Function): any {
// console.log(originalMethod);
// // 由于JavaScript装饰器原生不支持异步直接定义我们保持async定义以便处理异步方法。
// async function decoratorWrapper(this: any, ...args: any[]): Promise<any> {
// console.log(...args);
// const key = `${customKey}${originalMethod.name}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
// const cachedValue = cache.get(key);
// // 遍历cache 清除expiry内容
// cache.forEach((value, key) => {
// if (value.expiry < Date.now()) {
// cache.delete(key);
// }
// });
// if (cachedValue && cachedValue.expiry > Date.now()) {
// return cachedValue.value;
// }
// // 直接await异步方法的结果
// const result = await originalMethod.apply(this, args);
// cache.set(key, { expiry: Date.now() + ttl, value: result });
// return result;
// }
// // 返回装饰后的方法保持与原方法相同的名称和描述符如果需要更精细的控制可以考虑使用Object.getOwnPropertyDescriptor等
// return decoratorWrapper;
// };
// }
/**
* key生成缓存键
* @param ttl
* @param customKey
* @returns
*/
export function cacheFunc(ttl: number, customKey: string = '') {
const cache = new Map<string, { expiry: number; value: any }>();
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor {
const originalMethod = descriptor.value;
const className = target.constructor.name; // 获取类名
const methodName = propertyKey; // 获取方法名
descriptor.value = async function (...args: any[]) {
const cacheKey = `${customKey}${className}.${methodName}:${JSON.stringify(args)}`;
const cached = cache.get(cacheKey);
if (cached && cached.expiry > Date.now()) {
return cached.value;
} else {
const result = await originalMethod.apply(this, args);
cache.set(cacheKey, { value: result, expiry: Date.now() + ttl });
return result;
}
};
return descriptor;
};
}
export function isValidOldConfig(config: any) {
if (typeof config !== 'object') {
return false;
}
const requiredKeys = [
'httpHost', 'httpPort', 'httpPostUrls', 'httpSecret',
'wsHost', 'wsPort', 'wsReverseUrls', 'enableHttp',
'enableHttpHeart', 'enableHttpPost', 'enableWs', 'enableWsReverse',
'messagePostFormat', 'reportSelfMessage', 'enableLocalFile2Url',
'debug', 'heartInterval', 'token', 'musicSignUrl'
];
for (const key of requiredKeys) {
if (!(key in config)) {
return false;
if (obj !== null && typeof obj === "object") {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === "string") {
// 如果是字符串且超过指定长度,则截断
if (obj[key].length > maxLength) {
obj[key] = obj[key].substring(0, maxLength) + "...";
}
} else if (typeof obj[key] === "object") {
// 如果是对象或数组,则递归调用
truncateString(obj[key], maxLength);
}
});
}
}
if (!Array.isArray(config.httpPostUrls) || !Array.isArray(config.wsReverseUrls)) {
return false;
}
if (config.httpPostUrls.some((url: any) => typeof url !== 'string')) {
return false;
}
if (config.wsReverseUrls.some((url: any) => typeof url !== 'string')) {
return false;
}
if (typeof config.httpPort !== 'number' || typeof config.wsPort !== 'number' || typeof config.heartInterval !== 'number') {
return false;
}
if (
typeof config.enableHttp !== 'boolean' ||
typeof config.enableHttpHeart !== 'boolean' ||
typeof config.enableHttpPost !== 'boolean' ||
typeof config.enableWs !== 'boolean' ||
typeof config.enableWsReverse !== 'boolean' ||
typeof config.enableLocalFile2Url !== 'boolean' ||
typeof config.reportSelfMessage !== 'boolean'
) {
return false;
}
if (config.messagePostFormat !== 'array' && config.messagePostFormat !== 'string') {
return false;
}
return true;
}
export function migrateConfig(oldConfig: any) {
const newConfig = {
http: {
enable: oldConfig.enableHttp,
host: oldConfig.httpHost,
port: oldConfig.httpPort,
secret: oldConfig.httpSecret,
enableHeart: oldConfig.enableHttpHeart,
enablePost: oldConfig.enableHttpPost,
postUrls: oldConfig.httpPostUrls,
},
ws: {
enable: oldConfig.enableWs,
host: oldConfig.wsHost,
port: oldConfig.wsPort,
},
reverseWs: {
enable: oldConfig.enableWsReverse,
urls: oldConfig.wsReverseUrls,
},
GroupLocalTime: {
Record: false,
RecordList: []
},
debug: oldConfig.debug,
heartInterval: oldConfig.heartInterval,
messagePostFormat: oldConfig.messagePostFormat,
enableLocalFile2Url: oldConfig.enableLocalFile2Url,
musicSignUrl: oldConfig.musicSignUrl,
reportSelfMessage: oldConfig.reportSelfMessage,
token: oldConfig.token,
};
return newConfig;
return obj;
}
export function isEqual(obj1: any, obj2: any) {
if (obj1 === obj2) return true;
if (obj1 == null || obj2 == null) return false;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return obj1 === obj2;
if (obj1 === obj2) return true;
if (obj1 == null || obj2 == null) return false;
if (typeof obj1 !== "object" || typeof obj2 !== "object") return obj1 === obj2;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!isEqual(obj1[key], obj2[key])) return false;
}
return true;
for (const key of keys1) {
if (!isEqual(obj1[key], obj2[key])) return false;
}
return true;
}
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
if (os.platform() === 'linux') {
if (os.platform() === "linux") {
return {
baseVersion: "3.2.12-26702",
curVersion: "3.2.12-26702",
prevVersion: "",
onErrorVersions: [],
buildId: "26702",
};
}
return {
baseVersion: '3.2.12-26702',
curVersion: '3.2.12-26702',
prevVersion: '',
onErrorVersions: [],
buildId: '26702'
baseVersion: "9.9.15-26702",
curVersion: "9.9.15-26702",
prevVersion: "",
onErrorVersions: [],
buildId: "26702",
};
}
return {
baseVersion: '9.9.15-26702',
curVersion: '9.9.15-26702',
prevVersion: '',
onErrorVersions: [],
buildId: '26702'
};
}
export async function promisePipeline(promises: Promise<any>[], callback: (result: any) => boolean): Promise<void> {
let callbackCalled = false;
for (const promise of promises) {
if (callbackCalled) break;
try {
const result = await promise;
if (!callbackCalled) {
callbackCalled = callback(result);
}
} catch (error) {
console.error('Error in promise pipeline:', error);
}
}
}
export function getQQVersionConfigPath(exePath: string = ''): string | undefined {
let configVersionInfoPath;
if (os.platform() !== 'linux') {
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
} else {
const userPath = os.homedir();
const appDataPath = path.resolve(userPath, './.config/QQ');
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
}
if (typeof configVersionInfoPath !== 'string') {
return undefined;
}
if (!fs.existsSync(configVersionInfoPath)) {
return undefined;
}
return configVersionInfoPath;
export function getQQVersionConfigPath(exePath: string = ""): string | undefined {
let configVersionInfoPath;
if (os.platform() !== "linux") {
configVersionInfoPath = path.join(path.dirname(exePath), "resources", "app", "versions", "config.json");
} else {
const userPath = os.homedir();
const appDataPath = path.resolve(userPath, "./.config/QQ");
configVersionInfoPath = path.resolve(appDataPath, "./versions/config.json");
}
if (typeof configVersionInfoPath !== "string") {
return undefined;
}
if (!fs.existsSync(configVersionInfoPath)) {
return undefined;
}
return configVersionInfoPath;
}
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
try {
const files = await fsPromise.readdir(directoryPath);
try {
const files = await fsPromise.readdir(directoryPath);
for (const file of files) {
const filePath = path.join(directoryPath, file);
const stats = await fsPromise.stat(filePath);
const lastModifiedTime = stats.mtimeMs;
const currentTime = Date.now();
const timeDifference = currentTime - lastModifiedTime;
const daysDifference = timeDifference / (1000 * 60 * 60 * 24);
for (const file of files) {
const filePath = path.join(directoryPath, file);
const stats = await fsPromise.stat(filePath);
const lastModifiedTime = stats.mtimeMs;
const currentTime = Date.now();
const timeDifference = currentTime - lastModifiedTime;
const daysDifference = timeDifference / (1000 * 60 * 60 * 24);
if (daysDifference > daysThreshold) {
await fsPromise.unlink(filePath); // Delete the file
//console.log(`Deleted: ${filePath}`);
}
if (daysDifference > daysThreshold) {
await fsPromise.unlink(filePath); // Delete the file
//console.log(`Deleted: ${filePath}`);
}
}
} catch (error) {
//console.error('Error deleting files:', error);
}
} catch (error) {
//console.error('Error deleting files:', error);
}
}

View File

View File

@ -65,7 +65,6 @@ async function getMacMachineId(): Promise<string | undefined> {
const homeDir = os.homedir();
export const systemPlatform = os.platform();
export const cpuArch = os.arch();
export const systemVersion = os.release();

View File

@ -1,42 +1,49 @@
import { logDebug } from "@/common/utils/log";
import { LogWrapper } from "@/common/utils/log";
import { NodeIQQNTWrapperSession, WrapperNodeApi } from "./wrapper/wrapper";
import path from "node:path";
import fs from "node:fs";
import { NodeIKernelLoginService } from "./services";
import { NodeIQQNTWrapperSession } from "./wrapper/wrapper";
export enum NCoreWorkMode {
export enum NapCatCoreWorkingEnv {
Unknown = 0,
Shell = 1,
LiteLoader = 2
LiteLoader = 2,
}
export class NapCatCore {
public WorkMode: NCoreWorkMode = NCoreWorkMode.Unknown;
public isInit: boolean = false;
public session: NodeIQQNTWrapperSession | undefined;
private proxyHandler = {
get(target: any, prop: any, receiver: any) {
// console.log('get', prop, typeof target[prop]);
if (typeof target[prop] === 'undefined') {
// 如果方法不存在返回一个函数这个函数调用existentMethod
return (...args: unknown[]) => {
logDebug(`${target.constructor.name} has no method ${prop}`);
};
}
// 如果方法存在,正常返回
return Reflect.get(target, prop, receiver);
}
};
get IsInit(): boolean {
return this.isInit;
export function loadQQWrapper(QQVersion: string): WrapperNodeApi {
let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/app/wrapper.node');
if (!fs.existsSync(wrapperNodePath)) {
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${QQVersion}/wrapper.node`);
}
const nativemodule: any = { exports: {} };
process.dlopen(nativemodule, wrapperNodePath);
return nativemodule.exports;
}
export class NapCatShell extends NapCatCore {
public WorkMode: NCoreWorkMode = NCoreWorkMode.Shell;
Init() {
export class NapCatCore {
readonly workingEnv: NapCatCoreWorkingEnv;
readonly wrapper: WrapperNodeApi;
readonly session: NodeIQQNTWrapperSession;
readonly logger: LogWrapper;
readonly loginService: NodeIKernelLoginService;
constructor(
env: NapCatCoreWorkingEnv,
wrapper: WrapperNodeApi,
session: NodeIQQNTWrapperSession,
logger: LogWrapper,
loginService: NodeIKernelLoginService,
QQVersion: string
) {
this.workingEnv = env;
this.logger = logger;
this.wrapper = wrapper;
this.session = session;
this.loginService = loginService;
}
// Renamed from 'InitDataListener'
initNapCatCoreListeners() {
}
}
export class NapCatLiteLoader extends NapCatCore {
public WorkMode: NCoreWorkMode = NCoreWorkMode.LiteLoader;
Init(WrapperSession: NodeIQQNTWrapperSession, LoginService: NodeIKernelLoginService) {
}
}

0
src/core/index.ts Normal file
View File

8
src/core/wrapper/data.ts Normal file
View File

@ -0,0 +1,8 @@
import { SelfInfo } from "../entities";
export const selfInfo: SelfInfo = {
uid: '',
uin: '',
nick: '',
online: true
};

View File

@ -0,0 +1,55 @@
import path from "node:path";
import fs from "node:fs";
import { PlatformType, VendorType, WrapperSessionInitConfig } from "./wrapper";
import { getMachineId, hostname, systemName, systemVersion } from "@/common/utils/system";
export async function genSessionConfig(QQVersionAppid: string, QQVersion: string, selfUin: string, selfUid: string, account_path: string): Promise<WrapperSessionInitConfig> {
const downloadPath = path.join(account_path, 'NapCat', 'temp');
fs.mkdirSync(downloadPath, { recursive: true });
let guid: string = await getMachineId();//26702 支持JS获取guid值 在LoginService中获取 TODO mlikiow a
const config: WrapperSessionInitConfig = {
selfUin,
selfUid,
desktopPathConfig: {
account_path // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
},
clientVer: QQVersion, // 9.9.8-22355
a2: '',
d2: '',
d2Key: '',
machineId: '',
platform: PlatformType.KWINDOWS, // 3是Windows?
platVer: systemVersion, // 系统版本号, 应该可以固定
appid: QQVersionAppid,
rdeliveryConfig: {
appKey: '',
systemId: 0,
appId: '',
logicEnvironment: '',
platform: PlatformType.KWINDOWS,
language: '',
sdkVersion: '',
userId: '',
appVersion: '',
osVersion: '',
bundleId: '',
serverUrl: '',
fixedAfterHitKeys: ['']
},
defaultFileDownloadPath: downloadPath,
deviceInfo: {
guid,
buildVer: QQVersion,
localId: 2052,
devName: hostname,
devType: systemName,
vendorName: '',
osVer: systemVersion,
vendorOsName: systemName,
setMute: false,
vendorType: VendorType.KNOSETONIOS
},
deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}'
};
return config;
}

View File

@ -1,347 +1,361 @@
import path from 'node:path';
import fs from 'node:fs';
import path from "node:path";
import fs from "node:fs";
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from "../adapters";
import {
NodeIDependsAdapter,
NodeIDispatcherAdapter,
NodeIGlobalAdapter,
} from '../adapters';
NodeIKernelSessionListener,
NodeIKernelMsgListener,
NodeIKernelLoginListener,
NodeIKernelBuddyListener,
NodeIKernelGroupListener,
NodeIKernelProfileListener,
} from "../listeners";
import {
NodeIKernelSessionListener,
NodeIKernelMsgListener,
NodeIKernelLoginListener,
NodeIKernelBuddyListener,
NodeIKernelGroupListener,
NodeIKernelProfileListener,
} from '../listeners';
import {
NodeIKernelLoginService,
NodeIKernelMsgService,
NodeIKernelBuddyService,
NodeIKernelGroupService,
NodeIKernelProfileService,
NodeIKernelProfileLikeService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
NodeIKernelRichMediaService,
NodeIKernelAvatarService,
} from '../services';
import { NodeIKernelStorageCleanService } from '../services/NodeIKernelStorageCleanService';
import { NodeIKernelRobotService } from '../services/NodeIKernelRobotService';
import { NodeIKernelNodeMiscService } from '../services/NodeIKernelNodeMiscService';
import { NodeIKernelUixConvertService } from '../services/NodeIKernelUixConvertService';
import { NodeIKernelMsgBackupService } from '../services/NodeIKernelMsgBackupService';
import { NodeIKernelAlbumService } from '../services/NodeIKernelAlbumService';
import { NodeIKernelTianShuService } from '../services/NodeIKernelTianShuService';
import { NodeIKernelUnitedConfigService } from '../services/NodeIKernelUnitedConfigService';
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
import { NodeIKernelCollectionService } from '../services/NodeIKernelCollectionService';
import { NodeIKernelRecentContactService } from '../services/NodeIKernelRecentContactService';
import { NodeIKernelMSFService } from '../services/NodeIKernelMSFService';
import { NodeIkernelTestPerformanceService } from '../services/NodeIkernelTestPerformanceService';
import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService';
NodeIKernelLoginService,
NodeIKernelMsgService,
NodeIKernelBuddyService,
NodeIKernelGroupService,
NodeIKernelProfileService,
NodeIKernelProfileLikeService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
NodeIKernelRichMediaService,
NodeIKernelAvatarService,
} from "../services";
import { NodeIKernelStorageCleanService } from "../services/NodeIKernelStorageCleanService";
import { NodeIKernelRobotService } from "../services/NodeIKernelRobotService";
import { NodeIKernelNodeMiscService } from "../services/NodeIKernelNodeMiscService";
import { NodeIKernelUixConvertService } from "../services/NodeIKernelUixConvertService";
import { NodeIKernelMsgBackupService } from "../services/NodeIKernelMsgBackupService";
import { NodeIKernelAlbumService } from "../services/NodeIKernelAlbumService";
import { NodeIKernelTianShuService } from "../services/NodeIKernelTianShuService";
import { NodeIKernelUnitedConfigService } from "../services/NodeIKernelUnitedConfigService";
import { NodeIKernelSearchService } from "../services/NodeIKernelSearchService";
import { NodeIKernelCollectionService } from "../services/NodeIKernelCollectionService";
import { NodeIKernelRecentContactService } from "../services/NodeIKernelRecentContactService";
import { NodeIKernelMSFService } from "../services/NodeIKernelMSFService";
import { NodeIkernelTestPerformanceService } from "../services/NodeIkernelTestPerformanceService";
import { NodeIKernelECDHService } from "../services/NodeIKernelECDHService";
export interface NodeQQNTWrapperUtil {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeQQNTWrapperUtil
// eslint-disable-next-line @typescript-eslint/no-misused-new
new (): NodeQQNTWrapperUtil;
getNTUserDataInfoConfig(): string
getNTUserDataInfoConfig(): string;
emptyWorkingSet(n: number): void
emptyWorkingSet(n: number): void;
getSsoCmdOfOidbReq(arg1: number, arg2: number): unknown,
getSsoCmdOfOidbReq(arg1: number, arg2: number): unknown;
getSsoBufferOfOidbReq(...args: unknown[]): unknown,//有点看不懂参数定义 待补充 好像是三个参数
getSsoBufferOfOidbReq(...args: unknown[]): unknown; //有点看不懂参数定义 待补充 好像是三个参数
getOidbRspInfo(arg: string): unknown,//可能是错的
getOidbRspInfo(arg: string): unknown; //可能是错的
getFileSize(path: string): Promise<number>,//直接的猜测
getFileSize(path: string): Promise<number>; //直接的猜测
genFileMd5Buf(arg: string): unknown,//可能是错的
genFileMd5Buf(arg: string): unknown; //可能是错的
genFileMd5Hex(path: string): unknown,//直接的猜测
genFileMd5Hex(path: string): unknown; //直接的猜测
genFileShaBuf(path: string): unknown,//直接的猜测
genFileShaBuf(path: string): unknown; //直接的猜测
genFileCumulateSha1(path: string): unknown,//直接的猜测
genFileCumulateSha1(path: string): unknown; //直接的猜测
genFileShaHex(path: string): unknown,//直接的猜测
genFileShaHex(path: string): unknown; //直接的猜测
fileIsExist(path: string): unknown,
fileIsExist(path: string): unknown;
startTrace(path: string): unknown,//可能是错的
startTrace(path: string): unknown; //可能是错的
copyFile(src: string, dst: string): unknown,
copyFile(src: string, dst: string): unknown;
genFileShaAndMd5Hex(path: string, unknown: number): unknown,//可能是错的
genFileShaAndMd5Hex(path: string, unknown: number): unknown; //可能是错的
setTraceInfo(unknown: Object): unknown,
setTraceInfo(unknown: Object): unknown;
encodeOffLine(unknown: Object): unknown,
encodeOffLine(unknown: Object): unknown;
decodeOffLine(arg: string): unknown,//可能是错的 传递hex
decodeOffLine(arg: string): unknown; //可能是错的 传递hex
DecoderRecentInfo(arg: string): unknown,//可能是错的 传递hex
DecoderRecentInfo(arg: string): unknown; //可能是错的 传递hex
getPinyin(arg0: string, arg1: boolean): unknown,
getPinyin(arg0: string, arg1: boolean): unknown;
matchInPinyin(arg0: any[], arg1: string): unknown,//参数特复杂 arg0是个复杂数据类型
matchInPinyin(arg0: any[], arg1: string): unknown; //参数特复杂 arg0是个复杂数据类型
makeDirByPath(arg0: string): unknown,
makeDirByPath(arg0: string): unknown;
emptyWorkingSet(arg0: number): unknown,//参数是UINT32
emptyWorkingSet(arg0: number): unknown; //参数是UINT32
runProcess(arg0: string, arg1: boolean): unknown,
runProcess(arg0: string, arg1: boolean): unknown;
runProcessArgs(arg0: string, arg1: { [key: string]: string; }, arg2: boolean): unknown,
runProcessArgs(arg0: string, arg1: { [key: string]: string }, arg2: boolean): unknown;
calcThumbSize(arg0: number, arg1: number, arg2: Object): unknown,
calcThumbSize(arg0: number, arg1: number, arg2: Object): unknown;
fullWordToHalfWord(arg0: string): unknown,
fullWordToHalfWord(arg0: string): unknown;
getNTUserDataInfoConfig(): unknown,
getNTUserDataInfoConfig(): unknown;
pathIsReadableAndWriteable(path: string): unknown,//直接的猜测
pathIsReadableAndWriteable(path: string): unknown; //直接的猜测
resetUserDataSavePathToDocument(): unknown,
resetUserDataSavePathToDocument(): unknown;
getSoBuildInfo(): any,//例如 0[0]_d491dc01e0a_0
getSoBuildInfo(): any; //例如 0[0]_d491dc01e0a_0
registerCountInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown,
registerCountInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown;
registerValueInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown,
registerValueInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown;
registerValueInstrumentsWithBoundary(arg0: string, arg1: unknown, arg2: unknown, arg3: number, arg4: number): unknown,
registerValueInstrumentsWithBoundary(
arg0: string,
arg1: unknown,
arg2: unknown,
arg3: number,
arg4: number
): unknown;
reportCountIndicators(arg0: string, arg1: Map<unknown, unknown>, arg2: string, arg3: number, arg4: boolean): unknown,
reportCountIndicators(
arg0: string,
arg1: Map<unknown, unknown>,
arg2: string,
arg3: number,
arg4: boolean
): unknown;
reportValueIndicators(arg0: string, arg1: Map<unknown, unknown>, arg2: string, arg3: boolean, arg4: number): unknown,
reportValueIndicators(
arg0: string,
arg1: Map<unknown, unknown>,
arg2: string,
arg3: boolean,
arg4: number
): unknown;
checkNewUserDataSaveDirAvailable(arg0: string): unknown,
checkNewUserDataSaveDirAvailable(arg0: string): unknown;
copyUserData(arg0: string, arg1: string): Promise<any>,
copyUserData(arg0: string, arg1: string): Promise<any>;
setUserDataSaveDirectory(arg0: string): Promise<any>,
setUserDataSaveDirectory(arg0: string): Promise<any>;
hasOtherRunningQQProcess(): boolean,
hasOtherRunningQQProcess(): boolean;
quitAllRunningQQProcess(arg: boolean): unknown,
quitAllRunningQQProcess(arg: boolean): unknown;
checkNvidiaConfig(): unknown,
checkNvidiaConfig(): unknown;
repairNvidiaConfig(): unknown,
repairNvidiaConfig(): unknown;
getNvidiaDriverVersion(): unknown,
getNvidiaDriverVersion(): unknown;
isNull(): unknown
isNull(): unknown;
}
export interface NodeIQQNTWrapperSession {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeIQQNTWrapperSession;
// eslint-disable-next-line @typescript-eslint/no-misused-new
new (): NodeIQQNTWrapperSession;
init(
wrapperSessionInitConfig: WrapperSessionInitConfig,
nodeIDependsAdapter: NodeIDependsAdapter,
nodeIDispatcherAdapter: NodeIDispatcherAdapter,
nodeIKernelSessionListener: NodeIKernelSessionListener
): void;
init(
wrapperSessionInitConfig: WrapperSessionInitConfig,
nodeIDependsAdapter: NodeIDependsAdapter,
nodeIDispatcherAdapter: NodeIDispatcherAdapter,
nodeIKernelSessionListener: NodeIKernelSessionListener
): void;
startNT(n: 0): void;
startNT(n: 0): void;
startNT(): void;
startNT(): void;
getBdhUploadService(): unknown;
getECDHService(): NodeIKernelECDHService;
getBdhUploadService(): unknown;
getMsgService(): NodeIKernelMsgService;
getECDHService(): NodeIKernelECDHService;
getProfileService(): NodeIKernelProfileService;
getMsgService(): NodeIKernelMsgService;
getProfileLikeService(): NodeIKernelProfileLikeService;
getProfileService(): NodeIKernelProfileService;
getGroupService(): NodeIKernelGroupService;
getProfileLikeService(): NodeIKernelProfileLikeService;
getStorageCleanService(): NodeIKernelStorageCleanService;
getGroupService(): NodeIKernelGroupService;
getBuddyService(): NodeIKernelBuddyService;
getStorageCleanService(): NodeIKernelStorageCleanService;
getRobotService(): NodeIKernelRobotService;
getBuddyService(): NodeIKernelBuddyService;
getTicketService(): NodeIKernelTicketService;
getRobotService(): NodeIKernelRobotService;
getTipOffService(): NodeIKernelTipOffService;
getTicketService(): NodeIKernelTicketService;
getNodeMiscService(): NodeIKernelNodeMiscService;
getTipOffService(): NodeIKernelTipOffService;
getRichMediaService(): NodeIKernelRichMediaService;
getNodeMiscService(): NodeIKernelNodeMiscService;
getMsgBackupService(): NodeIKernelMsgBackupService;
getRichMediaService(): NodeIKernelRichMediaService;
getAlbumService(): NodeIKernelAlbumService;
getMsgBackupService(): NodeIKernelMsgBackupService;
getTianShuService(): NodeIKernelTianShuService;
getAlbumService(): NodeIKernelAlbumService;
getUnitedConfigService(): NodeIKernelUnitedConfigService;
getTianShuService(): NodeIKernelTianShuService;
getSearchService(): NodeIKernelSearchService;
getUnitedConfigService(): NodeIKernelUnitedConfigService;
getDirectSessionService(): unknown;
getSearchService(): NodeIKernelSearchService;
getRDeliveryService(): unknown;
getDirectSessionService(): unknown;
getAvatarService(): NodeIKernelAvatarService;
getRDeliveryService(): unknown;
getFeedChannelService(): unknown;
getAvatarService(): NodeIKernelAvatarService;
getYellowFaceService(): unknown;
getFeedChannelService(): unknown;
getCollectionService(): NodeIKernelCollectionService;
getYellowFaceService(): unknown;
getSettingService(): unknown;
getCollectionService(): NodeIKernelCollectionService;
getQiDianService(): unknown;
getSettingService(): unknown;
getFileAssistantService(): unknown;
getQiDianService(): unknown;
getGuildService(): unknown;
getFileAssistantService(): unknown;
getSkinService(): unknown;
getGuildService(): unknown;
getTestPerformanceService(): NodeIkernelTestPerformanceService;
getSkinService(): unknown;
getQQPlayService(): unknown;
getTestPerformanceService(): NodeIkernelTestPerformanceService;
getDbToolsService(): unknown;
getQQPlayService(): unknown;
getUixConvertService(): NodeIKernelUixConvertService;
getDbToolsService(): unknown;
getOnlineStatusService(): unknown;
getUixConvertService(): NodeIKernelUixConvertService;
getRemotingService(): unknown;
getOnlineStatusService(): unknown;
getGroupTabService(): unknown;
getRemotingService(): unknown;
getGroupSchoolService(): unknown;
getGroupTabService(): unknown;
getLiteBusinessService(): unknown;
getGroupSchoolService(): unknown;
getGuildMsgService(): unknown;
getLiteBusinessService(): unknown;
getLockService(): unknown;
getGuildMsgService(): unknown;
getMSFService(): NodeIKernelMSFService;
getLockService(): unknown;
getGuildHotUpdateService(): unknown;
getMSFService(): NodeIKernelMSFService;
getAVSDKService(): unknown;
getGuildHotUpdateService(): unknown;
getRecentContactService(): NodeIKernelRecentContactService;
getAVSDKService(): unknown;
getConfigMgrService(): unknown;
getRecentContactService(): NodeIKernelRecentContactService;
getConfigMgrService(): unknown;
}
export interface EnginInitDesktopConfig {
base_path_prefix: string,
platform_type: 3,
app_type: 4,
app_version: string,
os_version: string,
use_xlog: true,
qua: string,
global_path_config: {
desktopGlobalPath: string,
},
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }
base_path_prefix: string;
platform_type: 3;
app_type: 4;
app_version: string;
os_version: string;
use_xlog: true;
qua: string;
global_path_config: {
desktopGlobalPath: string;
};
thumb_config: { maxSide: 324; minSide: 48; longLimit: 6; density: 2 };
}
export interface NodeIQQNTWrapperEngine {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeIQQNTWrapperEngine;
initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void;
// eslint-disable-next-line @typescript-eslint/no-misused-new
new (): NodeIQQNTWrapperEngine;
initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void;
}
export interface WrapperNodeApi {
[key: string]: any;
[key: string]: any;
NodeIKernelBuddyListener: NodeIKernelBuddyListener;
NodeIKernelGroupListener: NodeIKernelGroupListener;
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
NodeIKernelMsgListener: NodeIKernelMsgListener;
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
NodeIGlobalAdapter: NodeIGlobalAdapter;
NodeIDependsAdapter: NodeIDependsAdapter;
NodeIDispatcherAdapter: NodeIDispatcherAdapter;
NodeIKernelSessionListener: NodeIKernelSessionListener;
NodeIKernelLoginService: NodeIKernelLoginService;
NodeIKernelLoginListener: NodeIKernelLoginListener;
NodeIKernelProfileService: NodeIKernelProfileService;
NodeIKernelProfileListener: NodeIKernelProfileListener;
NodeIKernelBuddyListener: NodeIKernelBuddyListener;
NodeIKernelGroupListener: NodeIKernelGroupListener;
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
NodeIKernelMsgListener: NodeIKernelMsgListener;
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
NodeIGlobalAdapter: NodeIGlobalAdapter;
NodeIDependsAdapter: NodeIDependsAdapter;
NodeIDispatcherAdapter: NodeIDispatcherAdapter;
NodeIKernelSessionListener: NodeIKernelSessionListener;
NodeIKernelLoginService: NodeIKernelLoginService;
NodeIKernelLoginListener: NodeIKernelLoginListener;
NodeIKernelProfileService: NodeIKernelProfileService;
NodeIKernelProfileListener: NodeIKernelProfileListener;
}
export enum PlatformType {
KUNKNOWN,
KANDROID,
KIOS,
KWINDOWS,
KMAC
KUNKNOWN,
KANDROID,
KIOS,
KWINDOWS,
KMAC,
}
export enum DeviceType {
KUNKNOWN,
KPHONE,
KPAD,
KCOMPUTER
KUNKNOWN,
KPHONE,
KPAD,
KCOMPUTER,
}
//推送类型
export enum VendorType {
KNOSETONIOS = 0,
KSUPPORTGOOGLEPUSH = 99,
KSUPPORTHMS = 3,
KSUPPORTOPPOPUSH = 4,
KSUPPORTTPNS = 2,
KSUPPORTVIVOPUSH = 5,
KUNSUPPORTANDROIDPUSH = 1
KNOSETONIOS = 0,
KSUPPORTGOOGLEPUSH = 99,
KSUPPORTHMS = 3,
KSUPPORTOPPOPUSH = 4,
KSUPPORTTPNS = 2,
KSUPPORTVIVOPUSH = 5,
KUNSUPPORTANDROIDPUSH = 1,
}
export interface WrapperSessionInitConfig {
selfUin: string
selfUid: string
desktopPathConfig: {
account_path: string // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
}
clientVer: string // 9.9.8-22355
a2: string,
d2: string,
d2Key: string,
machineId: string,
platform: PlatformType, // 3是Windows?
platVer: string, // 系统版本号, 应该可以固定
appid: string,
rdeliveryConfig: {
appKey: string,
systemId: number,
appId: string,
logicEnvironment: string,
platform: PlatformType,
language: string,
sdkVersion: string,
userId: string,
appVersion: string,
osVersion: string,
bundleId: string,
serverUrl: string,
fixedAfterHitKeys: string[]
}
defaultFileDownloadPath: string, // 这个可以通过环境变量获取?
deviceInfo: {
guid: string,
buildVer: string,
localId: number,
devName: string,
devType: string,
vendorName: string,
osVer: string,
vendorOsName: string,
setMute: boolean,
vendorType: VendorType
},
deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}'
}
selfUin: string;
selfUid: string;
desktopPathConfig: {
account_path: string; // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
};
clientVer: string; // 9.9.8-22355
a2: string;
d2: string;
d2Key: string;
machineId: string;
platform: PlatformType; // 3是Windows?
platVer: string; // 系统版本号, 应该可以固定
appid: string;
rdeliveryConfig: {
appKey: string;
systemId: number;
appId: string;
logicEnvironment: string;
platform: PlatformType;
language: string;
sdkVersion: string;
userId: string;
appVersion: string;
osVersion: string;
bundleId: string;
serverUrl: string;
fixedAfterHitKeys: string[];
};
defaultFileDownloadPath: string; // 这个可以通过环境变量获取?
deviceInfo: {
guid: string;
buildVer: string;
localId: number;
devName: string;
devType: string;
vendorName: string;
osVer: string;
vendorOsName: string;
setMute: boolean;
vendorType: VendorType;
};
deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}';
}

View File

@ -1,4 +1,68 @@
import { NTEventChannel } from "@/common/framework/event";
import { NapCatPathWrapper } from "@/common/framework/napcat";
import { sleep } from "@/common/utils/helper";
import { LogWrapper } from "@/common/utils/log";
import { proxiedListenerOf } from "@/common/utils/proxy-handler";
import { QQBasicInfoWrapper } from "@/common/utils/QQBasicInfo";
import { NapCatCoreWorkingEnv, loadQQWrapper } from "@/core/core";
import { LoginListener } from "@/core/listeners";
import { NodeIKernelLoginService } from "@/core/services";
import { selfInfo } from "@/core/wrapper/data";
import { WrapperNodeApi, NodeIQQNTWrapperSession } from "@/core/wrapper/wrapper";
import { NapCatOneBot11Adapter } from "@/onebot";
//LiteLoader ES入口文件
export async function NCoreInitLiteLoader() {
export async function NCoreInitLiteLoader(session: NodeIQQNTWrapperSession, loginService: NodeIKernelLoginService) {
//在进入本层前是否登录未进行判断
console.log("NapCat LiteLoader App Loading...");
}
let Basicframework = new NapCatPathWrapper();
let logger = new LogWrapper(Basicframework.logsPath);
let BasicInfo = new QQBasicInfoWrapper({ logger });
let LLNC = new NapCatLiteLoader(logger, session, loginService, BasicInfo);
//直到登录成功后,执行下一步
await new Promise<void>((resolve) => {
let OBLoginListener = new LoginListener();
OBLoginListener.onQRCodeLoginSucceed = async (arg) => resolve();
loginService.addKernelLoginListener(new LLNC.wrapper.NodeIKernelLoginListener(proxiedListenerOf(OBLoginListener, logger)));
});
//启动WebUi
//初始化LLNC的Onebot实现
new NapCatOneBot11Adapter();
}
export class NapCatLiteLoader {
public workingEnv: NapCatCoreWorkingEnv = NapCatCoreWorkingEnv.LiteLoader;
public wrapper: WrapperNodeApi;
public EventChannel: NTEventChannel;
public session: NodeIQQNTWrapperSession;
public logger: LogWrapper;
public loginListener: LoginListener;
//public core: NapCatCore;
constructor(
logger: LogWrapper,
session: NodeIQQNTWrapperSession,
loginService: NodeIKernelLoginService,
QQBasic: QQBasicInfoWrapper
) {
this.session = session;
this.logger = logger;
//context保存
this.wrapper = loadQQWrapper(QQBasic.getFullQQVesion());
//载入Wrapper.node
this.EventChannel = new NTEventChannel(this.wrapper, session);
this.loginListener = new LoginListener();
this.loginListener.onQRCodeLoginSucceed = async (arg) => {
await sleep(2500); // TODO: 等待登录完成 init那堆不知道多久完成 搞清楚之前先用个sleep 2500顶着
selfInfo.uin = arg.uin;
selfInfo.uid = arg.uid;
// 保存基础登录信息
// 初始化DataListener
};
loginService.addKernelLoginListener(new this.wrapper.NodeIKernelLoginListener(
proxiedListenerOf(this.loginListener, logger)
));
}
}

View File

@ -1 +1,4 @@
//OneBot实现类
//OneBot实现类
export class NapCatOneBot11Adapter{
}

View File

@ -1,4 +1,127 @@
import { NapCatPathWrapper } from "@/common/framework/napcat";
import { LogWrapper } from "@/common/utils/log";
import { QQBasicInfoWrapper } from "@/common/utils/QQBasicInfo";
import { hostname, systemVersion } from "@/common/utils/system";
import { DependsAdapter, DispatcherAdapter, GlobalAdapter } from "@/core/adapters";
import { NapCatCoreWorkingEnv, NapCatCore, loadQQWrapper } from "@/core/core";
import { LoginListener, SessionListener } from "@/core/listeners";
import { NodeIKernelLoginService } from "@/core/services";
import { WrapperNodeApi, NodeIQQNTWrapperEngine, NodeQQNTWrapperUtil, NodeIQQNTWrapperSession } from "@/core/wrapper/wrapper";
import path from "path";
import fs from "fs";
import os from "os";
import { genSessionConfig } from "@/core/wrapper/helper";
import { selfInfo } from "@/core/wrapper/data";
// NapCat Shell App ES 入口文件
export async function NCoreInitShell() {
console.log("NapCat Shell App Loading...");
}
let Basicframework = new NapCatPathWrapper();
let logger = new LogWrapper(Basicframework.logsPath);
let BasicInfo = new QQBasicInfoWrapper({ logger });
new NapCatShell(logger, BasicInfo);
}
export class NapCatShell {
public QQWrapper: WrapperNodeApi;
public WorkMode: NapCatCoreWorkingEnv = NapCatCoreWorkingEnv.Shell;
public Core: NapCatCore | undefined;
public engine: NodeIQQNTWrapperEngine;
public util: NodeQQNTWrapperUtil;
loginService: NodeIKernelLoginService;
session: NodeIQQNTWrapperSession;
loginListener: LoginListener;
get dataPath(): string {
let result = this.util.getNTUserDataInfoConfig();
if (!result) {
result = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(result, { recursive: true });
}
return result;
}
get dataPathGlobal(): string {
return path.resolve(this.dataPath, './nt_qq/global');
}
private initSession(BasicInfo: QQBasicInfoWrapper, uin: string, uid: string): Promise<number> {
return new Promise(async (res, rej) => {
if (!BasicInfo.QQVersionAppid) throw new Error("QQVersionAppid must be provided");
const sessionConfig = await genSessionConfig(BasicInfo.QQVersionAppid, BasicInfo.getFullQQVesion(), uin, uid, this.dataPath);
const sessionListener = new SessionListener();
sessionListener.onSessionInitComplete = (r: unknown) => {
if ((r as number) === 0) {
return res(0);
}
rej(r);
};
// const oldOnSendOidbRepl = this.session.onSendOidbRepl;
// this.session.onSendOidbRepl = (...args: unknown[]) => {
// console.log('onSendOidbRepl', args);
// return oldOnSendOidbRepl(...args);
// };
this.session.init(sessionConfig,
new this.QQWrapper.NodeIDependsAdapter(new DependsAdapter()),
new this.QQWrapper.NodeIDispatcherAdapter(new DispatcherAdapter()),
new this.QQWrapper.NodeIKernelSessionListener(sessionListener)
);
try {
this.session.startNT(0);
} catch (__) { /* Empty */
try {
this.session.startNT();
} catch (e) {
rej('init failed ' + e);
}
}
});
}
constructor(logger: LogWrapper, QQBasic: QQBasicInfoWrapper) {
this.QQWrapper = loadQQWrapper(QQBasic.getFullQQVesion());
this.engine = new this.QQWrapper.NodeIQQNTWrapperEngine();
this.util = new this.QQWrapper.NodeQQNTWrapperUtil();
this.loginService = new this.QQWrapper.NodeIKernelLoginService();
this.session = new this.QQWrapper.NodeIQQNTWrapperSession();
this.loginListener = new LoginListener();
this.loginListener.onUserLoggedIn = (userid: string) => {
logger.logError('当前账号(' + userid + ')已登录,无法重复登录');
};
this.initConfig(QQBasic.getFullQQVesion(), QQBasic.QQVersionAppid, QQBasic.QQVersionQua);
this.loginListener.onQRCodeLoginSucceed = (arg) => {
this.initSession(QQBasic, arg.uin, arg.uid).then((r) => {
selfInfo.uin = arg.uin;
selfInfo.uid = arg.uid;
const dataPath = path.resolve(this.dataPath, './NapCat/data');
fs.mkdirSync(dataPath, { recursive: true });
logger.logDebug('本账号数据/缓存目录:', dataPath);
//this.initDataListener();
}).catch((e) => {
logger.logError('initSession failed', e);
throw new Error(`启动失败: ${JSON.stringify(e)}`);
});
};
}
initConfig(QQVersion: string | undefined, QQVersionAppid: string | undefined, QQVersionQua: string | undefined) {
if (!QQVersion || !QQVersionAppid || !QQVersionQua) throw new Error('QQVersion, QQVersionAppid, QQVersionQua must be provided');
this.engine.initWithDeskTopConfig({
base_path_prefix: '',
platform_type: 3,
app_type: 4,
app_version: QQVersion,
os_version: 'Windows 10 Pro',
use_xlog: true,
qua: QQVersionQua,
global_path_config: {
desktopGlobalPath: this.dataPathGlobal,
},
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }
}, new this.QQWrapper.NodeIGlobalAdapter(new GlobalAdapter()));
this.loginService.initConfig({
machineId: '',
appid: QQVersionAppid,
platVer: systemVersion,
commonPath: this.dataPathGlobal,
clientVer: QQVersion,
hostName: hostname
});
}
}