From 0ed969fa3fb695f5164e0c88f9d57b0b513718a5 Mon Sep 17 00:00:00 2001 From: "Wesley F. Young" Date: Sat, 10 Aug 2024 11:56:23 +0800 Subject: [PATCH] fix: hook session init when launching as LL plugin --- src/liteloader/napcat.cjs | 179 ++++++++++++++++++++++---------------- src/liteloader/napcat.ts | 25 ++++-- 2 files changed, 119 insertions(+), 85 deletions(-) diff --git a/src/liteloader/napcat.cjs b/src/liteloader/napcat.cjs index a649af70..656829ff 100644 --- a/src/liteloader/napcat.cjs +++ b/src/liteloader/napcat.cjs @@ -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')); -})(); \ No newline at end of file + 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(); diff --git a/src/liteloader/napcat.ts b/src/liteloader/napcat.ts index f792b066..e77b7dc0 100644 --- a/src/liteloader/napcat.ts +++ b/src/liteloader/napcat.ts @@ -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((resolve) => { + const selfInfo = await new Promise((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(resolvePendingInit => { + registerInitCallback(() => resolvePendingInit()); + }); + resolveSelfInfo({ + uid: loginResult.uid, + uin: loginResult.uin, + nick: '', // 获取不到 + online: true, + }); + }; loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( proxiedListenerOf(loginListener, logger))); });