From ab46375b3df5a07c0fdce33e30b092446278a722 Mon Sep 17 00:00:00 2001 From: ningmengchongshui <916415899@qq.com> Date: Sat, 8 Jun 2024 20:52:49 +0800 Subject: [PATCH] feat: ts-node --- .npmrc | 21 +++- .prettierignore | 2 + .prettierrc.json | 15 +++ README.md | 125 +++++++----------------- package.json | 42 +++++--- src/core/functional.ts | 63 ++++++++++++ src/core/index.ts | 4 + src/core/local.js | 1 + src/core/plugin.ts | 214 +++++++++++++++++++++++++++++++++++++++++ src/core/types.ts | 18 ++++ src/index.ts | 3 + src/mys/index.ts | 1 + src/utils/index.ts | 1 + tsconfig.json | 24 +++++ 14 files changed, 429 insertions(+), 105 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 src/core/functional.ts create mode 100644 src/core/index.ts create mode 100644 src/core/local.js create mode 100644 src/core/plugin.ts create mode 100644 src/core/types.ts create mode 100644 src/index.ts create mode 100644 src/mys/index.ts create mode 100644 src/utils/index.ts create mode 100644 tsconfig.json diff --git a/.npmrc b/.npmrc index cadb394..259a46b 100644 --- a/.npmrc +++ b/.npmrc @@ -1,5 +1,24 @@ +#为项目单独设置镜像 registry=https://registry.npmmirror.com +# canvas +canvas_binary_host_mirror=https://ghproxy.com/https://github.com/Automattic/node-canvas/releases/download/ +# node-sass +sass_binary_site=https://npmmirror.com/mirrors/node-sass/ +# sqlite3 node_sqlite3_binary_host_mirror=https://npmmirror.com/mirrors/sqlite3 -canvas_binary_host_mirror=https://npmmirror.com/mirrors/canvas +# TFJS +TFJS_NODE_CDN_STORAGE=https://cdn.npmmirror.com/binaries/ +# pup +PUPPETER_DOWNLOAD_BASE_URL=https://npmmirror.com/mirrors/chrome-for-testing +# 不生成lock +package-lock=false +# 改为 npm 依赖安装方式 +node-linker=hoisted +# 可耻的提升 +shamefully-hoist=true +# 严格的对等依赖关系 +strict-peer-dependencies=false +# sharp_binary_host=https://npmmirror.com/mirrors/sharp +# sharp_libvips_binary_host=https://npmmirror.com/mirrors/sharp-libvips \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..5d31392 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Node dependencies +node_modules diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..4d12705 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "tabWidth": 2, + "singleQuote": true, + "printWidth": 80, + "trailingComma": "none", + "useTabs": false, + "proseWrap": "preserve", + "arrowParens": "avoid", + "bracketSpacing": true, + "endOfLine": "auto", + "quoteProps": "consistent", + "vueIndentScriptAndStyle": true + } \ No newline at end of file diff --git a/README.md b/README.md index 9166236..d7cd11f 100644 --- a/README.md +++ b/README.md @@ -1,126 +1,75 @@ # Miao-Yunzai v3 -基于乐神版[云崽v3.0](https://gitee.com/le-niao/Yunzai-Bot) 改造 +这里是Miao-Yunzai V4 测试仓库, -需要同时安装[miao-plugin](https://github.com/yoimiya-kokomi/miao-plugin.git) ,且后续的一些底层改造可能会改变数据结构,无法直接迁回原版Yunzai,请根据自己需求情况慎重安装 +你应该积极使用 V3 ,它仍然是长期支持并维护的版本。 -使用[icqq](https://github.com/icqqjs/icqq) 登录,防止oicq可能出现的低版本问题 +哪怕 V4 后续发布,V3仍然接受长期支持并维护。 ---- -与原版Yunzai-Bot的差异: +在功能点未完成测试之前,仓库不会发布任何有关新功能信息。 -**【注意】:** 由于是独立新的仓库,【只建议新部署/部署后迁移】,不建议原Bot直接换源强更 +> 必要环境 Windows/Linux + Chrome/Chromium/Edge -* **一些新特性:** Miao-Yunzai会逐步重构,增加新特性与功能,可能会有功能与形态上的变化。如期望功能更加稳定可使用此仓库[Yunzai-V3](https://gitee.com/yoimiya-kokomi/Yunzai-Bot) -* **移除了签到功能:** 与原Yunzai独立的仓库,去除了较为敏感的签到功能,以尝试恢复[Github](https://github.com/yoimiya-kokomi/Miao-Yunzai.git) - 环境。附加[Gitee](https://gitee.com/yoimiya-kokomi/Miao-Yunzai.git) -* **默认启用喵版的功能:** 【#角色】【#深渊】【#帮助】等功能默认启用喵版,原版的逻辑会屏蔽,以便于后续逐步精简资源 -* **一键迁移 TRSS-Yunzai:** 若无法登录QQ,可尝试 `node trss` 迁移,迁移后可登录其他协议端 [TRSS-Yunzai](https://gitee.com/TimeRainStarSky/Yunzai) +> 必要环境 18.18.2>Node.js>16.14.0 + Redis>5.0.0 -## Miao-Yunzai后续计划 +如果你的系统不支持18.18.2版本,最低能下载16.14.0版本,这是最新的puppeteer版本限制。 -先刨坑,但也许会咕咕咕 - -* 功能与`miao-plugin`部分功能进行整合或升级 - * [√] 角色卡片、抽卡分析等使用`miao-plugin`版本 - * `miao-plugin`的帮助、设置、版本信息会升至`Miao-Yunzai`,以支持更多场景 -* 一些底层会与`miao-plugin`做更深层的联动,以支持一些高级功能 - * [√] 星铁底层支持,原神&星铁多UID支持 - * 基于面板信息的uid管理及认证 - * ck切换感知等 -* 逐步实验一些新的特性 - * 更完备的plugin基础能力支持 - * 第三方 IM / Bot / WebAPI 对接或适配等 - -项目仅供学习交流使用,严禁用于任何商业用途和非法行为 - -## 使用方法 - -> 环境准备: Windows or Linux,Node.js( [版本至少v16以上](http://nodejs.cn/download/) ), [Redis](https://redis.io/docs/getting-started/installation/ ) - -1.克隆项目并安装miao-plugin - -请根据网络情况选择Github安装或Gitee安装 - -``` -# 使用 Github -git clone --depth=1 https://github.com/yoimiya-kokomi/Miao-Yunzai.git -cd Miao-Yunzai -git clone --depth=1 https://github.com/yoimiya-kokomi/miao-plugin.git ./plugins/miao-plugin/ +该版本将支持TS、TSX环境,提供Miao-Yunzai完全的类型声明及其开发文档。 -# 使用Gitee -git clone --depth=1 https://gitee.com/yoimiya-kokomi/Miao-Yunzai.git -cd Miao-Yunzai -git clone --depth=1 https://gitee.com/yoimiya-kokomi/miao-plugin.git ./plugins/miao-plugin/ -``` +## 新版目录 -2.安装[pnpm](https://pnpm.io/zh/installation) ,已安装的可以跳过 +- 核心源码 -``` -# 使用npmjs.org安装 -npm install pnpm -g +src/core -# 指定国内源npmmirror.com安装 -npm --registry=https://registry.npmmirror.com install pnpm -g -``` +- 接口板块 -3.安装依赖 +src/mts -``` -# 直接安装 -pnpm install -P +- 工具类 -# 如依赖安装缓慢或失败,可尝试更换国内npm源后再执行install命令 -pnpm config set registry https://registry.npmmirror.com -pnpm install -P -``` +src/utils -4.运行(首次运行按提示输入登录) +## 环境补充 -``` -node app -``` - -## 常见问题 - -### puppeteer 相关问题 - -linux环境,其他环境请自行探索 +### Centos ```sh - puppeteer Chromium 启动中... - Error: Failed to launch the browser process! +yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y ``` -1. 先检查node版本是否大于14 (不大于14请去升级版本) +- 字体 ```sh - node -v +yum groupinstall fonts -y ``` -2. 如果大于14 则可能是缺失一些库 请安装这些 (点击代码块右上角直接复制,如果报错可以尝试 sudo) +- libstdc -### 依赖库 +下载 [libstdc++.so.6.0.29.zip](https://baiyin1314.lanzouq.com/i8Nr21ig8hyf) + +将 **解压缩后** 的文件放在/usr/lib64/中 ```sh - yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y -``` - -### 乱码字体解决办法(centos,安装不了请换源) - -```sh - yum groupinstall fonts -y -``` - -### centos7 监听事件错误 "CXXABI_1.3.8" not found 解决办法 -下载 [libstdc++.so.6.0.29.zip](https://baiyin1314.lanzouq.com/i8Nr21ig8hyf) 将 **解压缩后** 的文件放在/usr/lib64/中 -``` cd /usr/lib64/ sudo mv libstdc++.so.6 libstdc++.so.6.bak sudo ln -s libstdc++.so.6.0.29 libstdc++.so.6 ``` +# Unknown file ".ts" + +node >= 20.0.0 + +```ts +ts-node alemon.config.ts +``` + +更改为 + +```ts +node --no-warnings=ExperimentalWarning --loader ts-node/esm alemon.config.ts +``` ## 致谢 diff --git a/package.json b/package.json index a21ecf7..44a0f3c 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,15 @@ { "name": "miao-yunzai", - "version": "3.1.3", + "version": "4.0.0-rc.0", "author": "Yoimiya-Kokomi, Le-niao", - "description": "QQ group Bot", - "main": "app.js", + "description": "QQ Group Bot", + "main": "src/index.ts", "type": "module", "scripts": { - "app": "node .", - "dev": "node . dev", - "login": "node . login", - "web": "node ./lib/tools/web.js", - "test": "node ./lib/tools/test.js", - "start": "pm2 start ./config/pm2/pm2.json", - "stop": "pm2 stop ./config/pm2/pm2.json", - "restart": "pm2 restart ./config/pm2/pm2.json", - "log": "node ./lib/tools/log.js", - "ksr": "node ./lib/tools/ksr.js" + "app": "ts-node app.js", + "dev": "ts-node app.js dev", + "login": "ts-node app.js login", + "format": "prettier --write ." }, "dependencies": { "art-template": "^4.13.2", @@ -37,7 +31,11 @@ "redis": "^4.6.13", "sequelize": "^6.37.1", "sqlite3": "5.1.6", - "yaml": "^2.4.1" + "yaml": "^2.4.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "express": "^4.19.2", + "express-art-template": "^1.0.1" }, "devDependencies": { "eslint": "^8.57.0", @@ -45,8 +43,20 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^16.6.2", "eslint-plugin-promise": "^6.1.1", - "express": "^4.19.2", - "express-art-template": "^1.0.1" + "@rollup/plugin-alias": "^5.1.0", + "@rollup/plugin-multi-entry": "^6.0.0", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.3", + "@types/react-dom": "^18.2.22", + "@types/lodash": "^4.14.200", + "@types/node": "^20.8.5", + "@types/redis": "^4.0.11", + "@types/ws": "^8.5.7", + "nodemon": "^3.0.1", + "rollup": "^4.16.4", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "prettier": "^3.0.3" }, "imports": { "#miao": "./plugins/miao-plugin/components/index.js", diff --git a/src/core/functional.ts b/src/core/functional.ts new file mode 100644 index 0000000..0fded7d --- /dev/null +++ b/src/core/functional.ts @@ -0,0 +1,63 @@ +import { MessageCallBackType } from './types.js' +import plugin from '../../lib/plugins/plugin.js' + +// 插件super默认值 +export const PluginSuperDefine = { + name: 'group-app', + dsc: 'group-dsc', + event: 'message', + priority: 9999 +} + +// 消息 +export class Messages { + count = 0 + rule: { + reg: RegExp + fnc: string + }[] = [] + response(reg: RegExp, fnc: MessageCallBackType) { + this.count++ + const propName = `prop_${this.count}` + this[propName] = fnc + this.rule.push({ + reg, + fnc: propName + }) + } + get ok() { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const App = this + class Children extends plugin { + constructor() { + super({ + ...PluginSuperDefine, + rule: App.rule + }) + for (const key of App.rule) { + if (App[key.fnc] instanceof Function) { + this[key.fnc] = App[key.fnc].bind(App) + } + } + } + } + return Children + } +} + +/** + * 事件 + */ +export class Events { + count = 0 + data: { + [key: string]: typeof plugin + } = {} + use(val: typeof plugin) { + this.count++ + this.data[this.count] = val + } + get ok() { + return this.data + } +} \ No newline at end of file diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..864e0a8 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,4 @@ +import plugin from '../../lib/plugins/plugin.js' +export { plugin } +export * from './functional.js' +export * from './types.js' diff --git a/src/core/local.js b/src/core/local.js new file mode 100644 index 0000000..bf058f7 --- /dev/null +++ b/src/core/local.js @@ -0,0 +1 @@ +export * from "#miao" diff --git a/src/core/plugin.ts b/src/core/plugin.ts new file mode 100644 index 0000000..518ea71 --- /dev/null +++ b/src/core/plugin.ts @@ -0,0 +1,214 @@ +import { Common } from './local.js' + +import { EventType } from './types.js' + +const stateArr = {} +const SymbolTimeout = Symbol('Timeout') +const SymbolResolve = Symbol('Resolve') + +export default class plugin { + name = 'your-plugin' + dsc = '无' + rule: { + reg?: RegExp | string + fnc: string + event?: string + log?: boolean + permission?: string + }[] = [] + event = 'message' + priority = 9999 + task = null + namespace = null + handler = null + e: EventType + + /** + * @param name 插件名称 + * @param dsc 插件描述 + * @param handler handler配置 + * @param handler.key handler支持的事件key + * @param handler.fn handler的处理func + * @param namespace namespace,设置handler时建议设置 + * @param event 执行事件,默认message + * @param priority 优先级,数字越小优先级越高 + * @param rule + * @param rule.reg 命令正则 + * @param rule.fnc 命令执行方法 + * @param rule.event 执行事件,默认message + * @param rule.log false时不显示执行日志 + * @param rule.permission 权限 master,owner,admin,all + * @param task + * @param task.name 定时任务名称 + * @param task.cron 定时任务cron表达式 + * @param task.fnc 定时任务方法名 + * @param task.log false时不显示执行日志 + */ + constructor({ + name, + dsc, + handler, + namespace, + event, + priority = 5000, + task, + rule + }: { + name?: typeof this.name + dsc?: typeof this.dsc + namespace?: typeof this.namespace + priority?: typeof this.priority + handler?: typeof this.handler + event?: typeof this.event + task?: typeof this.task + rule?: typeof this.rule + }) { + name && (this.name = name) + dsc && (this.dsc = dsc) + event && (this.event = event) + priority && (this.priority = priority) + + /** 插件名称 */ + this.name = name + /** 插件描述 */ + this.dsc = dsc + /** 监听事件,默认message https://oicqjs.github.io/oicq/#events */ + this.event = event + /** 优先级 */ + this.priority = priority + /** 定时任务,可以是数组 */ + this.task = { + /** 任务名 */ + name: '', + /** 任务方法名 */ + fnc: task.fnc || '', + /** 任务cron表达式 */ + cron: task.cron || '' + } + /** 命令规则 */ + this.rule = rule + + if (handler) { + this.handler = handler + this.namespace = namespace || '' + } + } + + /** + * @param msg 发送的消息 + * @param quote 是否引用回复 + * @param data.recallMsg 群聊是否撤回消息,0-120秒,0不撤回 + * @param data.at 是否at用户 + */ + reply(msg = '', quote = false, data = {}) { + if (!this.e?.reply || !msg) return false + return this.e.reply(msg, quote, data) + } + + /** + * ****** + * tudo + * 异常写法 + * ***** + */ + group_id: number + groupId: number + user_id: number + userId: number + + /** + * + * @param isGroup + * @returns + */ + conKey(isGroup = false) { + if (isGroup) { + return `${this.name}.${this.group_id || this.groupId || this.e.group_id}` + } else { + return `${this.name}.${this.user_id || this.userId || this.e.user_id}` + } + } + + /** + * @param type 执行方法 + * @param isGroup 是否群聊 + * @param time 操作时间 + * @param timeout 操作超时回复 + */ + setContext( + type: string, + isGroup = false, + time = 120, + timeout = '操作超时已取消' + ) { + const key = this.conKey(isGroup) + if (!stateArr[key]) stateArr[key] = {} + stateArr[key][type] = this.e + if (time) + stateArr[key][type][SymbolTimeout] = setTimeout(() => { + if (stateArr[key][type]) { + const resolve = stateArr[key][type][SymbolResolve] + delete stateArr[key][type] + resolve ? resolve(false) : this.reply(timeout, true) + } + }, time * 1000) + return stateArr[key][type] + } + + /** + * + * @param type + * @param isGroup + * @returns + */ + getContext(type: string, isGroup?: boolean) { + if (type) return stateArr[this.conKey(isGroup)]?.[type] + return stateArr[this.conKey(isGroup)] + } + + /** + * + * @param type + * @param isGroup + */ + finish(type:string, isGroup?: boolean) { + const key = this.conKey(isGroup) + if (stateArr[key]?.[type]) { + clearTimeout(stateArr[key][type][SymbolTimeout]) + delete stateArr[key][type] + } + } + + /** + * + * @param args + * @returns + */ + awaitContext(...args) { + return new Promise( + resolve => + (this.setContext('resolveContext', ...args)[SymbolResolve] = resolve) + ) + } + + /** + * + * @param context + */ + resolveContext(context) { + this.finish('resolveContext') + context[SymbolResolve](this.e) + } + + /** + * + * @param plugin + * @param tpl + * @param data + * @param cfg + * @returns + */ + async renderImg(plugin, tpl, data, cfg) { + return Common.render(plugin, tpl, data, { ...cfg, e: this.e }) + } +} \ No newline at end of file diff --git a/src/core/types.ts b/src/core/types.ts new file mode 100644 index 0000000..e6d00dd --- /dev/null +++ b/src/core/types.ts @@ -0,0 +1,18 @@ +import { type GroupMessage } from 'icqq' + +// 机器人事件类型 +export interface EventType extends GroupMessage { + isMaster: boolean + group: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + recallMsg: (...arg) => any + } + msg: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reply: (...arg) => Promise +} + +// 函数式回调类型 +export type MessageCallBackType = ( + e: EventType +) => Promise \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..8b3ea21 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from './core/index.js' +export * from './mys/index.js' +export * from './utils/index.js' \ No newline at end of file diff --git a/src/mys/index.ts b/src/mys/index.ts new file mode 100644 index 0000000..693da49 --- /dev/null +++ b/src/mys/index.ts @@ -0,0 +1 @@ +export {} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..693da49 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1 @@ +export {} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1e33198 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "noImplicitAny": false, + "esModuleInterop": true, + "moduleResolution": "node", + "removeComments": true, + "preserveConstEnums": true, + "ignoreDeprecations": "5.0", + "jsx": "react", + "allowJs": false, + "suppressImplicitAnyIndexErrors": true, + "typeRoots": ["node_modules/@types"] + }, + "ts-node": { + "esm": true, + "transpileOnly": true, + "experimentalSpecifierResolution": "node" + }, + "include": ["plugins", "lib","src"], + "exclude": ["node_modules"] + } + \ No newline at end of file