fix: hook session init when launching as LL plugin

This commit is contained in:
Wesley F. Young 2024-08-10 11:56:23 +08:00
parent 90a7b5e0d3
commit 0ed969fa3f
2 changed files with 119 additions and 85 deletions

View File

@ -1,109 +1,134 @@
// NapCat CommonJS 入口文件
// https://github.com/NapNeko/LiteLoader-NapCatExample/blob/main/src/common/proxy.ts
// By Mlikiowa
const process = require('process');
const os = require('os');
const path = require('path');
const CurrentPath = path.dirname(__filename);
let Process = require('process');
let os = require('os');
Process.dlopenOrig = Process.dlopen;
const currentPath = path.dirname(__filename);
let proxyHandler = {
get(target, prop, receiver) {
if (typeof target[prop] === 'undefined') {
return (...args) => {
console.log(`[NapCat] [Info] ${target.constructor.name} ${prop}`, ...args);
};
}
return Reflect.get(target, prop, receiver);
}
};
const dlopenOrig = process.dlopen;
let WrapperSession = undefined;//NativeNpdeSession
let WrapperNodeApi = undefined;//NativeNpdeApi
let WrapperLoginService = undefined;
let wrapperSession;
let wrapperNodeApi;
let wrapperLoginService;
let initCallBack;
Process.dlopen = function (module, filename, flags = os.constants.dlopen.RTLD_LAZY) {
let dlopenRet = this.dlopenOrig(module, filename, flags);
for (let export_name in module.exports) {
// Proxy dlopen
process.dlopen = (module, filename, flags = os.constants.dlopen.RTLD_LAZY) => {
dlopenOrig(module, filename, flags);
for (const export_name in module.exports) {
module.exports[export_name] = new Proxy(module.exports[export_name], {
construct: (target, args, _newTarget) => {
let ret = new target(...args);
if (export_name === 'NodeIQQNTWrapperSession') WrapperSession = ret;
if (export_name === 'NodeIKernelLoginService') WrapperLoginService = ret;
return ret;
let constructed;
if (export_name === 'NodeIKernelSessionListener') {
let HookedArg = [];
for (let ArgIndex in args) {
if (args[ArgIndex] instanceof Object) {
let HookArg = {};
for (let ListenerName in args[ArgIndex]) {
HookArg[ListenerName] = function (...ListenerData) {
try {
if (ListenerName === "onSessionInitComplete") {
//回调成功
initCallBack.forEach((cb) => cb(...ListenerData));
clearHook();
}
//console.log("Construct-ARG-Apply", ListenerName, JSON.stringify(ListenerData, null, 2));
} catch (error) {
// ignored
}
args[ArgIndex][ListenerName](...ListenerData);
};
HookedArg.push(HookArg);
}
} else {
// 其它类型
//console.log("Construct-ARG-NotProxy", args[keyArg]);
}
}
constructed = new target(...HookedArg);
} else {
constructed = new target(...args);
}
if (export_name === 'NodeIQQNTWrapperSession') wrapperSession = constructed;
if (export_name === 'NodeIKernelLoginService') wrapperLoginService = constructed;
return constructed;
},
});
}
if (filename.toLowerCase().indexOf('wrapper.node') != -1) {
WrapperNodeApi = module.exports;
if (filename.toLowerCase().includes('wrapper.node')) {
wrapperNodeApi = module.exports;
}
return dlopenRet;
};
function getWrapperSession() {
return WrapperSession;
/**
* 清理 Hook
*/
function clearHook() {
initCallBack = [];
process.dlopen = dlopenOrig;
}
function getWrapperLoginService() {
return WrapperLoginService;
function ntIsInitialized_Internal() {
return wrapperSession !== undefined
&& wrapperNodeApi !== undefined
&& wrapperLoginService !== undefined;
}
function getWrapperNodeApi() {
return WrapperNodeApi;
}
function NTIsInit() {
return WrapperSession != undefined && WrapperNodeApi != undefined && WrapperLoginService != undefined;
}
function pollForNTInit() {
function pollForNTInitializationCheck() {
return new Promise((resolve, reject) => {
let isSolve = false;
const intervalId = setInterval(() => {
const intervalRef = setInterval(() => {
if (isSolve) return;
try {
if (NTIsInit()) {
clearInterval(intervalId);
if (ntIsInitialized_Internal()) {
isSolve = true;
resolve(true);
}
} catch (error) {
clearInterval(intervalId);
reject(error);
} finally {
clearInterval(intervalRef);
}
}, 500);
});
}
async function checkNTIsInit() {
return Promise.race([
pollForNTInit(),
new Promise((_, reject) => setTimeout(() => reject(new Error("NTIsInit is false after 10 seconds")), 10000))
]);
export function registerInitCallback(callback) {
if (initCallBack === undefined) {
initCallBack = [];
}
initCallBack.push(callback);
}
async function fetchServices(timeout = 10000) {
return Promise.race([
pollForNTInitializationCheck(),
new Promise((resolve) => {
setTimeout(() => resolve(false), timeout);
})
]).then(result => result ?
{ wrapperSession, wrapperNodeApi, wrapperLoginService } :
Promise.reject()
);
}
async function NCInit() {
console.log("[NapCat] [Info] 开始初始化NapCat");
const { NCoreInitLiteLoader } = await import("file://" + path.join(CurrentPath, './napcat.mjs'));
//传入LoginService Session 其余自载入
await NCoreInitLiteLoader(getWrapperSession(), getWrapperLoginService());
//console.log("[NapCat] [Info] NapCat初始化完成");
}
(async () => {
try {
await checkNTIsInit();
}
catch (error) {
console.log("[NapCat] [Error] 很遗憾在NTQQ初始化阶段失败");
return;
//阻止下一步
}
console.log("[NapCat] [Info] NTQQ初始化成功");
console.log(getWrapperSession(), getWrapperLoginService());
NCInit().then().catch(console.log);
//NTCore.instance = new NTCoreWrapper(getWrapperNodeApi(), getWrapperSession());
// 挂载NTQQ 到 NapCat Core
//let NCLoginListener = {};
// NCLoginListener.onQRCodeLoginSucceed = (arg) => {
// //登录成功 登录成功立刻进入真正初始化
// console.log("[NapCat] [Info] UIN: ", arg.uin, " 登录成功!")
// NCInit().then().catch();
// }
// 添加Login监听
//getWrapperLoginService().addKernelLoginListener(new (getWrapperNodeApi().NodeIKernelLoginListener)(new Proxy(NCLoginListener, proxyHandler)));
//await import("file://" + path.join(CurrentPath, './napcat.mjs'));
})();
try {
const { wrapperSession, wrapperLoginService } = await fetchServices();
const { NCoreInitLiteLoader } = await import('file://' + path.join(currentPath, './napcat.mjs'));
//传入LoginService Session 其余自载入
await NCoreInitLiteLoader(wrapperSession, wrapperLoginService, registerInitCallback);
//console.log("[NapCat] [Info] NapCat初始化完成");
} catch (error) {
console.error("[NapCat] [Error] 初始化NapCat失败", error);
}
}
NCInit();

View File

@ -12,7 +12,11 @@ import { NapCatOneBot11Adapter } from "@/onebot/main";
import { sleep } from "@/common/utils/helper";
//LiteLoader ES入口文件
export async function NCoreInitLiteLoader(session: NodeIQQNTWrapperSession, loginService: NodeIKernelLoginService) {
export async function NCoreInitLiteLoader(
session: NodeIQQNTWrapperSession,
loginService: NodeIKernelLoginService,
registerInitCallback: (callback: () => void) => void
) {
//在进入本层前是否登录未进行判断
console.log("NapCat LiteLoader App Loading...");
const pathWrapper = new NapCatPathWrapper();
@ -20,14 +24,19 @@ export async function NCoreInitLiteLoader(session: NodeIQQNTWrapperSession, logi
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
//直到登录成功后,执行下一步
const selfInfo = await new Promise<SelfInfo>((resolve) => {
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
const loginListener = new LoginListener();
loginListener.onQRCodeLoginSucceed = async (loginResult) => resolve({
uid: loginResult.uid,
uin: loginResult.uin,
nick: '', // 获取不到
online: true
});
loginListener.onQRCodeLoginSucceed = async (loginResult) => {
await new Promise<void>(resolvePendingInit => {
registerInitCallback(() => resolvePendingInit());
});
resolveSelfInfo({
uid: loginResult.uid,
uin: loginResult.uin,
nick: '', // 获取不到
online: true,
});
};
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener(
proxiedListenerOf(loginListener, logger)));
});