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 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 = { const dlopenOrig = process.dlopen;
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);
}
};
let WrapperSession = undefined;//NativeNpdeSession let wrapperSession;
let WrapperNodeApi = undefined;//NativeNpdeApi let wrapperNodeApi;
let WrapperLoginService = undefined; let wrapperLoginService;
let initCallBack;
Process.dlopen = function (module, filename, flags = os.constants.dlopen.RTLD_LAZY) { // Proxy dlopen
let dlopenRet = this.dlopenOrig(module, filename, flags); process.dlopen = (module, filename, flags = os.constants.dlopen.RTLD_LAZY) => {
for (let export_name in module.exports) { dlopenOrig(module, filename, flags);
for (const export_name in module.exports) {
module.exports[export_name] = new Proxy(module.exports[export_name], { module.exports[export_name] = new Proxy(module.exports[export_name], {
construct: (target, args, _newTarget) => { construct: (target, args, _newTarget) => {
let ret = new target(...args); let constructed;
if (export_name === 'NodeIQQNTWrapperSession') WrapperSession = ret; if (export_name === 'NodeIKernelSessionListener') {
if (export_name === 'NodeIKernelLoginService') WrapperLoginService = ret; let HookedArg = [];
return ret; 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) { if (filename.toLowerCase().includes('wrapper.node')) {
WrapperNodeApi = module.exports; 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 pollForNTInitializationCheck() {
}
function NTIsInit() {
return WrapperSession != undefined && WrapperNodeApi != undefined && WrapperLoginService != undefined;
}
function pollForNTInit() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let isSolve = false; let isSolve = false;
const intervalId = setInterval(() => { const intervalRef = setInterval(() => {
if (isSolve) return; if (isSolve) return;
try { try {
if (NTIsInit()) { if (ntIsInitialized_Internal()) {
clearInterval(intervalId);
isSolve = true; isSolve = true;
resolve(true); resolve(true);
} }
} catch (error) { } catch (error) {
clearInterval(intervalId);
reject(error); reject(error);
} finally {
clearInterval(intervalRef);
} }
}, 500); }, 500);
}); });
} }
async function checkNTIsInit() { export function registerInitCallback(callback) {
return Promise.race([ if (initCallBack === undefined) {
pollForNTInit(), initCallBack = [];
new Promise((_, reject) => setTimeout(() => reject(new Error("NTIsInit is false after 10 seconds")), 10000))
]);
} }
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() { async function NCInit() {
console.log("[NapCat] [Info] 开始初始化NapCat"); 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"; import { sleep } from "@/common/utils/helper";
//LiteLoader ES入口文件 //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..."); console.log("NapCat LiteLoader App Loading...");
const pathWrapper = new NapCatPathWrapper(); const pathWrapper = new NapCatPathWrapper();
@ -20,14 +24,19 @@ export async function NCoreInitLiteLoader(session: NodeIQQNTWrapperSession, logi
const basicInfoWrapper = new QQBasicInfoWrapper({ logger }); const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion()); const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
//直到登录成功后,执行下一步 //直到登录成功后,执行下一步
const selfInfo = await new Promise<SelfInfo>((resolve) => { const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
const loginListener = new LoginListener(); const loginListener = new LoginListener();
loginListener.onQRCodeLoginSucceed = async (loginResult) => resolve({ loginListener.onQRCodeLoginSucceed = async (loginResult) => {
await new Promise<void>(resolvePendingInit => {
registerInitCallback(() => resolvePendingInit());
});
resolveSelfInfo({
uid: loginResult.uid, uid: loginResult.uid,
uin: loginResult.uin, uin: loginResult.uin,
nick: '', // 获取不到 nick: '', // 获取不到
online: true online: true,
}); });
};
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener(
proxiedListenerOf(loginListener, logger))); proxiedListenerOf(loginListener, logger)));
}); });