diff --git a/package.json b/package.json index 8a5232bf..79400237 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "type": "module", "version": "1.8.3", "scripts": { - "build:liteloader": "vite build --mode ", - "build:shell": "vite build --mode production", + "build:liteloader": "vite build --mode liteloader", + "build:shell": "vite build --mode shell", "build:webui": "cd ./src/webui && vite build", "lint": "eslint --fix src/**/*.{js,ts}", "depend": "cd dist && npm install --omit=dev" diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..d72ae75b --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +//LiteLoader/Shell ES统一出包口 +if(!(window as any).LiteLoader) { + //LiteLoader 模式 +} +//Shell模式 \ No newline at end of file diff --git a/src/liteloader/napcat.ts b/src/liteloader/napcat.ts index 0c7d9f23..47dae4fc 100644 --- a/src/liteloader/napcat.ts +++ b/src/liteloader/napcat.ts @@ -9,7 +9,7 @@ import { SelfInfo } from "@/core/entities"; import { LoginListener } from "@/core/listeners"; import { NodeIKernelLoginService } from "@/core/services"; import { WrapperNodeApi, NodeIQQNTWrapperSession } from "@/core/wrapper/wrapper"; -import { NapCatOneBot11Adapter } from "@/onebot"; +import { NapCatOneBot11Adapter } from "@/onebot/main"; //LiteLoader ES入口文件 export async function NCoreInitLiteLoader(session: NodeIQQNTWrapperSession, loginService: NodeIKernelLoginService) { diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 0a00655c..9af304e0 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -1,12 +1,2 @@ -import { InstanceContext, NapCatCore } from "@/core"; - -//OneBot实现类 -export class NapCatOneBot11Adapter{ - readonly core: NapCatCore; - readonly context: InstanceContext; - - constructor(core: NapCatCore, context: InstanceContext) { - this.core = core; - this.context = context; - } -} \ No newline at end of file +export * from './main'; +export * from './types'; \ No newline at end of file diff --git a/src/onebot/main.ts b/src/onebot/main.ts new file mode 100644 index 00000000..498199eb --- /dev/null +++ b/src/onebot/main.ts @@ -0,0 +1,14 @@ +import { NapCatCore, InstanceContext } from "@/core"; + + +//OneBot实现类 + +export class NapCatOneBot11Adapter { + readonly core: NapCatCore; + readonly context: InstanceContext; + + constructor(core: NapCatCore, context: InstanceContext) { + this.core = core; + this.context = context; + } +} diff --git a/src/onebot/types/entity.ts b/src/onebot/types/entity.ts new file mode 100644 index 00000000..d17eab8b --- /dev/null +++ b/src/onebot/types/entity.ts @@ -0,0 +1,65 @@ +export interface OB11User { + user_id: number; + nickname: string; + remark?: string; + sex?: OB11UserSex; + level?: number; + age?: number; + qid?: string; + login_days?: number; + categroyName?:string; + categoryId?:number; +} + +export enum OB11UserSex { + male = 'male', + female = 'female', + unknown = 'unknown' +} + +export enum OB11GroupMemberRole { + owner = 'owner', + admin = 'admin', + member = 'member', +} + +export interface OB11GroupMember { + group_id: number + user_id: number + nickname: string + card?: string + sex?: OB11UserSex + age?: number + join_time?: number + last_sent_time?: number + level?: string + qq_level?: number + role?: OB11GroupMemberRole + title?: string + area?: string + unfriendly?: boolean + title_expire_time?: number + card_changeable?: boolean + // 以下为gocq字段 + shut_up_timestamp?: number + // 以下为扩展字段 + is_robot?: boolean + qage?: number +} + +export interface OB11Group { + group_id: number + group_name: string + member_count?: number + max_member_count?: number +} + +export interface OB11Sender { + user_id: number, + nickname: string, + sex?: OB11UserSex, + age?: number, + card?: string, // 群名片 + level?: string, // 群等级 + role?: OB11GroupMemberRole +} diff --git a/src/onebot/types/index.ts b/src/onebot/types/index.ts new file mode 100644 index 00000000..4f248b07 --- /dev/null +++ b/src/onebot/types/index.ts @@ -0,0 +1 @@ +export * from './entity'; diff --git a/src/shell/napcat.ts b/src/shell/napcat.ts index ef4ceff4..23676107 100644 --- a/src/shell/napcat.ts +++ b/src/shell/napcat.ts @@ -28,119 +28,95 @@ export async function NCoreInitShell() { let basicInfoWrapper = new QQBasicInfoWrapper({ logger }); let wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion()); + // from constructor + let engine = new wrapper.NodeIQQNTWrapperEngine(); + let util = new wrapper.NodeQQNTWrapperUtil(); + let loginService = new wrapper.NodeIKernelLoginService(); + let session = new wrapper.NodeIQQNTWrapperSession(); + + // from get dataPath + let dataPath = util.getNTUserDataInfoConfig(); + if (!dataPath) { + dataPath = path.resolve(os.homedir(), './.config/QQ'); + fs.mkdirSync(dataPath, { recursive: true }); + } + let dataPathGlobal = path.resolve(dataPath, './nt_qq/global'); + + // from initConfig + engine.initWithDeskTopConfig( + { + base_path_prefix: '', + platform_type: 3, + app_type: 4, + app_version: basicInfoWrapper.getFullQQVesion(), + os_version: 'Windows 10 Pro', + use_xlog: true, + qua: basicInfoWrapper.QQVersionQua!, + global_path_config: { + desktopGlobalPath: dataPathGlobal, + }, + thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 } + }, + new wrapper.NodeIGlobalAdapter(new GlobalAdapter()) + ); + loginService.initConfig({ + machineId: '', + appid: basicInfoWrapper.QQVersionAppid!, + platVer: systemVersion, + commonPath: dataPathGlobal, + clientVer: basicInfoWrapper.getFullQQVesion(), + hostName: hostname + }); + let selfInfo = await new Promise((resolve) => { let loginListener = new LoginListener(); + + // from constructor + loginListener.onUserLoggedIn = (userid: string) => { + logger.logError('当前账号(' + userid + ')已登录,无法重复登录'); + }; + loginListener.onQRCodeLoginSucceed = async (loginResult) => resolve({ uid: loginResult.uid, uin: loginResult.uin, nick: '', // 获取不到 online: true }); - loginService.addKernelLoginListener(); + loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( + proxiedListenerOf(loginListener, logger))); + }); - let ShellNapCat = new NapCatShell(logger, BasicInfo); - ShellNapCat.loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( - proxiedListenerOf(loginListener, logger))); -}) + // from initSession + const sessionConfig = await genSessionConfig( + basicInfoWrapper.QQVersionAppid!, + basicInfoWrapper.getFullQQVesion(), + selfInfo.uin, + selfInfo.uid, + dataPath + ); + const sessionListener = new SessionListener(); + sessionListener.onSessionInitComplete = (r: unknown) => { + if (r !== 0) { + throw r; + } + }; + session.init( + sessionConfig, + new wrapper.NodeIDependsAdapter(new DependsAdapter()), + new wrapper.NodeIDispatcherAdapter(new DispatcherAdapter()), + new wrapper.NodeIKernelSessionListener(sessionListener) + ); + try { + session.startNT(0); + } catch (__) { + session.startNT(); // may still throw error; we do not catch that + } + // Initialization end! + + const accountDataPath = path.resolve(dataPath, './NapCat/data'); + fs.mkdirSync(dataPath, { recursive: true }); + logger.logDebug('本账号数据/缓存目录:', accountDataPath); } export class NapCatShell { - public QQWrapper: WrapperNodeApi; - public WorkMode: NapCatCoreWorkingEnv = NapCatCoreWorkingEnv.Shell; - public Core: NapCatCore | undefined; - public engine: NodeIQQNTWrapperEngine; - public util: NodeQQNTWrapperUtil; - loginService: NodeIKernelLoginService; - session: NodeIQQNTWrapperSession; - loginListener: LoginListener; - - get dataPath(): string { - let result = this.util.getNTUserDataInfoConfig(); - if (!result) { - result = path.resolve(os.homedir(), './.config/QQ'); - fs.mkdirSync(result, { recursive: true }); - } - return result; - } - - get dataPathGlobal(): string { - return path.resolve(this.dataPath, './nt_qq/global'); - } - - private initSession(BasicInfo: QQBasicInfoWrapper, uin: string, uid: string): Promise { - return new Promise(async (res, rej) => { - if (!BasicInfo.QQVersionAppid) throw new Error("QQVersionAppid must be provided"); - const sessionConfig = await genSessionConfig(BasicInfo.QQVersionAppid, BasicInfo.getFullQQVesion(), uin, uid, this.dataPath); - const sessionListener = new SessionListener(); - sessionListener.onSessionInitComplete = (r: unknown) => { - if ((r as number) === 0) { - return res(0); - } - rej(r); - }; - this.session.init(sessionConfig, - new this.QQWrapper.NodeIDependsAdapter(new DependsAdapter()), - new this.QQWrapper.NodeIDispatcherAdapter(new DispatcherAdapter()), - new this.QQWrapper.NodeIKernelSessionListener(sessionListener) - ); - try { - this.session.startNT(0); - } catch (__) { /* Empty */ - try { - this.session.startNT(); - } catch (e) { - rej('init failed ' + e); - } - } - }); - } - constructor(logger: LogWrapper, QQBasic: QQBasicInfoWrapper) { - this.QQWrapper = loadQQWrapper(QQBasic.getFullQQVesion()); - this.engine = new this.QQWrapper.NodeIQQNTWrapperEngine(); - this.util = new this.QQWrapper.NodeQQNTWrapperUtil(); - this.loginService = new this.QQWrapper.NodeIKernelLoginService(); - this.session = new this.QQWrapper.NodeIQQNTWrapperSession(); - this.loginListener = new LoginListener(); - this.loginListener.onUserLoggedIn = (userid: string) => { - logger.logError('当前账号(' + userid + ')已登录,无法重复登录'); - }; - this.initConfig(QQBasic.getFullQQVesion(), QQBasic.QQVersionAppid, QQBasic.QQVersionQua); - this.loginListener.onQRCodeLoginSucceed = (arg) => { - this.initSession(QQBasic, arg.uin, arg.uid).then((r) => { - selfInfo.uin = arg.uin; - selfInfo.uid = arg.uid; - const dataPath = path.resolve(this.dataPath, './NapCat/data'); - fs.mkdirSync(dataPath, { recursive: true }); - logger.logDebug('本账号数据/缓存目录:', dataPath); - //this.initDataListener(); - }).catch((e) => { - logger.logError('initSession failed', e); - throw new Error(`启动失败: ${JSON.stringify(e)}`); - }); - }; - } - initConfig(QQVersion: string | undefined, QQVersionAppid: string | undefined, QQVersionQua: string | undefined) { - if (!QQVersion || !QQVersionAppid || !QQVersionQua) throw new Error('QQVersion, QQVersionAppid, QQVersionQua must be provided'); - this.engine.initWithDeskTopConfig({ - base_path_prefix: '', - platform_type: 3, - app_type: 4, - app_version: QQVersion, - os_version: 'Windows 10 Pro', - use_xlog: true, - qua: QQVersionQua, - global_path_config: { - desktopGlobalPath: this.dataPathGlobal, - }, - thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 } - }, new this.QQWrapper.NodeIGlobalAdapter(new GlobalAdapter())); - this.loginService.initConfig({ - machineId: '', - appid: QQVersionAppid, - platVer: systemVersion, - commonPath: this.dataPathGlobal, - clientVer: QQVersion, - hostName: hostname - }); - } } diff --git a/vite.config.ts b/vite.config.ts index 082fe9d8..e4953f9c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,14 +8,14 @@ import nodeResolve from '@rollup/plugin-node-resolve'; import { builtinModules } from 'module'; import fs from 'node:fs'; import babel from 'vite-plugin-babel'; +//依赖排除 const external = ['silk-wasm', 'ws', 'express', 'fluent-ffmpeg', 'log4js', 'qrcode-terminal']; - const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat(); -// let nodeModules = ["fs", "path", "events", "buffer", "url", "crypto", "fs/promise", "fsPromise", "os", "http", "net"] -// nodeModules = [...nodeModules, ...nodeModules.map(m => `node:${m}`)] + 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') { if (process.env.NAPCAT_BUILDARCH == 'x64') { @@ -47,47 +47,63 @@ const baseConfigPlugin: PluginOption[] = [ cp({ targets: [ // ...external.map(genCpModule), - { src: './src/napcat.json', dest: 'dist/config/' }, - { src: './static/', dest: 'dist/static/', flatten: false }, - { src: './src/onebot11/onebot11.json', dest: 'dist/config/' }, - { src: './package.json', dest: 'dist' }, - { src: './README.md', dest: 'dist' }, - { src: './logo.png', dest: 'dist/logs' }, - ...(startScripts.map((startScript) => { - return { src: startScript, dest: 'dist' }; - })), + // { src: './src/napcat.json', dest: 'dist/config/' }, + // { src: './static/', dest: 'dist/static/', flatten: false }, + // { src: './src/onebot11/onebot11.json', dest: 'dist/config/' }, + // { src: './package.json', dest: 'dist' }, + // { src: './README.md', dest: 'dist' }, + // { src: './logo.png', dest: 'dist/logs' }, + // ...(startScripts.map((startScript) => { + // return { src: startScript, dest: 'dist' }; + // })), ] }), nodeResolve(), ]; - -let corePath = resolve(__dirname, './src/core/src'); -if (!fs.existsSync(corePath)) { - corePath = resolve(__dirname, './src/core.lib/src'); -} -const baseConfig = (mode: string = 'shell') => defineConfig({ +const ShellBaseConfig = () => defineConfig({ resolve: { conditions: ['node', 'default'], alias: { - '@/core': corePath, + '@/core': resolve(__dirname, './src/core/src'), '@': resolve(__dirname, './src'), './lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg', }, }, build: { - sourcemap: mode === 'development', + sourcemap: false, target: 'esnext', - // minify: mode === 'production' ? 'esbuild' : false, - // 压缩代码出现了未知问题导致无法运行,暂时不启用 minify: false, lib: { - entry: mode === "shell" ? 'src/shell/napcat.ts' : "src/liteloader/napcat.ts", + entry: 'src/shell/napcat.ts', + formats: ['es'], + fileName: () => 'napcat.mjs', + }, + rollupOptions: { + external: [...nodeModules, ...external] + }, + }, +}); + +const LLBaseConfig = () => defineConfig({ + resolve: { + conditions: ['node', 'default'], + alias: { + '@/core': resolve(__dirname, './src/core/src'), + '@': resolve(__dirname, './src'), + './lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg', + }, + }, + build: { + sourcemap: false, + target: 'esnext', + minify: false, + lib: { + entry: "src/liteloader/napcat.ts", formats: ['es'], fileName: () => 'napcat.mjs', }, rollupOptions: { - // external: [ /node:*/ ], external: [...nodeModules, ...external] }, }, @@ -96,14 +112,14 @@ const baseConfig = (mode: string = 'shell') => defineConfig({ export default defineConfig(({ mode }): UserConfig => { if (mode === 'shell') { return { - ...baseConfig(mode), + ...ShellBaseConfig(), plugins: [ ...baseConfigPlugin ] }; } else { return { - ...baseConfig(mode), + ...LLBaseConfig(), plugins: baseConfigPlugin, }; }