diff --git a/src/shell/napcat.ts b/src/shell/napcat.ts index 36254a1e..73c8ac95 100644 --- a/src/shell/napcat.ts +++ b/src/shell/napcat.ts @@ -34,54 +34,46 @@ program.option('-q, --qq [number]', 'QQ号').parse(process.argv); const cmdOptions = program.opts(); // NapCat Shell App ES 入口文件 -export async function NCoreInitShell() { - console.log('NapCat Shell App Loading...'); +async function handleUncaughtExceptions(logger: LogWrapper) { process.on('uncaughtException', (err) => { - console.log('[NapCat] [Error] Unhandled Exception:', err.message); + logger.logError('[NapCat] [Error] Unhandled Exception:', err.message); }); process.on('unhandledRejection', (reason, promise) => { - console.log('[NapCat] [Error] unhandledRejection:', reason); + logger.logError('[NapCat] [Error] unhandledRejection:', reason); }); - const pathWrapper = new NapCatPathWrapper(); - const logger = new LogWrapper(pathWrapper.logsPath); - const basicInfoWrapper = new QQBasicInfoWrapper({ logger }); - const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion()); +} - const o3Service = wrapper.NodeIO3MiscService.get(); - o3Service.addO3MiscListener(new NodeIO3MiscListener()); +function getDataPaths(wrapper: WrapperNodeApi): [string, string] { + if (os.platform() === 'darwin') { + const userPath = os.homedir(); + const appDataPath = path.resolve(userPath, './Library/Application Support/QQ'); + return [appDataPath, path.join(appDataPath, 'global')]; + } + let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig(); + if (!dataPath) { + dataPath = path.resolve(os.homedir(), './.config/QQ'); + fs.mkdirSync(dataPath, { recursive: true }); + } + const dataPathGlobal = path.resolve(dataPath, './nt_qq/global'); + return [dataPath, dataPathGlobal]; +} - logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion); - InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger)); - - // from constructor - const engine = wrapper.NodeIQQNTWrapperEngine.get(); - //const util = wrapper.NodeQQNTWrapperUtil.get(); - const loginService = wrapper.NodeIKernelLoginService.get(); - - const session = wrapper.NodeIQQNTWrapperSession.create(); - // from get dataPath - const [dataPath, dataPathGlobal] = (() => { - if (os.platform() === 'darwin') { - const userPath = os.homedir(); - const appDataPath = path.resolve(userPath, './Library/Application Support/QQ'); - return [appDataPath, path.join(appDataPath, 'global')]; - } - let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig(); - if (!dataPath) { - dataPath = path.resolve(os.homedir(), './.config/QQ'); - fs.mkdirSync(dataPath, { recursive: true }); - } - const dataPathGlobal = path.resolve(dataPath, './nt_qq/global'); - return [dataPath, dataPathGlobal]; - })(); +function getPlatformType(): PlatformType { const platformMapping: Partial> = { win32: PlatformType.KWINDOWS, darwin: PlatformType.KMAC, linux: PlatformType.KLINUX, }; - const systemPlatform = platformMapping[os.platform()] ?? PlatformType.KWINDOWS; - if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined'); - // from initConfig + return platformMapping[os.platform()] ?? PlatformType.KWINDOWS; +} + +async function initializeEngine( + engine: any, + basicInfoWrapper: QQBasicInfoWrapper, + dataPathGlobal: string, + systemPlatform: PlatformType, + systemVersion: string +) { engine.initWithDeskTopConfig( { base_path_prefix: '', @@ -98,33 +90,41 @@ export async function NCoreInitShell() { }, new NodeIGlobalAdapter(), ); +} + +async function initializeLoginService( + loginService: NodeIKernelLoginService, + basicInfoWrapper: QQBasicInfoWrapper, + dataPathGlobal: string, + systemVersion: string, + hostname: string +) { loginService.initConfig({ machineId: '', - appid: basicInfoWrapper.QQVersionAppid, + appid: basicInfoWrapper.QQVersionAppid ?? '', platVer: systemVersion, commonPath: dataPathGlobal, clientVer: basicInfoWrapper.getFullQQVesion(), hostName: hostname, }); +} - let quickLoginUin = cmdOptions.qq; // undefined | 'true' | string - const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList; - if (quickLoginUin == 'true') { - if (historyLoginList.length > 0) { - quickLoginUin = historyLoginList[0].uin; - logger.log(`-q 指令指定使用最近的 QQ ${quickLoginUin} 进行快速登录`); - } else { - quickLoginUin = ''; - } - } - const dataTimestape = new Date().getTime().toString(); - o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']); - const selfInfo = await new Promise((resolve) => { +async function handleLogin( + loginService: NodeIKernelLoginService, + logger: LogWrapper, + pathWrapper: NapCatPathWrapper, + quickLoginUin: string | undefined, + historyLoginList: any[], + basicInfoWrapper: QQBasicInfoWrapper, + o3Service: any, + dataTimestape: string +): Promise { + return new Promise((resolve) => { const loginListener = new NodeIKernelLoginListener(); let isLogined = false; - // from constructor + loginListener.onUserLoggedIn = (userid: string) => { - logger.logError.bind(logger)(`当前账号(${userid})已登录,无法重复登录`); + logger.logError(`当前账号(${userid})已登录,无法重复登录`); }; loginListener.onQRCodeLoginSucceed = async (loginResult) => { @@ -132,13 +132,12 @@ export async function NCoreInitShell() { resolve({ uid: loginResult.uid, uin: loginResult.uin, - nick: '', // 获取不到 + nick: '', online: true, }); }; loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => { - //设置WebuiQrcode WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl); const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, ''); @@ -157,50 +156,46 @@ export async function NCoreInitShell() { }); }); }; + loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => { if (!isLogined) { - logger.logError.bind(logger)('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg); + logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg); if (errType == 1 && errCode == 3) { // 二维码过期刷新 } loginService.getQRCodePicture(); } }; + loginListener.onLoginFailed = (args) => { - logger.logError.bind(logger)('[Core] [Login] Login Error , ErrInfo: ', args); + logger.logError('[Core] [Login] Login Error , ErrInfo: ', args); }; loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger)); const isConnect = loginService.connect(); if (!isConnect) { - logger.logError.bind(logger)('核心登录服务连接失败!'); + logger.logError('核心登录服务连接失败!'); return; } + logger.log('核心登录服务连接成功!'); - // 实现WebUi快速登录 + loginService.getLoginList().then((res) => { // 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString())); }); - if (basicInfoWrapper.QQVersionConfig?.curVersion) { - loginService.getLoginMiscData('hotUpdateSign').then((res) => { - if (res.result === 0) { - loginService.setLoginMiscData('hotUpdateSign', res.value); - } - }); - session.getNodeMiscService().writeVersionToRegistry(basicInfoWrapper.QQVersionConfig?.curVersion); - } + WebUiDataRuntime.setQuickLoginCall(async (uin: string) => { return await new Promise((resolve) => { if (uin) { - logger.log.bind(logger)('正在快速登录 ', uin); + logger.log('正在快速登录 ', uin); loginService.quickLoginWithUin(uin).then(res => { if (res.loginErrorInfo.errMsg) { resolve({ result: false, message: res.loginErrorInfo.errMsg }); } resolve({ result: true, message: '' }); }).catch((e) => { - logger.logError.bind(logger)(e); + logger.logError(e); resolve({ result: false, message: '快速登录发生错误' }); }); } else { @@ -216,14 +211,14 @@ export async function NCoreInitShell() { loginService.quickLoginWithUin(quickLoginUin) .then(result => { if (result.loginErrorInfo.errMsg) { - logger.logError.bind(logger)('快速登录错误:', result.loginErrorInfo.errMsg); + logger.logError('快速登录错误:', result.loginErrorInfo.errMsg); if (!isLogined) loginService.getQRCodePicture(); } }) .catch(); }, 1000); } else { - logger.logError.bind(logger)('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式'); + logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式'); if (!isLogined) loginService.getQRCodePicture(); } } else { @@ -237,31 +232,14 @@ export async function NCoreInitShell() { loginService.getQRCodePicture(); } }); - // BEFORE LOGGING IN - const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129'; - o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex'))); - // AFTER LOGGING IN - //99b15bdb4c984fc69d5aa1feb9aa16xx --> 99b15bdb-4c98-4fc6-9d5a-a1feb9aa16xx - //把guid从左向右转换为guid格式 loginService.getMachineGuid() +} - let guid = loginService.getMachineGuid(); - guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20); - //console.log('guid:', guid); - //NodeIO3MiscService/reportAmgomWeather login a6 [ '1726748166943', '184', '329' ] - o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']); - // if(session.getUnitedConfigService()){ - // session.getUnitedConfigService().fetchUnitedCommendConfig([]); - // } - // from initSession - const sessionConfig = await genSessionConfig( - guid, - basicInfoWrapper.QQVersionAppid, - basicInfoWrapper.getFullQQVesion(), - selfInfo.uin, - selfInfo.uid, - dataPath, - ); - await new Promise((resolve, reject) => { +async function initializeSession( + session: NodeIQQNTWrapperSession, + sessionConfig: any, + logger: LogWrapper +) { + return new Promise((resolve, reject) => { const sessionListener = new NodeIKernelSessionListener(); sessionListener.onSessionInitComplete = (r: unknown) => { if (r === 0) { @@ -278,7 +256,7 @@ export async function NCoreInitShell() { ); try { session.startNT(0); - } catch (_) { /* Empty */ + } catch (_) { try { session.startNT(); } catch (e: unknown) { @@ -286,7 +264,60 @@ export async function NCoreInitShell() { } } }); - // Initialization end! +} + +export async function NCoreInitShell() { + console.log('NapCat Shell App Loading...'); + const pathWrapper = new NapCatPathWrapper(); + const logger = new LogWrapper(pathWrapper.logsPath); + handleUncaughtExceptions(logger); + + const basicInfoWrapper = new QQBasicInfoWrapper({ logger }); + const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion()); + + const o3Service = wrapper.NodeIO3MiscService.get(); + o3Service.addO3MiscListener(new NodeIO3MiscListener()); + + logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion); + InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger)); + + const engine = wrapper.NodeIQQNTWrapperEngine.get(); + const loginService = wrapper.NodeIKernelLoginService.get(); + const session = wrapper.NodeIQQNTWrapperSession.create(); + + const [dataPath, dataPathGlobal] = getDataPaths(wrapper); + const systemPlatform = getPlatformType(); + + if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined'); + + await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion); + await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname); + + let quickLoginUin = cmdOptions.qq; + const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList; + + const dataTimestape = new Date().getTime().toString(); + o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']); + + const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList, basicInfoWrapper, o3Service, dataTimestape); + + const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129'; + o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex'))); + + let guid = loginService.getMachineGuid(); + guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20); + o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']); + + const sessionConfig = await genSessionConfig( + guid, + basicInfoWrapper.QQVersionAppid, + basicInfoWrapper.getFullQQVesion(), + selfInfo.uin, + selfInfo.uid, + dataPath, + ); + + await initializeSession(session, sessionConfig, logger); const accountDataPath = path.resolve(dataPath, './NapCat/data'); fs.mkdirSync(dataPath, { recursive: true }); @@ -303,6 +334,7 @@ export async function NCoreInitShell() { ).InitNapCat(); } + export class NapCatShell { readonly core: NapCatCore; readonly context: InstanceContext;