diff --git a/.env.universal b/.env.universal new file mode 100644 index 00000000..6949eb6a --- /dev/null +++ b/.env.universal @@ -0,0 +1,2 @@ +VITE_BUILD_TYPE = Production +VITE_BUILD_PLATFORM = Universal \ No newline at end of file diff --git a/package.json b/package.json index d84974da..51e22e4a 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,11 @@ "type": "module", "version": "4.2.7", "scripts": { + "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", "build:shell": "npm run build:webui && vite build --mode shell || exit 1", "build:webui": "cd napcat.webui && vite build", + "dev:universal": "vite build --mode universal", "dev:framework": "vite build --mode framework", "dev:shell": "vite build --mode shell", "dev:webui": "cd napcat.webui && npm run webui:dev", diff --git a/src/shell/base.ts b/src/shell/base.ts new file mode 100644 index 00000000..d39c1bb3 --- /dev/null +++ b/src/shell/base.ts @@ -0,0 +1,366 @@ +import type { SelfInfo } from '@/core/types'; + +import { LogWrapper } from '@/common/log'; +import { NodeIKernelLoginListener, NodeIKernelSessionListener } from '@/core/listeners'; +import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '@/core/adapters'; +import { NapCatPathWrapper } from '@/common/path'; +import { + genSessionConfig, + InstanceContext, + loadQQWrapper, + NapCatCore, + NapCatCoreWorkingEnv, + NodeIQQNTWrapperSession, + PlatformType, + WrapperNodeApi, +} from '@/core'; +import { QQBasicInfoWrapper } from '@/common/qq-basic-info'; +import { hostname, systemVersion } from '@/common/system'; +import { proxiedListenerOf } from '@/common/proxy-handler'; + +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import { NodeIKernelLoginService } from '@/core/services'; +import { program } from 'commander'; +import qrcode from 'qrcode-terminal'; +import { NapCatOneBot11Adapter } from '@/onebot'; +import { InitWebUi } from '@/webui'; +import { WebUiDataRuntime } from '@/webui/src/helper/Data'; +import { napCatVersion } from '@/common/version'; +import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener'; +// NapCat Shell App ES 入口文件 +async function handleUncaughtExceptions(logger: LogWrapper) { + process.on('uncaughtException', (err) => { + logger.logError('[NapCat] [Error] Unhandled Exception:', err.message); + }); + process.on('unhandledRejection', (reason, promise) => { + logger.logError('[NapCat] [Error] unhandledRejection:', reason); + }); +} + +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]; +} + +function getPlatformType(): PlatformType { + const platformMapping: Partial> = { + win32: PlatformType.KWINDOWS, + darwin: PlatformType.KMAC, + linux: PlatformType.KLINUX, + }; + return platformMapping[os.platform()] ?? PlatformType.KWINDOWS; +} + +async function initializeEngine( + engine: any, + basicInfoWrapper: QQBasicInfoWrapper, + dataPathGlobal: string, + systemPlatform: PlatformType, + systemVersion: string +) { + engine.initWithDeskTopConfig( + { + base_path_prefix: '', + platform_type: systemPlatform, + app_type: 4, + app_version: basicInfoWrapper.getFullQQVesion(), + os_version: systemVersion, + use_xlog: false, + qua: basicInfoWrapper.QQVersionQua, + global_path_config: { + desktopGlobalPath: dataPathGlobal, + }, + thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }, + }, + new NodeIGlobalAdapter(), + ); +} + +async function initializeLoginService( + loginService: NodeIKernelLoginService, + basicInfoWrapper: QQBasicInfoWrapper, + dataPathGlobal: string, + systemVersion: string, + hostname: string +) { + loginService.initConfig({ + machineId: '', + appid: basicInfoWrapper.QQVersionAppid ?? '', + platVer: systemVersion, + commonPath: dataPathGlobal, + clientVer: basicInfoWrapper.getFullQQVesion(), + hostName: hostname, + }); +} + +async function handleLogin( + loginService: NodeIKernelLoginService, + logger: LogWrapper, + pathWrapper: NapCatPathWrapper, + quickLoginUin: string | undefined, + historyLoginList: any[] +): Promise { + return new Promise((resolve) => { + const loginListener = new NodeIKernelLoginListener(); + let isLogined = false; + + loginListener.onUserLoggedIn = (userid: string) => { + logger.logError(`当前账号(${userid})已登录,无法重复登录`); + }; + + loginListener.onQRCodeLoginSucceed = async (loginResult) => { + isLogined = true; + resolve({ + uid: loginResult.uid, + uin: loginResult.uin, + nick: '', + online: true, + }); + }; + + loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => { + WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl); + + const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, ''); + const buffer = Buffer.from(realBase64, 'base64'); + logger.logWarn('请扫描下面的二维码,然后在手Q上授权登录:'); + const qrcodePath = path.join(pathWrapper.cachePath, 'qrcode.png'); + qrcode.generate(qrcodeUrl, { small: true }, (res) => { + logger.logWarn([ + '\n', + res, + '二维码解码URL: ' + qrcodeUrl, + '如果控制台二维码无法扫码,可以复制解码url到二维码生成网站生成二维码再扫码,也可以打开下方的二维码路径图片进行扫码。', + ].join('\n')); + fs.writeFile(qrcodePath, buffer, {}, () => { + logger.logWarn('二维码已保存到', qrcodePath); + }); + }); + }; + + loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => { + if (!isLogined) { + logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg); + if (errType == 1 && errCode == 3) { + // 二维码过期刷新 + } + loginService.getQRCodePicture(); + } + }; + + loginListener.onLoginFailed = (args) => { + logger.logError('[Core] [Login] Login Error , ErrInfo: ', args); + }; + + loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger)); + const isConnect = loginService.connect(); + if (!isConnect) { + logger.logError('核心登录服务连接失败!'); + return; + } + + logger.log('核心登录服务连接成功!'); + + 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())); + }); + + WebUiDataRuntime.setQuickLoginCall(async (uin: string) => { + return await new Promise((resolve) => { + if (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(e); + resolve({ result: false, message: '快速登录发生错误' }); + }); + } else { + resolve({ result: false, message: '快速登录失败' }); + } + }); + }); + + if (quickLoginUin) { + if (historyLoginList.some(u => u.uin === quickLoginUin)) { + logger.log('正在快速登录 ', quickLoginUin); + setTimeout(() => { + loginService.quickLoginWithUin(quickLoginUin) + .then(result => { + if (result.loginErrorInfo.errMsg) { + logger.logError('快速登录错误:', result.loginErrorInfo.errMsg); + if (!isLogined) loginService.getQRCodePicture(); + } + }) + .catch(); + }, 1000); + } else { + logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式'); + if (!isLogined) loginService.getQRCodePicture(); + } + } else { + logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式'); + if (historyLoginList.length > 0) { + logger.log(`可用于快速登录的 QQ:\n${historyLoginList + .map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`) + .join('\n') + }`); + } + loginService.getQRCodePicture(); + } + }); +} + +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) { + resolve(); + } else { + reject(new Error('登录异常' + r?.toString())); + } + }; + session.init( + sessionConfig, + new NodeIDependsAdapter(), + new NodeIDispatcherAdapter(), + sessionListener, + ); + try { + session.startNT(0); + } catch (_) { + try { + session.startNT(); + } catch (e: unknown) { + reject(new Error('init failed ' + (e as Error).message)); + } + } + }); +} + +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(e => logger.logError(e)); + + 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); + + program.option('-q, --qq [number]', 'QQ号').parse(process.argv); + const cmdOptions = program.opts(); + const 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); + + 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 }); + logger.logDebug('本账号数据/缓存目录:', accountDataPath); + + await new NapCatShell( + wrapper, + session, + logger, + loginService, + selfInfo, + basicInfoWrapper, + pathWrapper, + ).InitNapCat(); +} + + +export class NapCatShell { + readonly core: NapCatCore; + readonly context: InstanceContext; + + constructor( + wrapper: WrapperNodeApi, + session: NodeIQQNTWrapperSession, + logger: LogWrapper, + loginService: NodeIKernelLoginService, + selfInfo: SelfInfo, + basicInfoWrapper: QQBasicInfoWrapper, + pathWrapper: NapCatPathWrapper, + ) { + this.context = { + workingEnv: NapCatCoreWorkingEnv.Shell, + wrapper, + session, + logger, + loginService, + basicInfoWrapper, + pathWrapper, + }; + this.core = new NapCatCore(this.context, selfInfo); + + + + } + async InitNapCat() { + await this.core.initCore(); + new NapCatOneBot11Adapter(this.core, this.context, this.context.pathWrapper).InitOneBot() + .catch(e => this.context.logger.logError('初始化OneBot失败', e)); + } +} + diff --git a/src/shell/napcat.ts b/src/shell/napcat.ts index 8b4d5a69..ae05d794 100644 --- a/src/shell/napcat.ts +++ b/src/shell/napcat.ts @@ -1,369 +1,3 @@ -import type { SelfInfo } from '@/core/types'; - -import { LogWrapper } from '@/common/log'; -import { NodeIKernelLoginListener, NodeIKernelSessionListener } from '@/core/listeners'; -import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '@/core/adapters'; -import { NapCatPathWrapper } from '@/common/path'; -import { - genSessionConfig, - InstanceContext, - loadQQWrapper, - NapCatCore, - NapCatCoreWorkingEnv, - NodeIQQNTWrapperSession, - PlatformType, - WrapperNodeApi, -} from '@/core'; -import { QQBasicInfoWrapper } from '@/common/qq-basic-info'; -import { hostname, systemVersion } from '@/common/system'; -import { proxiedListenerOf } from '@/common/proxy-handler'; - -import path from 'path'; -import fs from 'fs'; -import os from 'os'; -import { NodeIKernelLoginService } from '@/core/services'; -import { program } from 'commander'; -import qrcode from 'qrcode-terminal'; -import { NapCatOneBot11Adapter } from '@/onebot'; -import { InitWebUi } from '@/webui'; -import { WebUiDataRuntime } from '@/webui/src/helper/Data'; -import { napCatVersion } from '@/common/version'; -import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener'; - -program.option('-q, --qq [number]', 'QQ号').parse(process.argv); -const cmdOptions = program.opts(); - -// NapCat Shell App ES 入口文件 -async function handleUncaughtExceptions(logger: LogWrapper) { - process.on('uncaughtException', (err) => { - logger.logError('[NapCat] [Error] Unhandled Exception:', err.message); - }); - process.on('unhandledRejection', (reason, promise) => { - logger.logError('[NapCat] [Error] unhandledRejection:', reason); - }); -} - -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]; -} - -function getPlatformType(): PlatformType { - const platformMapping: Partial> = { - win32: PlatformType.KWINDOWS, - darwin: PlatformType.KMAC, - linux: PlatformType.KLINUX, - }; - return platformMapping[os.platform()] ?? PlatformType.KWINDOWS; -} - -async function initializeEngine( - engine: any, - basicInfoWrapper: QQBasicInfoWrapper, - dataPathGlobal: string, - systemPlatform: PlatformType, - systemVersion: string -) { - engine.initWithDeskTopConfig( - { - base_path_prefix: '', - platform_type: systemPlatform, - app_type: 4, - app_version: basicInfoWrapper.getFullQQVesion(), - os_version: systemVersion, - use_xlog: false, - qua: basicInfoWrapper.QQVersionQua, - global_path_config: { - desktopGlobalPath: dataPathGlobal, - }, - thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }, - }, - new NodeIGlobalAdapter(), - ); -} - -async function initializeLoginService( - loginService: NodeIKernelLoginService, - basicInfoWrapper: QQBasicInfoWrapper, - dataPathGlobal: string, - systemVersion: string, - hostname: string -) { - loginService.initConfig({ - machineId: '', - appid: basicInfoWrapper.QQVersionAppid ?? '', - platVer: systemVersion, - commonPath: dataPathGlobal, - clientVer: basicInfoWrapper.getFullQQVesion(), - hostName: hostname, - }); -} - -async function handleLogin( - loginService: NodeIKernelLoginService, - logger: LogWrapper, - pathWrapper: NapCatPathWrapper, - quickLoginUin: string | undefined, - historyLoginList: any[] -): Promise { - return new Promise((resolve) => { - const loginListener = new NodeIKernelLoginListener(); - let isLogined = false; - - loginListener.onUserLoggedIn = (userid: string) => { - logger.logError(`当前账号(${userid})已登录,无法重复登录`); - }; - - loginListener.onQRCodeLoginSucceed = async (loginResult) => { - isLogined = true; - resolve({ - uid: loginResult.uid, - uin: loginResult.uin, - nick: '', - online: true, - }); - }; - - loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => { - WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl); - - const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, ''); - const buffer = Buffer.from(realBase64, 'base64'); - logger.logWarn('请扫描下面的二维码,然后在手Q上授权登录:'); - const qrcodePath = path.join(pathWrapper.cachePath, 'qrcode.png'); - qrcode.generate(qrcodeUrl, { small: true }, (res) => { - logger.logWarn([ - '\n', - res, - '二维码解码URL: ' + qrcodeUrl, - '如果控制台二维码无法扫码,可以复制解码url到二维码生成网站生成二维码再扫码,也可以打开下方的二维码路径图片进行扫码。', - ].join('\n')); - fs.writeFile(qrcodePath, buffer, {}, () => { - logger.logWarn('二维码已保存到', qrcodePath); - }); - }); - }; - - loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => { - if (!isLogined) { - logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg); - if (errType == 1 && errCode == 3) { - // 二维码过期刷新 - } - loginService.getQRCodePicture(); - } - }; - - loginListener.onLoginFailed = (args) => { - logger.logError('[Core] [Login] Login Error , ErrInfo: ', args); - }; - - loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger)); - const isConnect = loginService.connect(); - if (!isConnect) { - logger.logError('核心登录服务连接失败!'); - return; - } - - logger.log('核心登录服务连接成功!'); - - 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())); - }); - - WebUiDataRuntime.setQuickLoginCall(async (uin: string) => { - return await new Promise((resolve) => { - if (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(e); - resolve({ result: false, message: '快速登录发生错误' }); - }); - } else { - resolve({ result: false, message: '快速登录失败' }); - } - }); - }); - - if (quickLoginUin) { - if (historyLoginList.some(u => u.uin === quickLoginUin)) { - logger.log('正在快速登录 ', quickLoginUin); - setTimeout(() => { - loginService.quickLoginWithUin(quickLoginUin) - .then(result => { - if (result.loginErrorInfo.errMsg) { - logger.logError('快速登录错误:', result.loginErrorInfo.errMsg); - if (!isLogined) loginService.getQRCodePicture(); - } - }) - .catch(); - }, 1000); - } else { - logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式'); - if (!isLogined) loginService.getQRCodePicture(); - } - } else { - logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式'); - if (historyLoginList.length > 0) { - logger.log(`可用于快速登录的 QQ:\n${historyLoginList - .map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`) - .join('\n') - }`); - } - loginService.getQRCodePicture(); - } - }); -} - -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) { - resolve(); - } else { - reject(new Error('登录异常' + r?.toString())); - } - }; - session.init( - sessionConfig, - new NodeIDependsAdapter(), - new NodeIDispatcherAdapter(), - sessionListener, - ); - try { - session.startNT(0); - } catch (_) { - try { - session.startNT(); - } catch (e: unknown) { - reject(new Error('init failed ' + (e as Error).message)); - } - } - }); -} - -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(e => logger.logError(e)); - - 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); - - const 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); - - 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 }); - logger.logDebug('本账号数据/缓存目录:', accountDataPath); - - await new NapCatShell( - wrapper, - session, - logger, - loginService, - selfInfo, - basicInfoWrapper, - pathWrapper, - ).InitNapCat(); -} - - -export class NapCatShell { - readonly core: NapCatCore; - readonly context: InstanceContext; - - constructor( - wrapper: WrapperNodeApi, - session: NodeIQQNTWrapperSession, - logger: LogWrapper, - loginService: NodeIKernelLoginService, - selfInfo: SelfInfo, - basicInfoWrapper: QQBasicInfoWrapper, - pathWrapper: NapCatPathWrapper, - ) { - this.context = { - workingEnv: NapCatCoreWorkingEnv.Shell, - wrapper, - session, - logger, - loginService, - basicInfoWrapper, - pathWrapper, - }; - this.core = new NapCatCore(this.context, selfInfo); - - - - } - async InitNapCat() { - await this.core.initCore(); - new NapCatOneBot11Adapter(this.core, this.context, this.context.pathWrapper).InitOneBot() - .catch(e => this.context.logger.logError('初始化OneBot失败', e)); - } -} +import { NCoreInitShell } from "./base"; NCoreInitShell(); diff --git a/src/universal/napcat.ts b/src/universal/napcat.ts new file mode 100644 index 00000000..1c07175f --- /dev/null +++ b/src/universal/napcat.ts @@ -0,0 +1,7 @@ +import { NCoreInitShell } from "@/shell/base"; + +export * from "@/framework/napcat"; +export * from "@/shell/base"; +if ((global as any).LiteLoader == undefined) { + NCoreInitShell(); +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index d3502f50..fe592985 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,9 +6,7 @@ import { builtinModules } from 'module'; //依赖排除 const external = ['silk-wasm', 'ws', 'express', 'qrcode-terminal', 'fluent-ffmpeg', 'piscina']; const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat(); -function genCpModule(module: string) { - return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false }; -} + let startScripts: string[] | undefined = undefined; if (process.env.NAPCAT_BUILDSYS == 'linux') { startScripts = []; @@ -17,6 +15,29 @@ if (process.env.NAPCAT_BUILDSYS == 'linux') { } else { startScripts = ['./script/KillQQ.bat']; } + +const UniversalBaseConfigPlugin: PluginOption[] = [ + cp({ + targets: [ + { src: './manifest.json', dest: 'dist' }, + { src: './src/core/external/napcat.json', dest: 'dist/config/' }, + { src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, + { src: './napcat.webui/dist/', dest: 'dist/static/', flatten: false }, + { src: './src/framework/liteloader.cjs', dest: 'dist' }, + { src: './src/framework/napcat.cjs', dest: 'dist' }, + { src: './src/framework/preload.cjs', dest: 'dist' }, + { src: './src/framework/renderer.js', dest: 'dist' }, + { src: './package.json', dest: 'dist' }, + { src: './logo.png', dest: 'dist' }, + { src: './launcher/', dest: 'dist', flatten: true }, + ...startScripts.map((startScript) => { + return { src: startScript, dest: 'dist' }; + }), + ], + }), + nodeResolve(), +]; + const FrameworkBaseConfigPlugin: PluginOption[] = [ cp({ targets: [ @@ -34,6 +55,8 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [ }), nodeResolve(), ]; + + const ShellBaseConfigPlugin: PluginOption[] = [ cp({ targets: [ @@ -49,6 +72,35 @@ const ShellBaseConfigPlugin: PluginOption[] = [ }), nodeResolve(), ]; +const UniversalBaseConfig = () => + defineConfig({ + resolve: { + conditions: ['node', 'default'], + alias: { + '@/core': resolve(__dirname, './src/core'), + '@': resolve(__dirname, './src'), + './lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg', + '@webapi': resolve(__dirname, './src/webui/src'), + }, + }, + build: { + sourcemap: false, + target: 'esnext', + minify: false, + lib: { + entry: { + napcat: 'src/universal/napcat.ts', + 'audio-worker': 'src/common/audio-worker.ts', + }, + formats: ['es'], + fileName: (_, entryName) => `${entryName}.mjs`, + }, + rollupOptions: { + external: [...nodeModules, ...external], + }, + }, + }); + const ShellBaseConfig = () => defineConfig({ @@ -114,6 +166,11 @@ export default defineConfig(({ mode }): UserConfig => { ...ShellBaseConfig(), plugins: [...ShellBaseConfigPlugin], }; + } else if (mode == 'universal') { + return { + ...UniversalBaseConfig(), + plugins: [...UniversalBaseConfigPlugin], + }; } else { return { ...FrameworkBaseConfig(),