dfghfhgfg commited on
Commit
481eba0
·
verified ·
1 Parent(s): 02fc7f7

Upload 73 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Yunzai/.eslintrc.cjs +3 -1
  2. Yunzai/CHANGELOG.md +1 -1
  3. Yunzai/README.md +110 -82
  4. Yunzai/app.js +22 -3
  5. Yunzai/config/default_config/bot.yaml +24 -11
  6. Yunzai/config/default_config/group.yaml +1 -1
  7. Yunzai/config/default_config/other.yaml +6 -4
  8. Yunzai/config/pm2.yaml +7 -0
  9. Yunzai/lib/bot.js +333 -67
  10. Yunzai/lib/common/common.js +3 -6
  11. Yunzai/lib/config/config.js +19 -18
  12. Yunzai/lib/config/init.js +22 -35
  13. Yunzai/lib/config/log.js +40 -62
  14. Yunzai/lib/config/redis.js +38 -49
  15. Yunzai/lib/events/message.js +2 -2
  16. Yunzai/lib/events/notice.js +2 -2
  17. Yunzai/lib/events/online.js +3 -4
  18. Yunzai/lib/events/request.js +2 -2
  19. Yunzai/lib/listener/listener.js +2 -2
  20. Yunzai/lib/listener/loader.js +6 -6
  21. Yunzai/lib/modules/md5/index.js +4 -0
  22. Yunzai/lib/modules/md5/package.json +5 -0
  23. Yunzai/lib/modules/node-fetch/index.js +2 -0
  24. Yunzai/lib/modules/node-fetch/package.json +5 -0
  25. Yunzai/lib/modules/oicq/index.js +10 -44
  26. Yunzai/lib/plugins/config.js +20 -0
  27. Yunzai/lib/plugins/loader.js +294 -454
  28. Yunzai/lib/plugins/plugin.js +41 -47
  29. Yunzai/lib/plugins/runtime.js +65 -72
  30. Yunzai/lib/plugins/stdin.js +7 -8
  31. Yunzai/lib/renderer/loader.js +16 -11
  32. Yunzai/lib/tools/web.js +28 -28
  33. Yunzai/package.json +21 -21
  34. Yunzai/plugins/adapter/ComWeChat.js +52 -86
  35. Yunzai/plugins/adapter/GSUIDCore.js +101 -49
  36. Yunzai/plugins/adapter/OPQBot.js +327 -0
  37. Yunzai/plugins/adapter/OneBotv11.js +989 -0
  38. Yunzai/plugins/example/主动复读.js +13 -14
  39. Yunzai/plugins/example/进群退群通知.js +9 -12
  40. Yunzai/plugins/other/install.js +10 -20
  41. Yunzai/plugins/other/restart.js +74 -95
  42. Yunzai/plugins/other/sendLog.js +5 -7
  43. Yunzai/plugins/other/update.js +69 -81
  44. Yunzai/plugins/other/version.js +31 -23
  45. Yunzai/plugins/system/add.js +36 -56
  46. Yunzai/plugins/system/botOperate.js +6 -3
  47. Yunzai/plugins/system/disablePrivate.js +7 -3
  48. Yunzai/plugins/system/friend.js +6 -7
  49. Yunzai/plugins/system/invite.js +4 -6
  50. Yunzai/plugins/system/master.js +13 -13
Yunzai/.eslintrc.cjs CHANGED
@@ -12,7 +12,9 @@ module.exports = {
12
  Bot: true,
13
  redis: true,
14
  logger: true,
15
- plugin: true
 
 
16
  },
17
  rules: {
18
  eqeqeq: ['off'],
 
12
  Bot: true,
13
  redis: true,
14
  logger: true,
15
+ plugin: true,
16
+ Renderer: true,
17
+ segment: true
18
  },
19
  rules: {
20
  eqeqeq: ['off'],
Yunzai/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
  # 3.1.3
2
 
3
- * 支持协议端:QQBot
4
  * **请注意:**
5
  * 从3.1.3版本开始,原genshin包内的功能会逐步重构,与miao-plugin进行整合,以降低后续游戏版本升级时的维护成本
6
  * 在整合过程中,可能会移除一些重复或迁移成本较高的功能,以及可能会有功能不稳定情况
 
1
  # 3.1.3
2
 
3
+ * 支持协议端:QQBot、go-cqhttp → OneBotv11、Lagrange
4
  * **请注意:**
5
  * 从3.1.3版本开始,原genshin包内的功能会逐步重构,与miao-plugin进行整合,以降低后续游戏版本升级时的维护成本
6
  * 在整合过程中,可能会移除一些重复或迁移成本较高的功能,以及可能会有功能不稳定情况
Yunzai/README.md CHANGED
@@ -2,7 +2,7 @@
2
 
3
  # TRSS-Yunzai
4
 
5
- Yunzai 应用端,支持多账号,支持协议端:go-cqhttp、ComWeChat、GSUIDCore、ICQQ、QQBot、QQ频道、微信、KOOK、Telegram、Discord、OPQBot
6
 
7
  [![访问量](https://visitor-badge.glitch.me/badge?page_id=TimeRainStarSky.Yunzai&right_color=red&left_text=访%20问%20量)](https://github.com/TimeRainStarSky/Yunzai)
8
  [![Stars](https://img.shields.io/github/stars/TimeRainStarSky/Yunzai?color=yellow&label=收藏)](../../stargazers)
@@ -13,7 +13,7 @@ Yunzai 应用端,支持多账号,支持协议端:go-cqhttp、ComWeChat、G
13
 
14
  </div>
15
 
16
- - 基于 [Miao-Yunzai](../../../../yoimiya-kokomi/Miao-Yunzai) 改造,需要同时安装 [miao-plugin](../../../../yoimiya-kokomi/miao-plugin)
17
  - 开发文档:[docs 分支](../../tree/docs)
18
 
19
  ## TRSS-Yunzai 后续计划
@@ -36,151 +36,179 @@ Yunzai 应用端,支持多账号,支持协议端:go-cqhttp、ComWeChat、G
36
 
37
  ### 手动安装
38
 
39
- > 环境准备: Windows or Linux,Node.js( [版本至少 v18 以上](http://nodejs.cn/download) ), [Redis](https://redis.io/docs/getting-started/installation)
 
40
 
41
- 1.克隆项目并安装 genshin miao-plugin TRSS-Plugin(可选)
42
 
43
  请根据网络情况选择使用 GitHub 或 Gitee 安装
44
 
45
- ```
46
  git clone --depth 1 https://github.com/TimeRainStarSky/Yunzai
 
47
  cd Yunzai
 
 
 
 
 
48
  git clone --depth 1 https://github.com/TimeRainStarSky/Yunzai-genshin plugins/genshin
49
  git clone --depth 1 https://github.com/yoimiya-kokomi/miao-plugin plugins/miao-plugin
50
  git clone --depth 1 https://github.com/TimeRainStarSky/TRSS-Plugin plugins/TRSS-Plugin
51
  ```
52
 
53
- ```
54
- git clone --depth 1 https://gitee.com/TimeRainStarSky/Yunzai
55
- cd Yunzai
56
  git clone --depth 1 https://gitee.com/TimeRainStarSky/Yunzai-genshin plugins/genshin
57
  git clone --depth 1 https://gitee.com/yoimiya-kokomi/miao-plugin plugins/miao-plugin
58
  git clone --depth 1 https://Yunzai.TRSS.me plugins/TRSS-Plugin
59
  ```
60
 
61
- 2.安装 [pnpm](https://pnpm.io/zh/installation)
62
 
63
- ```
64
- npm install -g pnpm
65
- ```
66
-
67
- 3.安装依赖
68
-
69
- ```
70
  pnpm i
71
  ```
72
 
73
- 4.运行
74
 
75
- ```
76
- node app
77
- ```
 
78
 
79
- 5.启动协议端:
80
 
81
- <details><summary>go-cqhttp</summary>
82
 
83
- 下载运行 [go-cqhttp](https://docs.go-cqhttp.org),选择反向 WebSocket,修改 `config.yml`,以下为必改项:
84
 
85
- ```
86
- uin: 账号
87
- password: '密码'
88
- post-format: array
89
- universal: ws://localhost:2536/go-cqhttp
90
- ```
91
 
92
- </details>
93
 
94
- <details><summary>ComWeChat</summary>
 
 
 
 
 
95
 
96
- 下载运行 [ComWeChat](https://justundertaker.github.io/ComWeChatBotClient),修改 `.env`,以下为必改项:
97
 
98
- ```
99
- websocekt_type = "Backward"
100
- websocket_url = ["ws://localhost:2536/ComWeChat"]
101
- ```
102
 
103
- </details>
104
 
105
- <details><summary>GSUIDCore</summary>
 
 
106
 
107
- 下载运行 [GenshinUID 插件](http://docs.gsuid.gbots.work/#/AdapterList),GSUIDCore 连接地址 修改为:
108
 
109
- ```
110
- ws://localhost:2536/GSUIDCore
111
- ```
112
 
113
- </details>
114
 
115
- <details><summary>ICQQ</summary>
 
 
116
 
117
- [TRSS-Yunzai ICQQ Plugin](../../../Yunzai-ICQQ-Plugin)
118
 
119
- </details>
120
-
121
- <details><summary>QQBot</summary>
122
 
123
- [TRSS-Yunzai QQBot Plugin](../../../Yunzai-QQBot-Plugin)
124
 
125
- </details>
 
 
 
 
 
 
 
 
 
 
126
 
127
- <details><summary>QQ频道</summary>
128
 
129
- [TRSS-Yunzai QQGuild Plugin](../../../Yunzai-QQGuild-Plugin)
130
 
131
- </details>
132
 
133
- <details><summary>微信</summary>
134
 
135
- [TRSS-Yunzai WeChat Plugin](../../../Yunzai-WeChat-Plugin)
 
 
 
136
 
137
- </details>
138
 
139
- <details><summary>米游社大别野</summary>
140
 
141
- [TRSS-Yunzai mysVilla Plugin](../../../Yunzai-mysVilla-Plugin)
142
 
143
- </details>
 
 
144
 
145
- <details><summary>KOOK</summary>
146
 
147
- [TRSS-Yunzai KOOK Plugin](../../../Yunzai-KOOK-Plugin)
148
 
149
- </details>
150
 
151
- <details><summary>Telegram</summary>
 
 
152
 
153
- [TRSS-Yunzai Telegram Plugin](../../../Yunzai-Telegram-Plugin)
154
 
155
- </details>
156
 
157
- <details><summary>Discord</summary>
158
 
159
- [TRSS-Yunzai Discord Plugin](../../../Yunzai-Discord-Plugin)
 
 
 
 
 
 
 
160
 
161
  </details>
162
 
163
- <details><summary>OPQBot</summary>
164
-
165
- 下载运行 [OPQBot](https://opqbot.com),启动参数添加:
166
 
167
- ```
168
- -wsserver ws://localhost:2536/OPQBot
169
- ```
170
 
171
- </details>
 
 
 
 
172
 
173
- <details><summary>路由</summary>
174
 
175
- [TRSS-Yunzai Route Plugin](../../../Yunzai-Route-Plugin)
 
 
 
 
176
 
177
- </details>
178
 
179
- 6.设置主人:发送 `#设置主人`,后台日志获取验证码并发送
 
 
180
 
181
  ## 致谢
182
 
183
- | Nickname | Contribution |
184
- | :-----------------------------------------------------------: | -------------------- |
185
- | [Yunzai-Bot](../../../../Le-niao/Yunzai-Bot) | 乐神的 Yunzai-Bot |
186
- | [Miao-Yunzai](../../../../yoimiya-kokomi/Miao-Yunzai) | 喵喵的 Miao-Yunzai |
 
2
 
3
  # TRSS-Yunzai
4
 
5
+ Yunzai 应用端,支持多账号,支持协议端:OneBotv11、ComWeChat、GSUIDCore、ICQQ、QQBot、QQ频道、微信、KOOK、Telegram、Discord、OPQBot、Lagrange
6
 
7
  [![访问量](https://visitor-badge.glitch.me/badge?page_id=TimeRainStarSky.Yunzai&right_color=red&left_text=访%20问%20量)](https://github.com/TimeRainStarSky/Yunzai)
8
  [![Stars](https://img.shields.io/github/stars/TimeRainStarSky/Yunzai?color=yellow&label=收藏)](../../stargazers)
 
13
 
14
  </div>
15
 
16
+ - 基于 [Miao-Yunzai](../../../../yoimiya-kokomi/Miao-Yunzai)
17
  - 开发文档:[docs 分支](../../tree/docs)
18
 
19
  ## TRSS-Yunzai 后续计划
 
36
 
37
  ### 手动安装
38
 
39
+ > 环境准备:Windows/Linux/MacOS/Android
40
+ > [Node.js(>=v21)](https://nodejs.org), [Redis](https://redis.io), [Git](https://git-scm.com), [Chrome(可选)](https://google.cn/chrome)
41
 
42
+ 1. Git Clone 项目
43
 
44
  请根据网络情况选择使用 GitHub 或 Gitee 安装
45
 
46
+ ```sh
47
  git clone --depth 1 https://github.com/TimeRainStarSky/Yunzai
48
+ git clone --depth 1 https://gitee.com/TimeRainStarSky/Yunzai
49
  cd Yunzai
50
+ ```
51
+
52
+ 2. 推荐安装插件(可选)
53
+
54
+ ```sh
55
  git clone --depth 1 https://github.com/TimeRainStarSky/Yunzai-genshin plugins/genshin
56
  git clone --depth 1 https://github.com/yoimiya-kokomi/miao-plugin plugins/miao-plugin
57
  git clone --depth 1 https://github.com/TimeRainStarSky/TRSS-Plugin plugins/TRSS-Plugin
58
  ```
59
 
60
+ ```sh
 
 
61
  git clone --depth 1 https://gitee.com/TimeRainStarSky/Yunzai-genshin plugins/genshin
62
  git clone --depth 1 https://gitee.com/yoimiya-kokomi/miao-plugin plugins/miao-plugin
63
  git clone --depth 1 https://Yunzai.TRSS.me plugins/TRSS-Plugin
64
  ```
65
 
66
+ 3. 安装 [pnpm](https://pnpm.io/zh/installation) 和依赖
67
 
68
+ ```sh
69
+ npm i -g pnpm
 
 
 
 
 
70
  pnpm i
71
  ```
72
 
73
+ 4. 前台运行
74
 
75
+ | 操作 | 命令 |
76
+ | ---- | ---- |
77
+ | 启动 | node . |
78
+ | 停止 | node . stop |
79
 
80
+ 5. 启动协议端
81
 
82
+ <details><summary>WebSocket</summary><blockquote>
83
 
84
+ <details><summary>OneBotv11</summary><blockquote>
85
 
86
+ <details><summary>go-cqhttp</summary><blockquote>
 
 
 
 
 
87
 
88
+ 下载运行 [go-cqhttp](https://docs.go-cqhttp.org),选择反向 WebSocket,修改 `config.yml`,以下为必改项:
89
 
90
+ ```yaml
91
+ uin: 账号
92
+ password: '密码'
93
+ post-format: array
94
+ universal: ws://localhost:2536/OneBotv11
95
+ ```
96
 
97
+ </blockquote></details>
98
 
99
+ <details><summary>LLOneBot</summary><blockquote>
 
 
 
100
 
101
+ 下载安装 [LLOneBot](https://github.com/LLOneBot/LLOneBot),启用反向 WebSocket,添加地址:
102
 
103
+ ```
104
+ ws://localhost:2536/OneBotv11
105
+ ```
106
 
107
+ </blockquote></details>
108
 
109
+ <details><summary>Shamrock</summary><blockquote>
 
 
110
 
111
+ 下载安装 [Shamrock](https://whitechi73.github.io/OpenShamrock),启用被动 WebSocket,添加地址:
112
 
113
+ ```
114
+ ws://localhost:2536/OneBotv11
115
+ ```
116
 
117
+ </blockquote></details>
118
 
119
+ <details><summary>Lagrange</summary><blockquote>
 
 
120
 
121
+ 下载运行 [Lagrange.OneBot](https://lagrangedev.github.io/Lagrange.Doc/Lagrange.OneBot),修改 `appsettings.json` 中 `Implementations`:
122
 
123
+ ```json
124
+ {
125
+ "Type": "ReverseWebSocket",
126
+ "Host": "localhost",
127
+ "Port": 2536,
128
+ "Suffix": "/OneBotv11",
129
+ "ReconnectInterval": 5000,
130
+ "HeartBeatInterval": 5000,
131
+ "AccessToken": ""
132
+ }
133
+ ```
134
 
135
+ </blockquote></details>
136
 
137
+ </blockquote></details>
138
 
139
+ <details><summary>ComWeChat</summary><blockquote>
140
 
141
+ 下载运行 [ComWeChat](https://justundertaker.github.io/ComWeChatBotClient),修改 `.env`,以下为必改项:
142
 
143
+ ```python
144
+ websocekt_type = "Backward"
145
+ websocket_url = ["ws://localhost:2536/ComWeChat"]
146
+ ```
147
 
148
+ <blockquote></details>
149
 
150
+ <details><summary>GSUIDCore</summary><blockquote>
151
 
152
+ 下载运行 [GenshinUID 插件](http://docs.gsuid.gbots.work/#/AdapterList),GSUIDCore 连接地址 修改为:
153
 
154
+ ```
155
+ ws://localhost:2536/GSUIDCore
156
+ ```
157
 
158
+ <blockquote></details>
159
 
160
+ <details><summary>OPQBot</summary><blockquote>
161
 
162
+ 下载运行 [OPQBot](https://opqbot.com),启动参数添加:
163
 
164
+ ```
165
+ -wsserver ws://localhost:2536/OPQBot
166
+ ```
167
 
168
+ </blockquote></details>
169
 
170
+ </blockquote></details>
171
 
172
+ <details><summary>插件</summary>
173
 
174
+ - [ICQQ](../../../Yunzai-ICQQ-Plugin)
175
+ - [QQBot](../../../Yunzai-QQBot-Plugin)
176
+ - [WeChat](../../../Yunzai-WeChat-Plugin)
177
+ - [KOOK](../../../Yunzai-KOOK-Plugin)
178
+ - [Telegram](../../../Yunzai-Telegram-Plugin)
179
+ - [Discord](../../../Yunzai-Discord-Plugin)
180
+ - [Route](../../../Yunzai-Route-Plugin)
181
+ - [Lagrange](../../../Yunzai-Lagrange-Plugin)
182
 
183
  </details>
184
 
185
+ 6. 设置主人:发送 `#设置主人`,日志获取验证码并发送
 
 
186
 
187
+ 7. 使用 [pm2](https://pm2.keymetrics.io) 后台运行
 
 
188
 
189
+ | 操作 | 命令 |
190
+ | ---- | ---- |
191
+ | 启动 | pnpm start |
192
+ | 停止 | pnpm stop |
193
+ | 日志 | pnpm log |
194
 
195
+ 8. 开机自启
196
 
197
+ ```sh
198
+ pnpm start
199
+ pnpm pm2 save
200
+ pnpm pm2 startup
201
+ ```
202
 
203
+ ## 班级群(¿
204
 
205
+ 1. [用户(897643592)](https://qm.qq.com/q/7NxbviGbj)
206
+ 2. [开发者(833565573)](https://qm.qq.com/q/oFJR8VVECA)
207
+ 3. [机器人(907431599)](https://qm.qq.com/q/oCBOrfE29U)
208
 
209
  ## 致谢
210
 
211
+ | Nickname | Contribution |
212
+ | -------- | ------------ |
213
+ | [Yunzai-Bot](../../../../Le-niao/Yunzai-Bot) | 乐神的 Yunzai-Bot |
214
+ | [Miao-Yunzai](../../../../yoimiya-kokomi/Miao-Yunzai) | 喵喵的 Miao-Yunzai |
Yunzai/app.js CHANGED
@@ -1,3 +1,22 @@
1
- import Yunzai from "./lib/bot.js"
2
- global.Bot = new Yunzai
3
- Bot.run()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ switch (process.env.app_type || process.argv[2]) {
2
+ case "pm2":
3
+ case "start": {
4
+ global.Bot = new (await import("./lib/bot.js")).default
5
+ Bot.run()
6
+ break
7
+ } case "stop": {
8
+ const cfg = (await import("./lib/config/config.js")).default
9
+ const fetch = (await import("node-fetch")).default
10
+ try {
11
+ await fetch(`http://localhost:${cfg.bot.port}/exit`)
12
+ } catch (err) {}
13
+ process.exit()
14
+ } default: {
15
+ const { spawnSync } = await import("node:child_process")
16
+ while (!spawnSync(process.argv[0],
17
+ [process.argv[1], "start"],
18
+ { stdio: "inherit" },
19
+ ).status) {}
20
+ process.exit()
21
+ }
22
+ }
Yunzai/config/default_config/bot.yaml CHANGED
@@ -1,9 +1,30 @@
1
- # 日志等级:trace,debug,info,warn,fatal,mark,error,off
2
- # mark时只显示执行命令,不显示聊天记录
3
  log_level: info
 
 
 
 
 
 
 
4
  # 服务器端口
5
  port: 2536
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  # chromium其他路径
8
  chromium_path:
9
  # puppeteer接口地址
@@ -12,12 +33,4 @@ puppeteer_ws:
12
  puppeteer_timeout:
13
 
14
  # 米游社接口代理地址,国际服用
15
- proxyAddress:
16
-
17
- # 上线时给主人推送帮助
18
- online_msg: true
19
- # 上线推送通知的冷却时间
20
- online_msg_exp: 86400
21
-
22
- # 单条日志长度
23
- logLength: 1000
 
1
+ # 日志等级 trace,debug,info,warn,fatal,mark,error,off
 
2
  log_level: info
3
+ # 单条日志长度
4
+ log_length: 10000
5
+ # 对象日志格式
6
+ log_object: true
7
+
8
+ # 服务器地址
9
+ url: http://localhost:2536
10
  # 服务器端口
11
  port: 2536
12
 
13
+ # 自动更新时间
14
+ update_time: 1440
15
+ # 自动重启时间
16
+ restart_time: 0
17
+ # 上线推送通知的冷却时间
18
+ online_msg_exp: 1440
19
+ # 文件保存时间
20
+ file_to_url_time: 1
21
+ # 文件访问次数
22
+ file_to_url_times:
23
+ # 消息类型统计
24
+ msg_type_count: false
25
+ # 以/开头转为#
26
+ /→#: true
27
+
28
  # chromium其他路径
29
  chromium_path:
30
  # puppeteer接口地址
 
33
  puppeteer_timeout:
34
 
35
  # 米游社接口代理地址,国际服用
36
+ proxyAddress:
 
 
 
 
 
 
 
 
Yunzai/config/default_config/group.yaml CHANGED
@@ -3,7 +3,7 @@ default:
3
  groupCD: 500 # 群聊中所有指令操作冷却时间,单位毫秒,0则无限制
4
  singleCD: 2000 # 群聊中个人操作冷却时间,单位毫秒
5
 
6
- onlyReplyAt: 0 # 是否只仅关注主动提及Bot的消息 0-否 1-是
7
  botAlias: # 开启后则只回复提及Bot的消息及特定前缀的消息
8
  - 云崽
9
  - 云宝
 
3
  groupCD: 500 # 群聊中所有指令操作冷却时间,单位毫秒,0则无限制
4
  singleCD: 2000 # 群聊中个人操作冷却时间,单位毫秒
5
 
6
+ onlyReplyAt: 0 # 是否只仅关注主动提及Bot的消息 0-否 1-是 2-非主人
7
  botAlias: # 开启后则只回复提及Bot的消息及特定前缀的消息
8
  - 云崽
9
  - 云宝
Yunzai/config/default_config/other.yaml CHANGED
@@ -16,12 +16,14 @@ disableMsg: "私聊功能已禁用,仅支持发送cookie,抽卡记录链接
16
  # 私聊通行字符串
17
  disableAdopt:
18
  - stoken
19
- #白名单群,配置后只在该群生效
20
- whiteGroup:
21
 
 
 
 
 
22
  #黑名单群
23
  blackGroup:
24
  - 213938015
25
- #黑名单账号
26
- blackQQ:
27
  - 528952540
 
16
  # 私聊通行字符串
17
  disableAdopt:
18
  - stoken
 
 
19
 
20
+ #白名单群
21
+ whiteGroup:
22
+ #白名单用户
23
+ whiteUser:
24
  #黑名单群
25
  blackGroup:
26
  - 213938015
27
+ #黑名单用户
28
+ blackUser:
29
  - 528952540
Yunzai/config/pm2.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ apps:
2
+ - name: TRSS-Yunzai
3
+ script: ./app.js
4
+ max_memory_restart: 512M
5
+ restart_delay: 60000
6
+ env:
7
+ app_type: pm2
Yunzai/lib/bot.js CHANGED
@@ -1,74 +1,103 @@
1
  import "./config/init.js"
2
  import cfg from "./config/config.js"
 
3
  import PluginsLoader from "./plugins/loader.js"
4
  import ListenerLoader from "./listener/loader.js"
5
  import { EventEmitter } from "events"
6
  import express from "express"
7
- import http from "http"
8
  import { WebSocketServer } from "ws"
9
  import _ from "lodash"
10
- import fs from "node:fs"
 
 
11
  import fetch from "node-fetch"
12
  import { randomUUID } from "node:crypto"
13
- import { exec } from "child_process"
14
  import { fileTypeFromBuffer } from "file-type"
15
  import md5 from "md5"
 
16
 
17
  export default class Yunzai extends EventEmitter {
18
  constructor() {
19
  super()
 
20
  this.uin = []
21
  this.adapter = []
22
 
23
  this.express = express()
 
 
 
 
 
 
 
 
 
 
 
 
24
  this.server = http.createServer(this.express)
 
 
 
 
 
25
 
26
  this.server.on("upgrade", (...args) => this.wsConnect(...args))
27
  this.wss = new WebSocketServer({ noServer: true })
28
- this.wsf = {}
29
 
30
- this.fs = {}
31
  this.express.use("/File", (...args) => this.fileSend(...args))
32
- this.fileToUrl("resources/http/File/404.jpg", { name: 404, time: 0 })
33
- this.fileToUrl("resources/http/File/timeout.jpg", { name: "timeout", time: 0 })
34
  }
35
 
36
  wsConnect(req, socket, head) {
37
  this.wss.handleUpgrade(req, socket, head, conn => {
38
- conn.id = `${req.connection.remoteAddress}-${req.headers["sec-websocket-key"]}`
39
- this.makeLog("mark", `${logger.blue(`[${conn.id} <=> ws://${req.headers.host}${req.url}]`)} 建立连接:${JSON.stringify(req.headers)}`)
40
- conn.on("error", logger.error)
41
- conn.on("close", () => this.makeLog("mark", `${logger.blue(`[${conn.id} <≠> ws://${req.headers.host}${req.url}]`)} 断开连接`))
42
- conn.on("message", msg => this.makeLog("debug", `${logger.blue(`[${conn.id} => ws://${req.headers.host}${req.url}]`)} 消息:${String(msg).trim()}`))
 
43
  conn.sendMsg = msg => {
44
  if (!Buffer.isBuffer(msg)) msg = this.String(msg)
45
- this.makeLog("debug", `${logger.blue(`[${conn.id} <= ws://${req.headers.host}${req.url}]`)} 消息:${msg}`)
46
  return conn.send(msg)
47
  }
48
- for (const i of this.wsf[req.url.split("/")[1]] || [])
49
  i(conn, req, socket, head)
50
  })
51
  }
52
 
53
- serverLoad() {
54
- this.express.use(req => {
55
- logger.mark(`${logger.blue(`[${req.ip} => http://${req.headers.host}${req.url}]`)} HTTP ${req.method} 请求:${JSON.stringify(req.headers)}`)
56
- req.res.redirect("https://github.com/TimeRainStarSky/Yunzai")
57
- })
 
 
 
 
58
 
59
- this.server.listen(cfg.bot.port, () => {
60
- logger.mark(`启动 HTTP 服务器:${logger.green(`http://[${this.server.address().address}]:${this.server.address().port}`)}`)
61
- const url = cfg.bot.url.replace(/^http/, "ws")
62
- for (const i of Object.keys(this.wsf))
63
- logger.info(`${i} 连接地址:${logger.blue(`${url}/${i}`)}`)
64
- })
65
  }
66
 
67
  async run() {
 
 
68
  await import("./plugins/stdin.js")
69
  await PluginsLoader.load()
70
  await ListenerLoader.load()
71
- this.serverLoad()
 
 
72
  this.emit("online", this)
73
  }
74
 
@@ -76,17 +105,195 @@ export default class Yunzai extends EventEmitter {
76
  return new Promise(resolve => setTimeout(resolve, time))
77
  }
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  String(data) {
80
  switch (typeof data) {
81
  case "string":
82
  return data
83
  case "object":
84
- return JSON.stringify(data)
 
 
 
85
  }
86
- return String(data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
 
89
- Buffer(data, opts = {}) {
90
  if (Buffer.isBuffer(data)) return data
91
  data = this.String(data)
92
 
@@ -94,43 +301,48 @@ export default class Yunzai extends EventEmitter {
94
  return Buffer.from(data.replace(/^base64:\/\//, ""), "base64")
95
  } else if (data.match(/^https?:\/\//)) {
96
  if (opts.http) return data
97
- return (async () => Buffer.from(await (await fetch(data)).arrayBuffer()))()
98
- } else if (fs.existsSync(data.replace(/^file:\/\//, ""))) {
99
  if (opts.file) return data
100
- return Buffer.from(fs.readFileSync(data.replace(/^file:\/\//, "")))
101
  }
102
  return data
103
  }
104
 
105
- async fileType(data, name) {
106
- const file = {}
107
  try {
108
- if (Buffer.isBuffer(data)) {
109
- file.url = name || "Buffer"
110
- file.buffer = data
111
  } else {
112
- file.url = data.replace(/^base64:\/\/.*/, "base64://...")
113
- file.buffer = await this.Buffer(data)
114
  }
115
  if (Buffer.isBuffer(file.buffer)) {
116
  file.type = await fileTypeFromBuffer(file.buffer)
117
  file.md5 = md5(file.buffer)
118
- file.name = name || `${Date.now()}.${file.md5.slice(0,8)}.${file.type.ext}`
119
- } else {
120
- file.name = name || `${Date.now()}-path.basename(file.buffer)`
121
  }
122
  } catch (err) {
123
- logger.error(`文件类型检测错误:${logger.red(err)}`)
124
  }
 
 
125
  return file
126
  }
127
 
128
  async fileToUrl(file, opts = {}) {
129
- const { name, time = 60000, times } = opts
 
 
 
 
130
 
131
- file = await this.fileType(file, name)
132
  if (!Buffer.isBuffer(file.buffer)) return file.buffer
133
- if (!file.name) file.name = randomUUID()
134
 
135
  if (typeof times == "number") file.times = times
136
  this.fs[file.name] = file
@@ -144,25 +356,24 @@ export default class Yunzai extends EventEmitter {
144
  if (!file) file = this.fs[404]
145
 
146
  if (typeof file.times == "number") {
147
- if (file.times > 0) file.times = file.times-1
148
  else file = this.fs.timeout
149
  }
150
 
151
  if (file.type?.mime)
152
  req.res.setHeader("Content-Type", file.type.mime)
153
 
154
- logger.mark(`${logger.blue(`[${req.ip} => http://${req.headers.host}/File/${url}]`)} HTTP ${req.method} 请求:${JSON.stringify(req.headers)}`)
155
- logger.mark(`${logger.blue(`[${req.ip} <= http://${req.headers.host}/File/${url}]`)} 发送文件:${file.name}(${file.url} ${(file.buffer.length/1024).toFixed(2)}KB)`)
156
  req.res.send(file.buffer)
157
  }
158
 
159
  async exec(cmd) {
160
  return new Promise(resolve => {
161
- this.makeLog("mark", `[命令执行开始] ${logger.blue(cmd)}`)
162
  exec(cmd, (error, stdout, stderr) => {
163
- this.makeLog("mark", `[命令执行完成] ${logger.blue(cmd)}${stdout?`\n${this.String(stdout).trim()}`:""}${stderr?logger.red(`\n${this.String(stderr).trim()}`):""}`)
164
- if (error) this.makeLog("mark", `[命令执行错误] ${logger.blue(cmd)}\n${logger.red(this.String(error).trim())}`)
165
  resolve({ error, stdout, stderr })
 
 
166
  })
167
  })
168
  }
@@ -170,18 +381,50 @@ export default class Yunzai extends EventEmitter {
170
  makeLog(level, msg, id) {
171
  const log = []
172
  if (id) log.push(logger.blue(`[${id}]`))
173
- for (const i of Array.isArray(msg) ? msg : [msg]) {
174
- if (i?.replace)
175
- log.push(_.truncate(i.replace(/{"type":"Buffer","data":\[.*?\]}/g, "(Buffer)"), { length: cfg.bot.log_length }))
176
- else
177
- log.push(i)
178
- }
179
  logger[level](...log)
180
  }
181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  em(name = "", data = {}) {
183
- if (data.self_id)
184
- Object.defineProperty(data, "bot", { value: Bot[data.self_id] })
185
  while (true) {
186
  this.emit(name, data)
187
  const i = name.lastIndexOf(".")
@@ -251,7 +494,7 @@ export default class Yunzai extends EventEmitter {
251
  user_id = Number(user_id) || String(user_id)
252
  const user = this.fl.get(user_id)
253
  if (user) return this[user.bot_id].pickFriend(user_id)
254
- logger.error(`获取用户对象失败:找不到用户 ${logger.red(user_id)}`)
255
  }
256
  get pickUser() { return this.pickFriend }
257
 
@@ -259,7 +502,7 @@ export default class Yunzai extends EventEmitter {
259
  group_id = Number(group_id) || String(group_id)
260
  const group = this.gl.get(group_id)
261
  if (group) return this[group.bot_id].pickGroup(group_id)
262
- logger.error(`获取群对象失败:找不到群 ${logger.red(group_id)}`)
263
  }
264
 
265
  pickMember(group_id, user_id) {
@@ -279,7 +522,7 @@ export default class Yunzai extends EventEmitter {
279
  this.once(`connect.${bot_id}`, data =>
280
  resolve(data.bot.pickFriend(user_id).sendMsg(msg))))
281
  } catch (err) {
282
- logger.error(`${logger.blue(`[${bot_id}]`)} 发送好友消息失败:[$${user_id}] ${err}`)
283
  }
284
  return false
285
  }
@@ -296,7 +539,7 @@ export default class Yunzai extends EventEmitter {
296
  this.once(`connect.${bot_id}`, data =>
297
  resolve(data.bot.pickGroup(group_id).sendMsg(msg))))
298
  } catch (err) {
299
- logger.error(`${logger.blue(`[${bot_id}]`)} 发送群消息失败:[$${group_id}] ${err}`)
300
  }
301
  return false
302
  }
@@ -307,13 +550,13 @@ export default class Yunzai extends EventEmitter {
307
  fnc = data => data.self_id == self_id && data.user_id == user_id
308
  }
309
 
310
- while (true) {
311
  const msg = await new Promise(resolve => {
312
  this.once("message", data => {
313
  if (data.message && fnc(data)) {
314
  let msg = ""
315
  for (const i of data.message)
316
- if (i.type = "text")
317
  msg += i.text.trim()
318
  resolve(msg)
319
  } else {
@@ -322,7 +565,9 @@ export default class Yunzai extends EventEmitter {
322
  })
323
  })
324
  if (msg) return msg
325
- }
 
 
326
  }
327
 
328
  getMasterMsg() {
@@ -338,10 +583,31 @@ export default class Yunzai extends EventEmitter {
338
 
339
  makeForwardMsg(msg) { return { type: "node", data: msg } }
340
 
 
 
 
 
 
 
 
341
  async sendForwardMsg(send, msg) {
342
  const messages = []
343
  for (const { message } of msg)
344
  messages.push(await send(message))
345
  return messages
346
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  }
 
1
  import "./config/init.js"
2
  import cfg from "./config/config.js"
3
+ import redisInit from "./config/redis.js"
4
  import PluginsLoader from "./plugins/loader.js"
5
  import ListenerLoader from "./listener/loader.js"
6
  import { EventEmitter } from "events"
7
  import express from "express"
8
+ import http from "node:http"
9
  import { WebSocketServer } from "ws"
10
  import _ from "lodash"
11
+ import fs from "node:fs/promises"
12
+ import path from "node:path"
13
+ import util from "node:util"
14
  import fetch from "node-fetch"
15
  import { randomUUID } from "node:crypto"
16
+ import { exec } from "node:child_process"
17
  import { fileTypeFromBuffer } from "file-type"
18
  import md5 from "md5"
19
+ import { Level } from "level"
20
 
21
  export default class Yunzai extends EventEmitter {
22
  constructor() {
23
  super()
24
+ this.stat = { start_time: Date.now()/1000 }
25
  this.uin = []
26
  this.adapter = []
27
 
28
  this.express = express()
29
+ for (const i of ["urlencoded", "json", "raw", "text"])
30
+ this.express.use(express[i]({ extended: false }))
31
+ this.express.use(req => {
32
+ req.rid = `${req.ip}:${req.socket.remotePort}`
33
+ req.sid = `${req.protocol}://${req.hostname}:${req.socket.localPort}${req.originalUrl}`
34
+ this.makeLog("mark", ["HTTP", req.method, "请求", req.headers, req.query, req.body], `${req.sid} <= ${req.rid}`)
35
+ req.next()
36
+ })
37
+ this.express.use("/exit", req => {
38
+ if (["::1", "::ffff:127.0.0.1"].includes(req.ip) || req.hostname == "localhost")
39
+ process.exit(1)
40
+ })
41
  this.server = http.createServer(this.express)
42
+ this.server.on("error", err => {
43
+ if (typeof this[`server${err.code}`] == "function")
44
+ return this[`server${err.code}`](err)
45
+ this.makeLog("error", err, "Server")
46
+ })
47
 
48
  this.server.on("upgrade", (...args) => this.wsConnect(...args))
49
  this.wss = new WebSocketServer({ noServer: true })
50
+ this.wsf = Object.create(null)
51
 
52
+ this.fs = Object.create(null)
53
  this.express.use("/File", (...args) => this.fileSend(...args))
54
+ for (const name of [404, "timeout"])
55
+ this.fileToUrl(`resources/http/File/${name}.jpg`, { name, time: false, times: false })
56
  }
57
 
58
  wsConnect(req, socket, head) {
59
  this.wss.handleUpgrade(req, socket, head, conn => {
60
+ conn.rid = `${req.socket.remoteAddress}:${req.socket.remotePort}-${req.headers["sec-websocket-key"]}`
61
+ conn.sid = `ws://${req.headers["x-forwarded-host"] || req.headers.host || `${req.socket.localAddress}:${req.socket.localPort}`}${req.url}`
62
+ this.makeLog("mark", ["建立连接", req.headers], `${conn.sid} <=> ${conn.rid}`)
63
+ conn.on("error", (...args) => this.makeLog("error", args, `${conn.sid} <=> ${conn.rid}`))
64
+ conn.on("close", () => this.makeLog("mark", "断开连接", `${conn.sid} <≠> ${conn.rid}`))
65
+ conn.on("message", msg => this.makeLog("debug", ["消息", this.String(msg)], `${conn.sid} <= ${conn.rid}`))
66
  conn.sendMsg = msg => {
67
  if (!Buffer.isBuffer(msg)) msg = this.String(msg)
68
+ this.makeLog("debug", ["消息", msg], `${conn.sid} => ${conn.rid}`)
69
  return conn.send(msg)
70
  }
71
+ for (const i of this.wsf[req.url.split("/")[1]] || [() => conn.terminate()])
72
  i(conn, req, socket, head)
73
  })
74
  }
75
 
76
+ async serverEADDRINUSE(err) {
77
+ this.makeLog("error", ["监听端口", cfg.bot.port, "错误", err], "Server")
78
+ try {
79
+ await fetch(`http://localhost:${cfg.bot.port}/exit`)
80
+ } catch (err) {}
81
+ this.server_listen_time = (this.server_listen_time || 0) + 1
82
+ await this.sleep(this.server_listen_time * 1000)
83
+ this.server.listen(cfg.bot.port)
84
+ }
85
 
86
+ async serverLoad() {
87
+ this.server.listen(cfg.bot.port)
88
+ await new Promise(resolve => this.server.once("listening", resolve))
89
+ this.makeLog("mark", ["启动 HTTP 服务器", logger.green(`http://[${this.server.address().address}]:${this.server.address().port}`)], "Server")
 
 
90
  }
91
 
92
  async run() {
93
+ await redisInit()
94
+ await this.serverLoad()
95
  await import("./plugins/stdin.js")
96
  await PluginsLoader.load()
97
  await ListenerLoader.load()
98
+
99
+ this.express.use(req => req.res.redirect("https://github.com/TimeRainStarSky/Yunzai"))
100
+ this.makeLog("info", `连接地址:${logger.blue(`${cfg.bot.url.replace(/^http/, "ws")}/`)}${logger.cyan(`[${Object.keys(this.wsf)}]`)}`, "WebSocket")
101
  this.emit("online", this)
102
  }
103
 
 
105
  return new Promise(resolve => setTimeout(resolve, time))
106
  }
107
 
108
+ async fsStat(path) { try {
109
+ const stat = await fs.stat(path)
110
+ return stat
111
+ } catch (err) {
112
+ this.makeLog("trace", ["获取", path, "状态错误", err])
113
+ return false
114
+ }}
115
+
116
+ async mkdir(dir) { try {
117
+ if (await this.fsStat(dir)) return true
118
+ if (!await this.mkdir(path.dirname(dir))) return false
119
+ await fs.mkdir(dir)
120
+ return true
121
+ } catch (err) {
122
+ this.makeLog("error", ["创建", dir, "错误", err])
123
+ return false
124
+ }}
125
+
126
+ async rmdir(dir) { try {
127
+ if (!await this.fsStat(dir)) return true
128
+ for (const i of await fs.readdir(dir))
129
+ await this.rm(`${dir}/${i}`)
130
+ await fs.rmdir(dir)
131
+ return true
132
+ } catch (err) {
133
+ this.makeLog("error", ["删除", dir, "错误", err])
134
+ return false
135
+ }}
136
+
137
+ async rm(file) { try {
138
+ const stat = await this.fsStat(file)
139
+ if (!stat) return true
140
+ if (stat.isDirectory())
141
+ return this.rmdir(file)
142
+ await fs.unlink(file)
143
+ return true
144
+ } catch (err) {
145
+ this.makeLog("error", ["删除", file, "错误", err])
146
+ return false
147
+ }}
148
+
149
+ async download(url, file) {
150
+ let buffer
151
+ if (!file || (await this.fsStat(file))?.isDirectory?.()) {
152
+ const type = await this.fileType(url)
153
+ file = file ? path.join(file, type.name) : type.name
154
+ buffer = type.buffer
155
+ } else {
156
+ await this.mkdir(path.dirname(file))
157
+ buffer = await this.Buffer(url)
158
+ }
159
+ await fs.writeFile(file, buffer)
160
+ return file
161
+ }
162
+
163
+ makeMap(parent_map, parent_key, map) {
164
+ const save = async () => { try {
165
+ await parent_map.db.put(parent_key, { map_array: Array.from(map) })
166
+ } catch (err) {
167
+ this.makeLog("error", ["写入", parent_map.db.location, parent_key, "错误", map, err])
168
+ }}
169
+
170
+ const set = map.set.bind(map)
171
+ Object.defineProperty(map, "set", {
172
+ value: async (key, value) => {
173
+ if (JSON.stringify(map.get(key)) != JSON.stringify(value)) {
174
+ set(key, value)
175
+ await save()
176
+ }
177
+ return map
178
+ },
179
+ })
180
+ const del = map.delete.bind(map)
181
+ Object.defineProperty(map, "delete", {
182
+ value: async key => {
183
+ if (!del(key)) return false
184
+ await save()
185
+ return true
186
+ },
187
+ })
188
+ return map
189
+ }
190
+
191
+ async setMap(map, set, key, value) {
192
+ try {
193
+ if (value instanceof Map) {
194
+ set(key, this.makeMap(map, key, value))
195
+ await map.db.put(key, { map_array: Array.from(value) })
196
+ } else if (JSON.stringify(map.get(key)) != JSON.stringify(value)) {
197
+ set(key, value)
198
+ await map.db.put(key, value)
199
+ }
200
+ } catch (err) {
201
+ this.makeLog("error", ["写入", map.db.location, key, "错误", value, err])
202
+ }
203
+ return map
204
+ }
205
+
206
+ async delMap(map, del, key) {
207
+ if (!del(key)) return false
208
+ try {
209
+ await map.db.del(key)
210
+ } catch (err) {
211
+ this.makeLog("error", ["删除", map.db.location, key, "错误", err])
212
+ }
213
+ return true
214
+ }
215
+
216
+ async importMap(dir, map) {
217
+ for (const i of await fs.readdir(dir)) {
218
+ const path = `${dir}/${i}`
219
+ try {
220
+ await map.set(i, (await this.fsStat(path)).isDirectory() ?
221
+ await this.importMap(path, new Map) :
222
+ JSON.parse(await fs.readFile(path, "utf-8")))
223
+ } catch (err) {
224
+ this.makeLog("error", ["读取", path, "错误", err])
225
+ }
226
+ await this.rm(path)
227
+ }
228
+ await this.rm(dir)
229
+ return map
230
+ }
231
+
232
+ async getMap(dir) {
233
+ const map = new Map()
234
+ const db = new Level(`${dir}-leveldb`, { valueEncoding: "json" })
235
+ try {
236
+ await db.open()
237
+ for await (let [key, value] of db.iterator()) {
238
+ if (typeof value == "object" && value.map_array)
239
+ value = this.makeMap(map, key, new Map(value.map_array))
240
+ map.set(key, value)
241
+ }
242
+ } catch (err) {
243
+ this.makeLog("error", ["打开", dir, "数据库错误", err])
244
+ return map
245
+ }
246
+
247
+ Object.defineProperty(map, "db", { value: db })
248
+ const set = map.set.bind(map)
249
+ Object.defineProperty(map, "set", {
250
+ value: (key, value) => this.setMap(map, set, key, value),
251
+ })
252
+ const del = map.delete.bind(map)
253
+ Object.defineProperty(map, "delete", {
254
+ value: key => this.delMap(map, del, key),
255
+ })
256
+
257
+ if (await this.fsStat(dir))
258
+ await this.importMap(dir, map)
259
+ return map
260
+ }
261
+
262
  String(data) {
263
  switch (typeof data) {
264
  case "string":
265
  return data
266
  case "object":
267
+ if (data instanceof Error)
268
+ return data.stack
269
+ if (Buffer.isBuffer(data))
270
+ return String(data)
271
  }
272
+ return JSON.stringify(data)
273
+ }
274
+
275
+ Loging(data) {
276
+ if (typeof data == "string") return data
277
+ if (!cfg.bot.log_object && typeof data == "object")
278
+ if (typeof data.toString == "function")
279
+ return String(data)
280
+ else
281
+ return "[object null]"
282
+
283
+ return util.inspect(data, {
284
+ depth: null,
285
+ colors: true,
286
+ showHidden: true,
287
+ showProxy: true,
288
+ getters: true,
289
+ breakLength: 100,
290
+ maxArrayLength: 100,
291
+ maxStringLength: 1000,
292
+ ...cfg.bot.log_object,
293
+ })
294
  }
295
 
296
+ async Buffer(data, opts = {}) {
297
  if (Buffer.isBuffer(data)) return data
298
  data = this.String(data)
299
 
 
301
  return Buffer.from(data.replace(/^base64:\/\//, ""), "base64")
302
  } else if (data.match(/^https?:\/\//)) {
303
  if (opts.http) return data
304
+ return Buffer.from(await (await fetch(data)).arrayBuffer())
305
+ } else if (await this.fsStat(data.replace(/^file:\/\//, ""))) {
306
  if (opts.file) return data
307
+ return Buffer.from(await fs.readFile(data.replace(/^file:\/\//, "")))
308
  }
309
  return data
310
  }
311
 
312
+ async fileType(data, opts = {}) {
313
+ const file = { name: data.name }
314
  try {
315
+ if (Buffer.isBuffer(data.file)) {
316
+ file.url = data.name || "Buffer"
317
+ file.buffer = data.file
318
  } else {
319
+ file.url = data.file.replace(/^base64:\/\/.*/, "base64://...")
320
+ file.buffer = await this.Buffer(data.file, opts)
321
  }
322
  if (Buffer.isBuffer(file.buffer)) {
323
  file.type = await fileTypeFromBuffer(file.buffer)
324
  file.md5 = md5(file.buffer)
325
+ if (!file.name)
326
+ file.name = `${Date.now()}.${file.md5.slice(0,8)}.${file.type.ext}`
 
327
  }
328
  } catch (err) {
329
+ this.makeLog("error", ["文件类型检测错误", file, err])
330
  }
331
+ if (!file.name)
332
+ file.name = `${Date.now()}-${path.basename(file.url)}`
333
  return file
334
  }
335
 
336
  async fileToUrl(file, opts = {}) {
337
+ const {
338
+ name,
339
+ time = cfg.bot.file_to_url_time*60000,
340
+ times = cfg.bot.file_to_url_times,
341
+ } = opts
342
 
343
+ file = await this.fileType({ file, name }, { http: true })
344
  if (!Buffer.isBuffer(file.buffer)) return file.buffer
345
+ file.name = file.name ? encodeURIComponent(file.name) : randomUUID()
346
 
347
  if (typeof times == "number") file.times = times
348
  this.fs[file.name] = file
 
356
  if (!file) file = this.fs[404]
357
 
358
  if (typeof file.times == "number") {
359
+ if (file.times > 0) file.times--
360
  else file = this.fs.timeout
361
  }
362
 
363
  if (file.type?.mime)
364
  req.res.setHeader("Content-Type", file.type.mime)
365
 
366
+ this.makeLog("mark", `发送文件:${file.name}(${file.url} ${(file.buffer.length/1024).toFixed(2)}KB)`, `${req.sid} => ${req.rid}`)
 
367
  req.res.send(file.buffer)
368
  }
369
 
370
  async exec(cmd) {
371
  return new Promise(resolve => {
372
+ this.makeLog("mark", `[执行命令] ${logger.blue(cmd)}`)
373
  exec(cmd, (error, stdout, stderr) => {
 
 
374
  resolve({ error, stdout, stderr })
375
+ this.makeLog("mark", `[执行命令完成] ${logger.blue(cmd)}${stdout?`\n${String(stdout).trim()}`:""}${stderr?logger.red(`\n${String(stderr).trim()}`):""}`)
376
+ if (error) this.makeLog("error", `[执行命令错误] ${logger.blue(cmd)}\n${logger.red(this.Loging(error).trim())}`)
377
  })
378
  })
379
  }
 
381
  makeLog(level, msg, id) {
382
  const log = []
383
  if (id) log.push(logger.blue(`[${id}]`))
384
+ for (const i of Array.isArray(msg) ? msg : [msg])
385
+ log.push(_.truncate(this.Loging(i), { length: cfg.bot.log_length }))
 
 
 
 
386
  logger[level](...log)
387
  }
388
 
389
+ makeEvent(data) {
390
+ if (!this[data.self_id]) return
391
+ if (!data.bot)
392
+ Object.defineProperty(data, "bot", {
393
+ value: this[data.self_id],
394
+ })
395
+ if (!data.friend && data.user_id)
396
+ Object.defineProperty(data, "friend", {
397
+ value: data.bot.pickFriend(data.user_id),
398
+ })
399
+ if (!data.group && data.group_id)
400
+ Object.defineProperty(data, "group", {
401
+ value: data.bot.pickGroup(data.group_id),
402
+ })
403
+ if (!data.member && data.group && data.user_id)
404
+ Object.defineProperty(data, "member", {
405
+ value: data.group.pickMember(data.user_id),
406
+ })
407
+
408
+ if (data.bot.adapter?.id)
409
+ data.adapter_id = data.bot.adapter.id
410
+ if (data.bot.adapter?.name)
411
+ data.adapter_name = data.bot.adapter.name
412
+
413
+ for (const i of [data.friend, data.group, data.member]) {
414
+ if (typeof i != "object") continue
415
+ if (!i.sendFile)
416
+ i.sendFile = (file, name) => i.sendMsg(segment.file(file, name))
417
+ if (!i.makeForwardMsg)
418
+ i.makeForwardMsg = this.makeForwardMsg
419
+ if (!i.sendForwardMsg)
420
+ i.sendForwardMsg = msg => this.sendForwardMsg(msg => i.sendMsg(msg), msg)
421
+ if (!i.getInfo)
422
+ i.getInfo = () => i
423
+ }
424
+ }
425
+
426
  em(name = "", data = {}) {
427
+ this.makeEvent(data)
 
428
  while (true) {
429
  this.emit(name, data)
430
  const i = name.lastIndexOf(".")
 
494
  user_id = Number(user_id) || String(user_id)
495
  const user = this.fl.get(user_id)
496
  if (user) return this[user.bot_id].pickFriend(user_id)
497
+ this.makeLog("error", ["获取用户对象错误:找不到用户", user_id])
498
  }
499
  get pickUser() { return this.pickFriend }
500
 
 
502
  group_id = Number(group_id) || String(group_id)
503
  const group = this.gl.get(group_id)
504
  if (group) return this[group.bot_id].pickGroup(group_id)
505
+ this.makeLog("error", ["获取群对象错误:找不到群", group_id])
506
  }
507
 
508
  pickMember(group_id, user_id) {
 
522
  this.once(`connect.${bot_id}`, data =>
523
  resolve(data.bot.pickFriend(user_id).sendMsg(msg))))
524
  } catch (err) {
525
+ this.makeLog("error", [`发送好友消息错误:[${user_id}]`, err], bot_id)
526
  }
527
  return false
528
  }
 
539
  this.once(`connect.${bot_id}`, data =>
540
  resolve(data.bot.pickGroup(group_id).sendMsg(msg))))
541
  } catch (err) {
542
+ this.makeLog("error", [`发送群消息错误:[${group_id}]`, err], bot_id)
543
  }
544
  return false
545
  }
 
550
  fnc = data => data.self_id == self_id && data.user_id == user_id
551
  }
552
 
553
+ while (true) { try {
554
  const msg = await new Promise(resolve => {
555
  this.once("message", data => {
556
  if (data.message && fnc(data)) {
557
  let msg = ""
558
  for (const i of data.message)
559
+ if (i.type == "text" && i.text)
560
  msg += i.text.trim()
561
  resolve(msg)
562
  } else {
 
565
  })
566
  })
567
  if (msg) return msg
568
+ } catch (err) {
569
+ this.makeLog("error", err)
570
+ }}
571
  }
572
 
573
  getMasterMsg() {
 
583
 
584
  makeForwardMsg(msg) { return { type: "node", data: msg } }
585
 
586
+ makeForwardArray(msg = [], node = {}) {
587
+ const forward = []
588
+ for (const message of Array.isArray(msg) ? msg : [msg])
589
+ forward.push({ ...node, message })
590
+ return this.makeForwardMsg(forward)
591
+ }
592
+
593
  async sendForwardMsg(send, msg) {
594
  const messages = []
595
  for (const { message } of msg)
596
  messages.push(await send(message))
597
  return messages
598
  }
599
+
600
+ getTimeDiff(time1 = this.stat.start_time, time2 = Date.now()/1000) {
601
+ const time = time2 - time1
602
+ let ret = ""
603
+ const day = Math.floor(time / 3600 / 24)
604
+ if (day) ret += `${day}天`
605
+ const hour = Math.floor((time / 3600) % 24)
606
+ if (hour) ret += `${hour}时`
607
+ const min = Math.floor((time / 60) % 60)
608
+ if (min) ret += `${min}分`
609
+ const sec = Math.floor(time % 60)
610
+ if (sec) ret += `${sec}秒`
611
+ return ret || "0秒"
612
+ }
613
  }
Yunzai/lib/common/common.js CHANGED
@@ -41,13 +41,10 @@ async function downFile(fileUrl, savePath,param = {}) {
41
  }
42
 
43
  function mkdirs(dirname) {
44
- if (fs.existsSync(dirname)) {
 
 
45
  return true
46
- } else {
47
- if (mkdirs(path.dirname(dirname))) {
48
- fs.mkdirSync(dirname)
49
- return true
50
- }
51
  }
52
  }
53
 
 
41
  }
42
 
43
  function mkdirs(dirname) {
44
+ if (fs.existsSync(dirname)) return true
45
+ if (mkdirs(path.dirname(dirname))) {
46
+ fs.mkdirSync(dirname)
47
  return true
 
 
 
 
 
48
  }
49
  }
50
 
Yunzai/lib/config/config.js CHANGED
@@ -4,7 +4,7 @@ import chokidar from "chokidar"
4
 
5
  /** 配置文件 */
6
  class Cfg {
7
- constructor () {
8
  this.config = {}
9
 
10
  /** 监听文件 */
@@ -14,7 +14,7 @@ class Cfg {
14
  }
15
 
16
  /** 初始化配置 */
17
- initCfg () {
18
  let path = "config/config/"
19
  let pathDef = "config/default_config/"
20
  const files = fs.readdirSync(pathDef).filter(file => file.endsWith(".yaml"))
@@ -22,11 +22,12 @@ class Cfg {
22
  if (!fs.existsSync(`${path}${file}`))
23
  fs.copyFileSync(`${pathDef}${file}`, `${path}${file}`)
24
  for (const i of ["data", "temp"])
25
- if (!fs.existsSync(i)) fs.mkdirSync(i)
 
26
  }
27
 
28
  /** Bot配置 */
29
- get bot () {
30
  let bot = this.getConfig("bot")
31
  let defbot = this.getdefSet("bot")
32
  bot = { ...defbot, ...bot }
@@ -34,11 +35,11 @@ class Cfg {
34
  return bot
35
  }
36
 
37
- get other () {
38
  return this.getConfig("other")
39
  }
40
 
41
- get redis () {
42
  return this.getConfig("redis")
43
  }
44
 
@@ -47,7 +48,7 @@ class Cfg {
47
  }
48
 
49
  /** 主人账号 */
50
- get masterQQ () {
51
  let masterQQ = this.getConfig("other").masterQQ || []
52
 
53
  if (!Array.isArray(masterQQ))
@@ -60,7 +61,7 @@ class Cfg {
60
  }
61
 
62
  /** Bot账号:[主人帐号] */
63
- get master () {
64
  let master = this.getConfig("other").master || []
65
 
66
  if (!Array.isArray(master))
@@ -80,15 +81,15 @@ class Cfg {
80
  }
81
 
82
  /** 机器人账号 */
83
- get uin () {
84
  return Object.keys(this.master)
85
  }
86
- get qq () {
87
  return this.uin
88
  }
89
 
90
  /** package.json */
91
- get package () {
92
  if (this._package) return this._package
93
 
94
  this._package = JSON.parse(fs.readFileSync("package.json", "utf8"))
@@ -96,7 +97,7 @@ class Cfg {
96
  }
97
 
98
  /** 群配置 */
99
- getGroup (bot_id = "", group_id = "") {
100
  const config = this.getConfig("group")
101
  const defCfg = this.getdefSet("group")
102
  return {
@@ -109,7 +110,7 @@ class Cfg {
109
  }
110
 
111
  /** other配置 */
112
- getOther () {
113
  let def = this.getdefSet("other")
114
  let config = this.getConfig("other")
115
  return { ...def, ...config }
@@ -119,12 +120,12 @@ class Cfg {
119
  * @param app 功能
120
  * @param name 配置文件名称
121
  */
122
- getdefSet (name) {
123
  return this.getYaml("default_config", name)
124
  }
125
 
126
  /** 用户配置 */
127
- getConfig (name) {
128
  return this.getYaml("config", name)
129
  }
130
 
@@ -133,7 +134,7 @@ class Cfg {
133
  * @param type 默认跑配置-defSet,用户配置-config
134
  * @param name 名称
135
  */
136
- getYaml (type, name) {
137
  let file = `config/${type}/${name}.yaml`
138
  let key = `${type}.${name}`
139
  if (this.config[key]) return this.config[key]
@@ -148,7 +149,7 @@ class Cfg {
148
  }
149
 
150
  /** 监听配置文件 */
151
- watch (file, name, type = "default_config") {
152
  let key = `${type}.${name}`
153
 
154
  if (this.watcher[key]) return
@@ -166,7 +167,7 @@ class Cfg {
166
  this.watcher[key] = watcher
167
  }
168
 
169
- async change_bot () {
170
  /** 修改日志等级 */
171
  let log = await import("./log.js")
172
  log.default()
 
4
 
5
  /** 配置文件 */
6
  class Cfg {
7
+ constructor() {
8
  this.config = {}
9
 
10
  /** 监听文件 */
 
14
  }
15
 
16
  /** 初始化配置 */
17
+ initCfg() {
18
  let path = "config/config/"
19
  let pathDef = "config/default_config/"
20
  const files = fs.readdirSync(pathDef).filter(file => file.endsWith(".yaml"))
 
22
  if (!fs.existsSync(`${path}${file}`))
23
  fs.copyFileSync(`${pathDef}${file}`, `${path}${file}`)
24
  for (const i of ["data", "temp"])
25
+ if (!fs.existsSync(i))
26
+ fs.mkdirSync(i)
27
  }
28
 
29
  /** Bot配置 */
30
+ get bot() {
31
  let bot = this.getConfig("bot")
32
  let defbot = this.getdefSet("bot")
33
  bot = { ...defbot, ...bot }
 
35
  return bot
36
  }
37
 
38
+ get other() {
39
  return this.getConfig("other")
40
  }
41
 
42
+ get redis() {
43
  return this.getConfig("redis")
44
  }
45
 
 
48
  }
49
 
50
  /** 主人账号 */
51
+ get masterQQ() {
52
  let masterQQ = this.getConfig("other").masterQQ || []
53
 
54
  if (!Array.isArray(masterQQ))
 
61
  }
62
 
63
  /** Bot账号:[主人帐号] */
64
+ get master() {
65
  let master = this.getConfig("other").master || []
66
 
67
  if (!Array.isArray(master))
 
81
  }
82
 
83
  /** 机器人账号 */
84
+ get uin() {
85
  return Object.keys(this.master)
86
  }
87
+ get qq() {
88
  return this.uin
89
  }
90
 
91
  /** package.json */
92
+ get package() {
93
  if (this._package) return this._package
94
 
95
  this._package = JSON.parse(fs.readFileSync("package.json", "utf8"))
 
97
  }
98
 
99
  /** 群配置 */
100
+ getGroup(bot_id = "", group_id = "") {
101
  const config = this.getConfig("group")
102
  const defCfg = this.getdefSet("group")
103
  return {
 
110
  }
111
 
112
  /** other配置 */
113
+ getOther() {
114
  let def = this.getdefSet("other")
115
  let config = this.getConfig("other")
116
  return { ...def, ...config }
 
120
  * @param app 功能
121
  * @param name 配置文件名称
122
  */
123
+ getdefSet(name) {
124
  return this.getYaml("default_config", name)
125
  }
126
 
127
  /** 用户配置 */
128
+ getConfig(name) {
129
  return this.getYaml("config", name)
130
  }
131
 
 
134
  * @param type 默认跑配置-defSet,用户配置-config
135
  * @param name 名称
136
  */
137
+ getYaml(type, name) {
138
  let file = `config/${type}/${name}.yaml`
139
  let key = `${type}.${name}`
140
  if (this.config[key]) return this.config[key]
 
149
  }
150
 
151
  /** 监听配置文件 */
152
+ watch(file, name, type = "default_config") {
153
  let key = `${type}.${name}`
154
 
155
  if (this.watcher[key]) return
 
167
  this.watcher[key] = watcher
168
  }
169
 
170
+ async change_bot() {
171
  /** 修改日志等级 */
172
  let log = await import("./log.js")
173
  log.default()
Yunzai/lib/config/init.js CHANGED
@@ -1,6 +1,4 @@
1
  import setLog from "./log.js"
2
- import redisInit from "./redis.js"
3
- import { checkRun } from "./check.js"
4
  import cfg from "./config.js"
5
 
6
  /** 设置标题 */
@@ -9,41 +7,30 @@ process.title = `TRSS Yunzai v${cfg.package.version} © 2023 - 2024 TimeRainStar
9
  /** 设置时区 */
10
  process.env.TZ = "Asia/Shanghai"
11
 
12
- /** 捕获未处理的错误 */
13
- process.on("uncaughtException", error => {
14
- if (typeof logger == "undefined") console.log(error)
15
- else logger.error(error)
16
- })
17
-
18
- /** 捕获未处理的Promise错误 */
19
- process.on("unhandledRejection", (error, promise) => {
20
- if (typeof logger == "undefined") console.log(error)
21
- else logger.error(error)
22
- })
23
-
24
- /** 退出事件 */
25
- process.on("exit", async code => {
26
- if (typeof redis != "undefined" && typeof test == "undefined")
27
- await redis.save()
28
-
29
- if (typeof logger == "undefined")
30
- console.log("TRSS-Yunzai 已停止运行")
31
- else
32
- logger.mark(logger.magenta("TRSS-Yunzai 已停止运行"))
33
- })
34
 
35
- await checkInit()
 
36
 
37
- /** 初始化事件 */
38
- async function checkInit() {
39
- /** 日志设置 */
40
- setLog()
 
 
 
 
 
 
41
 
42
- logger.mark("----^_^----")
43
- logger.mark(logger.yellow(`TRSS-Yunzai v${cfg.package.version} 启动中...`))
44
- logger.mark(logger.cyan("https://github.com/TimeRainStarSky/Yunzai"))
 
45
 
46
- await redisInit()
 
47
 
48
- checkRun()
49
- }
 
 
1
  import setLog from "./log.js"
 
 
2
  import cfg from "./config.js"
3
 
4
  /** 设置标题 */
 
7
  /** 设置时区 */
8
  process.env.TZ = "Asia/Shanghai"
9
 
10
+ process.on("SIGHUP", () => process.exit())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ /** 日志设置 */
13
+ setLog()
14
 
15
+ /** 捕获未处理的错误 */
16
+ for (const i of ["uncaughtException", "unhandledRejection"])
17
+ process.on(i, e => {
18
+ try {
19
+ Bot.makeLog("error", e, i)
20
+ } catch (err) {
21
+ console.error(i, e, err)
22
+ process.exit()
23
+ }
24
+ })
25
 
26
+ /** 退出事件 */
27
+ process.on("exit", code => {
28
+ if (typeof redis != "undefined")
29
+ redis.save()
30
 
31
+ logger.mark(logger.magenta(`TRSS-Yunzai 已停止运行,本次运行时长:${Bot.getTimeDiff()} (${code})`))
32
+ })
33
 
34
+ logger.mark("----^_^----")
35
+ logger.mark(logger.yellow(`TRSS-Yunzai v${cfg.package.version} 启动中...`))
36
+ logger.mark(logger.cyan("https://github.com/TimeRainStarSky/Yunzai"))
Yunzai/lib/config/log.js CHANGED
@@ -1,16 +1,15 @@
1
- import log4js from 'log4js'
2
- import chalk from 'chalk'
3
- import cfg from './config.js'
4
- import fs from 'node:fs'
5
 
6
  /**
7
  * 设置日志样式
8
  */
9
- export default function setLog () {
10
- let file = './logs'
11
- if (!fs.existsSync(file)) {
12
  fs.mkdirSync(file)
13
- }
14
 
15
  /** 调整error日志等级 */
16
  // log4js.levels.levels[5].level = Number.MAX_VALUE
@@ -19,80 +18,59 @@ export default function setLog () {
19
  log4js.configure({
20
  appenders: {
21
  console: {
22
- type: 'console',
23
  layout: {
24
- type: 'pattern',
25
- pattern: '%[[TRSSYz][%d{hh:mm:ss.SSS}][%4.4p]%] %m'
26
  }
27
  },
28
  command: {
29
- type: 'dateFile', // 可以是console,dateFile,file,Logstash等
30
- filename: 'logs/command', // 将会按照filename和pattern拼接文件名
31
- pattern: 'yyyy-MM-dd.log',
32
  numBackups: 15,
33
  alwaysIncludePattern: true,
34
  layout: {
35
- type: 'pattern',
36
- pattern: '[%d{hh:mm:ss.SSS}][%4.4p] %m'
37
  }
38
  },
39
  error: {
40
- type: 'file',
41
- filename: 'logs/error.log',
42
  alwaysIncludePattern: true,
43
  layout: {
44
- type: 'pattern',
45
- pattern: '[%d{hh:mm:ss.SSS}][%4.4p] %m'
46
  }
47
  }
48
  },
49
  categories: {
50
- default: { appenders: ['console'], level: cfg.bot.log_level },
51
- command: { appenders: ['console', 'command'], level: 'warn' },
52
- error: { appenders: ['console', 'command', 'error'], level: 'error' }
53
  }
54
  })
55
 
56
- const defaultLogger = log4js.getLogger('message')
57
- const commandLogger = log4js.getLogger('command')
58
- const errorLogger = log4js.getLogger('error')
59
 
60
- /* eslint-disable no-useless-call */
61
  /** 全局变量 logger */
62
  global.logger = {
63
- trace () {
64
- defaultLogger.trace.call(defaultLogger, ...arguments)
65
- },
66
- debug () {
67
- defaultLogger.debug.call(defaultLogger, ...arguments)
68
- },
69
- info () {
70
- defaultLogger.info.call(defaultLogger, ...arguments)
71
- },
72
- // warn及以上的日志采用error策略
73
- warn () {
74
- commandLogger.warn.call(defaultLogger, ...arguments)
75
- },
76
- error () {
77
- errorLogger.error.call(errorLogger, ...arguments)
78
- },
79
- fatal () {
80
- errorLogger.fatal.call(errorLogger, ...arguments)
81
- },
82
- mark () {
83
- errorLogger.mark.call(commandLogger, ...arguments)
84
- }
85
  }
86
-
87
- logColor()
88
- }
89
-
90
- function logColor () {
91
- logger.chalk = chalk
92
- logger.red = chalk.red
93
- logger.green = chalk.green
94
- logger.yellow = chalk.yellow
95
- logger.blue = chalk.blue
96
- logger.magenta = chalk.magenta
97
- logger.cyan = chalk.cyan
98
- }
 
1
+ import log4js from "log4js"
2
+ import chalk from "chalk"
3
+ import cfg from "./config.js"
4
+ import fs from "node:fs"
5
 
6
  /**
7
  * 设置日志样式
8
  */
9
+ export default function setLog() {
10
+ let file = "./logs"
11
+ if (!fs.existsSync(file))
12
  fs.mkdirSync(file)
 
13
 
14
  /** 调整error日志等级 */
15
  // log4js.levels.levels[5].level = Number.MAX_VALUE
 
18
  log4js.configure({
19
  appenders: {
20
  console: {
21
+ type: "console",
22
  layout: {
23
+ type: "pattern",
24
+ pattern: "%[[TRSSYz][%d{hh:mm:ss.SSS}][%4.4p]%] %m"
25
  }
26
  },
27
  command: {
28
+ type: "dateFile", // 可以是console,dateFile,file,Logstash等
29
+ filename: "logs/command", // 将会按照filename和pattern拼接文件名
30
+ pattern: "yyyy-MM-dd.log",
31
  numBackups: 15,
32
  alwaysIncludePattern: true,
33
  layout: {
34
+ type: "pattern",
35
+ pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
36
  }
37
  },
38
  error: {
39
+ type: "file",
40
+ filename: "logs/error.log",
41
  alwaysIncludePattern: true,
42
  layout: {
43
+ type: "pattern",
44
+ pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
45
  }
46
  }
47
  },
48
  categories: {
49
+ default: { appenders: ["console"], level: cfg.bot.log_level },
50
+ command: { appenders: ["console", "command"], level: "warn" },
51
+ error: { appenders: ["console", "command", "error"], level: "error" }
52
  }
53
  })
54
 
55
+ const defaultLogger = log4js.getLogger("message")
56
+ const commandLogger = log4js.getLogger("command")
57
+ const errorLogger = log4js.getLogger("error")
58
 
 
59
  /** 全局变量 logger */
60
  global.logger = {
61
+ trace: (...args) => defaultLogger.trace(...args),
62
+ debug: (...args) => defaultLogger.debug(...args),
63
+ info: (...args) => defaultLogger.info(...args),
64
+ warn: (...args) => commandLogger.warn(...args),
65
+ error: (...args) => errorLogger.error(...args),
66
+ fatal: (...args) => errorLogger.fatal(...args),
67
+ mark: (...args) => commandLogger.mark(...args),
68
+ chalk: chalk,
69
+ red: chalk.red,
70
+ green: chalk.green,
71
+ yellow: chalk.yellow,
72
+ blue: chalk.blue,
73
+ magenta: chalk.magenta,
74
+ cyan: chalk.cyan,
 
 
 
 
 
 
 
 
75
  }
76
+ }
 
 
 
 
 
 
 
 
 
 
 
 
Yunzai/lib/config/redis.js CHANGED
@@ -1,8 +1,8 @@
1
  import cfg from "./config.js"
2
- import common from "../common/common.js"
3
  import { createClient } from "redis"
4
  import { exec } from "node:child_process"
5
 
 
6
  /**
7
  * 初始化全局redis客户端
8
  */
@@ -13,65 +13,54 @@ export default async function redisInit() {
13
  if (rc.username || rc.password)
14
  redisPw += "@"
15
  const redisUrl = `redis://${redisUn}${redisPw}${rc.host}:${rc.port}/${rc.db}`
16
- let client = createClient({ url: redisUrl })
 
 
 
 
 
 
17
 
18
  try {
19
- logger.info(`正在连接 ${logger.blue(redisUrl)}`)
20
- await client.connect()
21
  } catch (err) {
22
- logger.error(`Redis 错误:${logger.red(err)}`)
23
-
24
- const cmd = "redis-server --save 900 1 --save 300 10 --daemonize yes" + await aarch64()
25
- logger.info("正在启动 Redis...")
26
- await execSync(cmd)
27
- await common.sleep(1000)
28
-
29
- try {
30
- client = createClient({ url: redisUrl })
31
- await client.connect()
32
- } catch (err) {
33
- logger.error(`Redis 错误:${logger.red(err)}`)
34
- logger.error(`请先启动 Redis:${logger.blue(cmd)}`)
35
- process.exit()
36
- }
37
  }
38
 
39
- client.on("error", async err => {
40
- logger.error(`Redis 错误:${logger.red(err)}`)
41
- const cmd = "redis-server --save 900 1 --save 300 10 --daemonize yes" + await aarch64()
42
- logger.error(`请先启动 Redis:${cmd}`)
43
- process.exit()
44
  })
45
 
46
- /** 全局变量 redis */
47
- client.url = redisUrl
48
- global.redis = client
49
- logger.info("Redis 连接成功")
50
- return client
 
 
 
 
 
 
 
 
51
  }
52
 
53
  async function aarch64() {
54
- if (process.platform == "win32")
55
  return ""
56
- /** 判断arch */
57
- const arch = await execSync("uname -m")
58
- if (arch.stdout && arch.stdout.includes("aarch64")) {
59
- /** 判断redis版本 */
60
- let v = await execSync("redis-server -v")
61
- if (v.stdout) {
62
- v = v.stdout.match(/v=(\d)./)
63
- /** 忽略arm警告 */
64
- if (v && v[1] >= 6)
65
- return " --ignore-warnings ARM64-COW-BUG"
66
- }
67
  }
68
  return ""
69
- }
70
-
71
- function execSync (cmd) {
72
- return new Promise((resolve, reject) => {
73
- exec(cmd, (error, stdout, stderr) => {
74
- resolve({ error, stdout, stderr })
75
- })
76
- })
77
  }
 
1
  import cfg from "./config.js"
 
2
  import { createClient } from "redis"
3
  import { exec } from "node:child_process"
4
 
5
+ let lock = false
6
  /**
7
  * 初始化全局redis客户端
8
  */
 
13
  if (rc.username || rc.password)
14
  redisPw += "@"
15
  const redisUrl = `redis://${redisUn}${redisPw}${rc.host}:${rc.port}/${rc.db}`
16
+ Bot.makeLog("info", `正在连接 ${logger.blue(redisUrl)}`, "Redis")
17
+ return connectRedis(redisUrl)
18
+ }
19
+
20
+ async function connectRedis(redisUrl, cmd) {
21
+ if (lock && !cmd) return
22
+ lock = true
23
 
24
  try {
25
+ global.redis = createClient({ url: redisUrl })
26
+ await redis.connect()
27
  } catch (err) {
28
+ Bot.makeLog("error", ["连接错误", err], "Redis")
29
+ if (!cmd) return startRedis(redisUrl)
30
+ Bot.makeLog("error", ["请先启动", logger.blue(cmd)], "Redis")
31
+ process.exit(1)
 
 
 
 
 
 
 
 
 
 
 
32
  }
33
 
34
+ redis.on("error", err => {
35
+ Bot.makeLog("error", err, "Redis")
36
+ return connectRedis(redisUrl)
 
 
37
  })
38
 
39
+ lock = false
40
+ return redis
41
+ }
42
+
43
+ async function startRedis(redisUrl) {
44
+ if (cfg.redis.host != "127.0.0.1") {
45
+ Bot.makeLog("error", `连接失败,请确认连接地址正确`, "Redis")
46
+ process.exit(1)
47
+ }
48
+ const cmd = `redis-server --port ${cfg.redis.port} --save 900 1 --save 300 10 --daemonize yes${await aarch64()}`
49
+ await Bot.exec(cmd)
50
+ await Bot.sleep(1000)
51
+ return connectRedis(redisUrl, cmd)
52
  }
53
 
54
  async function aarch64() {
55
+ if (process.platform == "win32" || process.arch != "arm64")
56
  return ""
57
+ /** 判断redis版本 */
58
+ let v = await Bot.exec("redis-server -v")
59
+ if (v.stdout?.match) {
60
+ v = v.stdout.match(/v=(\d)./)
61
+ /** 忽略arm警告 */
62
+ if (v && v[1] >= 6)
63
+ return " --ignore-warnings ARM64-COW-BUG"
 
 
 
 
64
  }
65
  return ""
 
 
 
 
 
 
 
 
66
  }
Yunzai/lib/events/message.js CHANGED
@@ -1,11 +1,11 @@
1
- import EventListener from '../listener/listener.js'
2
 
3
  /**
4
  * 监听群聊消息
5
  */
6
  export default class messageEvent extends EventListener {
7
  constructor () {
8
- super({ event: 'message' })
9
  }
10
 
11
  async execute (e) {
 
1
+ import EventListener from "../listener/listener.js"
2
 
3
  /**
4
  * 监听群聊消息
5
  */
6
  export default class messageEvent extends EventListener {
7
  constructor () {
8
+ super({ event: "message" })
9
  }
10
 
11
  async execute (e) {
Yunzai/lib/events/notice.js CHANGED
@@ -1,11 +1,11 @@
1
- import EventListener from '../listener/listener.js'
2
 
3
  /**
4
  * 监听群聊消息
5
  */
6
  export default class noticeEvent extends EventListener {
7
  constructor () {
8
- super({ event: 'notice' })
9
  }
10
 
11
  async execute (e) {
 
1
+ import EventListener from "../listener/listener.js"
2
 
3
  /**
4
  * 监听群聊消息
5
  */
6
  export default class noticeEvent extends EventListener {
7
  constructor () {
8
+ super({ event: "notice" })
9
  }
10
 
11
  async execute (e) {
Yunzai/lib/events/online.js CHANGED
@@ -1,5 +1,4 @@
1
- import EventListener from '../listener/listener.js'
2
- import cfg from '../config/config.js'
3
 
4
  /**
5
  * 监听上线事件
@@ -7,12 +6,12 @@ import cfg from '../config/config.js'
7
  export default class onlineEvent extends EventListener {
8
  constructor() {
9
  super({
10
- event: 'online',
11
  once: true
12
  })
13
  }
14
 
15
  async execute() {
16
- logger.mark('----^_^----')
17
  }
18
  }
 
1
+ import EventListener from "../listener/listener.js"
 
2
 
3
  /**
4
  * 监听上线事件
 
6
  export default class onlineEvent extends EventListener {
7
  constructor() {
8
  super({
9
+ event: "online",
10
  once: true
11
  })
12
  }
13
 
14
  async execute() {
15
+ logger.mark("----^_^----")
16
  }
17
  }
Yunzai/lib/events/request.js CHANGED
@@ -1,11 +1,11 @@
1
- import EventListener from '../listener/listener.js'
2
 
3
  /**
4
  * 监听群聊消息
5
  */
6
  export default class requestEvent extends EventListener {
7
  constructor () {
8
- super({ event: 'request' })
9
  }
10
 
11
  async execute (e) {
 
1
+ import EventListener from "../listener/listener.js"
2
 
3
  /**
4
  * 监听群聊消息
5
  */
6
  export default class requestEvent extends EventListener {
7
  constructor () {
8
+ super({ event: "request" })
9
  }
10
 
11
  async execute (e) {
Yunzai/lib/listener/listener.js CHANGED
@@ -1,4 +1,4 @@
1
- import PluginsLoader from '../plugins/loader.js'
2
 
3
  export default class EventListener {
4
  /**
@@ -8,7 +8,7 @@ export default class EventListener {
8
  * @param data.once 是否只监听一次
9
  */
10
  constructor (data) {
11
- this.prefix = data.prefix || ''
12
  this.event = data.event
13
  this.once = data.once || false
14
  this.plugins = PluginsLoader
 
1
+ import PluginsLoader from "../plugins/loader.js"
2
 
3
  export default class EventListener {
4
  /**
 
8
  * @param data.once 是否只监听一次
9
  */
10
  constructor (data) {
11
+ this.prefix = data.prefix || ""
12
  this.event = data.event
13
  this.once = data.once || false
14
  this.plugins = PluginsLoader
Yunzai/lib/listener/loader.js CHANGED
@@ -1,5 +1,5 @@
1
- import fs from 'node:fs'
2
- import lodash from 'lodash'
3
 
4
  /**
5
  * 加载监听事件
@@ -12,21 +12,21 @@ class ListenerLoader {
12
  logger.info("-----------")
13
  logger.info("加载监听事件中...")
14
  let eventCount = 0
15
- for (const file of fs.readdirSync('./lib/events').filter(file => file.endsWith('.js'))) {
16
  logger.debug(`加载监听事件:${file}`)
17
  try {
18
  let listener = await import(`../events/${file}`)
19
  if (!listener.default) continue
20
  listener = new listener.default()
21
- const on = listener.once ? 'once' : 'on'
22
 
23
  if (lodash.isArray(listener.event)) {
24
  listener.event.forEach((type) => {
25
- const e = listener[type] ? type : 'execute'
26
  Bot[on](listener.prefix + type, event => listener[e](event))
27
  })
28
  } else {
29
- const e = listener[listener.event] ? listener.event : 'execute'
30
  Bot[on](listener.prefix + listener.event, event => listener[e](event))
31
  }
32
  eventCount++
 
1
+ import fs from "node:fs/promises"
2
+ import lodash from "lodash"
3
 
4
  /**
5
  * 加载监听事件
 
12
  logger.info("-----------")
13
  logger.info("加载监听事件中...")
14
  let eventCount = 0
15
+ for (const file of (await fs.readdir("./lib/events")).filter(file => file.endsWith(".js"))) {
16
  logger.debug(`加载监听事件:${file}`)
17
  try {
18
  let listener = await import(`../events/${file}`)
19
  if (!listener.default) continue
20
  listener = new listener.default()
21
+ const on = listener.once ? "once" : "on"
22
 
23
  if (lodash.isArray(listener.event)) {
24
  listener.event.forEach((type) => {
25
+ const e = listener[type] ? type : "execute"
26
  Bot[on](listener.prefix + type, event => listener[e](event))
27
  })
28
  } else {
29
+ const e = listener[listener.event] ? listener.event : "execute"
30
  Bot[on](listener.prefix + listener.event, event => listener[e](event))
31
  }
32
  eventCount++
Yunzai/lib/modules/md5/index.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import { createHash } from "node:crypto"
2
+ export default function md5(data) {
3
+ return createHash("md5").update(data).digest("hex")
4
+ }
Yunzai/lib/modules/md5/package.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "name": "md5",
3
+ "type": "module",
4
+ "main": "index.js"
5
+ }
Yunzai/lib/modules/node-fetch/index.js ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ export const { Blob, File, FormData, Headers, Request, Response } = global
2
+ export default global.fetch
Yunzai/lib/modules/node-fetch/package.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "name": "node-fetch",
3
+ "type": "module",
4
+ "main": "index.js"
5
+ }
Yunzai/lib/modules/oicq/index.js CHANGED
@@ -1,67 +1,33 @@
1
- import fs from "node:fs"
2
- import path from "node:path"
3
-
4
- function toSegment(type, data) {
5
- for (const i in data)
6
- if (typeof data[i] == "string" && (i == "file" || data[i].match(/^file:\/\//)) && fs.existsSync(data[i].replace(/^file:\/\//, ""))) {
7
- if (i == "file" && !data.name)
8
- data.name = `${Date.now()}-${path.basename(data[i])}`
9
- data[i] = fs.readFileSync(data[i].replace(/^file:\/\//, ""))
10
- }
11
- return { type, ...data }
12
- }
13
-
14
  const segment = new class segment {
15
  custom(type, data) {
16
- return toSegment(type, data)
17
  }
18
  raw(data) {
19
- return toSegment("raw", { data })
20
  }
21
  button(...data) {
22
- return toSegment("button", { data })
23
  }
24
  markdown(data) {
25
- return toSegment("markdown", { data })
26
  }
27
  image(file, name) {
28
- return toSegment("image", { file, name })
29
  }
30
  at(qq, name) {
31
- return toSegment("at", { qq, name })
32
  }
33
  record(file, name) {
34
- return toSegment("record", { file, name })
35
  }
36
  video(file, name) {
37
- return toSegment("video", { file, name })
38
  }
39
  file(file, name) {
40
- return toSegment("file", { file, name })
41
  }
42
  reply(id, text, qq, time, seq) {
43
- return toSegment("reply", { id, text, qq, time, seq })
44
- }
45
- face(id) {
46
- return toSegment("face", { id })
47
- }
48
- share(url, title, content, image) {
49
- return toSegment("share", { url, title, content, image })
50
- }
51
- music(type, id, url, audio, title) {
52
- return toSegment("music", { type, id, url, audio, title })
53
- }
54
- poke(qq) {
55
- return toSegment("poke", { qq })
56
- }
57
- gift(qq, id) {
58
- return toSegment("gift", { qq, id })
59
- }
60
- cardimage(file, name, minwidth, minheight, maxwidth, maxheight, source, icon) {
61
- return toSegment("cardimage", { file, name, minwidth, minheight, maxwidth, maxheight, source, icon })
62
- }
63
- tts(text) {
64
- return toSegment("tts", { text })
65
  }
66
  }
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  const segment = new class segment {
2
  custom(type, data) {
3
+ return { type, ...data }
4
  }
5
  raw(data) {
6
+ return { type: "raw", data }
7
  }
8
  button(...data) {
9
+ return { type: "button", data }
10
  }
11
  markdown(data) {
12
+ return { type: "markdown", data }
13
  }
14
  image(file, name) {
15
+ return { type: "image", file, name }
16
  }
17
  at(qq, name) {
18
+ return { type: "at", qq, name }
19
  }
20
  record(file, name) {
21
+ return { type: "record", file, name }
22
  }
23
  video(file, name) {
24
+ return { type: "video", file, name }
25
  }
26
  file(file, name) {
27
+ return { type: "file", file, name }
28
  }
29
  reply(id, text, qq, time, seq) {
30
+ return { type: "reply", id, text, qq, time, seq }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
  }
33
 
Yunzai/lib/plugins/config.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fs from "node:fs/promises"
2
+ import YAML from "yaml"
3
+ import _ from "lodash"
4
+ export default async function(name, config, keep) {
5
+ const configFile = `config/${name}.yaml`
6
+ const configSave = () => fs.writeFile(configFile, YAML.stringify(config), "utf-8")
7
+
8
+ let configData
9
+ try {
10
+ configData = YAML.parse(await fs.readFile(configFile, "utf-8"))
11
+ _.merge(config, configData)
12
+ } catch (err) {
13
+ logger.debug("配置文件", configFile, "读取失败", err)
14
+ }
15
+ _.merge(config, keep)
16
+
17
+ if (YAML.stringify(config) != YAML.stringify(configData))
18
+ await configSave()
19
+ return { config, configSave }
20
+ }
Yunzai/lib/plugins/loader.js CHANGED
@@ -1,5 +1,5 @@
1
  import util from "node:util"
2
- import fs from "node:fs"
3
  import lodash from "lodash"
4
  import cfg from "../config/config.js"
5
  import plugin from "./plugin.js"
@@ -8,9 +8,8 @@ import { segment } from "oicq"
8
  import chokidar from "chokidar"
9
  import moment from "moment"
10
  import path from "node:path"
11
- import common from "../common/common.js"
12
  import Runtime from "./runtime.js"
13
- import Handler from './handler.js'
14
 
15
  /** 全局变量 plugin */
16
  global.plugin = plugin
@@ -24,7 +23,7 @@ class PluginsLoader {
24
  this.priority = []
25
  this.handler = {}
26
  this.task = []
27
- this.dir = "./plugins"
28
 
29
  /** 命令冷却cd */
30
  this.groupCD = {}
@@ -32,6 +31,11 @@ class PluginsLoader {
32
 
33
  /** 插件监听 */
34
  this.watcher = {}
 
 
 
 
 
35
 
36
  this.msgThrottle = {}
37
 
@@ -39,133 +43,127 @@ class PluginsLoader {
39
  this.srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/
40
  }
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  /**
43
  * 监听事件加载
44
  * @param isRefresh 是否刷新
45
  */
46
  async load(isRefresh = false) {
47
- this.delCount()
48
- if (!lodash.isEmpty(this.priority) && !isRefresh) return
49
-
50
- const files = this.getPlugins()
51
 
52
  logger.info("-----------")
53
  logger.info("加载插件中...")
54
 
55
- let pluCount = 0
 
 
56
 
57
- let packageErr = []
58
- for (let File of files) {
59
- try {
60
- let tmp = await import(File.path)
61
- let apps = tmp
62
- if (tmp.apps) {
63
- apps = { ...tmp.apps }
64
- }
65
- lodash.forEach(apps, (p, i) => {
66
- if (!p.prototype) return
67
- pluCount++
68
- /* eslint-disable new-cap */
69
- let plugin = new p()
70
- logger.debug(`载入插件 [${File.name}][${plugin.name}]`)
71
- /** 执行初始化 */
72
- this.runInit(plugin)
73
- /** 初始化定时任务 */
74
- this.collectTask(plugin.task)
75
- this.priority.push({
76
- class: p,
77
- key: File.name,
78
- name: plugin.name,
79
- priority: plugin.priority
80
- })
81
- if (plugin.handler) {
82
- lodash.forEach(plugin.handler, ({ fn, key, priority }) => {
83
- Handler.add({
84
- ns: plugin.namespace || File.name,
85
- key: key,
86
- self: plugin,
87
- property: priority || plugin.priority || 500,
88
- fn: plugin[fn]
89
- })
90
- })
91
- }
92
- })
93
- } catch (error) {
94
- if (error.stack.includes("Cannot find package")) {
95
- packageErr.push({ error, File })
96
- } else {
97
- logger.error(`载入插件错误:${logger.red(File.name)}`)
98
- logger.error(decodeURI(error.stack))
99
- }
100
- }
101
- }
102
 
103
  this.packageTips(packageErr)
104
- this.creatTask()
105
 
106
  logger.info(`加载定时任务[${this.task.length}个]`)
107
- logger.info(`加载插件[${pluCount}个]`)
108
 
109
  /** 优先级排序 */
110
  this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
111
  }
112
 
113
- async runInit(plugin) {
114
- plugin.init && plugin.init()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  }
116
 
117
- packageTips(packageErr) {
118
- if (!packageErr || packageErr.length <= 0) return
119
- logger.mark("--------插件载入错误--------")
120
- packageErr.forEach(v => {
121
- let pack = v.error.stack.match(/'(.+?)'/g)[0].replace(/'/g, "")
122
- logger.mark(`${v.File.name} 缺少依赖:${logger.red(pack)}`)
123
- logger.mark(`新增插件后请执行安装命令:${logger.red("pnpm i")} 安装依赖`)
124
- logger.mark("如安装后仍未解决可联系插件作者解决")
 
 
 
 
 
 
125
  })
126
- // logger.error("或者使用其他包管理工具安装依赖")
127
- logger.mark("---------------------")
128
- }
129
-
130
- getPlugins() {
131
- let ignore = ["index.js"]
132
- let files = fs.readdirSync(this.dir, { withFileTypes: true })
133
- let ret = []
134
- for (let val of files) {
135
- let filepath = "../../plugins/" + val.name
136
- let tmp = {
137
- name: val.name
138
- }
139
- if (val.isFile()) {
140
- if (!val.name.endsWith(".js")) continue
141
- if (ignore.includes(val.name)) continue
142
- tmp.path = filepath
143
- ret.push(tmp)
144
- continue
145
- }
146
-
147
- if (fs.existsSync(`${this.dir}/${val.name}/index.js`)) {
148
- tmp.path = filepath + "/index.js"
149
- ret.push(tmp)
150
- continue
151
- }
152
-
153
- let apps = fs.readdirSync(`${this.dir}/${val.name}`, { withFileTypes: true })
154
- for (let app of apps) {
155
- if (!app.name.endsWith(".js")) continue
156
- if (ignore.includes(app.name)) continue
157
-
158
- ret.push({
159
- name: `${val.name}/${app.name}`,
160
- path: `../../plugins/${val.name}/${app.name}`
161
  })
162
-
163
- /** 监听热更新 */
164
- this.watch(val.name, app.name)
165
- }
166
  }
 
167
 
168
- return ret
 
 
 
 
 
 
 
 
 
169
  }
170
 
171
  /**
@@ -175,52 +173,38 @@ class PluginsLoader {
175
  * @param e 事件
176
  */
177
  async deal(e) {
 
178
  /** 检查黑白名单 */
179
  if (!this.checkBlack(e)) return
180
  /** 冷却 */
181
  if (!this.checkLimit(e)) return
182
  /** 处理事件 */
183
  this.dealEvent(e)
184
- /** 处理消息 */
185
- this.dealMsg(e)
186
  /** 处理回复 */
187
  this.reply(e)
188
- /** 过滤事件 */
189
- let priority = []
190
  /** 注册runtime */
191
  await Runtime.init(e)
192
 
193
- this.priority.forEach(v => {
194
- let p = new v.class(e)
 
195
  p.e = e
196
- /** 判断是否启用功能 */
197
- if (!this.checkDisable(e, p)) return
198
- /** 过滤事件 */
199
- if (!this.filtEvent(e, p)) return
200
- priority.push(p)
201
- })
202
 
203
- for (let plugin of priority) {
204
  /** 上下文hook */
205
- if (plugin.getContext) {
206
- let context = plugin.getContext()
207
- if (!lodash.isEmpty(context)) {
208
- for (let fnc in context) {
209
- plugin[fnc](context[fnc])
210
- }
211
- return
212
- }
213
  }
214
-
215
- /** 群上下文hook */
216
- if (plugin.getContextGroup) {
217
- let context = plugin.getContextGroup()
218
- if (!lodash.isEmpty(context)) {
219
- for (let fnc in context) {
220
- plugin[fnc](context[fnc])
221
- }
222
- return
223
- }
224
  }
225
  }
226
 
@@ -242,59 +226,43 @@ class PluginsLoader {
242
  e.msg = e.msg.replace(this.srReg, "#星铁")
243
  }
244
 
245
- /** accept */
246
- for (let plugin of priority) {
247
- /** accept hook */
248
  if (plugin.accept) {
249
- let res = plugin.accept(e)
250
-
251
- if (util.types.isPromise(res)) res = await res
252
-
253
- if (res === "return") return
254
-
255
  if (res) break
256
  }
257
- }
258
 
259
- /* eslint-disable no-labels */
260
- a: for (let plugin of priority) {
261
  /** 正则匹配 */
262
- if (plugin.rule) {
263
- for (let v of plugin.rule) {
264
- /** 判断事件 */
265
- if (v.event && !this.filtEvent(e, v)) continue
266
-
267
- if (new RegExp(v.reg).test(e.msg)) {
268
- e.logFnc = `[${plugin.name}][${v.fnc}]`
269
-
270
- if (v.log !== false) {
271
- logger.info(`${e.logFnc}${e.logText} ${lodash.truncate(e.msg, { length: 80 })}`)
272
- }
273
-
274
- /** 判断权限 */
275
- if (!this.filtPermission(e, v)) break a
276
-
277
- try {
278
- let res = plugin[v.fnc] && plugin[v.fnc](e)
279
-
280
- let start = Date.now()
281
-
282
- if (util.types.isPromise(res)) res = await res
283
-
284
- if (res !== false) {
285
- /** 设置冷却cd */
286
- this.setLimit(e)
287
- if (v.log !== false) {
288
- logger.mark(`${e.logFnc} ${lodash.truncate(e.msg, { length: 80 })} 处理完成 ${Date.now() - start}ms`)
289
- }
290
- break a
291
- }
292
- } catch (error) {
293
- logger.error(`${e.logFnc}`)
294
- logger.error(error.stack)
295
- break a
296
- }
297
  }
 
 
 
 
298
  }
299
  }
300
  }
@@ -303,23 +271,16 @@ class PluginsLoader {
303
  /** 过滤事件 */
304
  filtEvent(e, v) {
305
  if (!v.event) return false
306
- let event = v.event.split(".")
307
- let eventMap = {
308
- message: ["post_type", "message_type", "sub_type"],
309
- notice: ["post_type", "notice_type", "sub_type"],
310
- request: ["post_type", "request_type", "sub_type"]
 
 
 
311
  }
312
- let newEvent = []
313
- event.forEach((val, index) => {
314
- if (val === "*") {
315
- newEvent.push(val)
316
- } else if (eventMap[e.post_type]) {
317
- newEvent.push(e[eventMap[e.post_type][index]])
318
- }
319
- })
320
- newEvent = newEvent.join(".")
321
-
322
- return v.event === newEvent
323
  }
324
 
325
  /** 判断权限 */
@@ -336,10 +297,6 @@ class PluginsLoader {
336
  }
337
 
338
  if (e.isGroup) {
339
- if (!e.member?._info) {
340
- e.reply("数据加载中,请稍后再试")
341
- return false
342
- }
343
  if (v.permission == "owner") {
344
  if (!e.member.is_owner) {
345
  e.reply("暂无权限,只有群主才能操作")
@@ -357,20 +314,17 @@ class PluginsLoader {
357
  return true
358
  }
359
 
360
- dealEvent(e) {
361
- if (!e.friend && e.user_id) e.friend = e.bot.pickFriend(e.user_id)
362
- if (!e.group && e.group_id) e.group = e.bot.pickGroup(e.group_id)
363
- if (!e.member && e.group && e.user_id) e.member = e.group.pickMember(e.user_id)
364
- for (const i of [e.friend, e.group, e.member]) {
365
- if (typeof i != "object") continue
366
- if (!i.makeForwardMsg) i.makeForwardMsg = Bot.makeForwardMsg
367
- if (!i.sendForwardMsg) i.sendForwardMsg = msg => Bot.sendForwardMsg(msg => i.sendMsg(msg), msg)
368
- if (!i.getInfo) i.getInfo = () => i
369
- }
370
  }
371
 
372
  /**
373
- * 处理消息,加入自定义字段
374
  * @param e.msg 文本消息,多行会自动拼接
375
  * @param e.img 图片消息数组
376
  * @param e.atBot 是否at机器人
@@ -382,12 +336,11 @@ class PluginsLoader {
382
  * @param e.logText 日志用户字符串
383
  * @param e.logFnc 日志方法字符串
384
  */
385
- dealMsg(e) {
386
  if (e.message) for (const i of e.message) {
387
  switch (i.type) {
388
  case "text":
389
- if (!e.msg) e.msg = ""
390
- if (i.text) e.msg += i.text.replace(/^\s*[##井]+\s*/, "#").replace(/^\s*[\\**※]+\s*/, "*").trim()
391
  break
392
  case "image":
393
  if (Array.isArray(e.img))
@@ -411,6 +364,10 @@ class PluginsLoader {
411
  case "file":
412
  e.file = i
413
  break
 
 
 
 
414
  }
415
  }
416
 
@@ -423,37 +380,31 @@ class PluginsLoader {
423
  e.sender.card = e.sender.nickname
424
  } else {
425
  e.sender = {
 
 
426
  card: e.friend?.nickname,
427
- nickname: e.friend?.nickname
428
  }
429
  }
430
 
431
  e.logText = `[${e.sender?.nickname ? `${e.sender.nickname}(${e.user_id})` : e.user_id}]`
432
- }
433
-
434
- if (e.message_type == "group" || e.notice_type == "group") {
435
  e.isGroup = true
 
436
  if (e.sender) {
437
- e.sender.card = e.sender.card || e.sender.nickname
438
- } else if (e.member) {
439
- e.sender = {
440
- card: e.member.card || e.member.nickname
441
- }
442
- } else if (e.nickname) {
443
- e.sender = {
444
- card: e.nickname,
445
- nickname: e.nickname
446
- }
447
  } else {
448
  e.sender = {
449
- card: "",
450
- nickname: ""
 
451
  }
452
  }
453
 
454
- if (!e.group_name) e.group_name = e.group?.name
 
455
 
456
- e.logText = `[${e.group_name ? `${e.group_name}(${e.group_id})` : e.group_id}, ${e.sender?.nickname ? `${e.sender.nickname}(${e.user_id})` : e.user_id}]`
457
  }
458
 
459
  if (e.user_id && cfg.master[e.self_id]?.includes(String(e.user_id))) {
@@ -479,24 +430,19 @@ class PluginsLoader {
479
 
480
  /** 处理回复,捕获发送失败异常 */
481
  reply(e) {
482
- if (e.reply)
483
- e.replyNew = e.reply
484
- else
485
- e.replyNew = msg => {
486
- if (e.isGroup) {
487
- if (e.group?.sendMsg) {
488
- return e.group.sendMsg(msg)
489
- } else {
490
- return e.bot.pickGroup(e.group_id).sendMsg(msg)
491
- }
492
- } else {
493
- if (e.friend?.sendMsg) {
494
- return e.friend.sendMsg(msg)
495
- } else {
496
- return e.bot.pickFriend(e.user_id).sendMsg(msg)
497
- }
498
- }
499
  }
 
500
 
501
  /**
502
  * @param msg 发送的消息
@@ -509,13 +455,13 @@ class PluginsLoader {
509
 
510
  let { recallMsg = 0, at = "" } = data
511
 
512
- if (at) {
513
  if (at === true)
514
  at = e.user_id
515
  if (Array.isArray(msg))
516
- msg.unshift(segment.at(at))
517
  else
518
- msg = [segment.at(at), msg]
519
  }
520
 
521
  if (quote && e.message_id) {
@@ -527,10 +473,9 @@ class PluginsLoader {
527
 
528
  let res
529
  try {
530
- res = await e.replyNew(msg)
531
  } catch (err) {
532
- Bot.makeLog("error", `发送消息错误:${Bot.String(msg)}`, e.self_id)
533
- logger.error(err)
534
  }
535
 
536
  if (recallMsg > 0 && res?.message_id) {
@@ -548,93 +493,63 @@ class PluginsLoader {
548
  }, recallMsg * 1000)
549
  }
550
 
551
- this.count(e, msg)
552
  return res
553
  }
554
  }
555
 
556
- count(e, msg) {
557
- let screenshot = false
558
- if (msg && msg?.file)
559
- screenshot = true
560
-
561
- this.saveCount("sendMsg")
562
- if (screenshot)
563
- this.saveCount("screenshot")
564
-
565
- if (e.group_id) {
566
- this.saveCount("sendMsg", e.group_id)
567
- if (screenshot)
568
- this.saveCount("screenshot", e.group_id)
569
- }
570
  }
571
 
572
- saveCount(type, groupId = "") {
573
- let key = "Yz:count:"
574
-
575
- if (groupId) {
576
- key += `group:${groupId}:`
 
 
 
 
 
 
577
  }
578
 
579
- let dayKey = `${key}${type}:day:${moment().format("MMDD")}`
580
- let monthKey = `${key}${type}:month:${Number(moment().month()) + 1}`
581
- let totalKey = `${key}${type}:total`
582
-
583
- redis.incr(dayKey)
584
- redis.incr(monthKey)
585
- if (!groupId) redis.incr(totalKey)
586
- redis.expire(dayKey, 3600 * 24 * 30)
587
- redis.expire(monthKey, 3600 * 24 * 30)
588
- }
589
-
590
- delCount() {
591
- let key = "Yz:count:"
592
- redis.set(`${key}sendMsg:total`, "0")
593
- redis.set(`${key}screenshot:total`, "0")
594
  }
595
 
596
  /** 收集定时任务 */
597
  collectTask(task) {
598
- if (Array.isArray(task)) {
599
- task.forEach((val) => {
600
- if (!val.cron) return
601
- if (!val.name) throw new Error("插件任务名称错误")
602
- this.task.push(val)
603
- })
604
- } else {
605
- if (task.fnc && task.cron) {
606
- if (!task.name) throw new Error("插件任务名称错误")
607
- this.task.push(task)
608
- }
609
- }
610
  }
611
 
612
  /** 创建定时任务 */
613
- creatTask() {
614
- if (process.argv[1].includes("test")) return
615
- this.task.forEach((val) => {
616
- val.job = schedule.scheduleJob(val.cron, async () => {
617
  try {
618
- if (val.log === true) {
619
- logger.mark(`开始定时任务:${val.name}`)
620
- }
621
- let res = val.fnc()
622
- if (util.types.isPromise(res)) res = await res
623
- if (val.log === true) {
624
- logger.mark(`定时任务完成:${val.name}`)
625
- }
626
  } catch (error) {
627
- logger.error(`定时任务报错:${val.name}`)
628
  logger.error(error)
629
  }
630
  })
631
- })
632
  }
633
 
634
  /** 检查命令冷却cd */
635
  checkLimit(e) {
636
  /** 禁言中 */
637
- if (e.isGroup && e?.group?.mute_left > 0) return false
638
  if (!e.message || e.isPrivate) return true
639
 
640
  const config = cfg.getGroup(e.self_id, e.group_id)
@@ -661,16 +576,12 @@ class PluginsLoader {
661
 
662
  if (config.groupCD) {
663
  this.groupCD[e.group_id] = true
664
- setTimeout(() => {
665
- delete this.groupCD[e.group_id]
666
- }, config.groupCD)
667
  }
668
  if (config.singleCD) {
669
- let key = `${e.group_id}.${e.user_id}`
670
  this.singleCD[key] = true
671
- setTimeout(() => {
672
- delete this.singleCD[key]
673
- }, config.singleCD)
674
  }
675
  }
676
 
@@ -680,7 +591,11 @@ class PluginsLoader {
680
 
681
  let groupCfg = cfg.getGroup(e.self_id, e.group_id)
682
 
683
- if (groupCfg.onlyReplyAt != 1 || !groupCfg.botAlias) return true
 
 
 
 
684
 
685
  /** at机器人 */
686
  if (e.atBot) return true
@@ -693,50 +608,54 @@ class PluginsLoader {
693
 
694
  /** 判断黑白名单 */
695
  checkBlack(e) {
696
- let other = cfg.getOther()
697
-
698
- if (e.test) return true
699
 
700
- /** 黑名单qq */
701
- if (other.blackQQ?.length && other.blackQQ.includes(Number(e.user_id) || String(e.user_id))) {
 
 
 
702
  return false
703
- }
704
 
705
  if (e.group_id) {
706
- /** 白名单群 */
707
- if (other.whiteGroup?.length) {
708
- if (other.whiteGroup.includes(Number(e.group_id) || String(e.group_id))) return true
709
- return false
710
- }
711
  /** 黑名单群 */
712
- if (other.blackGroup?.length && other.blackGroup.includes(Number(e.group_id) || String(e.group_id))) {
 
 
 
713
  return false
714
- }
715
  }
716
 
717
  return true
718
  }
719
 
720
  /** 判断是否启用功能 */
721
- checkDisable(e, p) {
722
- let groupCfg = cfg.getGroup(e.self_id, e.group_id)
723
- if (!lodash.isEmpty(groupCfg.enable)) {
724
- if (groupCfg.enable.includes(p.name)) {
725
- return true
726
- }
727
- // logger.debug(`${e.logText}[${p.name}]功能已禁用`)
728
  return false
729
- }
730
-
731
- if (!lodash.isEmpty(groupCfg.disable)) {
732
- if (groupCfg.disable.includes(p.name)) {
733
- // logger.debug(`${e.logText}[${p.name}]功能已禁用`)
734
- return false
735
- }
736
 
737
- return true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
738
  }
739
- return true
740
  }
741
 
742
  /** 监听热更新 */
@@ -744,129 +663,50 @@ class PluginsLoader {
744
  this.watchDir(dirName)
745
  if (this.watcher[`${dirName}.${appName}`]) return
746
 
747
- let file = `./plugins/${dirName}/${appName}`
748
  const watcher = chokidar.watch(file)
749
- let key = `${dirName}/${appName}`
750
 
751
  /** 监听修改 */
752
- watcher.on("change", async path => {
753
  logger.mark(`[修改插件][${dirName}][${appName}]`)
754
-
755
- let tmp = {}
756
- try {
757
- tmp = await import(`../../plugins/${dirName}/${appName}?${moment().format("x")}`)
758
- } catch (error) {
759
- logger.error(`载入插件错误:${logger.red(dirName + "/" + appName)}`)
760
- logger.error(decodeURI(error.stack))
761
- return
762
- }
763
-
764
- if (tmp.apps) tmp = { ...tmp.apps }
765
- lodash.forEach(tmp, (p) => {
766
- /* eslint-disable new-cap */
767
- let plugin = new p()
768
- for (let i in this.priority) {
769
- if (this.priority[i].key == key) {
770
- this.priority[i].class = p
771
- this.priority[i].priority = plugin.priority
772
- }
773
- }
774
-
775
- if (plugin.handler) {
776
- lodash.forEach(plugin.handler, ({ fn, key, priority }) => {
777
- Handler.add({
778
- ns: plugin.namespace || File.name,
779
- key: key,
780
- self: plugin,
781
- property: priority || plugin.priority || 500,
782
- fn: plugin[fn]
783
- })
784
- })
785
- }
786
- })
787
-
788
- this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
789
  })
790
 
791
  /** 监听删除 */
792
  watcher.on("unlink", async path => {
793
  logger.mark(`[卸载插件][${dirName}][${appName}]`)
794
- for (let i in this.priority) {
795
- if (this.priority[i].key == key) {
 
 
796
  this.priority.splice(i, 1)
797
- /** 停止更新监听 */
798
- this.watcher[`${dirName}.${appName}`].removeAllListeners("change")
799
- break
800
- }
801
- }
802
  })
803
-
804
  this.watcher[`${dirName}.${appName}`] = watcher
805
  }
806
 
807
  /** 监听文件夹更新 */
808
  watchDir(dirName) {
809
  if (this.watcher[dirName]) return
810
-
811
- let file = `./plugins/${dirName}/`
812
- const watcher = chokidar.watch(file)
813
-
814
  /** 热更新 */
815
- setTimeout(() => {
816
  /** 新增文件 */
817
  watcher.on("add", async PluPath => {
818
- let appName = path.basename(PluPath)
819
  if (!appName.endsWith(".js")) return
820
- if (!fs.existsSync(`${this.dir}/${dirName}/${appName}`)) return
821
-
822
- let key = `${dirName}/${appName}`
823
-
824
- this.watch(dirName, appName)
825
-
826
- /** 太快了延迟下 */
827
- await common.sleep(500)
828
-
829
  logger.mark(`[新增插件][${dirName}][${appName}]`)
830
- let tmp = {}
831
- try {
832
- tmp = await import(`../../plugins/${dirName}/${appName}?${moment().format("X")}`)
833
- } catch (error) {
834
- logger.error(`载入插件错误:${logger.red(dirName + "/" + appName)}`)
835
- logger.error(decodeURI(error.stack))
836
- return
837
- }
838
-
839
- if (tmp.apps) tmp = { ...tmp.apps }
840
-
841
- lodash.forEach(tmp, (p) => {
842
- if (!p.prototype) {
843
- logger.error(`[载入失败][${dirName}][${appName}] 格式错误已跳过`)
844
- return
845
- }
846
- /* eslint-disable new-cap */
847
- let plugin = new p()
848
-
849
- for (let i in this.priority) {
850
- if (this.priority[i].key == key) {
851
- return
852
- }
853
- }
854
-
855
- this.priority.push({
856
- class: p,
857
- key,
858
- name: plugin.name,
859
- priority: plugin.priority
860
- })
861
  })
862
-
863
  /** 优先级排序 */
864
  this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
 
865
  })
866
- }, 500)
867
-
868
  this.watcher[dirName] = watcher
869
  }
870
  }
871
-
872
  export default new PluginsLoader()
 
1
  import util from "node:util"
2
+ import fs from "node:fs/promises"
3
  import lodash from "lodash"
4
  import cfg from "../config/config.js"
5
  import plugin from "./plugin.js"
 
8
  import chokidar from "chokidar"
9
  import moment from "moment"
10
  import path from "node:path"
 
11
  import Runtime from "./runtime.js"
12
+ import Handler from "./handler.js"
13
 
14
  /** 全局变量 plugin */
15
  global.plugin = plugin
 
23
  this.priority = []
24
  this.handler = {}
25
  this.task = []
26
+ this.dir = "plugins"
27
 
28
  /** 命令冷却cd */
29
  this.groupCD = {}
 
31
 
32
  /** 插件监听 */
33
  this.watcher = {}
34
+ this.eventMap = {
35
+ message: ["post_type", "message_type", "sub_type"],
36
+ notice: ["post_type", "notice_type", "sub_type"],
37
+ request: ["post_type", "request_type", "sub_type"],
38
+ }
39
 
40
  this.msgThrottle = {}
41
 
 
43
  this.srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/
44
  }
45
 
46
+ async getPlugins() {
47
+ const files = await fs.readdir(this.dir, { withFileTypes: true })
48
+ const ret = []
49
+ for (const val of files) {
50
+ if (val.isFile()) continue
51
+ const tmp = {
52
+ name: val.name,
53
+ path: `../../${this.dir}/${val.name}`,
54
+ }
55
+
56
+ if (await Bot.fsStat(`${this.dir}/${val.name}/index.js`)) {
57
+ tmp.path = `${tmp.path}/index.js`
58
+ ret.push(tmp)
59
+ continue
60
+ }
61
+
62
+ const apps = await fs.readdir(`${this.dir}/${val.name}`, { withFileTypes: true })
63
+ for (const app of apps) {
64
+ if (!app.isFile()) continue
65
+ if (!app.name.endsWith(".js")) continue
66
+ ret.push({
67
+ name: `${tmp.name}/${app.name}`,
68
+ path: `${tmp.path}/${app.name}`,
69
+ })
70
+ /** 监听热更新 */
71
+ this.watch(val.name, app.name)
72
+ }
73
+ }
74
+ return ret
75
+ }
76
+
77
  /**
78
  * 监听事件加载
79
  * @param isRefresh 是否刷新
80
  */
81
  async load(isRefresh = false) {
82
+ if (isRefresh) this.priority = []
83
+ if (this.priority.length) return
 
 
84
 
85
  logger.info("-----------")
86
  logger.info("加载插件中...")
87
 
88
+ const files = await this.getPlugins()
89
+ this.pluginCount = 0
90
+ const packageErr = []
91
 
92
+ await Promise.allSettled(files.map(file =>
93
+ this.importPlugin(file, packageErr)
94
+ ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  this.packageTips(packageErr)
97
+ this.createTask()
98
 
99
  logger.info(`加载定时任务[${this.task.length}个]`)
100
+ logger.info(`加载插件[${this.pluginCount}个]`)
101
 
102
  /** 优先级排序 */
103
  this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
104
  }
105
 
106
+ async importPlugin(file, packageErr) {
107
+ try {
108
+ let app = await import(file.path)
109
+ if (app.apps) app = { ...app.apps }
110
+ const pluginArray = []
111
+ lodash.forEach(app, p =>
112
+ pluginArray.push(this.loadPlugin(file, p))
113
+ )
114
+ for (const i of await Promise.allSettled(pluginArray))
115
+ if (i?.status && i.status != "fulfilled") {
116
+ logger.error(`加载插件错误:${logger.red(file.name)}`)
117
+ logger.error(decodeURI(i.reason))
118
+ }
119
+ } catch (error) {
120
+ if (packageErr && error.stack.includes("Cannot find package")) {
121
+ packageErr.push({ error, file })
122
+ } else {
123
+ logger.error(`加载插件错误:${logger.red(file.name)}`)
124
+ logger.error(decodeURI(error.stack))
125
+ }
126
+ }
127
  }
128
 
129
+ async loadPlugin(file, p) {
130
+ if (!p?.prototype) return
131
+ this.pluginCount++
132
+ const plugin = new p
133
+ logger.debug(`加载插件 [${file.name}][${plugin.name}]`)
134
+ /** 执行初始化,返回 return 则跳过加载 */
135
+ if (plugin.init && await plugin.init() == "return") return
136
+ /** 初始化定时任务 */
137
+ this.collectTask(plugin.task)
138
+ this.priority.push({
139
+ class: p,
140
+ key: file.name,
141
+ name: plugin.name,
142
+ priority: plugin.priority
143
  })
144
+ if (plugin.handler) {
145
+ lodash.forEach(plugin.handler, ({ fn, key, priority }) => {
146
+ Handler.add({
147
+ ns: plugin.namespace || file.name,
148
+ key,
149
+ self: plugin,
150
+ property: priority || plugin.priority || 500,
151
+ fn: plugin[fn]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  })
153
+ })
 
 
 
154
  }
155
+ }
156
 
157
+ packageTips(packageErr) {
158
+ if (!packageErr.length) return
159
+ logger.mark("--------- 插件加载错误 ---------")
160
+ for (const i of packageErr) {
161
+ const pack = i.error.stack.match(/'(.+?)'/g)[0].replace(/'/g, "")
162
+ logger.mark(`${logger.blue(i.file.name)} 缺少依赖 ${logger.red(pack)}`)
163
+ }
164
+ logger.mark(`安装插件后请 ${logger.red("pnpm i")} 安装依赖`)
165
+ logger.mark(`仍报错${logger.red("进入插件目录")} pnpm add 依赖`)
166
+ logger.mark("--------------------------------")
167
  }
168
 
169
  /**
 
173
  * @param e 事件
174
  */
175
  async deal(e) {
176
+ this.count(e, "receive", e.message)
177
  /** 检查黑白名单 */
178
  if (!this.checkBlack(e)) return
179
  /** 冷却 */
180
  if (!this.checkLimit(e)) return
181
  /** 处理事件 */
182
  this.dealEvent(e)
 
 
183
  /** 处理回复 */
184
  this.reply(e)
 
 
185
  /** 注册runtime */
186
  await Runtime.init(e)
187
 
188
+ const priority = []
189
+ for (const i of this.priority) {
190
+ const p = new i.class(e)
191
  p.e = e
192
+ /** 判断是否启用功能,过滤事件 */
193
+ if (this.checkDisable(p) && this.filtEvent(e, p))
194
+ priority.push(p)
195
+ }
 
 
196
 
197
+ for (const plugin of priority) {
198
  /** 上下文hook */
199
+ if (!plugin.getContext) continue
200
+ const context = {
201
+ ...plugin.getContext(),
202
+ ...plugin.getContext(false, true),
 
 
 
 
203
  }
204
+ if (!lodash.isEmpty(context)) {
205
+ for (const fnc in context)
206
+ plugin[fnc](context[fnc])
207
+ return
 
 
 
 
 
 
208
  }
209
  }
210
 
 
226
  e.msg = e.msg.replace(this.srReg, "#星铁")
227
  }
228
 
229
+ /** 优先执行 accept */
230
+ for (const plugin of priority)
 
231
  if (plugin.accept) {
232
+ const res = await plugin.accept(e)
233
+ if (res == "return") return
 
 
 
 
234
  if (res) break
235
  }
 
236
 
237
+ a: for (const plugin of priority) {
 
238
  /** 正则匹配 */
239
+ if (plugin.rule) for (const v of plugin.rule) {
240
+ /** 判断事件 */
241
+ if (v.event && !this.filtEvent(e, v)) continue
242
+
243
+ if (!new RegExp(v.reg).test(e.msg)) continue
244
+ e.logFnc = `[${plugin.name}][${v.fnc}]`
245
+
246
+ if (v.log !== false)
247
+ logger.info(`${e.logFnc}${e.logText} ${lodash.truncate(e.msg, { length: 100 })}`)
248
+
249
+ /** 判断权限 */
250
+ if (!this.filtPermission(e, v)) break a
251
+
252
+ try {
253
+ const start = Date.now()
254
+ const res = plugin[v.fnc] && (await plugin[v.fnc](e))
255
+ if (res !== false) {
256
+ /** 设置冷却cd */
257
+ this.setLimit(e)
258
+ if (v.log !== false)
259
+ logger.mark(`${e.logFnc} ${lodash.truncate(e.msg, { length: 100 })} 处理完成 ${Date.now() - start}ms`)
260
+ break a
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  }
262
+ } catch (error) {
263
+ logger.error(`${e.logFnc}`)
264
+ logger.error(error.stack)
265
+ break a
266
  }
267
  }
268
  }
 
271
  /** 过滤事件 */
272
  filtEvent(e, v) {
273
  if (!v.event) return false
274
+ const event = v.event.split(".")
275
+ const eventMap = this.eventMap[e.post_type] || []
276
+ const newEvent = []
277
+ for (const i in event) {
278
+ if (event[i] == "*")
279
+ newEvent.push(event[i])
280
+ else
281
+ newEvent.push(e[eventMap[i]])
282
  }
283
+ return v.event == newEvent.join(".")
 
 
 
 
 
 
 
 
 
 
284
  }
285
 
286
  /** 判断权限 */
 
297
  }
298
 
299
  if (e.isGroup) {
 
 
 
 
300
  if (v.permission == "owner") {
301
  if (!e.member.is_owner) {
302
  e.reply("暂无权限,只有群主才能操作")
 
314
  return true
315
  }
316
 
317
+ dealText(text = "") {
318
+ if (cfg.bot["/→#"])
319
+ text = text.replace(/^\s*\/\s*/, "#")
320
+ return text
321
+ .replace(/^\s*[#井]\s*/, "#")
322
+ .replace(/^\s*[*※]\s*/, "*")
323
+ .trim()
 
 
 
324
  }
325
 
326
  /**
327
+ * 处理事件,加入自定义字段
328
  * @param e.msg 文本消息,多行会自动拼接
329
  * @param e.img 图片消息数组
330
  * @param e.atBot 是否at机器人
 
336
  * @param e.logText 日志用户字符串
337
  * @param e.logFnc 日志方法字符串
338
  */
339
+ dealEvent(e) {
340
  if (e.message) for (const i of e.message) {
341
  switch (i.type) {
342
  case "text":
343
+ e.msg = (e.msg || "") + this.dealText(i.text)
 
344
  break
345
  case "image":
346
  if (Array.isArray(e.img))
 
364
  case "file":
365
  e.file = i
366
  break
367
+ case "xml":
368
+ case "json":
369
+ e.msg = (e.msg || "") + (typeof i.data == "string" ? i.data : JSON.stringify(i.data))
370
+ break
371
  }
372
  }
373
 
 
380
  e.sender.card = e.sender.nickname
381
  } else {
382
  e.sender = {
383
+ user_id: e.user_id,
384
+ nickname: e.friend?.nickname,
385
  card: e.friend?.nickname,
 
386
  }
387
  }
388
 
389
  e.logText = `[${e.sender?.nickname ? `${e.sender.nickname}(${e.user_id})` : e.user_id}]`
390
+ } else if (e.message_type == "group" || e.notice_type == "group") {
 
 
391
  e.isGroup = true
392
+
393
  if (e.sender) {
394
+ if (!e.sender.card)
395
+ e.sender.card = e.sender.nickname
 
 
 
 
 
 
 
 
396
  } else {
397
  e.sender = {
398
+ user_id: e.user_id,
399
+ nickname: e.member?.nickname || e.friend?.nickname,
400
+ card: e.member?.card || e.member?.nickname || e.friend?.nickname,
401
  }
402
  }
403
 
404
+ if (!e.group_name && e.group?.name)
405
+ e.group_name = e.group.name
406
 
407
+ e.logText = `[${e.group_name ? `${e.group_name}(${e.group_id})` : e.group_id}, ${e.sender?.card ? `${e.sender.card}(${e.user_id})` : e.user_id}]`
408
  }
409
 
410
  if (e.user_id && cfg.master[e.self_id]?.includes(String(e.user_id))) {
 
430
 
431
  /** 处理回复,捕获发送失败异常 */
432
  reply(e) {
433
+ const reply = e.reply ? e.reply.bind(e) : msg => {
434
+ if (e.isGroup) {
435
+ if (e.group?.sendMsg)
436
+ return e.group.sendMsg(msg)
437
+ else
438
+ return e.bot.pickGroup(e.group_id).sendMsg(msg)
439
+ } else {
440
+ if (e.friend?.sendMsg)
441
+ return e.friend.sendMsg(msg)
442
+ else
443
+ return e.bot.pickFriend(e.user_id).sendMsg(msg)
 
 
 
 
 
 
444
  }
445
+ }
446
 
447
  /**
448
  * @param msg 发送的消息
 
455
 
456
  let { recallMsg = 0, at = "" } = data
457
 
458
+ if (at && e.isGroup) {
459
  if (at === true)
460
  at = e.user_id
461
  if (Array.isArray(msg))
462
+ msg.unshift(segment.at(at), "\n")
463
  else
464
+ msg = [segment.at(at), "\n", msg]
465
  }
466
 
467
  if (quote && e.message_id) {
 
473
 
474
  let res
475
  try {
476
+ res = await reply(msg)
477
  } catch (err) {
478
+ Bot.makeLog("error", ["发送消息错误", msg, err], e.self_id)
 
479
  }
480
 
481
  if (recallMsg > 0 && res?.message_id) {
 
493
  }, recallMsg * 1000)
494
  }
495
 
496
+ this.count(e, "send", msg)
497
  return res
498
  }
499
  }
500
 
501
+ async count(e, type, msg) {
502
+ if (cfg.bot.msg_type_count)
503
+ for (const i of Array.isArray(msg) ? msg : [msg])
504
+ await this.saveCount(e, `${type}:${i?.type || "text"}`)
505
+ await this.saveCount(e, `${type}:msg`)
 
 
 
 
 
 
 
 
 
506
  }
507
 
508
+ async saveCount(e, type) {
509
+ const key = []
510
+
511
+ const day = moment().format("YYYY:MM:DD")
512
+ const month = moment().format("YYYY:MM")
513
+ const year = moment().format("YYYY")
514
+ for (const i of [day, month, year, "total"]) {
515
+ key.push(`total:${i}`)
516
+ if (e.self_id) key.push(`bot:${e.self_id}:${i}`)
517
+ if (e.user_id) key.push(`user:${e.user_id}:${i}`)
518
+ if (e.group_id) key.push(`group:${e.group_id}:${i}`)
519
  }
520
 
521
+ for (const i of key)
522
+ await redis.incr(`Yz:count:${type}:${i}`)
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  }
524
 
525
  /** 收集定时任务 */
526
  collectTask(task) {
527
+ for (const i of Array.isArray(task) ? task : [task])
528
+ if (i.cron && i.name)
529
+ this.task.push(i)
 
 
 
 
 
 
 
 
 
530
  }
531
 
532
  /** 创建定时任务 */
533
+ createTask() {
534
+ for (const i of this.task)
535
+ i.job = schedule.scheduleJob(i.cron, async () => {
 
536
  try {
537
+ if (i.log == true)
538
+ logger.mark(`开始定时任务:${i.name}`)
539
+ await i.fnc()
540
+ if (i.log == true)
541
+ logger.mark(`定时任务完成:${i.name}`)
 
 
 
542
  } catch (error) {
543
+ logger.error(`定时任务报错:${i.name}`)
544
  logger.error(error)
545
  }
546
  })
 
547
  }
548
 
549
  /** 检查命令冷却cd */
550
  checkLimit(e) {
551
  /** 禁言中 */
552
+ if (e.isGroup && e.group?.mute_left > 0) return false
553
  if (!e.message || e.isPrivate) return true
554
 
555
  const config = cfg.getGroup(e.self_id, e.group_id)
 
576
 
577
  if (config.groupCD) {
578
  this.groupCD[e.group_id] = true
579
+ setTimeout(() => delete this.groupCD[e.group_id], config.groupCD)
 
 
580
  }
581
  if (config.singleCD) {
582
+ const key = `${e.group_id}.${e.user_id}`
583
  this.singleCD[key] = true
584
+ setTimeout(() => delete this.singleCD[key], config.singleCD)
 
 
585
  }
586
  }
587
 
 
591
 
592
  let groupCfg = cfg.getGroup(e.self_id, e.group_id)
593
 
594
+ /** 模式0,未开启前缀 */
595
+ if (groupCfg.onlyReplyAt == 0 || !groupCfg.botAlias) return true
596
+
597
+ /** 模式2,非主人开启 */
598
+ if (groupCfg.onlyReplyAt == 2 && e.isMaster) return true
599
 
600
  /** at机器人 */
601
  if (e.atBot) return true
 
608
 
609
  /** 判断黑白名单 */
610
  checkBlack(e) {
611
+ const other = cfg.getOther()
 
 
612
 
613
+ /** 黑名单用户 */
614
+ if (other.blackUser?.length && other.blackUser.includes(Number(e.user_id) || String(e.user_id)))
615
+ return false
616
+ /** 白名单用户 */
617
+ if (other.whiteUser?.length && !other.whiteUser.includes(Number(e.user_id) || String(e.user_id)))
618
  return false
 
619
 
620
  if (e.group_id) {
 
 
 
 
 
621
  /** 黑名单群 */
622
+ if (other.blackGroup?.length && other.blackGroup.includes(Number(e.group_id) || String(e.group_id)))
623
+ return false
624
+ /** 白名单群 */
625
+ if (other.whiteGroup?.length && !other.whiteGroup.includes(Number(e.group_id) || String(e.group_id)))
626
  return false
 
627
  }
628
 
629
  return true
630
  }
631
 
632
  /** 判断是否启用功能 */
633
+ checkDisable(p) {
634
+ const groupCfg = cfg.getGroup(p.e.self_id, p.e.group_id)
635
+ if (groupCfg.disable?.length && groupCfg.disable.includes(p.name))
 
 
 
 
636
  return false
637
+ if (groupCfg.enable?.length && !groupCfg.enable.includes(p.name))
638
+ return false
639
+ return true
640
+ }
 
 
 
641
 
642
+ async changePlugin(key) {
643
+ try {
644
+ let app = await import(`../../${this.dir}/${key}?${moment().format("x")}`)
645
+ if (app.apps) app = { ...app.apps }
646
+ lodash.forEach(app, p => {
647
+ const plugin = new p
648
+ for (const i in this.priority)
649
+ if (this.priority[i].key == key && this.priority[i].name == plugin.name) {
650
+ this.priority[i].class = p
651
+ this.priority[i].priority = plugin.priority
652
+ }
653
+ })
654
+ this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
655
+ } catch (error) {
656
+ logger.error(`加载插件错误:${logger.red(key)}`)
657
+ logger.error(decodeURI(error.stack))
658
  }
 
659
  }
660
 
661
  /** 监听热更新 */
 
663
  this.watchDir(dirName)
664
  if (this.watcher[`${dirName}.${appName}`]) return
665
 
666
+ const file = `./${this.dir}/${dirName}/${appName}`
667
  const watcher = chokidar.watch(file)
668
+ const key = `${dirName}/${appName}`
669
 
670
  /** 监听修改 */
671
+ watcher.on("change", path => {
672
  logger.mark(`[修改插件][${dirName}][${appName}]`)
673
+ this.changePlugin(key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  })
675
 
676
  /** 监听删除 */
677
  watcher.on("unlink", async path => {
678
  logger.mark(`[卸载插件][${dirName}][${appName}]`)
679
+ /** 停止更新监听 */
680
+ this.watcher[`${dirName}.${appName}`].removeAllListeners("change")
681
+ for (const i in this.priority)
682
+ if (this.priority[i].key == key)
683
  this.priority.splice(i, 1)
 
 
 
 
 
684
  })
 
685
  this.watcher[`${dirName}.${appName}`] = watcher
686
  }
687
 
688
  /** 监听文件夹更新 */
689
  watchDir(dirName) {
690
  if (this.watcher[dirName]) return
691
+ const watcher = chokidar.watch(`./${this.dir}/${dirName}/`)
 
 
 
692
  /** 热更新 */
693
+ Bot.once("online", () => {
694
  /** 新增文件 */
695
  watcher.on("add", async PluPath => {
696
+ const appName = path.basename(PluPath)
697
  if (!appName.endsWith(".js")) return
 
 
 
 
 
 
 
 
 
698
  logger.mark(`[新增插件][${dirName}][${appName}]`)
699
+ const key = `${dirName}/${appName}`
700
+ await this.importPlugin({
701
+ name: key,
702
+ path: `../../${this.dir}/${key}?${moment().format("X")}`,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
703
  })
 
704
  /** 优先级排序 */
705
  this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
706
+ this.watch(dirName, appName)
707
  })
708
+ })
 
709
  this.watcher[dirName] = watcher
710
  }
711
  }
 
712
  export default new PluginsLoader()
Yunzai/lib/plugins/plugin.js CHANGED
@@ -1,6 +1,9 @@
1
- import { Common } from '#miao'
 
 
 
2
 
3
- let stateArr = {}
4
 
5
  export default class plugin {
6
  /**
@@ -24,16 +27,16 @@ export default class plugin {
24
  * @param task.fnc 定时任务方法名
25
  * @param task.log false时不显示执行日志
26
  */
27
- constructor ({
28
- name = 'your-plugin',
29
- dsc = '',
30
- handler,
31
- namespace,
32
- event = 'message',
33
- priority = 5000,
34
- task = { fnc: '', cron: '' },
35
- rule = []
36
- }) {
37
  /** 插件名称 */
38
  this.name = name
39
  /** 插件描述 */
@@ -45,18 +48,18 @@ export default class plugin {
45
  /** 定时任务,可以是数组 */
46
  this.task = {
47
  /** 任务名 */
48
- name: '',
49
  /** 任务方法名 */
50
- fnc: task.fnc || '',
51
  /** 任务cron表达式 */
52
- cron: task.cron || ''
53
  }
54
  /** 命令规则 */
55
  this.rule = rule
56
 
57
  if (handler) {
58
  this.handler = handler
59
- this.namespace = namespace || ''
60
  }
61
  }
62
 
@@ -66,12 +69,12 @@ export default class plugin {
66
  * @param data.recallMsg 群聊是否撤回消息,0-120秒,0不撤回
67
  * @param data.at 是否at用户
68
  */
69
- reply (msg = '', quote = false, data = {}) {
70
- if (!this.e.reply || !msg) return false
71
  return this.e.reply(msg, quote, data)
72
  }
73
 
74
- conKey (isGroup = false) {
75
  if (isGroup) {
76
  return `${this.name}.${this.e.group_id}`
77
  } else {
@@ -84,45 +87,36 @@ export default class plugin {
84
  * @param isGroup 是否群聊
85
  * @param time 操作时间,默认120秒
86
  */
87
- setContext (type, isGroup = false, time = 120) {
88
- let key = this.conKey(isGroup)
89
  if (!stateArr[key]) stateArr[key] = {}
90
  stateArr[key][type] = this.e
91
- if (time) {
92
- /** 操作时间 */
93
- setTimeout(() => {
94
- if (stateArr[key][type]) {
95
- delete stateArr[key][type]
96
- this.e.reply('操作超时已取消', true)
97
- }
98
- }, time * 1000)
99
- }
100
- }
101
-
102
- getContext () {
103
- let key = this.conKey()
104
- return stateArr[key]
105
  }
106
 
107
- getContextGroup () {
108
- let key = this.conKey(true)
109
- return stateArr[key]
110
  }
111
 
112
  /**
113
  * @param type 执行方法
114
  * @param isGroup 是否群聊
115
  */
116
- finish (type, isGroup = false) {
117
- if (stateArr[this.conKey(isGroup)] && stateArr[this.conKey(isGroup)][type]) {
118
- delete stateArr[this.conKey(isGroup)][type]
 
 
119
  }
120
  }
121
 
122
- async renderImg (plugin, tpl, data, cfg) {
123
- return Common.render(plugin, tpl, data, {
124
- ...cfg,
125
- e: this.e
126
- })
127
  }
128
- }
 
1
+ let Common
2
+ try {
3
+ Common = (await import("#miao")).Common
4
+ } catch (err) {}
5
 
6
+ const stateArr = {}
7
 
8
  export default class plugin {
9
  /**
 
27
  * @param task.fnc 定时任务方法名
28
  * @param task.log false时不显示执行日志
29
  */
30
+ constructor({
31
+ name = "your-plugin",
32
+ dsc = "",
33
+ handler,
34
+ namespace,
35
+ event = "message",
36
+ priority = 5000,
37
+ task = { fnc: "", cron: "" },
38
+ rule = []
39
+ }) {
40
  /** 插件名称 */
41
  this.name = name
42
  /** 插件描述 */
 
48
  /** 定时任务,可以是数组 */
49
  this.task = {
50
  /** 任务名 */
51
+ name: "",
52
  /** 任务方法名 */
53
+ fnc: task.fnc || "",
54
  /** 任务cron表达式 */
55
+ cron: task.cron || ""
56
  }
57
  /** 命令规则 */
58
  this.rule = rule
59
 
60
  if (handler) {
61
  this.handler = handler
62
+ this.namespace = namespace || ""
63
  }
64
  }
65
 
 
69
  * @param data.recallMsg 群聊是否撤回消息,0-120秒,0不撤回
70
  * @param data.at 是否at用户
71
  */
72
+ reply(msg = "", quote = false, data = {}) {
73
+ if (!this.e?.reply || !msg) return false
74
  return this.e.reply(msg, quote, data)
75
  }
76
 
77
+ conKey(isGroup = false) {
78
  if (isGroup) {
79
  return `${this.name}.${this.e.group_id}`
80
  } else {
 
87
  * @param isGroup 是否群聊
88
  * @param time 操作时间,默认120秒
89
  */
90
+ setContext(type, isGroup, time = 120) {
91
+ const key = this.conKey(isGroup)
92
  if (!stateArr[key]) stateArr[key] = {}
93
  stateArr[key][type] = this.e
94
+ if (time) stateArr[key][type].timeout = setTimeout(() => {
95
+ if (stateArr[key][type]) {
96
+ delete stateArr[key][type]
97
+ this.reply("操作超时已取消", true)
98
+ }
99
+ }, time * 1000)
 
 
 
 
 
 
 
 
100
  }
101
 
102
+ getContext(type, isGroup) {
103
+ if (type) return stateArr[this.conKey(isGroup)]?.[type]
104
+ return stateArr[this.conKey(isGroup)]
105
  }
106
 
107
  /**
108
  * @param type 执行方法
109
  * @param isGroup 是否群聊
110
  */
111
+ finish(type, isGroup) {
112
+ const key = this.conKey(isGroup)
113
+ if (stateArr[key] && stateArr[key][type]) {
114
+ clearTimeout(stateArr[key][type].timeout)
115
+ delete stateArr[key][type]
116
  }
117
  }
118
 
119
+ async renderImg(plugin, tpl, data, cfg) {
120
+ return Common.render(plugin, tpl, data, { ...cfg, e: this.e })
 
 
 
121
  }
122
+ }
Yunzai/lib/plugins/runtime.js CHANGED
@@ -4,25 +4,29 @@
4
  * 提供一些常用的运行时变量、方法及model获取
5
  * 降低对目录结构的依赖
6
  */
7
- import lodash from 'lodash'
8
- import fs from 'node:fs'
9
- import gsCfg from '../../plugins/genshin/model/gsCfg.js'
10
- import common from '../common/common.js'
11
- import cfg from '../config/config.js'
12
- import MysApi from '../../plugins/genshin/model/mys/mysApi.js'
13
- import MysInfo from '../../plugins/genshin/model/mys/mysInfo.js'
14
- import puppeteer from '../puppeteer/puppeteer.js'
15
- import { Version } from '#miao'
16
- import NoteUser from '../../plugins/genshin/model/mys/NoteUser.js'
17
- import MysUser from '../../plugins/genshin/model/mys/MysUser.js'
18
- import Handler from './handler.js'
 
 
 
 
19
 
20
  /**
21
  * 常用的处理方法
22
  */
23
 
24
  export default class Runtime {
25
- constructor (e) {
26
  this.e = e
27
  this._mysInfo = {}
28
 
@@ -33,55 +37,54 @@ export default class Runtime {
33
  }
34
  }
35
 
36
- get uid () {
37
  return this.user?.uid
38
  }
39
 
40
- get hasCk () {
41
  return this.user?.hasCk
42
  }
43
 
44
- get user () {
45
  return this.e.user
46
  }
47
 
48
- get cfg () {
49
  return cfg
50
  }
51
 
52
- get gsCfg () {
53
  return gsCfg
54
  }
55
 
56
- get common () {
57
  return common
58
  }
59
 
60
- get puppeteer () {
61
  return puppeteer
62
  }
63
 
64
- get MysInfo () {
65
  return MysInfo
66
  }
67
 
68
- get NoteUser () {
69
  return NoteUser
70
  }
71
 
72
- get MysUser () {
73
  return MysUser
74
  }
75
 
76
- static async init (e) {
77
- await MysInfo.initCache()
78
- let runtime = new Runtime(e)
79
- e.runtime = runtime
80
- await runtime.initUser()
81
- return runtime
82
  }
83
 
84
- async initUser () {
85
  let e = this.e
86
  let user = await NoteUser.create(e)
87
  if (user) {
@@ -89,24 +92,24 @@ export default class Runtime {
89
  get (self, key, receiver) {
90
  let game = e.game
91
  let fnMap = {
92
- uid: 'getUid',
93
- uidList: 'getUidList',
94
- mysUser: 'getMysUser',
95
- ckUidList: 'getCkUidList'
96
  }
97
  if (fnMap[key]) {
98
  return self[fnMap[key]](game)
99
  }
100
- if (key === 'uidData') {
101
- return self.getUidData('', game)
102
  }
103
- if (['getUid', 'getUidList', 'getMysUser', 'getCkUidList', 'getUidMapList', 'getGameDs'].includes(key)) {
104
  return (_game, arg2) => {
105
  return self[key](_game || game, arg2)
106
  }
107
  }
108
- if (['getUidData', 'hasUid', 'addRegUid', 'delRegUid', 'setMainUid'].includes(key)) {
109
- return (uid, _game = '') => {
110
  return self[key](uid, _game || game)
111
  }
112
  }
@@ -122,14 +125,14 @@ export default class Runtime {
122
  * @param targetType all: 所有用户均可, cookie:查询用户必须具备Cookie
123
  * @returns {Promise<boolean|MysInfo>}
124
  */
125
- async getMysInfo (targetType = 'all') {
126
  if (!this._mysInfo[targetType]) {
127
- this._mysInfo[targetType] = await MysInfo.init(this.e, targetType === 'cookie' ? 'detail' : 'roleIndex')
128
  }
129
  return this._mysInfo[targetType]
130
  }
131
 
132
- async getUid () {
133
  return await MysInfo.getUid(this.e)
134
  }
135
 
@@ -140,7 +143,7 @@ export default class Runtime {
140
  * @param option MysApi option
141
  * @returns {Promise<boolean|MysApi>}
142
  */
143
- async getMysApi (targetType = 'all', option = {}) {
144
  let mys = await this.getMysInfo(targetType)
145
  if (mys.uid && mys?.ckInfo?.ck) {
146
  return new MysApi(mys.uid, mys.ckInfo.ck, option)
@@ -155,7 +158,7 @@ export default class Runtime {
155
  * @param option
156
  * @returns {Promise<MysApi>}
157
  */
158
- async createMysApi (uid, ck, option) {
159
  return new MysApi(uid, ck, option)
160
  }
161
 
@@ -172,27 +175,17 @@ export default class Runtime {
172
  * @param cfg.beforeRender({data}) 可改写渲染的data数据
173
  * @returns {Promise<boolean>}
174
  */
175
- async render (plugin, path, data = {}, cfg = {}) {
176
  // 处理传入的path
177
- path = path.replace(/.html$/, '')
178
- let paths = lodash.filter(path.split('/'), (p) => !!p)
179
- path = paths.join('/')
180
  // 创建目录
181
- const mkdir = (check) => {
182
- let currDir = `${process.cwd()}/temp`
183
- for (let p of check.split('/')) {
184
- currDir = `${currDir}/${p}`
185
- if (!fs.existsSync(currDir)) {
186
- fs.mkdirSync(currDir)
187
- }
188
- }
189
- return currDir
190
- }
191
- mkdir(`html/${plugin}/${path}`)
192
  // 自动计算pluResPath
193
- let pluResPath = `../../../${lodash.repeat('../', paths.length)}plugins/${plugin}/resources/`
194
- let miaoResPath = `../../../${lodash.repeat('../', paths.length)}plugins/miao-plugin/resources/`
195
- const layoutPath = process.cwd() + '/plugins/miao-plugin/resources/common/layout/'
196
  // 渲染data
197
  data = {
198
  sys: {
@@ -202,9 +195,9 @@ export default class Runtime {
202
  copyright: `Created By TRSS-Yunzai<span class="version">${Version.yunzai}</span> `,
203
  _res_path: pluResPath,
204
  _miao_path: miaoResPath,
205
- _tpl_path: process.cwd() + '/plugins/miao-plugin/resources/common/tpl/',
206
- defaultLayout: layoutPath + 'default.html',
207
- elemLayout: layoutPath + 'elem.html',
208
 
209
  ...data,
210
 
@@ -215,7 +208,7 @@ export default class Runtime {
215
  tplFile: `./plugins/${plugin}/resources/${path}.html`,
216
  saveId: data.saveId || data.save_id || paths[paths.length - 1],
217
  pageGotoParams: {
218
- waitUntil: 'networkidle2'
219
  }
220
  }
221
  // 处理beforeRender
@@ -223,16 +216,16 @@ export default class Runtime {
223
  data = cfg.beforeRender({ data }) || data
224
  }
225
  // 保存模板数据
226
- if (process.argv.includes('dev')) {
227
  // debug下保存当前页面的渲染数据,方便模板编写与调试
228
  // 由于只用于调试,开发者只关注自己当时开发的文件即可,暂不考虑app及plugin的命名冲突
229
- let saveDir = mkdir(`ViewData/${plugin}`)
230
- let file = `${saveDir}/${data._htmlPath.split('/').join('_')}.json`
231
- fs.writeFileSync(file, JSON.stringify(data))
232
  }
233
  // 截图
234
  let base64 = await puppeteer.screenshot(`${plugin}/${path}`, data)
235
- if (cfg.retType === 'base64') {
236
  return base64
237
  }
238
  let ret = true
@@ -243,6 +236,6 @@ export default class Runtime {
243
  ret = await this.e.reply(base64)
244
  }
245
  }
246
- return cfg.retType === 'msgId' ? ret : true
247
  }
248
  }
 
4
  * 提供一些常用的运行时变量、方法及model获取
5
  * 降低对目录结构的依赖
6
  */
7
+ import lodash from "lodash"
8
+ import fs from "node:fs/promises"
9
+ import common from "../common/common.js"
10
+ import cfg from "../config/config.js"
11
+ import puppeteer from "../puppeteer/puppeteer.js"
12
+ import Handler from "./handler.js"
13
+
14
+ let gsCfg, MysApi, MysInfo, NoteUser, MysUser, Version
15
+ try {
16
+ gsCfg = (await import("../../plugins/genshin/model/gsCfg.js")).default
17
+ MysApi = (await import("../../plugins/genshin/model/mys/mysApi.js")).default
18
+ MysInfo = (await import("../../plugins/genshin/model/mys/mysInfo.js")).default
19
+ NoteUser = (await import("../../plugins/genshin/model/mys/NoteUser.js")).default
20
+ MysUser = (await import("../../plugins/genshin/model/mys/MysUser.js")).default
21
+ Version = (await import("#miao")).Version
22
+ } catch (err) {}
23
 
24
  /**
25
  * 常用的处理方法
26
  */
27
 
28
  export default class Runtime {
29
+ constructor(e) {
30
  this.e = e
31
  this._mysInfo = {}
32
 
 
37
  }
38
  }
39
 
40
+ get uid() {
41
  return this.user?.uid
42
  }
43
 
44
+ get hasCk() {
45
  return this.user?.hasCk
46
  }
47
 
48
+ get user() {
49
  return this.e.user
50
  }
51
 
52
+ get cfg() {
53
  return cfg
54
  }
55
 
56
+ get gsCfg() {
57
  return gsCfg
58
  }
59
 
60
+ get common() {
61
  return common
62
  }
63
 
64
+ get puppeteer() {
65
  return puppeteer
66
  }
67
 
68
+ get MysInfo() {
69
  return MysInfo
70
  }
71
 
72
+ get NoteUser() {
73
  return NoteUser
74
  }
75
 
76
+ get MysUser() {
77
  return MysUser
78
  }
79
 
80
+ static async init(e) {
81
+ if (MysInfo) await MysInfo.initCache()
82
+ e.runtime = new Runtime(e)
83
+ if (NoteUser) await e.runtime.initUser()
84
+ return e.runtime
 
85
  }
86
 
87
+ async initUser() {
88
  let e = this.e
89
  let user = await NoteUser.create(e)
90
  if (user) {
 
92
  get (self, key, receiver) {
93
  let game = e.game
94
  let fnMap = {
95
+ uid: "getUid",
96
+ uidList: "getUidList",
97
+ mysUser: "getMysUser",
98
+ ckUidList: "getCkUidList"
99
  }
100
  if (fnMap[key]) {
101
  return self[fnMap[key]](game)
102
  }
103
+ if (key === "uidData") {
104
+ return self.getUidData("", game)
105
  }
106
+ if (["getUid", "getUidList", "getMysUser", "getCkUidList", "getUidMapList", "getGameDs"].includes(key)) {
107
  return (_game, arg2) => {
108
  return self[key](_game || game, arg2)
109
  }
110
  }
111
+ if (["getUidData", "hasUid", "addRegUid", "delRegUid", "setMainUid"].includes(key)) {
112
+ return (uid, _game = "") => {
113
  return self[key](uid, _game || game)
114
  }
115
  }
 
125
  * @param targetType all: 所有用户均可, cookie:查询用户必须具备Cookie
126
  * @returns {Promise<boolean|MysInfo>}
127
  */
128
+ async getMysInfo(targetType = "all") {
129
  if (!this._mysInfo[targetType]) {
130
+ this._mysInfo[targetType] = await MysInfo.init(this.e, targetType === "cookie" ? "detail" : "roleIndex")
131
  }
132
  return this._mysInfo[targetType]
133
  }
134
 
135
+ async getUid() {
136
  return await MysInfo.getUid(this.e)
137
  }
138
 
 
143
  * @param option MysApi option
144
  * @returns {Promise<boolean|MysApi>}
145
  */
146
+ async getMysApi(targetType = "all", option = {}) {
147
  let mys = await this.getMysInfo(targetType)
148
  if (mys.uid && mys?.ckInfo?.ck) {
149
  return new MysApi(mys.uid, mys.ckInfo.ck, option)
 
158
  * @param option
159
  * @returns {Promise<MysApi>}
160
  */
161
+ async createMysApi(uid, ck, option) {
162
  return new MysApi(uid, ck, option)
163
  }
164
 
 
175
  * @param cfg.beforeRender({data}) 可改写渲染的data数据
176
  * @returns {Promise<boolean>}
177
  */
178
+ async render(plugin, path, data = {}, cfg = {}) {
179
  // 处理传入的path
180
+ path = path.replace(/.html$/, "")
181
+ let paths = lodash.filter(path.split("/"), (p) => !!p)
182
+ path = paths.join("/")
183
  // 创建目录
184
+ await Bot.mkdir(`temp/html/${plugin}/${path}`)
 
 
 
 
 
 
 
 
 
 
185
  // 自动计算pluResPath
186
+ let pluResPath = `../../../${lodash.repeat("../", paths.length)}plugins/${plugin}/resources/`
187
+ let miaoResPath = `../../../${lodash.repeat("../", paths.length)}plugins/miao-plugin/resources/`
188
+ const layoutPath = process.cwd() + "/plugins/miao-plugin/resources/common/layout/"
189
  // 渲染data
190
  data = {
191
  sys: {
 
195
  copyright: `Created By TRSS-Yunzai<span class="version">${Version.yunzai}</span> `,
196
  _res_path: pluResPath,
197
  _miao_path: miaoResPath,
198
+ _tpl_path: process.cwd() + "/plugins/miao-plugin/resources/common/tpl/",
199
+ defaultLayout: layoutPath + "default.html",
200
+ elemLayout: layoutPath + "elem.html",
201
 
202
  ...data,
203
 
 
208
  tplFile: `./plugins/${plugin}/resources/${path}.html`,
209
  saveId: data.saveId || data.save_id || paths[paths.length - 1],
210
  pageGotoParams: {
211
+ waitUntil: "networkidle2"
212
  }
213
  }
214
  // 处理beforeRender
 
216
  data = cfg.beforeRender({ data }) || data
217
  }
218
  // 保存模板数据
219
+ if (process.argv.includes("dev")) {
220
  // debug下保存当前页面的渲染数据,方便模板编写与调试
221
  // 由于只用于调试,开发者只关注自己当时开发的文件即可,暂不考虑app及plugin的命名冲突
222
+ let saveDir = await Bot.mkdir(`temp/ViewData/${plugin}`)
223
+ let file = `${saveDir}/${data._htmlPath.split("/").join("_")}.json`
224
+ await fs.writeFile(file, JSON.stringify(data))
225
  }
226
  // 截图
227
  let base64 = await puppeteer.screenshot(`${plugin}/${path}`, data)
228
+ if (cfg.retType === "base64") {
229
  return base64
230
  }
231
  let ret = true
 
236
  ret = await this.e.reply(base64)
237
  }
238
  }
239
+ return cfg.retType === "msgId" ? ret : true
240
  }
241
  }
Yunzai/lib/plugins/stdin.js CHANGED
@@ -1,13 +1,11 @@
1
- import fs from "node:fs"
2
  import path from "node:path"
3
- import common from "../common/common.js"
4
 
5
  Bot.adapter.push(new class stdinAdapter {
6
  constructor() {
7
  this.id = "stdin"
8
  this.name = "标准输入"
9
  this.path = "data/stdin/"
10
- common.mkdirs(this.path)
11
  }
12
 
13
  async sendMsg(msg) {
@@ -19,10 +17,10 @@ Bot.adapter.push(new class stdinAdapter {
19
 
20
  let file
21
  if (i.file) {
22
- file = await Bot.fileType(i.file, i.name)
23
  if (Buffer.isBuffer(file.buffer)) {
24
  file.path = `${this.path}${file.name || Date.now()}`
25
- fs.writeFileSync(file.path, file.buffer)
26
  }
27
  }
28
 
@@ -71,7 +69,7 @@ Bot.adapter.push(new class stdinAdapter {
71
 
72
  const files = `${this.path}${Date.now()}-${name}`
73
  logger.info(`${logger.blue(`[${this.id}]`)} 发送文件:${file}\n文件已保存到:${logger.cyan(files)}`)
74
- return fs.writeFileSync(files, buffer)
75
  }
76
 
77
  pickFriend() {
@@ -103,14 +101,15 @@ Bot.adapter.push(new class stdinAdapter {
103
  Bot.em(`${data.post_type}.${data.message_type}`, data)
104
  }
105
 
106
- load() {
 
107
  Bot[this.id] = {
108
  adapter: this,
109
  uin: this.id,
110
  nickname: this.name,
111
- stat: { start_time: Date.now()/1000 },
112
  version: { id: this.id, name: this.name },
113
  pickFriend: () => this.pickFriend(),
 
114
  get pickUser() { return this.pickFriend },
115
  get pickMember() { return this.pickFriend },
116
  get pickGroup() { return this.pickFriend },
 
1
+ import fs from "node:fs/promises"
2
  import path from "node:path"
 
3
 
4
  Bot.adapter.push(new class stdinAdapter {
5
  constructor() {
6
  this.id = "stdin"
7
  this.name = "标准输入"
8
  this.path = "data/stdin/"
 
9
  }
10
 
11
  async sendMsg(msg) {
 
17
 
18
  let file
19
  if (i.file) {
20
+ file = await Bot.fileType(i)
21
  if (Buffer.isBuffer(file.buffer)) {
22
  file.path = `${this.path}${file.name || Date.now()}`
23
+ await fs.writeFile(file.path, file.buffer)
24
  }
25
  }
26
 
 
69
 
70
  const files = `${this.path}${Date.now()}-${name}`
71
  logger.info(`${logger.blue(`[${this.id}]`)} 发送文件:${file}\n文件已保存到:${logger.cyan(files)}`)
72
+ return fs.writeFile(files, buffer)
73
  }
74
 
75
  pickFriend() {
 
101
  Bot.em(`${data.post_type}.${data.message_type}`, data)
102
  }
103
 
104
+ async load() {
105
+ await Bot.mkdir(this.path)
106
  Bot[this.id] = {
107
  adapter: this,
108
  uin: this.id,
109
  nickname: this.name,
 
110
  version: { id: this.id, name: this.name },
111
  pickFriend: () => this.pickFriend(),
112
+ get stat() { return Bot.stat },
113
  get pickUser() { return this.pickFriend },
114
  get pickMember() { return this.pickFriend },
115
  get pickGroup() { return this.pickFriend },
Yunzai/lib/renderer/loader.js CHANGED
@@ -1,9 +1,13 @@
1
- import fs from 'node:fs'
2
- import yaml from 'yaml'
3
- import lodash from 'lodash'
4
- import cfg from '../config/config.js'
5
- import { Data } from '#miao'
6
- import Renderer from './Renderer.js'
 
 
 
 
7
 
8
  /** 全局变量 Renderer */
9
  global.Renderer = Renderer
@@ -14,7 +18,7 @@ global.Renderer = Renderer
14
  class RendererLoader {
15
  constructor() {
16
  this.renderers = new Map()
17
- this.dir = './renderers'
18
  // TODO 渲染器热加载
19
  this.watcher = {}
20
  }
@@ -26,16 +30,17 @@ class RendererLoader {
26
  }
27
 
28
  async load() {
 
29
  const subFolders = fs.readdirSync(this.dir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory())
30
  for (let subFolder of subFolders) {
31
  let name = subFolder.name
32
  try {
33
  const rendererFn = await Data.importDefault(`${this.dir}/${name}/index.js`)
34
  let configFile = `${this.dir}/${name}/config.yaml`
35
- let rendererCfg = fs.existsSync(configFile) ? yaml.parse(fs.readFileSync(configFile, 'utf8')) : {}
36
  let renderer = rendererFn(rendererCfg)
37
  if (!renderer.id || !renderer.type || !renderer.render || !lodash.isFunction(renderer.render)) {
38
- logger.warn('渲染后端 ' + (renderer.id || subFolder.name) + ' 不可用')
39
  }
40
  this.renderers.set(renderer.id, renderer)
41
  logger.info(`加载渲染后端 ${renderer.id}`)
@@ -46,9 +51,9 @@ class RendererLoader {
46
  }
47
  }
48
 
49
- getRenderer(name = cfg.renderer?.name || 'puppeteer') {
50
  // TODO 渲染器降级
51
- return this.renderers.get(name)
52
  }
53
  }
54
 
 
1
+ import fs from "node:fs"
2
+ import yaml from "yaml"
3
+ import lodash from "lodash"
4
+ import cfg from "../config/config.js"
5
+ import Renderer from "./Renderer.js"
6
+
7
+ let Data
8
+ try {
9
+ Data = (await import("#miao")).Data
10
+ } catch (err) {}
11
 
12
  /** 全局变量 Renderer */
13
  global.Renderer = Renderer
 
18
  class RendererLoader {
19
  constructor() {
20
  this.renderers = new Map()
21
+ this.dir = "./renderers"
22
  // TODO 渲染器热加载
23
  this.watcher = {}
24
  }
 
30
  }
31
 
32
  async load() {
33
+ if (!Data) return
34
  const subFolders = fs.readdirSync(this.dir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory())
35
  for (let subFolder of subFolders) {
36
  let name = subFolder.name
37
  try {
38
  const rendererFn = await Data.importDefault(`${this.dir}/${name}/index.js`)
39
  let configFile = `${this.dir}/${name}/config.yaml`
40
+ let rendererCfg = fs.existsSync(configFile) ? yaml.parse(fs.readFileSync(configFile, "utf8")) : {}
41
  let renderer = rendererFn(rendererCfg)
42
  if (!renderer.id || !renderer.type || !renderer.render || !lodash.isFunction(renderer.render)) {
43
+ logger.warn("渲染后端 " + (renderer.id || subFolder.name) + " 不可用")
44
  }
45
  this.renderers.set(renderer.id, renderer)
46
  logger.info(`加载渲染后端 ${renderer.id}`)
 
51
  }
52
  }
53
 
54
+ getRenderer(name = cfg.renderer?.name || "puppeteer") {
55
  // TODO 渲染器降级
56
+ return this.renderers.get(name) || {}
57
  }
58
  }
59
 
Yunzai/lib/tools/web.js CHANGED
@@ -1,7 +1,7 @@
1
- import express from 'express'
2
- import template from 'express-art-template'
3
- import fs from 'fs'
4
- import lodash from 'lodash'
5
 
6
  /*
7
  * npm run app web-debug开启Bot后
@@ -17,53 +17,53 @@ let app = express()
17
 
18
  let _path = process.cwd()
19
 
20
- app.engine('html', template)
21
- app.set('views', _path + '/resources/')
22
- app.set('view engine', 'art')
23
- app.use(express.static(_path + '/resources'))
24
- app.use('/plugins', express.static('plugins'))
25
 
26
- app.get('/', function (req, res) {
27
- let pluginList = fs.readdirSync(_path + '/temp/ViewData/') || []
28
  let html = [
29
- '在npm run web-dev模式下触发截图消息后,可在下方选择页面进行调试',
30
- '如果页面内资源路径不正确请使用{{_res_path}}作为根路径,对应之前的../../../../',
31
- '可直接修改模板html或css刷新查看效果'
32
  ]
33
  let li = {}
34
  for (let pIdx in pluginList) {
35
  const plugin = pluginList[pIdx]
36
- let fileList = fs.readdirSync(_path + `/temp/ViewData/${plugin}/`) || []
37
  for (let idx in fileList) {
38
  let ret = /(.+)\.json$/.exec(fileList[idx])
39
  if (ret && ret[1]) {
40
- let text = [plugin, ...ret[1].split('_')]
41
- li[text.join('')] = (`<li style="font-size:18px; line-height:30px;"><a href="/${plugin}_${ret[1]}">${text.join(' / ')}</a></li>`)
42
  }
43
  }
44
  }
45
- res.send(html.join('</br>') + '<ul>' + lodash.values(li).join('') + '</ul>')
46
  })
47
 
48
- app.get('/:page', function (req, res) {
49
- let [plugin, app, ...page] = req.params.page.split('_')
50
- page = page.join('_')
51
- if (plugin == 'favicon.ico') {
52
- return res.send('')
53
  }
54
- let data = JSON.parse(fs.readFileSync(_path + `/temp/ViewData/${plugin}/${app}_${page}.json`, 'utf8'))
55
  data = data || {}
56
- data._res_path = ''
57
  data._sys_res_path = data._res_path
58
 
59
  if (data._plugin) {
60
  data._res_path = `/plugins/${data._plugin}/resources/`
61
  data.pluResPath = data._res_path
62
  }
63
- let htmlPath = ''
64
  let tplPath = `${app}/${htmlPath}${page}/${page}.html`
65
  if (data._plugin) {
66
- tplPath = `../plugins/${data._plugin}/resources/${htmlPath}/${app}/${page.split('_').join('/')}.html`
67
  } else if (data._no_type_path) {
68
  tplPath = `${app}/${page}.html`
69
  }
@@ -71,4 +71,4 @@ app.get('/:page', function (req, res) {
71
  })
72
 
73
  app.listen(8000)
74
- console.log('页面服务已启动,触发消息图片后访问 http://localhost:8000/ 调试页面')
 
1
+ import express from "express"
2
+ import template from "express-art-template"
3
+ import fs from "node:fs/promises"
4
+ import lodash from "lodash"
5
 
6
  /*
7
  * npm run app web-debug开启Bot后
 
17
 
18
  let _path = process.cwd()
19
 
20
+ app.engine("html", template)
21
+ app.set("views", _path + "/resources/")
22
+ app.set("view engine", "art")
23
+ app.use(express.static(_path + "/resources"))
24
+ app.use("/plugins", express.static("plugins"))
25
 
26
+ app.get("/", async function (req, res) {
27
+ let pluginList = await fs.readdir(_path + "/temp/ViewData/") || []
28
  let html = [
29
+ "在npm run web-dev模式下触发截图消息后,可在下方选择页面进行调试",
30
+ "如果页面内资源路径不正确请使用{{_res_path}}作为根路径,对应之前的../../../../",
31
+ "可直接修改模板html或css刷新查看效果"
32
  ]
33
  let li = {}
34
  for (let pIdx in pluginList) {
35
  const plugin = pluginList[pIdx]
36
+ let fileList = await fs.readdir(_path + `/temp/ViewData/${plugin}/`) || []
37
  for (let idx in fileList) {
38
  let ret = /(.+)\.json$/.exec(fileList[idx])
39
  if (ret && ret[1]) {
40
+ let text = [plugin, ...ret[1].split("_")]
41
+ li[text.join("")] = (`<li style="font-size:18px; line-height:30px;"><a href="/${plugin}_${ret[1]}">${text.join(" / ")}</a></li>`)
42
  }
43
  }
44
  }
45
+ res.send(html.join("</br>") + "<ul>" + lodash.values(li).join("") + "</ul>")
46
  })
47
 
48
+ app.get("/:page", async function (req, res) {
49
+ let [plugin, app, ...page] = req.params.page.split("_")
50
+ page = page.join("_")
51
+ if (plugin == "favicon.ico") {
52
+ return res.send("")
53
  }
54
+ let data = JSON.parse(await fs.readFile(_path + `/temp/ViewData/${plugin}/${app}_${page}.json`, "utf8"))
55
  data = data || {}
56
+ data._res_path = ""
57
  data._sys_res_path = data._res_path
58
 
59
  if (data._plugin) {
60
  data._res_path = `/plugins/${data._plugin}/resources/`
61
  data.pluResPath = data._res_path
62
  }
63
+ let htmlPath = ""
64
  let tplPath = `${app}/${htmlPath}${page}/${page}.html`
65
  if (data._plugin) {
66
+ tplPath = `../plugins/${data._plugin}/resources/${htmlPath}/${app}/${page.split("_").join("/")}.html`
67
  } else if (data._no_type_path) {
68
  tplPath = `${app}/${page}.html`
69
  }
 
71
  })
72
 
73
  app.listen(8000)
74
+ console.log("页面服务已启动,触发消息图片后访问 http://localhost:8000/ 调试页面")
Yunzai/package.json CHANGED
@@ -8,41 +8,41 @@
8
  "scripts": {
9
  "app": "node .",
10
  "dev": "node . dev",
11
- "web": "node ./lib/tools/web.js",
12
- "test": "node ./lib/tools/test.js",
13
- "start": "pm2 start ./config/pm2/pm2.json",
14
- "stop": "pm2 stop ./config/pm2/pm2.json",
15
- "restart": "pm2 restart ./config/pm2/pm2.json",
16
- "log": "node ./lib/tools/log.js"
17
  },
18
  "dependencies": {
19
  "art-template": "^4.13.2",
20
  "chalk": "^5.3.0",
21
- "chokidar": "^3.5.3",
22
- "express": "^4.18.2",
23
- "file-type": "^18.7.0",
24
- "https-proxy-agent": "7.0.2",
25
- "image-size": "^1.0.2",
 
26
  "lodash": "^4.17.21",
27
  "log4js": "^6.9.1",
28
- "md5": "^2.3.0",
29
  "moment": "^2.30.1",
30
- "node-fetch": "^3.3.2",
31
  "node-schedule": "^2.1.1",
32
  "oicq": "link:lib/modules/oicq",
33
- "pm2": "^5.3.0",
34
- "puppeteer": "^21.6.1",
35
- "redis": "^4.6.12",
36
- "sequelize": "^6.35.2",
37
- "sqlite3": "^5.1.6",
38
  "ws": "^8.16.0",
39
- "yaml": "^2.3.4"
40
  },
41
  "devDependencies": {
42
- "eslint": "^8.56.0",
43
  "eslint-config-standard": "^17.1.0",
44
  "eslint-plugin-import": "^2.29.1",
45
- "eslint-plugin-n": "^16.5.0",
46
  "eslint-plugin-promise": "^6.1.1"
47
  },
48
  "imports": {
 
8
  "scripts": {
9
  "app": "node .",
10
  "dev": "node . dev",
11
+ "web": "node lib/tools/web.js",
12
+ "start": "pm2 start config/pm2.yaml",
13
+ "stop": "pm2 stop config/pm2.yaml",
14
+ "restart": "pm2 restart config/pm2.yaml",
15
+ "log": "pm2 log --lines 100"
 
16
  },
17
  "dependencies": {
18
  "art-template": "^4.13.2",
19
  "chalk": "^5.3.0",
20
+ "chokidar": "^3.6.0",
21
+ "express": "^4.19.1",
22
+ "file-type": "^19.0.0",
23
+ "https-proxy-agent": "7.0.4",
24
+ "image-size": "^1.1.1",
25
+ "level": "^8.0.1",
26
  "lodash": "^4.17.21",
27
  "log4js": "^6.9.1",
28
+ "md5": "link:lib/modules/md5",
29
  "moment": "^2.30.1",
30
+ "node-fetch": "link:lib/modules/node-fetch",
31
  "node-schedule": "^2.1.1",
32
  "oicq": "link:lib/modules/oicq",
33
+ "pm2": "^5.3.1",
34
+ "puppeteer": "*",
35
+ "redis": "^4.6.13",
36
+ "sequelize": "^6.37.1",
37
+ "sqlite3": "5.1.6",
38
  "ws": "^8.16.0",
39
+ "yaml": "^2.4.1"
40
  },
41
  "devDependencies": {
42
+ "eslint": "^8.57.0",
43
  "eslint-config-standard": "^17.1.0",
44
  "eslint-plugin-import": "^2.29.1",
45
+ "eslint-plugin-n": "^16.6.2",
46
  "eslint-plugin-promise": "^6.1.1"
47
  },
48
  "imports": {
Yunzai/plugins/adapter/ComWeChat.js CHANGED
@@ -1,7 +1,5 @@
1
- import { randomUUID } from "crypto"
2
  import path from "node:path"
3
- import fs from "node:fs"
4
- import { fileTypeFromBuffer } from "file-type"
5
 
6
  Bot.adapter.push(new class ComWeChatAdapter {
7
  constructor() {
@@ -10,71 +8,38 @@ Bot.adapter.push(new class ComWeChatAdapter {
10
  this.path = this.name
11
  }
12
 
13
- toStr(data) {
14
- switch (typeof data) {
15
- case "string":
16
- return data
17
- case "number":
18
- return String(data)
19
- case "object":
20
- if (Buffer.isBuffer(data))
21
- return Buffer.from(data, "utf8").toString()
22
- else
23
- return JSON.stringify(data)
24
- }
25
- return data
26
- }
27
-
28
  makeLog(msg) {
29
- return this.toStr(msg).replace(/(base64:\/\/|"type":"data","data":").*?"/g, '$1..."')
30
  }
31
 
32
  sendApi(ws, action, params = {}) {
33
  const echo = randomUUID()
34
- const msg = { action, params, echo }
35
- ws.sendMsg(msg)
36
- return new Promise(resolve =>
37
- Bot.once(echo, data =>
38
- resolve({ ...data, ...data.data })))
39
- }
40
-
41
- async fileName(file) {
42
- try {
43
- if (file.match(/^base64:\/\//)) {
44
- const buffer = Buffer.from(file.replace(/^base64:\/\//, ""), "base64")
45
- const type = await fileTypeFromBuffer(buffer)
46
- return `${Date.now()}.${type.ext}`
47
- } else {
48
- return path.basename(file)
49
- }
50
- } catch (err) {
51
- logger.error(`文件类型检测错误:${logger.red(err)}`)
52
- }
53
- return false
54
  }
55
 
56
- async uploadFile(data, file, name) {
57
- const opts = { name: name || await this.fileName(file) || randomUUID() }
 
58
 
59
- if (file.match(/^https?:\/\//)) {
60
- opts.type = "url"
61
- opts.url = file
62
- } else if (file.match(/^base64:\/\//)) {
63
  opts.type = "data"
64
- opts.data = file.replace(/^base64:\/\//, "")
65
- } else if (fs.existsSync(file)) {
66
- opts.type = "data"
67
- opts.data = fs.readFileSync(file).toString("base64")
68
  } else {
69
  opts.type = "path"
70
- opts.path = file
71
  }
72
 
73
- logger.info(`${logger.blue(`[${data.self_id}]`)} 上传文件:${this.makeLog(opts)}`)
74
  return data.bot.sendApi("upload_file", opts)
75
  }
76
 
77
- async makeMsg(data, msg) {
78
  if (!Array.isArray(msg))
79
  msg = [msg]
80
  const msgs = []
@@ -84,7 +49,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
84
  else if (!i.data)
85
  i = { type: i.type, data: { ...i, type: undefined }}
86
  if (i.data.file)
87
- i.data = { file_id: (await this.uploadFile(data, i.data.file, i.data.name)).file_id }
88
 
89
  switch (i.type) {
90
  case "text":
@@ -104,6 +69,10 @@ Bot.adapter.push(new class ComWeChatAdapter {
104
  i = { type: "mention", data: { user_id: i.data.qq }}
105
  break
106
  case "reply":
 
 
 
 
107
  continue
108
  default:
109
  i = { type: "text", data: { text: JSON.stringify(i) }}
@@ -114,11 +83,8 @@ Bot.adapter.push(new class ComWeChatAdapter {
114
  }
115
 
116
  async sendFriendMsg(data, msg) {
117
- if (msg?.type == "node")
118
- return Bot.sendForwardMsg(msg => this.sendFriendMsg(data, msg), msg.data)
119
-
120
- const message = await this.makeMsg(data, msg)
121
- logger.info(`${logger.blue(`[${data.self_id} => ${data.user_id}]`)} 发送好友消息:${this.makeLog(message)}`)
122
  return data.bot.sendApi("send_message", {
123
  detail_type: "private",
124
  user_id: data.user_id,
@@ -127,11 +93,8 @@ Bot.adapter.push(new class ComWeChatAdapter {
127
  }
128
 
129
  async sendGroupMsg(data, msg) {
130
- if (msg?.type == "node")
131
- return Bot.sendForwardMsg(msg => this.sendGroupMsg(data, msg), msg.data)
132
-
133
- const message = await this.makeMsg(data, msg)
134
- logger.info(`${logger.blue(`[${data.self_id} => ${data.group_id}]`)} 发送群消息:${this.makeLog(message)}`)
135
  return data.bot.sendApi("send_message", {
136
  detail_type: "group",
137
  group_id: data.group_id,
@@ -312,7 +275,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
312
  data.bot.getFriendMap()
313
  data.bot.getGroupMap()
314
 
315
- logger.mark(`${logger.blue(`[${data.self_id}]`)} ${this.name}(${this.id}) ${data.bot.version.impl}-${data.bot.version.version} 已连接`)
316
  Bot.em(`connect.${data.self_id}`, data)
317
  }
318
 
@@ -348,13 +311,13 @@ Bot.adapter.push(new class ComWeChatAdapter {
348
 
349
  switch (data.message_type) {
350
  case "private":
351
- logger.info(`${logger.blue(`[${data.self_id}]`)} 好友消息:[${data.user_id}] ${data.raw_message}`)
352
  break
353
  case "group":
354
- logger.info(`${logger.blue(`[${data.self_id}]`)} 群消息:[${data.group_id}, ${data.user_id}] ${data.raw_message}`)
355
  break
356
  default:
357
- logger.warn(`${logger.blue(`[${data.self_id}]`)} 未知消息:${logger.magenta(JSON.stringify(data))}`)
358
  }
359
 
360
  Bot.em(`${data.post_type}.${data.message_type}`, data)
@@ -369,43 +332,43 @@ Bot.adapter.push(new class ComWeChatAdapter {
369
 
370
  switch (data.detail_type) {
371
  case "private_message_delete":
372
- logger.info(`${logger.blue(`[${data.self_id}]`)} 好友消息撤回:[${data.user_id}] ${data.message_id}`)
373
  data.sub_type = "recall"
374
  break
375
  case "group_message_delete":
376
- logger.info(`${logger.blue(`[${data.self_id}]`)} 群消息撤回:[${data.group_id}, ${data.operator_id}=>${data.user_id}] ${data.message_id}`)
377
  data.sub_type = "recall"
378
  break
379
  case "wx.get_private_file":
380
- logger.info(`${logger.blue(`[${data.self_id}]`)} 私聊文件:[${data.user_id}] ${data.file_name} ${data.file_length} ${data.md5}`)
381
  break
382
  case "wx.get_group_file":
383
- logger.info(`${logger.blue(`[${data.self_id}]`)} 群文件:[${data.group_id}, ${data.user_id}] ${data.file_name} ${data.file_length} ${data.md5}`)
384
  break
385
  case "wx.get_private_redbag":
386
- logger.info(`${logger.blue(`[${data.self_id}]`)} 好友红包:[${data.user_id}]`)
387
  break
388
  case "wx.get_group_redbag":
389
- logger.info(`${logger.blue(`[${data.self_id}]`)} 群红包:[${data.group_id}, ${data.user_id}]`)
390
  break
391
  case "wx.get_private_poke":
392
  data.operator_id = data.from_user_id
393
  data.target_id = data.user_id
394
- logger.info(`${logger.blue(`[${data.self_id}]`)} 好友拍一拍:[${data.operator_id}=>${data.target_id}]`)
395
  break
396
  case "wx.get_group_poke":
397
  data.operator_id = data.from_user_id
398
  data.target_id = data.user_id
399
- logger.info(`${logger.blue(`[${data.self_id}]`)} 群拍一拍:[${data.group_id}, ${data.operator_id}=>${data.target_id}]`)
400
  break
401
  case "wx.get_private_card":
402
- logger.info(`${logger.blue(`[${data.self_id}]`)} 好友用户名片:[${data.user_id}] ${data.v3} ${data.v4} ${data.nickname} ${data.head_url} ${data.province} ${data.city} ${data.sex}`)
403
  break
404
  case "wx.get_group_card":
405
- logger.info(`${logger.blue(`[${data.self_id}]`)} 群用户名片:[${data.group_id}, ${data.user_id}] ${data.v3} ${data.v4} ${data.nickname} ${data.head_url} ${data.province} ${data.city} ${data.sex}`)
406
  break
407
  default:
408
- logger.warn(`${logger.blue(`[${data.self_id}]`)} 未知通知:${logger.magenta(JSON.stringify(data))}`)
409
  }
410
  if (!data.sub_type)
411
  data.sub_type = data.detail_type.split("_").pop()
@@ -422,11 +385,11 @@ Bot.adapter.push(new class ComWeChatAdapter {
422
 
423
  switch (data.detail_type) {
424
  case "wx.friend_request":
425
- logger.info(`${logger.blue(`[${data.self_id}]`)} 加好友请求:[${data.user_id}] ${data.v3} ${data.v4} ${data.nickname} ${data.content} ${data.province} ${data.city}`)
426
  data.sub_type = "add"
427
  break
428
  default:
429
- logger.warn(`${logger.blue(`[${data.self_id}]`)} 未知请求:${logger.magenta(JSON.stringify(data))}`)
430
  }
431
  if (!data.sub_type)
432
  data.sub_type = data.detail_type.split("_").pop()
@@ -444,15 +407,18 @@ Bot.adapter.push(new class ComWeChatAdapter {
444
  this.connect(data, ws)
445
  break
446
  default:
447
- logger.warn(`${logger.blue(`[${data.self_id}]`)} 未知消息:${logger.magenta(JSON.stringify(data))}`)
448
  }
449
  }
450
 
451
  message(data, ws) {
452
  try {
453
- data = JSON.parse(data)
 
 
 
454
  } catch (err) {
455
- return logger.error(`解码数据失败:${logger.red(err)}`)
456
  }
457
 
458
  if (data.self?.user_id) {
@@ -463,7 +429,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
463
 
464
  if (data.type) {
465
  if (data.type != "meta" && !Bot.uin.includes(data.self_id)) {
466
- logger.warn(`${logger.blue(`[${data.self_id}]`)} 找不到对应Bot,忽略消息:${logger.magenta(JSON.stringify(data))}`)
467
  return false
468
  }
469
  data.bot = Bot[data.self_id]
@@ -482,12 +448,12 @@ Bot.adapter.push(new class ComWeChatAdapter {
482
  this.makeRequest(data)
483
  break
484
  default:
485
- logger.warn(`${logger.blue(`[${data.self_id}]`)} 未知消息:${logger.magenta(JSON.stringify(data))}`)
486
  }
487
  } else if (data.echo) {
488
  Bot.emit(data.echo, data)
489
  } else {
490
- logger.warn(`${logger.blue(`[${data.self_id}]`)} 未知消息:${logger.magenta(JSON.stringify(data))}`)
491
  }
492
  }
493
 
 
1
+ import { randomUUID } from "node:crypto"
2
  import path from "node:path"
 
 
3
 
4
  Bot.adapter.push(new class ComWeChatAdapter {
5
  constructor() {
 
8
  this.path = this.name
9
  }
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  makeLog(msg) {
12
+ return Bot.String(msg).replace(/(base64:\/\/|"type":"data","data":").*?"/g, '$1..."')
13
  }
14
 
15
  sendApi(ws, action, params = {}) {
16
  const echo = randomUUID()
17
+ ws.sendMsg({ action, params, echo })
18
+ return new Promise(resolve => Bot.once(echo, data =>
19
+ resolve({ ...data, ...data.data })
20
+ ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
 
23
+ async uploadFile(data, file) {
24
+ file = await Bot.fileType(file, { http: true })
25
+ const opts = { name: file.name }
26
 
27
+ if (Buffer.isBuffer(file.buffer)) {
 
 
 
28
  opts.type = "data"
29
+ opts.data = file.buffer.toString("base64")
30
+ } else if (file.buffer.match(/^https?:\/\//)) {
31
+ opts.type = "url"
32
+ opts.url = file.buffer
33
  } else {
34
  opts.type = "path"
35
+ opts.path = file.buffer
36
  }
37
 
38
+ Bot.makeLog("info", `上传文件:${this.makeLog(opts)}`, data.self_id)
39
  return data.bot.sendApi("upload_file", opts)
40
  }
41
 
42
+ async makeMsg(data, msg, send) {
43
  if (!Array.isArray(msg))
44
  msg = [msg]
45
  const msgs = []
 
49
  else if (!i.data)
50
  i = { type: i.type, data: { ...i, type: undefined }}
51
  if (i.data.file)
52
+ i.data = { file_id: (await this.uploadFile(data, i.data)).file_id }
53
 
54
  switch (i.type) {
55
  case "text":
 
69
  i = { type: "mention", data: { user_id: i.data.qq }}
70
  break
71
  case "reply":
72
+ case "button":
73
+ continue
74
+ case "node":
75
+ await Bot.sendForwardMsg(send, i.data)
76
  continue
77
  default:
78
  i = { type: "text", data: { text: JSON.stringify(i) }}
 
83
  }
84
 
85
  async sendFriendMsg(data, msg) {
86
+ const message = await this.makeMsg(data, msg, msg => this.sendFriendMsg(data, msg))
87
+ Bot.makeLog("info", `发送好友消息:${this.makeLog(message)}`, `${data.self_id} => ${data.user_id}`)
 
 
 
88
  return data.bot.sendApi("send_message", {
89
  detail_type: "private",
90
  user_id: data.user_id,
 
93
  }
94
 
95
  async sendGroupMsg(data, msg) {
96
+ const message = await this.makeMsg(data, msg, msg => this.sendGroupMsg(data, msg))
97
+ Bot.makeLog("info", `发送群消息:${this.makeLog(message)}`, `${data.self_id} => ${data.group_id}`)
 
 
 
98
  return data.bot.sendApi("send_message", {
99
  detail_type: "group",
100
  group_id: data.group_id,
 
275
  data.bot.getFriendMap()
276
  data.bot.getGroupMap()
277
 
278
+ Bot.makeLog("mark", `${this.name}(${this.id}) ${data.bot.version.impl}-${data.bot.version.version} 已连接`, data.self_id)
279
  Bot.em(`connect.${data.self_id}`, data)
280
  }
281
 
 
311
 
312
  switch (data.message_type) {
313
  case "private":
314
+ Bot.makeLog("info", `好友消息:${data.raw_message}`, `${data.self_id} <= ${data.user_id}`)
315
  break
316
  case "group":
317
+ Bot.makeLog("info", `群消息:${data.raw_message}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
318
  break
319
  default:
320
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
321
  }
322
 
323
  Bot.em(`${data.post_type}.${data.message_type}`, data)
 
332
 
333
  switch (data.detail_type) {
334
  case "private_message_delete":
335
+ Bot.makeLog("info", `好友消息撤回:${data.message_id}`, `${data.self_id} <= ${data.user_id}`)
336
  data.sub_type = "recall"
337
  break
338
  case "group_message_delete":
339
+ Bot.makeLog("info", `群消息撤回:${data.operator_id} => ${data.user_id} ${data.message_id}`, `${data.self_id} <= ${data.group_id}`)
340
  data.sub_type = "recall"
341
  break
342
  case "wx.get_private_file":
343
+ Bot.makeLog("info", `私聊文件:${data.file_name} ${data.file_length} ${data.md5}`, `${data.self_id} <= ${data.user_id}`)
344
  break
345
  case "wx.get_group_file":
346
+ Bot.makeLog("info", `群文件:${data.file_name} ${data.file_length} ${data.md5}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
347
  break
348
  case "wx.get_private_redbag":
349
+ Bot.makeLog("info", `好友红包`, `${data.self_id} <= ${data.user_id}`)
350
  break
351
  case "wx.get_group_redbag":
352
+ Bot.makeLog("info", `群红包`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
353
  break
354
  case "wx.get_private_poke":
355
  data.operator_id = data.from_user_id
356
  data.target_id = data.user_id
357
+ Bot.makeLog("info", `好友拍一拍:${data.operator_id} => ${data.target_id}`, data.self_id)
358
  break
359
  case "wx.get_group_poke":
360
  data.operator_id = data.from_user_id
361
  data.target_id = data.user_id
362
+ Bot.makeLog("info", `群拍一拍:${data.operator_id} => ${data.target_id}`, `${data.self_id} <= ${data.group_id}`)
363
  break
364
  case "wx.get_private_card":
365
+ Bot.makeLog("info", `好友用户名片:${data.v3} ${data.v4} ${data.nickname} ${data.head_url} ${data.province} ${data.city} ${data.sex}`, `${data.self_id} <= ${data.user_id}`)
366
  break
367
  case "wx.get_group_card":
368
+ Bot.makeLog("info", `群用户名片:${data.v3} ${data.v4} ${data.nickname} ${data.head_url} ${data.province} ${data.city} ${data.sex}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
369
  break
370
  default:
371
+ Bot.makeLog("warn", `未知通知:${logger.magenta(data.raw)}`, data.self_id)
372
  }
373
  if (!data.sub_type)
374
  data.sub_type = data.detail_type.split("_").pop()
 
385
 
386
  switch (data.detail_type) {
387
  case "wx.friend_request":
388
+ Bot.makeLog("info", `加好友请求:${data.v3} ${data.v4} ${data.nickname} ${data.content} ${data.province} ${data.city}`, `${data.self_id} <= ${data.user_id}`)
389
  data.sub_type = "add"
390
  break
391
  default:
392
+ Bot.makeLog("warn", `未知请求:${logger.magenta(data.raw)}`, data.self_id)
393
  }
394
  if (!data.sub_type)
395
  data.sub_type = data.detail_type.split("_").pop()
 
407
  this.connect(data, ws)
408
  break
409
  default:
410
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
411
  }
412
  }
413
 
414
  message(data, ws) {
415
  try {
416
+ data = {
417
+ ...JSON.parse(data),
418
+ raw: Bot.String(data),
419
+ }
420
  } catch (err) {
421
+ return Bot.makeLog("error", ["解码数据失败", data, err])
422
  }
423
 
424
  if (data.self?.user_id) {
 
429
 
430
  if (data.type) {
431
  if (data.type != "meta" && !Bot.uin.includes(data.self_id)) {
432
+ Bot.makeLog("warn", `找不到对应Bot,忽略消息:${logger.magenta(data.raw)}`, data.self_id)
433
  return false
434
  }
435
  data.bot = Bot[data.self_id]
 
448
  this.makeRequest(data)
449
  break
450
  default:
451
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
452
  }
453
  } else if (data.echo) {
454
  Bot.emit(data.echo, data)
455
  } else {
456
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
457
  }
458
  }
459
 
Yunzai/plugins/adapter/GSUIDCore.js CHANGED
@@ -1,7 +1,3 @@
1
- import { randomUUID } from "crypto"
2
- import path from "node:path"
3
- import fs from "node:fs"
4
-
5
  Bot.adapter.push(new class GSUIDCoreAdapter {
6
  constructor() {
7
  this.id = "GSUIDCore"
@@ -9,26 +5,55 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
9
  this.path = this.id
10
  }
11
 
12
- toStr(data) {
13
- switch (typeof data) {
14
- case "string":
15
- return data
16
- case "number":
17
- return String(data)
18
- case "object":
19
- if (Buffer.isBuffer(data))
20
- return Buffer.from(data, "utf8").toString()
21
- else
22
- return JSON.stringify(data)
23
- }
24
- return data
25
  }
26
 
27
- makeLog(msg) {
28
- return this.toStr(msg).replace(/base64:\/\/.*?"/g, "base64://...\"")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
 
31
- makeMsg(msg) {
32
  if (!Array.isArray(msg))
33
  msg = [msg]
34
  const msgs = []
@@ -36,6 +61,12 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
36
  if (typeof i != "object")
37
  i = { type: "text", text: i }
38
 
 
 
 
 
 
 
39
  switch (i.type) {
40
  case "text":
41
  i = { type: "text", data: i.text }
@@ -44,7 +75,7 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
44
  i = { type: "image", data: i.file }
45
  break
46
  case "record":
47
- i = { type: "file", data: i.file }
48
  break
49
  case "video":
50
  i = { type: "file", data: i.file }
@@ -58,10 +89,15 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
58
  case "reply":
59
  i = { type: "reply", data: i.id }
60
  break
 
 
 
 
 
61
  case "node": {
62
  const array = []
63
  for (const { message } of i.data)
64
- array.push(...this.makeMsg(message))
65
  i.data = array
66
  break
67
  } default:
@@ -72,9 +108,9 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
72
  return msgs
73
  }
74
 
75
- sendFriendMsg(data, msg) {
76
- const content = this.makeMsg(msg)
77
- logger.info(`${logger.blue(`[${data.self_id} => ${data.user_id}]`)} 发送好友消息:${this.makeLog(content)}`)
78
  data.bot.sendApi({
79
  bot_id: data.bot.bot_id,
80
  bot_self_id: data.bot.bot_self_id,
@@ -85,10 +121,10 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
85
  return { message_id: Date.now() }
86
  }
87
 
88
- sendGroupMsg(data, msg) {
89
  const target = data.group_id.split("-")
90
- const content = this.makeMsg(msg)
91
- logger.info(`${logger.blue(`[${data.self_id} => ${data.group_id}]`)} 发送群消息:${this.makeLog(content)}`)
92
  data.bot.sendApi({
93
  bot_id: data.bot.bot_id,
94
  bot_self_id: data.bot.bot_self_id,
@@ -115,6 +151,7 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
115
  pickMember(id, group_id, user_id) {
116
  const i = {
117
  ...Bot[id].fl.get(user_id),
 
118
  self_id: id,
119
  bot: Bot[id],
120
  group_id: group_id,
@@ -146,8 +183,8 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
146
  ws: ws,
147
  get sendApi() { return this.ws.sendMsg },
148
  uin: data.self_id,
149
- bot_id: data.bot_id,
150
- bot_self_id: data.bot_self_id,
151
  stat: { start_time: Date.now()/1000 },
152
  version: {
153
  id: this.id,
@@ -161,19 +198,34 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
161
  gl: new Map,
162
  gml: new Map,
163
  }
 
164
 
165
- logger.mark(`${logger.blue(`[${data.self_id}]`)} ${this.name}(${this.id}) 已连接`)
166
  Bot.em(`connect.${data.self_id}`, data)
167
  }
168
 
169
- message(data, ws) {
170
  try {
171
- data = JSON.parse(data)
172
  } catch (err) {
173
- return logger.error(`解码数据失败:${logger.red(err)}`)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
175
 
176
- data.self_id = data.bot_self_id
177
  if (Bot[data.self_id]) {
178
  data.bot = Bot[data.self_id]
179
  data.bot.ws = ws
@@ -181,19 +233,10 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
181
  this.makeBot(data, ws)
182
  }
183
 
184
- data.post_type = "message"
185
- data.message_id = data.msg_id
186
- data.user_id = data.user_id
187
- data.sender = {
188
- user_id: data.user_id,
189
- user_pm: data.user_pm,
190
- }
191
  if (!data.bot.fl.has(data.user_id))
192
  data.bot.fl.set(data.user_id, data.sender)
193
 
194
- data.message = []
195
- data.raw_message = ""
196
- for (const i of data.content) {
197
  switch (i.type) {
198
  case "text":
199
  data.message.push({ type: "text", text: i.data })
@@ -225,15 +268,24 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
225
  }
226
  }
227
 
228
- if (data.user_type == "direct") {
229
  data.message_type = "private"
230
- logger.info(`${logger.blue(`[${data.self_id}]`)} 好友消息:[${data.user_id}] ${data.raw_message}`)
231
  } else {
232
  data.message_type = "group"
233
- data.group_id = `${data.user_type}-${data.group_id}`
 
234
  if (!data.bot.gl.has(data.group_id))
235
  data.bot.gl.set(data.group_id, { group_id: data.group_id })
236
- logger.info(`${logger.blue(`[${data.self_id}]`)} 群消息:[${data.group_id}, ${data.user_id}] ${data.raw_message}`)
 
 
 
 
 
 
 
 
237
  }
238
 
239
  Bot.em(`${data.post_type}.${data.message_type}`, data)
 
 
 
 
 
1
  Bot.adapter.push(new class GSUIDCoreAdapter {
2
  constructor() {
3
  this.id = "GSUIDCore"
 
5
  this.path = this.id
6
  }
7
 
8
+ makeLog(msg) {
9
+ return Bot.String(msg).replace(/base64:\/\/.*?"/g, "base64://...\"")
 
 
 
 
 
 
 
 
 
 
 
10
  }
11
 
12
+ makeButton(data, button) {
13
+ const msg = {
14
+ text: button.text,
15
+ pressed_text: button.clicked_text,
16
+ ...button.GSUIDCore,
17
+ }
18
+
19
+ if (button.input) {
20
+ msg.data = button.input
21
+ msg.action = 2
22
+ } else if (button.callback) {
23
+ msg.data = button.callback
24
+ msg.action = 1
25
+ } else if (button.link) {
26
+ msg.data = button.link
27
+ msg.action = 0
28
+ } else return false
29
+
30
+ if (button.permission) {
31
+ if (button.permission == "admin") {
32
+ msg.permission = 1
33
+ } else {
34
+ msg.permission = 0
35
+ if (!Array.isArray(button.permission))
36
+ button.permission = [button.permission]
37
+ msg.specify_user_ids = button.permission
38
+ }
39
+ }
40
+ return msg
41
+ }
42
+
43
+ makeButtons(button_square) {
44
+ const msgs = []
45
+ for (const button_row of button_square) {
46
+ const buttons = []
47
+ for (let button of button_row) {
48
+ button = this.makeButton(button)
49
+ if (button) buttons.push(button)
50
+ }
51
+ msgs.push(buttons)
52
+ }
53
+ return msgs
54
  }
55
 
56
+ async makeMsg(msg) {
57
  if (!Array.isArray(msg))
58
  msg = [msg]
59
  const msgs = []
 
61
  if (typeof i != "object")
62
  i = { type: "text", text: i }
63
 
64
+ if (i.file) {
65
+ i.file = await Bot.Buffer(i.file, { http: true })
66
+ if (Buffer.isBuffer(i.file))
67
+ i.file = `base64://${i.file.toString("base64")}`
68
+ }
69
+
70
  switch (i.type) {
71
  case "text":
72
  i = { type: "text", data: i.text }
 
75
  i = { type: "image", data: i.file }
76
  break
77
  case "record":
78
+ i = { type: "record", data: i.file }
79
  break
80
  case "video":
81
  i = { type: "file", data: i.file }
 
89
  case "reply":
90
  i = { type: "reply", data: i.id }
91
  break
92
+ case "button":
93
+ i = { type: "buttons", data: this.makeButtons(i.data) }
94
+ break
95
+ case "markdown":
96
+ break
97
  case "node": {
98
  const array = []
99
  for (const { message } of i.data)
100
+ array.push(...await this.makeMsg(message))
101
  i.data = array
102
  break
103
  } default:
 
108
  return msgs
109
  }
110
 
111
+ async sendFriendMsg(data, msg) {
112
+ const content = await this.makeMsg(msg)
113
+ Bot.makeLog("info", `发送好友消息:${this.makeLog(content)}`, `${data.self_id} => ${data.user_id}`)
114
  data.bot.sendApi({
115
  bot_id: data.bot.bot_id,
116
  bot_self_id: data.bot.bot_self_id,
 
121
  return { message_id: Date.now() }
122
  }
123
 
124
+ async sendGroupMsg(data, msg) {
125
  const target = data.group_id.split("-")
126
+ const content = await this.makeMsg(msg)
127
+ Bot.makeLog("info", `发送群消息:${this.makeLog(content)}`, `${data.self_id} => ${data.group_id}`)
128
  data.bot.sendApi({
129
  bot_id: data.bot.bot_id,
130
  bot_self_id: data.bot.bot_self_id,
 
151
  pickMember(id, group_id, user_id) {
152
  const i = {
153
  ...Bot[id].fl.get(user_id),
154
+ ...Bot[id].gml.get(group_id)?.get(user_id),
155
  self_id: id,
156
  bot: Bot[id],
157
  group_id: group_id,
 
183
  ws: ws,
184
  get sendApi() { return this.ws.sendMsg },
185
  uin: data.self_id,
186
+ bot_id: data.raw.bot_id,
187
+ bot_self_id: data.raw.bot_self_id,
188
  stat: { start_time: Date.now()/1000 },
189
  version: {
190
  id: this.id,
 
198
  gl: new Map,
199
  gml: new Map,
200
  }
201
+ data.bot = Bot[data.self_id]
202
 
203
+ Bot.makeLog("mark", `${this.name}(${this.id}) 已连接`, data.self_id)
204
  Bot.em(`connect.${data.self_id}`, data)
205
  }
206
 
207
+ message(raw, ws) {
208
  try {
209
+ raw = JSON.parse(raw)
210
  } catch (err) {
211
+ return Bot.makeLog("error", ["解码数据失败", raw, err])
212
+ }
213
+
214
+ const data = {
215
+ raw,
216
+ self_id: raw.bot_self_id,
217
+ post_type: "message",
218
+ message_id: raw.msg_id,
219
+ get user_id() { return this.sender.user_id },
220
+ sender: {
221
+ ...raw.sender,
222
+ user_id: raw.user_id,
223
+ user_pm: raw.user_pm,
224
+ },
225
+ message: [],
226
+ raw_message: "",
227
  }
228
 
 
229
  if (Bot[data.self_id]) {
230
  data.bot = Bot[data.self_id]
231
  data.bot.ws = ws
 
233
  this.makeBot(data, ws)
234
  }
235
 
 
 
 
 
 
 
 
236
  if (!data.bot.fl.has(data.user_id))
237
  data.bot.fl.set(data.user_id, data.sender)
238
 
239
+ for (const i of raw.content) {
 
 
240
  switch (i.type) {
241
  case "text":
242
  data.message.push({ type: "text", text: i.data })
 
268
  }
269
  }
270
 
271
+ if (raw.user_type == "direct") {
272
  data.message_type = "private"
273
+ Bot.makeLog("info", `好友消息:${data.raw_message}`, `${data.self_id} <= ${data.user_id}`)
274
  } else {
275
  data.message_type = "group"
276
+ data.group_id = `${raw.user_type}-${raw.group_id}`
277
+
278
  if (!data.bot.gl.has(data.group_id))
279
  data.bot.gl.set(data.group_id, { group_id: data.group_id })
280
+ let gml = data.bot.gml.get(data.group_id)
281
+ if (!gml) {
282
+ gml = new Map
283
+ data.bot.gml.set(data.group_id, gml)
284
+ }
285
+ if (!gml.has(data.user_id))
286
+ gml.set(data.user_id, data.sender)
287
+
288
+ Bot.makeLog("info", `群消息:${data.raw_message}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
289
  }
290
 
291
  Bot.em(`${data.post_type}.${data.message_type}`, data)
Yunzai/plugins/adapter/OPQBot.js ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Bot.adapter.push(new class OPQBotAdapter {
2
+ constructor() {
3
+ this.id = "QQ"
4
+ this.name = "OPQBot"
5
+ this.path = this.name
6
+ this.CommandId = {
7
+ FriendImage: 1,
8
+ GroupImage: 2,
9
+ FriendVoice: 26,
10
+ GroupVoice: 29,
11
+ }
12
+ }
13
+
14
+ sendApi(id, CgiCmd, CgiRequest) {
15
+ const ReqId = Math.round(Math.random()*10**16)
16
+ Bot[id].ws.sendMsg({ BotUin: String(id), CgiCmd, CgiRequest, ReqId })
17
+ return new Promise(resolve =>
18
+ Bot.once(ReqId, data => resolve(data)))
19
+ }
20
+
21
+ makeLog(msg) {
22
+ return Bot.String(msg).replace(/base64:\/\/.*?"/g, 'base64://..."')
23
+ }
24
+
25
+ async uploadFile(id, type, file) {
26
+ const opts = { CommandId: this.CommandId[type] }
27
+
28
+ file = await Bot.Buffer(file, { http: true })
29
+ if (Buffer.isBuffer(file))
30
+ opts.Base64Buf = file.toString("base64")
31
+ else if (file.match(/^https?:\/\//))
32
+ opts.FileUrl = file
33
+ else
34
+ opts.FilePath = file
35
+
36
+ return (await this.sendApi(id, "PicUp.DataUp", opts)).ResponseData
37
+ }
38
+
39
+ async sendMsg(send, upload, msg) {
40
+ if (!Array.isArray(msg))
41
+ msg = [msg]
42
+ const message = {
43
+ Content: "",
44
+ Images: [],
45
+ AtUinLists: [],
46
+ }
47
+
48
+ for (let i of msg) {
49
+ if (typeof i != "object")
50
+ i = { type: "text", text: i }
51
+
52
+ switch (i.type) {
53
+ case "text":
54
+ message.Content += i.text
55
+ break
56
+ case "image":
57
+ message.Images.push(await upload("Image", i.file))
58
+ break
59
+ case "record":
60
+ message.Voice = await upload("Voice", i.file)
61
+ break
62
+ case "at":
63
+ message.AtUinLists.push({ Uin: i.qq })
64
+ break
65
+ case "video":
66
+ case "file":
67
+ case "face":
68
+ case "reply":
69
+ case "button":
70
+ continue
71
+ case "node":
72
+ await Bot.sendForwardMsg(msg => this.sendMsg(send, upload, msg), i.data)
73
+ continue
74
+ default:
75
+ message.Content += JSON.stringify(i)
76
+ }
77
+ }
78
+
79
+ return send(message)
80
+ }
81
+
82
+ sendFriendMsg(data, msg, event) {
83
+ Bot.makeLog("info", `发送好友消息:${this.makeLog(msg)}`, `${data.self_id} => ${data.user_id}`)
84
+ return this.sendMsg(
85
+ msg => this.sendApi(data.self_id,
86
+ "MessageSvc.PbSendMsg", {
87
+ ToUin: data.user_id,
88
+ ToType: 1,
89
+ ...msg,
90
+ }),
91
+ (type, file) => this.uploadFile(data.self_id, `Friend${type}`, file),
92
+ msg
93
+ )
94
+ }
95
+
96
+ sendMemberMsg(data, msg, event) {
97
+ Bot.makeLog("info", `发送群员消息:${this.makeLog(msg)}`, `${data.self_id} => ${data.group_id}, ${data.user_id}`)
98
+ return this.sendMsg(
99
+ msg => this.sendApi(data.self_id,
100
+ "MessageSvc.PbSendMsg", {
101
+ ToUin: data.user_id,
102
+ GroupCode: data.group_id,
103
+ ToType: 3,
104
+ ...msg,
105
+ }),
106
+ (type, file) => this.uploadFile(data.self_id, `Friend${type}`, file),
107
+ msg
108
+ )
109
+ }
110
+
111
+ sendGroupMsg(data, msg) {
112
+ Bot.makeLog("info", `发送群消息:${this.makeLog(msg)}`, `${data.self_id} => ${data.group_id}`)
113
+ let ReplyTo
114
+ if (data.message_id && data.seq && data.time)
115
+ ReplyTo = {
116
+ MsgSeq: data.seq,
117
+ MsgTime: data.time,
118
+ MsgUid: data.message_id,
119
+ }
120
+
121
+ return this.sendMsg(
122
+ msg => this.sendApi(data.self_id,
123
+ "MessageSvc.PbSendMsg", {
124
+ ToUin: data.group_id,
125
+ ToType: 2,
126
+ ReplyTo,
127
+ ...msg,
128
+ }),
129
+ (type, file) => this.uploadFile(data.self_id, `Group${type}`, file),
130
+ msg
131
+ )
132
+ }
133
+
134
+ pickFriend(id, user_id) {
135
+ const i = {
136
+ ...Bot[id].fl.get(user_id),
137
+ self_id: id,
138
+ bot: Bot[id],
139
+ user_id: user_id,
140
+ }
141
+ return {
142
+ ...i,
143
+ sendMsg: msg => this.sendFriendMsg(i, msg),
144
+ getAvatarUrl: () => `https://q1.qlogo.cn/g?b=qq&s=0&nk=${user_id}`,
145
+ }
146
+ }
147
+
148
+ pickMember(id, group_id, user_id) {
149
+ const i = {
150
+ ...Bot[id].fl.get(user_id),
151
+ self_id: id,
152
+ bot: Bot[id],
153
+ user_id: user_id,
154
+ group_id: group_id,
155
+ }
156
+ return {
157
+ ...this.pickFriend(id, user_id),
158
+ ...i,
159
+ sendMsg: msg => this.sendMemberMsg(i, msg),
160
+ }
161
+ }
162
+
163
+ pickGroup(id, group_id) {
164
+ const i = {
165
+ ...Bot[id].gl.get(group_id),
166
+ self_id: id,
167
+ bot: Bot[id],
168
+ group_id: group_id,
169
+ }
170
+ return {
171
+ ...i,
172
+ sendMsg: msg => this.sendGroupMsg(i, msg),
173
+ pickMember: user_id => this.pickMember(id, group_id, user_id),
174
+ getAvatarUrl: () => `https://p.qlogo.cn/gh/${group_id}/${group_id}/0`,
175
+ }
176
+ }
177
+
178
+ makeMessage(id, event) {
179
+ const data = {
180
+ event,
181
+ bot: Bot[id],
182
+ self_id: id,
183
+ post_type: "message",
184
+ message_id: event.MsgHead.MsgUid,
185
+ seq: event.MsgHead.MsgSeq,
186
+ time: event.MsgHead.MsgTime,
187
+ user_id: event.MsgHead.SenderUin,
188
+ sender: {
189
+ user_id: event.MsgHead.SenderUin,
190
+ nickname: event.MsgHead.SenderNick,
191
+ },
192
+ message: [],
193
+ raw_message: "",
194
+ }
195
+
196
+ if (event.MsgBody.AtUinLists)
197
+ for (const i of event.MsgBody.AtUinLists) {
198
+ data.message.push({
199
+ type: "at",
200
+ qq: i.Uin,
201
+ data: i,
202
+ })
203
+ data.raw_message += `[提及:${i.Uin}]`
204
+ }
205
+
206
+ if (event.MsgBody.Content) {
207
+ data.message.push({
208
+ type: "text",
209
+ text: event.MsgBody.Content,
210
+ })
211
+ data.raw_message += event.MsgBody.Content
212
+ }
213
+
214
+ if (event.MsgBody.Images)
215
+ for (const i of event.MsgBody.Images) {
216
+ data.message.push({
217
+ type: "image",
218
+ url: i.Url,
219
+ data: i,
220
+ })
221
+ data.raw_message += `[图片:${i.Url}]`
222
+ }
223
+
224
+ return data
225
+ }
226
+
227
+ makeFriendMessage(id, data) {
228
+ if (!data.MsgBody) return
229
+ data = this.makeMessage(id, data)
230
+ data.message_type = "private"
231
+
232
+ Bot.makeLog("info", `好友消息:[${data.sender.nickname}] ${data.raw_message}`, `${data.self_id} <= ${data.user_id}`)
233
+ Bot.em(`${data.post_type}.${data.message_type}`, data)
234
+ }
235
+
236
+ makeGroupMessage(id, data) {
237
+ if (!data.MsgBody) return
238
+ data = this.makeMessage(id, data)
239
+ data.message_type = "group"
240
+ data.sender.card = data.event.MsgHead.GroupInfo.GroupCard
241
+ data.group_id = data.event.MsgHead.GroupInfo.GroupCode
242
+ data.group_name = data.event.MsgHead.GroupInfo.GroupName
243
+
244
+ data.reply = msg => this.sendGroupMsg(data, msg)
245
+ Bot.makeLog("info", `群消息:[${data.group_name}, ${data.sender.nickname}] ${data.raw_message}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
246
+ Bot.em(`${data.post_type}.${data.message_type}`, data)
247
+ }
248
+
249
+ makeEvent(id, data) {
250
+ switch (data.EventName) {
251
+ case "ON_EVENT_FRIEND_NEW_MSG":
252
+ this.makeFriendMessage(id, data.EventData)
253
+ break
254
+ case "ON_EVENT_GROUP_NEW_MSG":
255
+ this.makeGroupMessage(id, data.EventData)
256
+ break
257
+ default:
258
+ Bot.makeLog("warn", `未知事件:${logger.magenta(data.raw)}`, id)
259
+ }
260
+ }
261
+
262
+ makeBot(id, ws) {
263
+ Bot[id] = {
264
+ adapter: this,
265
+ ws,
266
+
267
+ uin: id,
268
+ info: { id },
269
+ get nickname() { return this.info.nickname },
270
+ get avatar() { return `https://q1.qlogo.cn/g?b=qq&s=0&nk=${this.uin}` },
271
+
272
+ version: {
273
+ id: this.id,
274
+ name: this.name,
275
+ version: this.version,
276
+ },
277
+ stat: { start_time: Date.now()/1000 },
278
+
279
+ pickFriend: user_id => this.pickFriend(id, user_id),
280
+ get pickUser() { return this.pickFriend },
281
+ getFriendMap() { return this.fl },
282
+ fl: new Map,
283
+
284
+ pickMember: (group_id, user_id) => this.pickMember(id, group_id, user_id),
285
+ pickGroup: group_id => this.pickGroup(id, group_id),
286
+ getGroupMap() { return this.gl },
287
+ gl: new Map,
288
+ gml: new Map,
289
+ }
290
+
291
+ Bot.makeLog("mark", `${this.name}(${this.id}) ${this.version} 已连接`, id)
292
+ Bot.em(`connect.${id}`, { self_id: id })
293
+ }
294
+
295
+ message(data, ws) {
296
+ try {
297
+ data = {
298
+ ...JSON.parse(data),
299
+ raw: Bot.String(data),
300
+ }
301
+ } catch (err) {
302
+ return Bot.makeLog("error", ["解码数据失败", data, err])
303
+ }
304
+
305
+ const id = data.CurrentQQ
306
+ if (id && data.CurrentPacket) {
307
+ if (Bot[id])
308
+ Bot[id].ws = ws
309
+ else
310
+ this.makeBot(id, ws)
311
+
312
+ this.makeEvent(id, data.CurrentPacket)
313
+ } else if (data.ReqId) {
314
+ Bot.emit(data.ReqId, data)
315
+ } else {
316
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, id)
317
+ }
318
+ }
319
+
320
+ load() {
321
+ if (!Array.isArray(Bot.wsf[this.path]))
322
+ Bot.wsf[this.path] = []
323
+ Bot.wsf[this.path].push((ws, ...args) =>
324
+ ws.on("message", data => this.message(data, ws, ...args))
325
+ )
326
+ }
327
+ })
Yunzai/plugins/adapter/OneBotv11.js ADDED
@@ -0,0 +1,989 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { randomUUID } from "node:crypto"
2
+ import path from "node:path"
3
+
4
+ Bot.adapter.push(new class OneBotv11Adapter {
5
+ constructor() {
6
+ this.id = "QQ"
7
+ this.name = "OneBotv11"
8
+ this.path = this.name
9
+ }
10
+
11
+ makeLog(msg) {
12
+ return Bot.String(msg).replace(/base64:\/\/.*?(,|]|")/g, "base64://...$1")
13
+ }
14
+
15
+ sendApi(ws, action, params) {
16
+ const echo = randomUUID()
17
+ ws.sendMsg({ action, params, echo })
18
+ return new Promise(resolve => Bot.once(echo, data =>
19
+ resolve({ ...data, ...data.data })
20
+ ))
21
+ }
22
+
23
+ setProfile(data, profile) {
24
+ Bot.makeLog("info", `设置资料:${JSON.stringify(profile)}`, data.self_id)
25
+ return data.bot.sendApi("set_qq_profile", profile)
26
+ }
27
+
28
+ async makeFile(file) {
29
+ file = await Bot.Buffer(file, { http: true })
30
+ if (Buffer.isBuffer(file))
31
+ file = `base64://${file.toString("base64")}`
32
+ return file
33
+ }
34
+
35
+ async makeMsg(msg) {
36
+ if (!Array.isArray(msg))
37
+ msg = [msg]
38
+ const msgs = []
39
+ const forward = []
40
+ for (let i of msg) {
41
+ if (typeof i != "object")
42
+ i = { type: "text", data: { text: i }}
43
+ else if (!i.data)
44
+ i = { type: i.type, data: { ...i, type: undefined }}
45
+
46
+ switch (i.type) {
47
+ case "at":
48
+ i.data.qq = String(i.data.qq)
49
+ break
50
+ case "reply":
51
+ i.data.id = String(i.data.id)
52
+ break
53
+ case "button":
54
+ continue
55
+ case "node":
56
+ forward.push(...i.data)
57
+ continue
58
+ }
59
+
60
+ if (i.data.file)
61
+ i.data.file = await this.makeFile(i.data.file)
62
+
63
+ msgs.push(i)
64
+ }
65
+ return [msgs, forward]
66
+ }
67
+
68
+ async sendMsg(msg, send, sendForwardMsg) {
69
+ const [message, forward] = await this.makeMsg(msg)
70
+ const ret = []
71
+
72
+ if (forward.length) {
73
+ const data = await sendForwardMsg(forward)
74
+ if (Array.isArray(data))
75
+ ret.push(...data)
76
+ else
77
+ ret.push(data)
78
+ }
79
+
80
+ if (message.length)
81
+ ret.push(await send(message))
82
+ if (ret.length == 1) return ret[0]
83
+
84
+ const message_id = []
85
+ for (const i of ret) if (i?.message_id)
86
+ message_id.push(i.message_id)
87
+ return { data: ret, message_id }
88
+ }
89
+
90
+ sendFriendMsg(data, msg) {
91
+ return this.sendMsg(msg, message => {
92
+ Bot.makeLog("info", `发送好友消息:${this.makeLog(message)}`, `${data.self_id} => ${data.user_id}`)
93
+ data.bot.sendApi("send_msg", {
94
+ user_id: data.user_id,
95
+ message,
96
+ })
97
+ }, msg => this.sendFriendForwardMsg(data, msg))
98
+ }
99
+
100
+ sendGroupMsg(data, msg) {
101
+ return this.sendMsg(msg, message => {
102
+ Bot.makeLog("info", `发送群消息:${this.makeLog(message)}`, `${data.self_id} => ${data.group_id}`)
103
+ return data.bot.sendApi("send_msg", {
104
+ group_id: data.group_id,
105
+ message,
106
+ })
107
+ }, msg => this.sendGroupForwardMsg(data, msg))
108
+ }
109
+
110
+ sendGuildMsg(data, msg) {
111
+ return this.sendMsg(msg, message => {
112
+ Bot.makeLog("info", `发送频道消息:${this.makeLog(message)}`, `${data.self_id}] => ${data.guild_id}-${data.channel_id}`)
113
+ return data.bot.sendApi("send_guild_channel_msg", {
114
+ guild_id: data.guild_id,
115
+ channel_id: data.channel_id,
116
+ message,
117
+ })
118
+ }, msg => Bot.sendForwardMsg(msg => this.sendGuildMsg(data, msg), msg))
119
+ }
120
+
121
+ async recallMsg(data, message_id) {
122
+ Bot.makeLog("info", `撤回消息:${message_id}`, data.self_id)
123
+ if (!Array.isArray(message_id))
124
+ message_id = [message_id]
125
+ const msgs = []
126
+ for (const i of message_id)
127
+ msgs.push(await data.bot.sendApi("delete_msg", { message_id: i }))
128
+ return msgs
129
+ }
130
+
131
+ parseMsg(msg) {
132
+ const array = []
133
+ for (const i of Array.isArray(msg) ? msg : [msg])
134
+ if (typeof i == "object")
135
+ array.push({ ...i.data, type: i.type })
136
+ else
137
+ array.push({ type: "text", text: String(i) })
138
+ return array
139
+ }
140
+
141
+ async getMsg(data, message_id) {
142
+ const msg = (await data.bot.sendApi("get_msg", { message_id })).data
143
+ if (msg?.message)
144
+ msg.message = this.parseMsg(msg.message)
145
+ return msg
146
+ }
147
+
148
+ async getGroupMsgHistory(data, message_seq, count) {
149
+ const msgs = (await data.bot.sendApi("get_group_msg_history", {
150
+ group_id: data.group_id,
151
+ message_seq,
152
+ count,
153
+ })).data?.messages
154
+
155
+ for (const i of Array.isArray(msgs) ? msgs : [msgs])
156
+ if (i?.message)
157
+ i.message = this.parseMsg(i.message)
158
+ return msgs
159
+ }
160
+
161
+ async getForwardMsg(data, message_id) {
162
+ const msgs = (await data.bot.sendApi("get_forward_msg", {
163
+ message_id,
164
+ })).data?.messages
165
+
166
+ for (const i of Array.isArray(msgs) ? msgs : [msgs])
167
+ if (i?.message)
168
+ i.message = this.parseMsg(i.message || i.content)
169
+ return msgs
170
+ }
171
+
172
+ async makeForwardMsg(msg) {
173
+ const msgs = []
174
+ for (const i of msg) {
175
+ const [content, forward] = await this.makeMsg(i.message)
176
+ if (forward.length)
177
+ msgs.push(...await this.makeForwardMsg(forward))
178
+ if (content.length)
179
+ msgs.push({ type: "node", data: {
180
+ name: i.nickname || "匿名消息",
181
+ uin: String(Number(i.user_id) || 80000000),
182
+ content,
183
+ time: i.time,
184
+ }})
185
+ }
186
+ return msgs
187
+ }
188
+
189
+ async sendFriendForwardMsg(data, msg) {
190
+ Bot.makeLog("info", `发送好友转发消息:${this.makeLog(msg)}`, `${data.self_id} => ${data.user_id}`)
191
+ return data.bot.sendApi("send_private_forward_msg", {
192
+ user_id: data.user_id,
193
+ messages: await this.makeForwardMsg(msg),
194
+ })
195
+ }
196
+
197
+ async sendGroupForwardMsg(data, msg) {
198
+ Bot.makeLog("info", `发送群转发消息:${this.makeLog(msg)}`, `${data.self_id} => ${data.group_id}`)
199
+ return data.bot.sendApi("send_group_forward_msg", {
200
+ group_id: data.group_id,
201
+ messages: await this.makeForwardMsg(msg),
202
+ })
203
+ }
204
+
205
+ async getFriendArray(data) {
206
+ return (await data.bot.sendApi("get_friend_list")).data || []
207
+ }
208
+
209
+ async getFriendList(data) {
210
+ const array = []
211
+ for (const { user_id } of await this.getFriendArray(data))
212
+ array.push(user_id)
213
+ return array
214
+ }
215
+
216
+ async getFriendMap(data) {
217
+ const map = new Map
218
+ for (const i of await this.getFriendArray(data))
219
+ map.set(i.user_id, i)
220
+ data.bot.fl = map
221
+ return map
222
+ }
223
+
224
+ getFriendInfo(data) {
225
+ return data.bot.sendApi("get_stranger_info", {
226
+ user_id: data.user_id,
227
+ })
228
+ }
229
+
230
+ async getGroupArray(data) {
231
+ const array = (await data.bot.sendApi("get_group_list")).data
232
+ try { for (const guild of await this.getGuildArray(data))
233
+ for (const channel of await this.getGuildChannelArray({
234
+ ...data,
235
+ guild_id: guild.guild_id,
236
+ }))
237
+ array.push({
238
+ guild,
239
+ channel,
240
+ group_id: `${guild.guild_id}-${channel.channel_id}`,
241
+ group_name: `${guild.guild_name}-${channel.channel_name}`,
242
+ })
243
+ } catch (err) {
244
+ Bot.makeLog("error", ["获取频道列表错误", err])
245
+ }
246
+ return array
247
+ }
248
+
249
+ async getGroupList(data) {
250
+ const array = []
251
+ for (const { group_id } of await this.getGroupArray(data))
252
+ array.push(group_id)
253
+ return array
254
+ }
255
+
256
+ async getGroupMap(data) {
257
+ const map = new Map
258
+ for (const i of await this.getGroupArray(data))
259
+ map.set(i.group_id, i)
260
+ data.bot.gl = map
261
+ return map
262
+ }
263
+
264
+ getGroupInfo(data) {
265
+ return data.bot.sendApi("get_group_info", {
266
+ group_id: data.group_id,
267
+ })
268
+ }
269
+
270
+ async getMemberArray(data) {
271
+ return (await data.bot.sendApi("get_group_member_list", {
272
+ group_id: data.group_id,
273
+ })).data || []
274
+ }
275
+
276
+ async getMemberList(data) {
277
+ const array = []
278
+ for (const { user_id } of await this.getMemberArray(data))
279
+ array.push(user_id)
280
+ return array
281
+ }
282
+
283
+ async getMemberMap(data) {
284
+ const map = new Map
285
+ for (const i of await this.getMemberArray(data))
286
+ map.set(i.user_id, i)
287
+ data.bot.gml.set(data.group_id, map)
288
+ return map
289
+ }
290
+
291
+ async getGroupMemberMap(data) {
292
+ for (const [group_id, group] of await this.getGroupMap(data)) {
293
+ if (group.guild) continue
294
+ await this.getMemberMap({ ...data, group_id })
295
+ }
296
+ }
297
+
298
+ getMemberInfo(data) {
299
+ return data.bot.sendApi("get_group_member_info", {
300
+ group_id: data.group_id,
301
+ user_id: data.user_id,
302
+ })
303
+ }
304
+
305
+ async getGuildArray(data) {
306
+ return (await data.bot.sendApi("get_guild_list")).data || []
307
+ }
308
+
309
+ getGuildInfo(data) {
310
+ return data.bot.sendApi("get_guild_meta_by_guest", {
311
+ guild_id: data.guild_id,
312
+ })
313
+ }
314
+
315
+ async getGuildChannelArray(data) {
316
+ return (await data.bot.sendApi("get_guild_channel_list", {
317
+ guild_id: data.guild_id,
318
+ })).data || []
319
+ }
320
+
321
+ async getGuildChannelMap(data) {
322
+ const map = new Map
323
+ for (const i of await this.getGuildChannelArray(data))
324
+ map.set(i.channel_id, i)
325
+ return map
326
+ }
327
+
328
+ async getGuildMemberArray(data) {
329
+ const array = []
330
+ let next_token = ""
331
+ while (true) {
332
+ const list = (await data.bot.sendApi("get_guild_member_list", {
333
+ guild_id: data.guild_id,
334
+ next_token,
335
+ })).data
336
+ if (!list) break
337
+
338
+ for (const i of list.members)
339
+ array.push({
340
+ ...i,
341
+ user_id: i.tiny_id,
342
+ })
343
+ if (list.finished) break
344
+ next_token = list.next_token
345
+ }
346
+ return array
347
+ }
348
+
349
+ async getGuildMemberList(data) {
350
+ const array = []
351
+ for (const { user_id } of await this.getGuildMemberArray(data))
352
+ array.push(user_id)
353
+ return array.push
354
+ }
355
+
356
+ async getGuildMemberMap(data) {
357
+ const map = new Map
358
+ for (const i of await this.getGuildMemberArray(data))
359
+ map.set(i.user_id, i)
360
+ data.bot.gml.set(data.group_id, map)
361
+ return map
362
+ }
363
+
364
+ getGuildMemberInfo(data) {
365
+ return data.bot.sendApi("get_guild_member_profile", {
366
+ guild_id: data.guild_id,
367
+ user_id: data.user_id,
368
+ })
369
+ }
370
+
371
+ sendLike(data, times) {
372
+ Bot.makeLog("info", `点赞:${times}次`, `${data.self_id} => ${data.user_id}`)
373
+ return data.bot.sendApi("send_like", {
374
+ user_id: data.user_id,
375
+ times,
376
+ })
377
+ }
378
+
379
+ setGroupName(data, group_name) {
380
+ Bot.makeLog("info", `设置群名:${group_name}`, `${data.self_id} => ${data.group_id}`)
381
+ return data.bot.sendApi("set_group_name", {
382
+ group_id: data.group_id,
383
+ group_name,
384
+ })
385
+ }
386
+
387
+ async setGroupAvatar(data, file) {
388
+ Bot.makeLog("info", `设置群头像:${file}`, `${data.self_id} => ${data.group_id}`)
389
+ file = await Bot.Buffer(file, { http: true })
390
+ return data.bot.sendApi("set_group_portrait", {
391
+ group_id: data.group_id,
392
+ file,
393
+ })
394
+ }
395
+
396
+ setGroupAdmin(data, user_id, enable) {
397
+ Bot.makeLog("info", `${enable ? "设置" : "取消"}群管理员:${user_id}`, `${data.self_id} => ${data.group_id}`)
398
+ return data.bot.sendApi("set_group_admin", {
399
+ group_id: data.group_id,
400
+ user_id,
401
+ enable,
402
+ })
403
+ }
404
+
405
+ setGroupCard(data, user_id, card) {
406
+ Bot.makeLog("info", `设置群名片:${card}`, `${data.self_id} => ${data.group_id}, ${user_id}`)
407
+ return data.bot.sendApi("set_group_card", {
408
+ group_id: data.group_id,
409
+ user_id,
410
+ card,
411
+ })
412
+ }
413
+
414
+ setGroupTitle(data, user_id, special_title, duration) {
415
+ Bot.makeLog("info", `设置群头衔:${special_title} ${duration}`, `${data.self_id} => ${data.group_id}, ${user_id}`)
416
+ return data.bot.sendApi("set_group_special_title", {
417
+ group_id: data.group_id,
418
+ user_id,
419
+ special_title,
420
+ duration,
421
+ })
422
+ }
423
+
424
+ sendGroupSign(data) {
425
+ Bot.makeLog("info", "群打卡", `${data.self_id} => ${data.group_id}`)
426
+ return data.bot.sendApi("send_group_sign", {
427
+ group_id: data.group_id,
428
+ })
429
+ }
430
+
431
+ setGroupBan(data, user_id, duration) {
432
+ Bot.makeLog("info", `禁言群成员:${duration}秒`, `${data.self_id} => ${data.group_id}, ${user_id}`)
433
+ return data.bot.sendApi("set_group_ban", {
434
+ group_id: data.group_id,
435
+ user_id,
436
+ duration,
437
+ })
438
+ }
439
+
440
+ setGroupWholeKick(data, enable) {
441
+ Bot.makeLog("info", `${enable ? "开启" : "关闭"}全员禁言`, `${data.self_id} => ${data.group_id}`)
442
+ return data.bot.sendApi("set_group_whole_ban", {
443
+ group_id: data.group_id,
444
+ enable,
445
+ })
446
+ }
447
+
448
+ setGroupKick(data, user_id, reject_add_request) {
449
+ Bot.makeLog("info", `踢出群成员${reject_add_request ? "拒绝再次加群" : ""}`, `${data.self_id} => ${data.group_id}, ${user_id}`)
450
+ return data.bot.sendApi("set_group_kick", {
451
+ group_id: data.group_id,
452
+ user_id,
453
+ reject_add_request,
454
+ })
455
+ }
456
+
457
+ setGroupLeave(data, is_dismiss) {
458
+ Bot.makeLog("info", is_dismiss ? "解散" : "退群", `${data.self_id} => ${data.group_id}`)
459
+ return data.bot.sendApi("set_group_leave", {
460
+ group_id: data.group_id,
461
+ is_dismiss,
462
+ })
463
+ }
464
+
465
+ downloadFile(data, url, thread_count, headers) {
466
+ return data.bot.sendApi("download_file", {
467
+ url,
468
+ thread_count,
469
+ headers,
470
+ })
471
+ }
472
+
473
+ async sendFriendFile(data, file, name = path.basename(file)) {
474
+ Bot.makeLog("info", `发送好友文件:${name}(${file})`, `${data.self_id} => ${data.user_id}`)
475
+ return data.bot.sendApi("upload_private_file", {
476
+ user_id: data.user_id,
477
+ file: await this.makeFile(file),
478
+ name,
479
+ })
480
+ }
481
+
482
+ async sendGroupFile(data, file, folder, name = path.basename(file)) {
483
+ Bot.makeLog("info", `发送群文件:${folder||""}/${name}(${file})`, `${data.self_id} => ${data.group_id}`)
484
+ return data.bot.sendApi("upload_group_file", {
485
+ group_id: data.group_id,
486
+ folder,
487
+ file: await this.makeFile(file),
488
+ name,
489
+ })
490
+ }
491
+
492
+ deleteGroupFile(data, file_id, busid) {
493
+ Bot.makeLog("info", `删除群文件:${file_id}(${busid})`, `${data.self_id} => ${data.group_id}`)
494
+ return data.bot.sendApi("delete_group_file", {
495
+ group_id: data.group_id,
496
+ file_id,
497
+ busid,
498
+ })
499
+ }
500
+
501
+ createGroupFileFolder(data, name) {
502
+ Bot.makeLog("info", `创建群文件夹:${name}`, `${data.self_id} => ${data.group_id}`)
503
+ return data.bot.sendApi("create_group_file_folder", {
504
+ group_id: data.group_id,
505
+ name,
506
+ })
507
+ }
508
+
509
+ getGroupFileSystemInfo(data) {
510
+ return data.bot.sendApi("get_group_file_system_info", {
511
+ group_id: data.group_id,
512
+ })
513
+ }
514
+
515
+ getGroupFiles(data, folder_id) {
516
+ if (folder_id)
517
+ return data.bot.sendApi("get_group_files_by_folder", {
518
+ group_id: data.group_id,
519
+ folder_id,
520
+ })
521
+ return data.bot.sendApi("get_group_root_files", {
522
+ group_id: data.group_id,
523
+ })
524
+ }
525
+
526
+ getGroupFileUrl(data, file_id, busid) {
527
+ return data.bot.sendApi("get_group_file_url", {
528
+ group_id: data.group_id,
529
+ file_id,
530
+ busid,
531
+ })
532
+ }
533
+
534
+ getGroupFs(data) {
535
+ return {
536
+ upload: (file, folder, name) => this.sendGroupFile(data, file, folder, name),
537
+ rm: (file_id, busid) => this.deleteGroupFile(data, file_id, busid),
538
+ mkdir: name => this.createGroupFileFolder(data, name),
539
+ df: () => this.getGroupFileSystemInfo(data),
540
+ ls: folder_id => this.getGroupFiles(data, folder_id),
541
+ download: (file_id, busid) => this.getGroupFileUrl(data, file_id, busid),
542
+ }
543
+ }
544
+
545
+ setFriendAddRequest(data, flag, approve, remark) {
546
+ return data.bot.sendApi("set_friend_add_request", {
547
+ flag,
548
+ approve,
549
+ remark,
550
+ })
551
+ }
552
+
553
+ setGroupAddRequest(data, flag, sub_type, approve, reason) {
554
+ return data.bot.sendApi("set_group_add_request", {
555
+ flag,
556
+ sub_type,
557
+ approve,
558
+ reason,
559
+ })
560
+ }
561
+
562
+ pickFriend(data, user_id) {
563
+ const i = {
564
+ ...data.bot.fl.get(user_id),
565
+ ...data,
566
+ user_id,
567
+ }
568
+ return {
569
+ ...i,
570
+ sendMsg: msg => this.sendFriendMsg(i, msg),
571
+ getMsg: message_id => this.getMsg(i, message_id),
572
+ recallMsg: message_id => this.recallMsg(i, message_id),
573
+ getForwardMsg: message_id => this.getForwardMsg(i, message_id),
574
+ sendForwardMsg: msg => this.sendFriendForwardMsg(i, msg),
575
+ sendFile: (file, name) => this.sendFriendFile(i, file, name),
576
+ getInfo: () => this.getFriendInfo(i),
577
+ getAvatarUrl: () => `https://q1.qlogo.cn/g?b=qq&s=0&nk=${user_id}`,
578
+ thumbUp: times => this.sendLike(i, times),
579
+ }
580
+ }
581
+
582
+ pickMember(data, group_id, user_id) {
583
+ if (typeof group_id == "string" && group_id.match("-")) {
584
+ const guild_id = group_id.split("-")
585
+ const i = {
586
+ ...data,
587
+ guild_id: guild_id[0],
588
+ channel_id: guild_id[1],
589
+ user_id,
590
+ }
591
+ return {
592
+ ...this.pickGroup(i, group_id),
593
+ ...i,
594
+ getInfo: () => this.getGuildMemberInfo(i),
595
+ getAvatarUrl: async () => (await this.getGuildMemberInfo(i)).avatar_url,
596
+ }
597
+ }
598
+
599
+ const i = {
600
+ ...data.bot.fl.get(user_id),
601
+ ...data.bot.gml.get(group_id)?.get(user_id),
602
+ ...data,
603
+ group_id,
604
+ user_id,
605
+ }
606
+ return {
607
+ ...this.pickFriend(i, user_id),
608
+ ...i,
609
+ getInfo: () => this.getMemberInfo(i),
610
+ poke: () => this.sendGroupMsg(i, { type: "poke", qq: user_id }),
611
+ mute: duration => this.setGroupBan(i, i.user_id, duration),
612
+ kick: reject_add_request => this.setGroupKick(i, i.user_id, reject_add_request),
613
+ get is_friend() { return data.bot.fl.has(user_id) },
614
+ get is_owner() { return i.role == "owner" },
615
+ get is_admin() { return i.role == "admin" },
616
+ }
617
+ }
618
+
619
+ pickGroup(data, group_id) {
620
+ if (typeof group_id == "string" && group_id.match("-")) {
621
+ const guild_id = group_id.split("-")
622
+ const i = {
623
+ ...data.bot.gl.get(group_id),
624
+ ...data,
625
+ guild_id: guild_id[0],
626
+ channel_id: guild_id[1],
627
+ }
628
+ return {
629
+ ...i,
630
+ sendMsg: msg => this.sendGuildMsg(i, msg),
631
+ getMsg: message_id => this.getMsg(i, message_id),
632
+ recallMsg: message_id => this.recallMsg(i, message_id),
633
+ getForwardMsg: message_id => this.getForwardMsg(i, message_id),
634
+ getInfo: () => this.getGuildInfo(i),
635
+ getChannelArray: () => this.getGuildChannelArray(i),
636
+ getChannelList: () => this.getGuildChannelList(i),
637
+ getChannelMap: () => this.getGuildChannelMap(i),
638
+ getMemberArray: () => this.getGuildMemberArray(i),
639
+ getMemberList: () => this.getGuildMemberList(i),
640
+ getMemberMap: () => this.getGuildMemberMap(i),
641
+ pickMember: user_id => this.pickMember(i, group_id, user_id),
642
+ }
643
+ }
644
+
645
+ const i = {
646
+ ...data.bot.gl.get(group_id),
647
+ ...data,
648
+ group_id,
649
+ }
650
+ return {
651
+ ...i,
652
+ sendMsg: msg => this.sendGroupMsg(i, msg),
653
+ getMsg: message_id => this.getMsg(i, message_id),
654
+ recallMsg: message_id => this.recallMsg(i, message_id),
655
+ getForwardMsg: message_id => this.getForwardMsg(i, message_id),
656
+ sendForwardMsg: msg => this.sendGroupForwardMsg(i, msg),
657
+ sendFile: (file, name) => this.sendGroupFile(i, file, undefined, name),
658
+ getInfo: () => this.getGroupInfo(i),
659
+ getAvatarUrl: () => `https://p.qlogo.cn/gh/${group_id}/${group_id}/0`,
660
+ getChatHistory: (seq, cnt) => this.getGroupMsgHistory(i, seq, cnt),
661
+ getMemberArray: () => this.getMemberArray(i),
662
+ getMemberList: () => this.getMemberList(i),
663
+ getMemberMap: () => this.getMemberMap(i),
664
+ pickMember: user_id => this.pickMember(i, group_id, user_id),
665
+ pokeMember: qq => this.sendGroupMsg(i, { type: "poke", qq }),
666
+ setName: group_name => this.setGroupName(i, group_name),
667
+ setAvatar: file => this.setGroupAvatar(i, file),
668
+ setAdmin: (user_id, enable) => this.setGroupAdmin(i, user_id, enable),
669
+ setCard: (user_id, card) => this.setGroupCard(i, user_id, card),
670
+ setTitle: (user_id, special_title, duration) => this.setGroupTitle(i, user_id, special_title, duration),
671
+ sign: () => this.sendGroupSign(i),
672
+ muteMember: (user_id, duration) => this.setGroupBan(i, user_id, duration),
673
+ muteAll: enable => this.setGroupWholeKick(i, enable),
674
+ kickMember: (user_id, reject_add_request) => this.setGroupKick(i, user_id, reject_add_request),
675
+ quit: is_dismiss => this.setGroupLeave(i, is_dismiss),
676
+ fs: this.getGroupFs(i),
677
+ get is_owner() { return data.bot.gml.get(group_id)?.get(data.self_id)?.role == "owner" },
678
+ get is_admin() { return data.bot.gml.get(group_id)?.get(data.self_id)?.role == "admin" },
679
+ }
680
+ }
681
+
682
+ async connect(data, ws) {
683
+ Bot[data.self_id] = {
684
+ adapter: this,
685
+ ws: ws,
686
+ sendApi: (action, params) => this.sendApi(ws, action, params),
687
+ stat: {
688
+ start_time: data.time,
689
+ stat: {},
690
+ get lost_pkt_cnt() { return this.stat.packet_lost },
691
+ get lost_times() { return this.stat.lost_times },
692
+ get recv_msg_cnt() { return this.stat.message_received },
693
+ get recv_pkt_cnt() { return this.stat.packet_received },
694
+ get sent_msg_cnt() { return this.stat.message_sent },
695
+ get sent_pkt_cnt() { return this.stat.packet_sent },
696
+ },
697
+ model: "TRSS Yunzai ",
698
+
699
+ info: {},
700
+ get uin() { return this.info.user_id },
701
+ get nickname() { return this.info.nickname },
702
+ get avatar() { return `https://q1.qlogo.cn/g?b=qq&s=0&nk=${this.uin}` },
703
+
704
+ setProfile: profile => this.setProfile(data, profile),
705
+ setNickname: nickname => this.setProfile(data, { nickname }),
706
+
707
+ pickFriend: user_id => this.pickFriend(data, user_id),
708
+ get pickUser() { return this.pickFriend },
709
+ getFriendArray: () => this.getFriendArray(data),
710
+ getFriendList: () => this.getFriendList(data),
711
+ getFriendMap: () => this.getFriendMap(data),
712
+ fl: new Map,
713
+
714
+ pickMember: (group_id, user_id) => this.pickMember(data, group_id, user_id),
715
+ pickGroup: group_id => this.pickGroup(data, group_id),
716
+ getGroupArray: () => this.getGroupArray(data),
717
+ getGroupList: () => this.getGroupList(data),
718
+ getGroupMap: () => this.getGroupMap(data),
719
+ getGroupMemberMap: () => this.getGroupMemberMap(data),
720
+ gl: new Map,
721
+ gml: new Map,
722
+
723
+ request_list: [],
724
+ getSystemMsg: () => data.bot.request_list,
725
+ setFriendAddRequest: (flag, approve, remark) => this.setFriendAddRequest(data, flag, approve, remark),
726
+ setGroupAddRequest: (flag, sub_type, approve, reason) => this.setGroupAddRequest(data, flag, sub_type, approve, reason),
727
+ }
728
+ data.bot = Bot[data.self_id]
729
+
730
+ if (!Bot.uin.includes(data.self_id))
731
+ Bot.uin.push(data.self_id)
732
+
733
+ data.bot.sendApi("_set_model_show", {
734
+ model: data.bot.model,
735
+ model_show: data.bot.model,
736
+ })
737
+
738
+ data.bot.info = (await data.bot.sendApi("get_login_info")).data
739
+ data.bot.guild_info = (await data.bot.sendApi("get_guild_service_profile")).data
740
+ data.bot.clients = (await data.bot.sendApi("get_online_clients")).clients
741
+ data.bot.version = {
742
+ ...(await data.bot.sendApi("get_version_info")).data,
743
+ id: this.id,
744
+ name: this.name,
745
+ get version() {
746
+ return this.app_full_name || `${this.app_name} v${this.app_version}`
747
+ },
748
+ }
749
+
750
+ data.bot.getFriendMap()
751
+ data.bot.getGroupMemberMap()
752
+
753
+ Bot.makeLog("mark", `${this.name}(${this.id}) ${data.bot.version.version} 已连接`, data.self_id)
754
+ Bot.em(`connect.${data.self_id}`, data)
755
+ }
756
+
757
+ makeMessage(data) {
758
+ data.message = this.parseMsg(data.message)
759
+ switch (data.message_type) {
760
+ case "private": {
761
+ const name = data.sender.card || data.sender.nickname || data.bot.fl.get(data.user_id)?.nickname
762
+ Bot.makeLog("info", `好友消息:${name ? `[${name}] ` : ""}${data.raw_message}`, `${data.self_id} <= ${data.user_id}`)
763
+ break
764
+ } case "group": {
765
+ const group_name = data.group_name || data.bot.gl.get(data.group_id)?.group_name
766
+ let user_name = data.sender.card || data.sender.nickname
767
+ if (!user_name) {
768
+ const user = data.bot.gml.get(data.group_id)?.get(data.user_id) || data.bot.fl.get(data.user_id)
769
+ if (user) user_name = user?.card || user?.nickname
770
+ }
771
+ Bot.makeLog("info", `群消息:${user_name ? `[${group_name ? `${group_name}, ` : ""}${user_name}] ` : ""}${data.raw_message}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
772
+ break
773
+ } case "guild":
774
+ data.message_type = "group"
775
+ data.group_id = `${data.guild_id}-${data.channel_id}`
776
+ Bot.makeLog("info", `频道消息:[${data.sender.nickname}] ${JSON.stringify(data.message)}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
777
+ Object.defineProperty(data, "friend", { get() { return this.member || {}}})
778
+ break
779
+ default:
780
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
781
+ }
782
+
783
+ Bot.em(`${data.post_type}.${data.message_type}.${data.sub_type}`, data)
784
+ }
785
+
786
+ async makeNotice(data) {
787
+ switch (data.notice_type) {
788
+ case "friend_recall":
789
+ Bot.makeLog("info", `好友消息撤回:${data.message_id}`, `${data.self_id} <= ${data.user_id}`)
790
+ break
791
+ case "group_recall":
792
+ Bot.makeLog("info", `群消息撤回:${data.operator_id} => ${data.user_id} ${data.message_id}`, `${data.self_id} <= ${data.group_id}`)
793
+ break
794
+ case "group_increase":
795
+ Bot.makeLog("info", `群成员增加:${data.operator_id} => ${data.user_id} ${data.sub_type}`, `${data.self_id} <= ${data.group_id}`)
796
+ if (data.user_id == data.self_id)
797
+ data.bot.getGroupMemberMap()
798
+ else
799
+ data.bot.pickGroup(data.group_id).getMemberMap()
800
+ break
801
+ case "group_decrease":
802
+ Bot.makeLog("info", `群成员减少:${data.operator_id} => ${data.user_id} ${data.sub_type}`, `${data.self_id} <= ${data.group_id}`)
803
+ if (data.user_id == data.self_id)
804
+ data.bot.getGroupMemberMap()
805
+ else
806
+ data.bot.pickGroup(data.group_id).getMemberMap()
807
+ break
808
+ case "group_admin":
809
+ Bot.makeLog("info", `群管理员变动:${data.sub_type}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
810
+ data.set = data.sub_type == "set"
811
+ break
812
+ case "group_upload":
813
+ Bot.makeLog("info", `群文件上传:${JSON.stringify(data.file)}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
814
+ break
815
+ case "group_ban":
816
+ Bot.makeLog("info", `群禁言:${data.operator_id} => ${data.user_id} ${data.sub_type} ${data.duration}秒`, `${data.self_id} <= ${data.group_id}`)
817
+ break
818
+ case "friend_add":
819
+ Bot.makeLog("info", "好友添加", `${data.self_id} <= ${data.user_id}`)
820
+ data.bot.getFriendMap()
821
+ break
822
+ case "notify":
823
+ if (data.group_id)
824
+ data.notice_type = "group"
825
+ else
826
+ data.notice_type = "friend"
827
+ switch (data.sub_type) {
828
+ case "poke":
829
+ data.operator_id = data.user_id
830
+ if (data.group_id)
831
+ Bot.makeLog("info", `群戳一戳:${data.operator_id} => ${data.target_id}`, `${data.self_id} <= ${data.group_id}`)
832
+ else
833
+ Bot.makeLog("info", `好友戳一戳:${data.operator_id} => ${data.target_id}`, data.self_id)
834
+ break
835
+ case "honor":
836
+ Bot.makeLog("info", `群荣誉:${data.honor_type}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
837
+ break
838
+ case "title":
839
+ Bot.makeLog("info", `群头衔:${data.title}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
840
+ break
841
+ default:
842
+ Bot.makeLog("warn", `未知通知:${logger.magenta(data.raw)}`, data.self_id)
843
+ }
844
+ break
845
+ case "group_card":
846
+ Bot.makeLog("info", `群名片更新:${data.card_old} => ${data.card_new}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
847
+ break
848
+ case "offline_file":
849
+ Bot.makeLog("info", `离线文件:${JSON.stringify(data.file)}`, `${data.self_id} <= ${data.user_id}`)
850
+ break
851
+ case "client_status":
852
+ Bot.makeLog("info", `客户端${data.online ? "上线" : "下线"}:${JSON.stringify(data.client)}`, data.self_id)
853
+ data.clients = (await data.bot.sendApi("get_online_clients")).clients
854
+ data.bot.clients = data.clients
855
+ break
856
+ case "essence":
857
+ data.notice_type = "group_essence"
858
+ Bot.makeLog("info", `群精华消息:${data.operator_id} => ${data.sender_id} ${data.sub_type} ${data.message_id}`, `${data.self_id} <= ${data.group_id}`)
859
+ break
860
+ case "guild_channel_recall":
861
+ Bot.makeLog("info", `频道消息撤回:${data.operator_id} => ${data.user_id} ${data.message_id}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}`)
862
+ break
863
+ case "message_reactions_updated":
864
+ data.notice_type = "guild_message_reactions_updated"
865
+ Bot.makeLog("info", `频道消息表情贴:${data.message_id} ${JSON.stringify(data.current_reactions)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
866
+ break
867
+ case "channel_updated":
868
+ data.notice_type = "guild_channel_updated"
869
+ Bot.makeLog("info", `子频道更新:${JSON.stringify(data.old_info)} => ${JSON.stringify(data.new_info)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
870
+ break
871
+ case "channel_created":
872
+ data.notice_type = "guild_channel_created"
873
+ Bot.makeLog("info", `子频道创建:${JSON.stringify(data.channel_info)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
874
+ data.bot.getGroupMap()
875
+ break
876
+ case "channel_destroyed":
877
+ data.notice_type = "guild_channel_destroyed"
878
+ Bot.makeLog("info", `子频道删除:${JSON.stringify(data.channel_info)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
879
+ data.bot.getGroupMap()
880
+ break
881
+ default:
882
+ Bot.makeLog("warn", `未知通知:${logger.magenta(data.raw)}`, data.self_id)
883
+ }
884
+
885
+ let notice = data.notice_type.split("_")
886
+ data.notice_type = notice.shift()
887
+ notice = notice.join("_")
888
+ if (notice)
889
+ data.sub_type = notice
890
+
891
+ if (data.guild_id && data.channel_id) {
892
+ data.group_id = `${data.guild_id}-${data.channel_id}`
893
+ Object.defineProperty(data, "friend", { get() { return this.member || {}}})
894
+ }
895
+
896
+ Bot.em(`${data.post_type}.${data.notice_type}.${data.sub_type}`, data)
897
+ }
898
+
899
+ makeRequest(data) {
900
+ switch (data.request_type) {
901
+ case "friend":
902
+ Bot.makeLog("info", `加好友请求:${data.comment}(${data.flag})`, `${data.self_id} <= ${data.user_id}`)
903
+ data.sub_type = "add"
904
+ data.approve = approve => data.bot.setFriendAddRequest(data.flag, approve)
905
+ break
906
+ case "group":
907
+ Bot.makeLog("info", `加群请求:${data.sub_type} ${data.comment}(${data.flag})`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
908
+ data.approve = approve => data.bot.setGroupAddRequest(data.flag, data.sub_type, approve)
909
+ break
910
+ default:
911
+ Bot.makeLog("warn", `未知请求:${logger.magenta(data.raw)}`, data.self_id)
912
+ }
913
+
914
+ data.bot.request_list.push(data)
915
+ Bot.em(`${data.post_type}.${data.request_type}.${data.sub_type}`, data)
916
+ }
917
+
918
+ heartbeat(data) {
919
+ if (data.status)
920
+ Object.assign(data.bot.stat, data.status)
921
+ }
922
+
923
+ makeMeta(data, ws) {
924
+ switch (data.meta_event_type) {
925
+ case "heartbeat":
926
+ this.heartbeat(data)
927
+ break
928
+ case "lifecycle":
929
+ this.connect(data, ws)
930
+ break
931
+ default:
932
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
933
+ }
934
+ }
935
+
936
+ message(data, ws) {
937
+ try {
938
+ data = {
939
+ ...JSON.parse(data),
940
+ raw: Bot.String(data),
941
+ }
942
+ } catch (err) {
943
+ return Bot.makeLog("error", ["解码数据失败", data, err])
944
+ }
945
+
946
+ if (data.post_type) {
947
+ if (data.meta_event_type != "lifecycle" && !Bot.uin.includes(data.self_id)) {
948
+ Bot.makeLog("warn", `找不到对应Bot,忽略消息:${logger.magenta(data.raw)}`, data.self_id)
949
+ return false
950
+ }
951
+ data.bot = Bot[data.self_id]
952
+
953
+ switch (data.post_type) {
954
+ case "meta_event":
955
+ this.makeMeta(data, ws)
956
+ break
957
+ case "message":
958
+ this.makeMessage(data)
959
+ break
960
+ case "notice":
961
+ this.makeNotice(data)
962
+ break
963
+ case "request":
964
+ this.makeRequest(data)
965
+ break
966
+ case "message_sent":
967
+ data.post_type = "message"
968
+ this.makeMessage(data)
969
+ break
970
+ default:
971
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
972
+ }
973
+ } else if (data.echo) {
974
+ Bot.emit(data.echo, data)
975
+ } else {
976
+ Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
977
+ }
978
+ }
979
+
980
+ load() {
981
+ for (const i of [this.path, "go-cqhttp"]) {
982
+ if (!Array.isArray(Bot.wsf[i]))
983
+ Bot.wsf[i] = []
984
+ Bot.wsf[i].push((ws, ...args) =>
985
+ ws.on("message", data => this.message(data, ws, ...args))
986
+ )
987
+ }
988
+ }
989
+ })
Yunzai/plugins/example/主动复读.js CHANGED
@@ -1,37 +1,36 @@
1
- import plugin from '../../lib/plugins/plugin.js'
2
-
3
  export class example2 extends plugin {
4
- constructor () {
5
  super({
6
- name: '复读',
7
- dsc: '复读用户发送的内容,然后撤回',
8
  /** https://oicqjs.github.io/oicq/#events */
9
- event: 'message',
10
  priority: 5000,
11
  rule: [
12
  {
13
  /** 命令正则匹配 */
14
- reg: '^#复读$',
15
  /** 执行方法 */
16
- fnc: 'repeat'
 
17
  }
18
  ]
19
  })
20
  }
21
 
22
  /** 复读 */
23
- async repeat () {
24
  /** 设置上下文,后续接收到内容会执行doRep方法 */
25
- this.setContext('doRep')
26
  /** 回复 */
27
- await this.reply('请发送要复读的内容', false, { at: true })
28
  }
29
 
30
  /** 接受内容 */
31
- doRep () {
32
  /** 复读内容 */
33
  this.reply(this.e.message, false, { recallMsg: 5 })
34
  /** 结束上下文 */
35
- this.finish('doRep')
36
  }
37
- }
 
 
 
1
  export class example2 extends plugin {
2
+ constructor() {
3
  super({
4
+ name: "复读",
5
+ dsc: "复读用户发送的内容,然后撤回",
6
  /** https://oicqjs.github.io/oicq/#events */
7
+ event: "message",
8
  priority: 5000,
9
  rule: [
10
  {
11
  /** 命令正则匹配 */
12
+ reg: "^#复读$",
13
  /** 执行方法 */
14
+ fnc: "repeat",
15
+ permission: "master",
16
  }
17
  ]
18
  })
19
  }
20
 
21
  /** 复读 */
22
+ async repeat() {
23
  /** 设置上下文,后续接收到内容会执行doRep方法 */
24
+ this.setContext("doRep")
25
  /** 回复 */
26
+ await this.reply("请发送要复读的内容", false, { at: true })
27
  }
28
 
29
  /** 接受内容 */
30
+ doRep() {
31
  /** 复读内容 */
32
  this.reply(this.e.message, false, { recallMsg: 5 })
33
  /** 结束上下文 */
34
+ this.finish("doRep")
35
  }
36
+ }
Yunzai/plugins/example/进群退群通知.js CHANGED
@@ -1,12 +1,9 @@
1
- import plugin from '../../lib/plugins/plugin.js'
2
  export class newcomer extends plugin {
3
  constructor() {
4
  super({
5
- name: '欢迎新人',
6
- dsc: '新人入群欢迎',
7
- /** https://oicqjs.github.io/oicq/#events */
8
- event: 'notice.group.increase',
9
- priority: 5000
10
  })
11
  }
12
 
@@ -15,14 +12,14 @@ export class newcomer extends plugin {
15
  if (this.e.user_id == this.e.self_id) return
16
 
17
  /** 定义入群欢迎内容 */
18
- let msg = '欢迎新人!'
19
  /** 冷却cd 30s */
20
  let cd = 30
21
 
22
  /** cd */
23
  let key = `Yz:newcomers:${this.e.group_id}`
24
  if (await redis.get(key)) return
25
- redis.set(key, '1', { EX: cd })
26
 
27
  /** 回复 */
28
  await this.reply([
@@ -36,13 +33,13 @@ export class newcomer extends plugin {
36
  export class outNotice extends plugin {
37
  constructor() {
38
  super({
39
- name: '退群通知',
40
- dsc: 'xx退群了',
41
- event: 'notice.group.decrease'
42
  })
43
 
44
  /** 退群提示词 */
45
- this.tips = '退群了'
46
  }
47
 
48
  async accept() {
 
 
1
  export class newcomer extends plugin {
2
  constructor() {
3
  super({
4
+ name: "欢迎新人",
5
+ dsc: "新人入群欢迎",
6
+ event: "notice.group.increase",
 
 
7
  })
8
  }
9
 
 
12
  if (this.e.user_id == this.e.self_id) return
13
 
14
  /** 定义入群欢迎内容 */
15
+ let msg = "欢迎新人!"
16
  /** 冷却cd 30s */
17
  let cd = 30
18
 
19
  /** cd */
20
  let key = `Yz:newcomers:${this.e.group_id}`
21
  if (await redis.get(key)) return
22
+ redis.set(key, "1", { EX: cd })
23
 
24
  /** 回复 */
25
  await this.reply([
 
33
  export class outNotice extends plugin {
34
  constructor() {
35
  super({
36
+ name: "退群通知",
37
+ dsc: "xx退群了",
38
+ event: "notice.group.decrease"
39
  })
40
 
41
  /** 退群提示词 */
42
+ this.tips = "退群了"
43
  }
44
 
45
  async accept() {
Yunzai/plugins/other/install.js CHANGED
@@ -1,13 +1,12 @@
1
- import { exec, execSync } from "child_process"
2
- import plugin from "../../lib/plugins/plugin.js"
3
- import fs from "node:fs"
4
  import { Restart } from "./restart.js"
5
 
6
  let insing = false
7
  const list = {
8
  "Atlas":"https://gitee.com/Nwflower/atlas",
 
9
  "ws-plugin":"https://gitee.com/xiaoye12123/ws-plugin",
10
  "TRSS-Plugin" :"https://Yunzai.TRSS.me",
 
11
  "yenai-plugin" :"https://gitee.com/yeyang52/yenai-plugin",
12
  "flower-plugin" :"https://gitee.com/Nwflower/flower-plugin",
13
  "xianyu-plugin" :"https://gitee.com/suancaixianyu/xianyu-plugin",
@@ -15,14 +14,13 @@ const list = {
15
  "useless-plugin":"https://gitee.com/SmallK111407/useless-plugin",
16
  "StarRail-plugin" :"https://gitee.com/hewang1an/StarRail-plugin",
17
  "xiaoyao-cvs-plugin":"https://gitee.com/Ctrlcvs/xiaoyao-cvs-plugin",
18
- "Jinmaocuicuisha-plugin":"https://gitee.com/JMCCS/jinmaocuicuisha",
19
  "trss-xianxin-plugin" :"https://gitee.com/snowtafir/xianxin-plugin",
20
- "mysVilla-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-mysVilla-Plugin",
21
  "Telegram-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-Telegram-Plugin",
22
  "Discord-Plugin":"https://gitee.com/TimeRainStarSky/Yunzai-Discord-Plugin",
23
- "QQGuild-Plugin":"https://gitee.com/TimeRainStarSky/Yunzai-QQGuild-Plugin",
24
  "WeChat-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-WeChat-Plugin",
25
- "Proxy-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-Proxy-Plugin",
 
26
  "ICQQ-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin",
27
  "KOOK-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-KOOK-Plugin",
28
  }
@@ -53,7 +51,7 @@ export class install extends plugin {
53
  if (name == "插件") {
54
  let msg = "\n"
55
  for (const name in list)
56
- if (!fs.existsSync(`plugins/${name}`))
57
  msg += `${name}\n`
58
 
59
  if (msg == "\n")
@@ -66,7 +64,7 @@ export class install extends plugin {
66
  }
67
 
68
  const path = `plugins/${name}`
69
- if (fs.existsSync(path)) {
70
  await this.reply(`${name} 插件已安装`)
71
  return false
72
  }
@@ -74,23 +72,15 @@ export class install extends plugin {
74
  this.restart()
75
  }
76
 
77
- async execSync(cmd) {
78
- return new Promise(resolve => {
79
- exec(cmd, (error, stdout, stderr) => {
80
- resolve({ error, stdout, stderr })
81
- })
82
- })
83
- }
84
-
85
  async runInstall(name, url, path) {
86
  logger.mark(`${this.e.logFnc} 开始安装:${name} 插件`)
87
  await this.reply(`开始安装 ${name} 插件`)
88
 
89
  const cm = `git clone --depth 1 --single-branch "${url}" "${path}"`
90
  insing = true
91
- const ret = await this.execSync(cm)
92
- if (fs.existsSync(`${path}/package.json`))
93
- await this.execSync("pnpm install")
94
  insing = false
95
 
96
  if (ret.error) {
 
 
 
 
1
  import { Restart } from "./restart.js"
2
 
3
  let insing = false
4
  const list = {
5
  "Atlas":"https://gitee.com/Nwflower/atlas",
6
+ "genshin" :"https://gitee.com/TimeRainStarSky/Yunzai-genshin",
7
  "ws-plugin":"https://gitee.com/xiaoye12123/ws-plugin",
8
  "TRSS-Plugin" :"https://Yunzai.TRSS.me",
9
+ "miao-plugin" :"https://gitee.com/yoimiya-kokomi/miao-plugin",
10
  "yenai-plugin" :"https://gitee.com/yeyang52/yenai-plugin",
11
  "flower-plugin" :"https://gitee.com/Nwflower/flower-plugin",
12
  "xianyu-plugin" :"https://gitee.com/suancaixianyu/xianyu-plugin",
 
14
  "useless-plugin":"https://gitee.com/SmallK111407/useless-plugin",
15
  "StarRail-plugin" :"https://gitee.com/hewang1an/StarRail-plugin",
16
  "xiaoyao-cvs-plugin":"https://gitee.com/Ctrlcvs/xiaoyao-cvs-plugin",
 
17
  "trss-xianxin-plugin" :"https://gitee.com/snowtafir/xianxin-plugin",
18
+ "Lagrange-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-Lagrange-Plugin",
19
  "Telegram-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-Telegram-Plugin",
20
  "Discord-Plugin":"https://gitee.com/TimeRainStarSky/Yunzai-Discord-Plugin",
 
21
  "WeChat-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-WeChat-Plugin",
22
+ "QQBot-Plugin":"https://gitee.com/TimeRainStarSky/Yunzai-QQBot-Plugin",
23
+ "Route-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-Route-Plugin",
24
  "ICQQ-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin",
25
  "KOOK-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-KOOK-Plugin",
26
  }
 
51
  if (name == "插件") {
52
  let msg = "\n"
53
  for (const name in list)
54
+ if (!await Bot.fsStat(`plugins/${name}`))
55
  msg += `${name}\n`
56
 
57
  if (msg == "\n")
 
64
  }
65
 
66
  const path = `plugins/${name}`
67
+ if (await Bot.fsStat(path)) {
68
  await this.reply(`${name} 插件已安装`)
69
  return false
70
  }
 
72
  this.restart()
73
  }
74
 
 
 
 
 
 
 
 
 
75
  async runInstall(name, url, path) {
76
  logger.mark(`${this.e.logFnc} 开始安装:${name} 插件`)
77
  await this.reply(`开始安装 ${name} 插件`)
78
 
79
  const cm = `git clone --depth 1 --single-branch "${url}" "${path}"`
80
  insing = true
81
+ const ret = await Bot.exec(cm)
82
+ if (await Bot.fsStat(`${path}/package.json`))
83
+ await Bot.exec("pnpm install")
84
  insing = false
85
 
86
  if (ret.error) {
Yunzai/plugins/other/restart.js CHANGED
@@ -1,122 +1,101 @@
1
- import plugin from '../../lib/plugins/plugin.js'
2
- import { createRequire } from 'module'
3
-
4
- const require = createRequire(import.meta.url)
5
- const { exec } = require('child_process')
6
 
7
  export class Restart extends plugin {
8
- constructor (e = '') {
9
  super({
10
- name: '重启',
11
- dsc: '#重启',
12
- event: 'message',
13
  priority: 10,
14
- rule: [{
15
- reg: '^#重启$',
16
- fnc: 'restart',
17
- permission: 'master'
18
- }, {
19
- reg: '^#(停机|关机)$',
20
- fnc: 'stop',
21
- permission: 'master'
22
- }]
 
 
 
23
  })
24
 
25
  if (e) this.e = e
 
 
26
 
27
- this.key = 'Yz:restart'
 
 
 
 
 
 
 
 
28
  }
29
 
30
- async init () {
31
  let restart = await redis.get(this.key)
32
- if (restart) {
33
- restart = JSON.parse(restart)
34
- let time = restart.time || new Date().getTime()
35
- time = (new Date().getTime() - time) / 1000
36
-
37
- let msg = `重启成功:耗时${time.toFixed(2)}秒`
38
-
 
 
 
 
 
39
  if (restart.isGroup)
40
  Bot.sendGroupMsg(restart.bot_id, restart.id, msg)
41
  else
42
  Bot.sendFriendMsg(restart.bot_id, restart.id, msg)
43
-
44
- redis.del(this.key)
45
  }
 
46
  }
47
 
48
- async restart () {
49
- await this.e.reply('开始执行重启,请稍等...')
50
- logger.mark(`${this.e.logFnc} 开始执行重启,请稍等...`)
51
-
52
- let data = JSON.stringify({
53
  isGroup: !!this.e.isGroup,
54
  id: this.e.isGroup ? this.e.group_id : this.e.user_id,
55
  bot_id: this.e.self_id,
56
- time: new Date().getTime()
57
- })
58
-
59
- let npm = await this.checkPnpm()
60
-
61
- try {
62
- await redis.set(this.key, data, { EX: 120 })
63
- let cm = `${npm} start`
64
- if (process.argv[1].includes('pm2')) {
65
- cm = `${npm} run restart`
66
- }
67
-
68
- exec(cm, { windowsHide: true }, (error, stdout, stderr) => {
69
- if (error) {
70
- redis.del(this.key)
71
- this.e.reply(`操作失败!\n${error.stack}`)
72
- logger.error(`重启失败\n${error.stack}`)
73
- } else if (stdout) {
74
- logger.mark('重启成功,运行已由前台转为后台')
75
- logger.mark(`查看日志请用命令:${npm} run log`)
76
- logger.mark(`停止后台运行命令:${npm} stop`)
77
- process.exit()
78
- }
79
- })
80
- } catch (error) {
81
- redis.del(this.key)
82
- let e = error.stack ?? error
83
- this.e.reply(`操作失败!\n${e}`)
84
  }
85
-
86
- return true
87
- }
88
-
89
- async checkPnpm () {
90
- let npm = 'npm'
91
- let ret = await this.execSync('pnpm -v')
92
- if (ret.stdout) npm = 'pnpm'
93
- return npm
94
- }
95
-
96
- async execSync (cmd) {
97
- return new Promise((resolve, reject) => {
98
- exec(cmd, { windowsHide: true }, (error, stdout, stderr) => {
99
- resolve({ error, stdout, stderr })
100
- })
101
- })
102
  }
103
 
104
- async stop () {
105
- if (!process.argv[1].includes('pm2')) {
106
- logger.mark('关机成功,已停止运行')
107
- await this.e.reply('关机成功,已停止运行')
108
- process.exit()
 
 
 
 
 
 
 
 
 
 
109
  }
110
-
111
- logger.mark('关机成功,已停止运行')
112
- await this.e.reply('关机成功,已停止运行')
113
-
114
- let npm = await this.checkPnpm()
115
- exec(`${npm} stop`, { windowsHide: true }, (error, stdout, stderr) => {
116
- if (error) {
117
- this.e.reply(`操作失败!\n${error.stack}`)
118
- logger.error(`关机失败\n${error.stack}`)
119
- }
120
- })
121
  }
122
  }
 
1
+ import cfg from "../../lib/config/config.js"
2
+ import { spawn } from "child_process"
 
 
 
3
 
4
  export class Restart extends plugin {
5
+ constructor (e = "") {
6
  super({
7
+ name: "重启",
8
+ dsc: "#重启",
9
+ event: "message",
10
  priority: 10,
11
+ rule: [
12
+ {
13
+ reg: "^#重启$",
14
+ fnc: "restart",
15
+ permission: "master"
16
+ },
17
+ {
18
+ reg: "^#(停机|关机)$",
19
+ fnc: "stop",
20
+ permission: "master"
21
+ }
22
+ ]
23
  })
24
 
25
  if (e) this.e = e
26
+ this.key = "Yz:restart"
27
+ }
28
 
29
+ init() {
30
+ Bot.once("online", () => this.restartMsg())
31
+ if (cfg.bot.restart_time) {
32
+ this.e = {
33
+ logFnc: "[自动重启]" ,
34
+ reply: msg => Bot.sendMasterMsg(msg),
35
+ }
36
+ setTimeout(() => this.restart(), cfg.bot.restart_time*60000)
37
+ }
38
  }
39
 
40
+ async restartMsg() {
41
  let restart = await redis.get(this.key)
42
+ if (!restart) return
43
+ restart = JSON.parse(restart)
44
+ const time = (Date.now() - (restart.time || Date.now()))/1000
45
+ const msg = []
46
+ if (restart.msg_id)
47
+ msg.push(segment.reply(restart.msg_id))
48
+ if (restart.isStop)
49
+ msg.push(`开机成功,距离上次关机${time}秒`)
50
+ else
51
+ msg.push(`重启成功,用时${time}秒`)
52
+
53
+ if (restart.id) {
54
  if (restart.isGroup)
55
  Bot.sendGroupMsg(restart.bot_id, restart.id, msg)
56
  else
57
  Bot.sendFriendMsg(restart.bot_id, restart.id, msg)
58
+ } else {
59
+ Bot.sendMasterMsg(msg)
60
  }
61
+ redis.del(this.key)
62
  }
63
 
64
+ async restart() {
65
+ await this.e.reply(`开始重启,本次运行时长:${Bot.getTimeDiff()}`)
66
+ await redis.set(this.key, JSON.stringify({
 
 
67
  isGroup: !!this.e.isGroup,
68
  id: this.e.isGroup ? this.e.group_id : this.e.user_id,
69
  bot_id: this.e.self_id,
70
+ msg_id: this.e.message_id,
71
+ time: Date.now(),
72
+ }))
73
+
74
+ if (process.env.app_type == "pm2") {
75
+ const ret = await Bot.exec("pnpm run restart")
76
+ if (!ret.error) process.exit()
77
+ await this.e.reply(`重启错误\n${ret.error}`)
78
+ Bot.makeLog("error", ["重启错误", ret])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
+ process.exit()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
 
83
+ async stop() {
84
+ await this.e.reply(`开始关机,本次运行时长:${Bot.getTimeDiff()}`)
85
+ await redis.set(this.key, JSON.stringify({
86
+ isStop: true,
87
+ isGroup: !!this.e.isGroup,
88
+ id: this.e.isGroup ? this.e.group_id : this.e.user_id,
89
+ bot_id: this.e.self_id,
90
+ msg_id: this.e.message_id,
91
+ time: Date.now(),
92
+ }))
93
+
94
+ if (process.env.app_type == "pm2") {
95
+ const ret = await Bot.exec("pnpm stop")
96
+ await this.e.reply(`关机错误\n${ret.error}\n${ret.stdout}\n${ret.stderr}`)
97
+ Bot.makeLog("error", ["关机错误", ret])
98
  }
99
+ process.exit(1)
 
 
 
 
 
 
 
 
 
 
100
  }
101
  }
Yunzai/plugins/other/sendLog.js CHANGED
@@ -1,6 +1,4 @@
1
- import plugin from "../../lib/plugins/plugin.js"
2
- import common from "../../lib/common/common.js"
3
- import fs from "node:fs"
4
  import lodash from "lodash"
5
  import moment from "moment"
6
 
@@ -43,16 +41,16 @@ export class sendLog extends plugin {
43
 
44
  if (this.keyWord) type = this.keyWord
45
 
46
- const log = this.getLog(logFile)
47
 
48
  if (lodash.isEmpty(log))
49
  return this.reply(`暂无相关日志:${type}`)
50
 
51
- return this.reply(await common.makeForwardMsg(this.e, [log.join("\n")], `最近${log.length}条${type}日志`))
52
  }
53
 
54
- getLog(logFile) {
55
- let log = fs.readFileSync(logFile, { encoding: "utf-8" })
56
  log = log.split("\n")
57
 
58
  if (this.keyWord) {
 
1
+ import fs from "node:fs/promises"
 
 
2
  import lodash from "lodash"
3
  import moment from "moment"
4
 
 
41
 
42
  if (this.keyWord) type = this.keyWord
43
 
44
+ const log = await this.getLog(logFile)
45
 
46
  if (lodash.isEmpty(log))
47
  return this.reply(`暂无相关日志:${type}`)
48
 
49
+ return this.reply(await Bot.makeForwardArray([`最近${log.length}条${type}日志`, log.join("\n")]))
50
  }
51
 
52
+ async getLog(logFile) {
53
+ let log = await fs.readFile(logFile, "utf-8")
54
  log = log.split("\n")
55
 
56
  if (this.keyWord) {
Yunzai/plugins/other/update.js CHANGED
@@ -1,12 +1,7 @@
1
- import plugin from '../../lib/plugins/plugin.js'
2
- import { createRequire } from 'module'
3
  import lodash from 'lodash'
4
- import fs from 'node:fs'
5
  import { Restart } from './restart.js'
6
- import common from '../../lib/common/common.js'
7
-
8
- const require = createRequire(import.meta.url)
9
- const { exec, execSync } = require('child_process')
10
 
11
  let uping = false
12
 
@@ -37,6 +32,25 @@ export class update extends plugin {
37
  this.typeName = 'TRSS-Yunzai'
38
  }
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  async update() {
41
  if (!this.e.isMaster) return false
42
  if (uping) return this.reply('已有命令更新中..请勿重复操作')
@@ -44,39 +58,29 @@ export class update extends plugin {
44
  if (/详细|详情|面板|面版/.test(this.e.msg)) return false
45
 
46
  /** 获取插件 */
47
- const plugin = this.getPlugin()
48
  if (plugin === false) return false
49
 
50
- /** 执行更新 */
51
  await this.runUpdate(plugin)
52
 
53
- /** 是否需要重启 */
54
- if (this.isUp) {
55
- // await this.reply('即将执行重启,以应用更新')
56
- setTimeout(() => this.restart(), 2000)
57
- }
58
  }
59
 
60
- getPlugin(plugin = '') {
61
  if (!plugin) {
62
  plugin = this.e.msg.replace(/#(强制)?更新(日志)?/, '')
63
  if (!plugin) return ''
64
  }
65
 
66
- if (!fs.existsSync(`plugins/${plugin}/.git`)) return false
67
 
68
  this.typeName = plugin
69
  return plugin
70
  }
71
 
72
- async execSync(cmd) {
73
- return new Promise((resolve, reject) => {
74
- exec(cmd, { windowsHide: true }, (error, stdout, stderr) => {
75
- resolve({ error, stdout, stderr })
76
- })
77
- })
78
- }
79
-
80
  async runUpdate(plugin = '') {
81
  this.isNowUp = false
82
 
@@ -95,22 +99,24 @@ export class update extends plugin {
95
 
96
  await this.reply(`开始${type} ${this.typeName}`)
97
  uping = true
98
- const ret = await this.execSync(cm)
99
  uping = false
100
 
 
101
  if (ret.error) {
102
  logger.mark(`${this.e.logFnc} 更新失败:${this.typeName}`)
103
- this.gitErr(ret.error, ret.stdout)
104
  return false
105
  }
106
 
107
  const time = await this.getTime(plugin)
108
-
109
  if (/Already up|已经是最新/g.test(ret.stdout)) {
110
  await this.reply(`${this.typeName} 已是最新\n最后更新时间:${time}`)
111
  } else {
112
- await this.reply(`${this.typeName} 更新成功\n更新时间:${time}`)
113
  this.isUp = true
 
 
 
114
  await this.reply(await this.getLog(plugin))
115
  }
116
 
@@ -121,69 +127,56 @@ export class update extends plugin {
121
  async getcommitId(plugin = '') {
122
  let cm = 'git rev-parse --short HEAD'
123
  if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
124
-
125
- const commitId = await execSync(cm, { encoding: 'utf-8' })
126
- return lodash.trim(commitId)
127
  }
128
 
129
  async getTime(plugin = '') {
130
  let cm = 'git log -1 --pretty=%cd --date=format:"%F %T"'
131
  if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
132
-
133
- let time = ''
134
- try {
135
- time = await execSync(cm, { encoding: 'utf-8' })
136
- time = lodash.trim(time)
137
- } catch (error) {
138
- logger.error(error.toString())
139
- time = '获取时间失败'
140
- }
141
-
142
- return time
143
  }
144
 
145
- async gitErr(err, stdout) {
146
  const msg = '更新失败!'
147
- const errMsg = err.toString()
148
- stdout = stdout.toString()
149
 
150
- if (errMsg.includes('Timed out')) {
151
- const remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
152
  return this.reply(`${msg}\n连接超时:${remote}`)
153
  }
154
 
155
- if (/Failed to connect|unable to access/g.test(errMsg)) {
156
- const remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
157
  return this.reply(`${msg}\n连接失败:${remote}`)
158
  }
159
 
160
- if (errMsg.includes('be overwritten by merge')) {
161
- return this.reply(`${msg}\n存在冲突:\n${errMsg}\n请解决冲突后��更新,或者执行#强制更新,放弃本地修改`)
162
  }
163
 
164
  if (stdout.includes('CONFLICT')) {
165
- return this.reply(`${msg}\n存在冲突:\n${errMsg}${stdout}\n请解决冲突后再更新,或者执行#强制更新,放弃本地修改`)
166
  }
167
 
168
- return this.reply([errMsg, stdout])
169
  }
170
 
171
  async updateAll() {
172
- const dirs = fs.readdirSync('./plugins/')
173
 
174
  await this.runUpdate()
175
 
176
  for (let plu of dirs) {
177
- plu = this.getPlugin(plu)
178
  if (plu === false) continue
179
- await common.sleep(1500)
180
  await this.runUpdate(plu)
181
  }
182
 
183
- if (this.isUp) {
184
- // await this.reply('即将执行重启,以应用更新')
185
- setTimeout(() => this.restart(), 2000)
186
- }
187
  }
188
 
189
  restart() {
@@ -194,17 +187,14 @@ export class update extends plugin {
194
  let cm = 'git log -100 --pretty="%h||[%cd] %s" --date=format:"%F %T"'
195
  if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
196
 
197
- let logAll
198
- try {
199
- logAll = await execSync(cm, { encoding: 'utf-8' })
200
- } catch (error) {
201
- logger.error(error.toString())
202
- await this.reply(error.toString())
203
  }
 
204
 
205
- if (!logAll) return false
206
-
207
- logAll = logAll.trim().split('\n')
208
 
209
  let log = []
210
  for (let str of logAll) {
@@ -218,23 +208,21 @@ export class update extends plugin {
218
 
219
  if (log.length <= 0) return ''
220
 
221
- let end = ''
222
- try {
223
- cm = 'git config -l'
224
- if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
225
- end = await execSync(cm, { encoding: 'utf-8' })
226
- end = end.match(/remote\..*\.url=.+/g).join('\n\n').replace(/remote\..*\.url=/g, '').replace(/\/\/([^@]+)@/, '//')
227
- } catch (error) {
228
- logger.error(error.toString())
229
- await this.reply(error.toString())
230
  }
231
 
232
- return common.makeForwardMsg(this.e, [log, end], `${plugin || 'TRSS-Yunzai'} 更新日志,共${line}条`)
233
  }
234
 
235
  async updateLog() {
236
- const plugin = this.getPlugin()
237
  if (plugin === false) return false
238
  return this.reply(await this.getLog(plugin))
239
  }
240
- }
 
1
+ import cfg from '../../lib/config/config.js'
 
2
  import lodash from 'lodash'
3
+ import fs from 'node:fs/promises'
4
  import { Restart } from './restart.js'
 
 
 
 
5
 
6
  let uping = false
7
 
 
32
  this.typeName = 'TRSS-Yunzai'
33
  }
34
 
35
+ init() {
36
+ if (cfg.bot.update_time) {
37
+ this.e = {
38
+ isMaster: true,
39
+ logFnc: "[自动更新]",
40
+ msg: "#全部更新",
41
+ reply: msg => Bot.sendMasterMsg(msg),
42
+ }
43
+ this.autoUpdate()
44
+ }
45
+ }
46
+
47
+ autoUpdate() {
48
+ setTimeout(() => {
49
+ this.updateAll()
50
+ this.autoUpdate()
51
+ }, cfg.bot.update_time*60000)
52
+ }
53
+
54
  async update() {
55
  if (!this.e.isMaster) return false
56
  if (uping) return this.reply('已有命令更新中..请勿重复操作')
 
58
  if (/详细|详情|面板|面版/.test(this.e.msg)) return false
59
 
60
  /** 获取插件 */
61
+ let plugin = await this.getPlugin()
62
  if (plugin === false) return false
63
 
 
64
  await this.runUpdate(plugin)
65
 
66
+ if (this.isPkgUp)
67
+ await Bot.exec("pnpm install")
68
+ if (this.isUp)
69
+ this.restart()
 
70
  }
71
 
72
+ async getPlugin(plugin = '') {
73
  if (!plugin) {
74
  plugin = this.e.msg.replace(/#(强制)?更新(日志)?/, '')
75
  if (!plugin) return ''
76
  }
77
 
78
+ if (!await Bot.fsStat(`plugins/${plugin}/.git`)) return false
79
 
80
  this.typeName = plugin
81
  return plugin
82
  }
83
 
 
 
 
 
 
 
 
 
84
  async runUpdate(plugin = '') {
85
  this.isNowUp = false
86
 
 
99
 
100
  await this.reply(`开始${type} ${this.typeName}`)
101
  uping = true
102
+ const ret = await Bot.exec(cm)
103
  uping = false
104
 
105
+ ret.stdout = String(ret.stdout)
106
  if (ret.error) {
107
  logger.mark(`${this.e.logFnc} 更新失败:${this.typeName}`)
108
+ this.gitErr(Bot.String(ret.error), ret.stdout)
109
  return false
110
  }
111
 
112
  const time = await this.getTime(plugin)
 
113
  if (/Already up|已经是最新/g.test(ret.stdout)) {
114
  await this.reply(`${this.typeName} 已是最新\n最后更新时间:${time}`)
115
  } else {
 
116
  this.isUp = true
117
+ if (/package\.json/.test(ret.stdout))
118
+ this.isPkgUp = true
119
+ await this.reply(`${this.typeName} 更新成功\n更新时间:${time}`)
120
  await this.reply(await this.getLog(plugin))
121
  }
122
 
 
127
  async getcommitId(plugin = '') {
128
  let cm = 'git rev-parse --short HEAD'
129
  if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
130
+ cm = await Bot.exec(cm)
131
+ return lodash.trim(String(cm.stdout))
 
132
  }
133
 
134
  async getTime(plugin = '') {
135
  let cm = 'git log -1 --pretty=%cd --date=format:"%F %T"'
136
  if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
137
+ cm = await Bot.exec(cm)
138
+ return lodash.trim(String(cm.stdout))
 
 
 
 
 
 
 
 
 
139
  }
140
 
141
+ async gitErr(error, stdout) {
142
  const msg = '更新失败!'
 
 
143
 
144
+ if (error.includes('Timed out')) {
145
+ const remote = error.match(/'(.+?)'/g)[0].replace(/'/g, '')
146
  return this.reply(`${msg}\n连接超时:${remote}`)
147
  }
148
 
149
+ if (/Failed to connect|unable to access/g.test(error)) {
150
+ const remote = error.match(/'(.+?)'/g)[0].replace(/'/g, '')
151
  return this.reply(`${msg}\n连接失败:${remote}`)
152
  }
153
 
154
+ if (error.includes('be overwritten by merge')) {
155
+ return this.reply(`${msg}\n存在冲突:\n${error}\n请解决冲突后再更新,或者执行#强制更新,放弃本地修改`)
156
  }
157
 
158
  if (stdout.includes('CONFLICT')) {
159
+ return this.reply(`${msg}\n存在冲突:\n${error}${stdout}\n请解决冲突后再更新,或者执行#强制更新,放弃本地修改`)
160
  }
161
 
162
+ return this.reply([error, stdout])
163
  }
164
 
165
  async updateAll() {
166
+ const dirs = await fs.readdir('./plugins/')
167
 
168
  await this.runUpdate()
169
 
170
  for (let plu of dirs) {
171
+ plu = await this.getPlugin(plu)
172
  if (plu === false) continue
 
173
  await this.runUpdate(plu)
174
  }
175
 
176
+ if (this.isPkgUp)
177
+ await Bot.exec("pnpm install")
178
+ if (this.isUp)
179
+ this.restart()
180
  }
181
 
182
  restart() {
 
187
  let cm = 'git log -100 --pretty="%h||[%cd] %s" --date=format:"%F %T"'
188
  if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
189
 
190
+ cm = await Bot.exec(cm)
191
+ if (cm.error) {
192
+ logger.error(cm.error)
193
+ await this.reply(String(cm.error))
 
 
194
  }
195
+ const logAll = String(cm.stdout).trim().split('\n')
196
 
197
+ if (!logAll.length) return false
 
 
198
 
199
  let log = []
200
  for (let str of logAll) {
 
208
 
209
  if (log.length <= 0) return ''
210
 
211
+ cm = 'git config -l'
212
+ if (plugin) cm = `cd "plugins/${plugin}" && ${cm}`
213
+ cm = await Bot.exec(cm)
214
+ const end = String(cm.stdout).match(/remote\..*\.url=.+/g).join('\n\n').replace(/remote\..*\.url=/g, '').replace(/\/\/([^@]+)@/, '//')
215
+ if (cm.error) {
216
+ logger.error(cm.error)
217
+ await this.reply(String(cm.error))
 
 
218
  }
219
 
220
+ return Bot.makeForwardArray([`${plugin || 'TRSS-Yunzai'} 更新日志,共${line}条`, log, end])
221
  }
222
 
223
  async updateLog() {
224
+ const plugin = await this.getPlugin()
225
  if (plugin === false) return false
226
  return this.reply(await this.getLog(plugin))
227
  }
228
+ }
Yunzai/plugins/other/version.js CHANGED
@@ -1,27 +1,35 @@
1
- import { App, Common, Version } from '#miao'
 
 
 
 
 
2
 
3
- let app = App.init({
4
- id: 'version',
5
- name: '版本',
6
- desc: '版本'
7
- })
 
 
8
 
9
- app.reg({
10
- version: {
11
- rule: /^#版本$/,
12
- desc: '【#帮助】 版本介绍',
13
- fn: async function (e) {
14
- let { changelogs, currentVersion } = Version.readLogFile('root')
15
- return await Common.render('help/version-info', {
16
- currentVersion,
17
- changelogs,
18
- name: 'TRSS-Yunzai',
19
- elem: 'cryo',
20
- pluginName: false,
21
- pluginVersion: false
22
- }, { e, scale: 1.2 })
 
23
  }
24
- }
25
- })
26
 
27
- export const version = app.v3App()
 
 
1
+ let App, Common, Version
2
+ try {
3
+ App = (await import("#miao")).App
4
+ Common = (await import("#miao")).Common
5
+ Version = (await import("#miao")).Version
6
+ } catch (err) {}
7
 
8
+ export let version = {}
9
+ if (App) {
10
+ let app = App.init({
11
+ id: "version",
12
+ name: "版本",
13
+ desc: "版本"
14
+ })
15
 
16
+ app.reg({
17
+ version: {
18
+ rule: /^#版本$/,
19
+ desc: "【#帮助】 版本介绍",
20
+ fn: async function (e) {
21
+ let { changelogs, currentVersion } = Version.readLogFile("root")
22
+ return await Common.render("help/version-info", {
23
+ currentVersion,
24
+ changelogs,
25
+ name: "TRSS-Yunzai",
26
+ elem: "cryo",
27
+ pluginName: false,
28
+ pluginVersion: false,
29
+ }, { e, scale: 1.2 })
30
+ }
31
  }
32
+ })
 
33
 
34
+ version = app.v3App()
35
+ }
Yunzai/plugins/system/add.js CHANGED
@@ -1,11 +1,7 @@
1
  import cfg from "../../lib/config/config.js"
2
- import plugin from "../../lib/plugins/plugin.js"
3
- import common from "../../lib/common/common.js"
4
- import fs from "node:fs"
5
  import path from "node:path"
6
  import lodash from "lodash"
7
- import fetch from "node-fetch"
8
- import { fileTypeFromBuffer } from "file-type"
9
 
10
  let messageMap = {}
11
 
@@ -41,7 +37,7 @@ export class add extends plugin {
41
  }
42
 
43
  async init() {
44
- common.mkdirs(this.path)
45
  }
46
 
47
  /** 群号key */
@@ -59,7 +55,7 @@ export class add extends plugin {
59
  return
60
  }
61
 
62
- this.initMessageMap()
63
 
64
  if (!this.checkAuth()) return false
65
  /** 获取关键词 */
@@ -101,6 +97,10 @@ export class add extends plugin {
101
 
102
  checkAuth() {
103
  if (this.e.isMaster) return true
 
 
 
 
104
 
105
  const groupCfg = cfg.getGroup(this.e.self_id, this.group_id)
106
  if (groupCfg.addLimit == 2) {
@@ -145,7 +145,7 @@ export class add extends plugin {
145
 
146
  /** 添加内容 */
147
  async addContext() {
148
- const context = this.getContext()?.addContext
149
  this.isGlobal = context.isGlobal
150
  await this.getGroupId()
151
  /** 关键词 */
@@ -181,46 +181,25 @@ export class add extends plugin {
181
  if (message.length > 1)
182
  this.keyWord += String(message.length)
183
 
184
- this.saveJson()
185
  return this.reply(`添加成功:${this.keyWord}`)
186
  }
187
 
188
- saveJson() {
189
  let obj = {}
190
  for (let [k, v] of messageMap[this.group_id])
191
  obj[k] = v
192
 
193
- fs.writeFileSync(`${this.path}${this.group_id}.json`, JSON.stringify(obj, "", "\t"))
194
- }
195
-
196
- async makeBuffer(file) {
197
- if (file.match(/^base64:\/\//))
198
- return Buffer.from(file.replace(/^base64:\/\//, ""), "base64")
199
- else if (file.match(/^https?:\/\//))
200
- return Buffer.from(await (await fetch(file)).arrayBuffer())
201
- else if (fs.existsSync(file))
202
- return Buffer.from(fs.readFileSync(file))
203
- return file
204
- }
205
-
206
- async fileType(data) {
207
- const file = { name: `${this.group_id}/${data.type}/${Date.now()}` }
208
- try {
209
- file.url = data.url.replace(/^base64:\/\/.*/, "base64://...")
210
- file.buffer = await this.makeBuffer(data.url)
211
- file.type = await fileTypeFromBuffer(file.buffer)
212
- file.name = `${file.name}.${file.type.ext}`
213
- } catch (err) {
214
- logger.error(`文件类型检测错误:${logger.red(err)}`)
215
- file.name = `${file.name}-${path.basename(data.file || data.url)}`
216
- }
217
- return file
218
  }
219
 
220
  async saveFile(data) {
221
- const file = await this.fileType(data)
222
- if (file.name && Buffer.isBuffer(file.buffer) && common.mkdirs(path.dirname(`${this.path}${file.name}`))) {
223
- fs.writeFileSync(`${this.path}${file.name}`, file.buffer)
 
 
 
224
  return file.name
225
  }
226
  return data.url
@@ -233,8 +212,8 @@ export class add extends plugin {
233
  await this.getGroupId()
234
  if (!this.group_id) return false
235
 
236
- this.initMessageMap()
237
- this.initGlobalMessageMap()
238
 
239
  this.keyWord = this.trimAlias(this.e.raw_message.trim())
240
  let keyWord = this.keyWord
@@ -260,8 +239,8 @@ export class add extends plugin {
260
 
261
  msg = [...msg[num]]
262
  for (const i in msg)
263
- if (msg[i].file && fs.existsSync(`${this.path}${msg[i].file}`))
264
- msg[i] = { ...msg[i], file: `base64://${fs.readFileSync(`${this.path}${msg[i].file}`).toString("base64")}` }
265
 
266
  logger.mark(`[发送消息]${this.e.logText} ${this.keyWord}`)
267
  const groupCfg = cfg.getGroup(this.e.self_id, this.group_id)
@@ -272,15 +251,15 @@ export class add extends plugin {
272
  }
273
 
274
  /** 初始化已添加内容 */
275
- initMessageMap() {
276
  if (messageMap[this.group_id]) return
277
  messageMap[this.group_id] = new Map()
278
 
279
  const path = `${this.path}${this.group_id}.json`
280
- if (!fs.existsSync(path)) return
281
 
282
  try {
283
- const message = JSON.parse(fs.readFileSync(path, "utf8"))
284
  for (const i in message)
285
  messageMap[this.group_id].set(i, message[i])
286
  } catch (err) {
@@ -289,15 +268,15 @@ export class add extends plugin {
289
  }
290
 
291
  /** 初始化全局已添加内容 */
292
- initGlobalMessageMap() {
293
  if (messageMap.global) return
294
  messageMap.global = new Map()
295
 
296
  const globalPath = `${this.path}global.json`
297
- if (!fs.existsSync(globalPath)) return
298
 
299
  try {
300
- const message = JSON.parse(fs.readFileSync(globalPath, "utf8"))
301
  for (const i in message)
302
  messageMap.global.set(i, message[i])
303
  } catch (err) {
@@ -310,7 +289,7 @@ export class add extends plugin {
310
  await this.getGroupId()
311
  if (!(this.group_id && this.checkAuth())) return false
312
 
313
- this.initMessageMap()
314
 
315
  this.getKeyWord()
316
  if (!this.keyWord) {
@@ -370,7 +349,7 @@ export class add extends plugin {
370
  }
371
  }
372
 
373
- this.saveJson()
374
  return this.reply(`删除成功:${this.keyWord}`)
375
  }
376
 
@@ -384,7 +363,7 @@ export class add extends plugin {
384
  await this.getGroupId()
385
  if (!this.group_id) return false
386
 
387
- this.initMessageMap()
388
 
389
  const search = this.e.msg.replace(/^#(全局)?(消息|词条)/, "").trim()
390
  if (search.match(/^列表/))
@@ -426,16 +405,17 @@ export class add extends plugin {
426
  msg.push(`${i.num}. ${keyWord}(${i.val.length})`)
427
  num++
428
  }
429
- msg = [msg.join("\n")]
430
-
431
- if (type == "list" && count > 100)
432
- msg.push(`更多内容请翻页查看\n如:#消息列表${Number(page)+1}`)
433
 
434
  let title = `消息列表:第${page}页,共${count}条`
435
  if (type == "search")
436
  title = `消息${search}:共${count}条`
437
 
438
- return this.reply(await common.makeForwardMsg(this.e, msg, title))
 
 
 
 
 
439
  }
440
 
441
  /** 分页 */
 
1
  import cfg from "../../lib/config/config.js"
2
+ import fs from "node:fs/promises"
 
 
3
  import path from "node:path"
4
  import lodash from "lodash"
 
 
5
 
6
  let messageMap = {}
7
 
 
37
  }
38
 
39
  async init() {
40
+ await Bot.mkdir(this.path)
41
  }
42
 
43
  /** 群号key */
 
55
  return
56
  }
57
 
58
+ await this.initMessageMap()
59
 
60
  if (!this.checkAuth()) return false
61
  /** 获取关键词 */
 
97
 
98
  checkAuth() {
99
  if (this.e.isMaster) return true
100
+ if (this.isGlobal) {
101
+ this.reply("暂无权限,只有主人才能操作")
102
+ return false
103
+ }
104
 
105
  const groupCfg = cfg.getGroup(this.e.self_id, this.group_id)
106
  if (groupCfg.addLimit == 2) {
 
145
 
146
  /** 添加内容 */
147
  async addContext() {
148
+ const context = this.getContext("addContext")
149
  this.isGlobal = context.isGlobal
150
  await this.getGroupId()
151
  /** 关键词 */
 
181
  if (message.length > 1)
182
  this.keyWord += String(message.length)
183
 
184
+ await this.saveJson()
185
  return this.reply(`添加成功:${this.keyWord}`)
186
  }
187
 
188
+ async saveJson() {
189
  let obj = {}
190
  for (let [k, v] of messageMap[this.group_id])
191
  obj[k] = v
192
 
193
+ await fs.writeFile(`${this.path}${this.group_id}.json`, JSON.stringify(obj, "", "\t"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  }
195
 
196
  async saveFile(data) {
197
+ const file = await Bot.fileType({ ...data, file: data.url })
198
+ if (Buffer.isBuffer(file.buffer)) {
199
+ file.name = `${this.group_id}/${data.type}/${file.name}`
200
+ file.path = `${this.path}${file.name}`
201
+ await Bot.mkdir(path.dirname(file.path))
202
+ await fs.writeFile(file.path, file.buffer)
203
  return file.name
204
  }
205
  return data.url
 
212
  await this.getGroupId()
213
  if (!this.group_id) return false
214
 
215
+ await this.initMessageMap()
216
+ await this.initGlobalMessageMap()
217
 
218
  this.keyWord = this.trimAlias(this.e.raw_message.trim())
219
  let keyWord = this.keyWord
 
239
 
240
  msg = [...msg[num]]
241
  for (const i in msg)
242
+ if (msg[i].file && await Bot.fsStat(`${this.path}${msg[i].file}`))
243
+ msg[i] = { ...msg[i], file: `${this.path}${msg[i].file}` }
244
 
245
  logger.mark(`[发送消息]${this.e.logText} ${this.keyWord}`)
246
  const groupCfg = cfg.getGroup(this.e.self_id, this.group_id)
 
251
  }
252
 
253
  /** 初始化已添加内容 */
254
+ async initMessageMap() {
255
  if (messageMap[this.group_id]) return
256
  messageMap[this.group_id] = new Map()
257
 
258
  const path = `${this.path}${this.group_id}.json`
259
+ if (!await Bot.fsStat(path)) return
260
 
261
  try {
262
+ const message = JSON.parse(await fs.readFile(path, "utf8"))
263
  for (const i in message)
264
  messageMap[this.group_id].set(i, message[i])
265
  } catch (err) {
 
268
  }
269
 
270
  /** 初始化全局已添加内容 */
271
+ async initGlobalMessageMap() {
272
  if (messageMap.global) return
273
  messageMap.global = new Map()
274
 
275
  const globalPath = `${this.path}global.json`
276
+ if (!await Bot.fsStat(globalPath)) return
277
 
278
  try {
279
+ const message = JSON.parse(await fs.readFile(globalPath, "utf8"))
280
  for (const i in message)
281
  messageMap.global.set(i, message[i])
282
  } catch (err) {
 
289
  await this.getGroupId()
290
  if (!(this.group_id && this.checkAuth())) return false
291
 
292
+ await this.initMessageMap()
293
 
294
  this.getKeyWord()
295
  if (!this.keyWord) {
 
349
  }
350
  }
351
 
352
+ await this.saveJson()
353
  return this.reply(`删除成功:${this.keyWord}`)
354
  }
355
 
 
363
  await this.getGroupId()
364
  if (!this.group_id) return false
365
 
366
+ await this.initMessageMap()
367
 
368
  const search = this.e.msg.replace(/^#(全局)?(消息|词条)/, "").trim()
369
  if (search.match(/^列表/))
 
405
  msg.push(`${i.num}. ${keyWord}(${i.val.length})`)
406
  num++
407
  }
 
 
 
 
408
 
409
  let title = `消息列表:第${page}页,共${count}条`
410
  if (type == "search")
411
  title = `消息${search}:共${count}条`
412
 
413
+ msg = [title, msg.join("\n")]
414
+
415
+ if (type == "list" && count > 100)
416
+ msg.push(`更多内容请翻页查看\n如:#消息列表${Number(page)+1}`)
417
+
418
+ return this.reply(await Bot.makeForwardArray(msg))
419
  }
420
 
421
  /** 分页 */
Yunzai/plugins/system/botOperate.js CHANGED
@@ -1,7 +1,7 @@
1
  export class botOperate extends plugin {
2
- constructor () {
3
  super({
4
- name: "Bot 操作",
5
  dsc: "Bot 操作",
6
  event: "message",
7
  rule: [
@@ -20,7 +20,10 @@ export class botOperate extends plugin {
20
  }
21
 
22
  Verify() {
23
- const data = { msg: this.e.msg.replace(/^#(Bot|机器人)验证/, "").trim().split(":") }
 
 
 
24
  data.self_id = data.msg.shift()
25
  data.msg = data.msg.join(":")
26
  Bot.em(`verify.${data.self_id}`, data)
 
1
  export class botOperate extends plugin {
2
+ constructor() {
3
  super({
4
+ name: "botOperate",
5
  dsc: "Bot 操作",
6
  event: "message",
7
  rule: [
 
20
  }
21
 
22
  Verify() {
23
+ const data = {
24
+ msg: this.e.msg.replace(/^#(Bot|机器人)验证/, "").trim().split(":"),
25
+ reply: msg => this.reply(msg, true),
26
+ }
27
  data.self_id = data.msg.shift()
28
  data.msg = data.msg.join(":")
29
  Bot.em(`verify.${data.self_id}`, data)
Yunzai/plugins/system/disablePrivate.js CHANGED
@@ -1,5 +1,4 @@
1
  import cfg from '../../lib/config/config.js'
2
- import plugin from '../../lib/plugins/plugin.js'
3
 
4
  export class disPri extends plugin {
5
  constructor () {
@@ -29,9 +28,14 @@ export class disPri extends plugin {
29
  /** 绑定ck,抽卡链接 */
30
  let wordReg = /(.*)(ltoken|_MHYUUID|authkey=)(.*)|导出记录(json)*|(记录|安卓|苹果|ck|cookie|体力)帮助|^帮助$|^#*(删除|我的)ck$|^#(我的)?(uid|UID)[0-9]{0,2}$/g
31
  /** 自定义通行字符 */
32
- let disableReg = `(.*)(${cfg.other?.disableAdopt?.join('|')})(.*)`
 
 
 
 
 
33
  if (this.e.raw_message) {
34
- if (!new RegExp(wordReg).test(this.e.raw_message) && (!new RegExp(disableReg).test(this.e.raw_message))) {
35
  this.sendTips()
36
  return 'return'
37
  }
 
1
  import cfg from '../../lib/config/config.js'
 
2
 
3
  export class disPri extends plugin {
4
  constructor () {
 
28
  /** 绑定ck,抽卡链接 */
29
  let wordReg = /(.*)(ltoken|_MHYUUID|authkey=)(.*)|导出记录(json)*|(记录|安卓|苹果|ck|cookie|体力)帮助|^帮助$|^#*(删除|我的)ck$|^#(我的)?(uid|UID)[0-9]{0,2}$/g
30
  /** 自定义通行字符 */
31
+ let disableAdopt = cfg.other?.disableAdopt
32
+ if (!Array.isArray(disableAdopt)) {
33
+ disableAdopt = []
34
+ }
35
+ disableAdopt = disableAdopt.filter(str => str != null && str !== '');
36
+ let disableReg = `(.*)(${disableAdopt.join('|')})(.*)`
37
  if (this.e.raw_message) {
38
+ if (!new RegExp(wordReg).test(this.e.raw_message) && (disableAdopt.length === 0 || !new RegExp(disableReg).test(this.e.raw_message))) {
39
  this.sendTips()
40
  return 'return'
41
  }
Yunzai/plugins/system/friend.js CHANGED
@@ -1,20 +1,19 @@
1
- import cfg from '../../lib/config/config.js'
2
- import common from '../../lib/common/common.js'
3
 
4
  export class friend extends plugin {
5
  constructor () {
6
  super({
7
- name: 'autoFriend',
8
- dsc: '自动同意好友',
9
- event: 'request.friend'
10
  })
11
  }
12
 
13
  async accept() {
14
- if (this.e.sub_type == 'add' || this.e.sub_type == 'single') {
15
  if (cfg.other.autoFriend == 1) {
16
  logger.mark(`[自动同意][添加好友] ${this.e.user_id}`)
17
- await common.sleep(2000)
18
  this.e.approve(true)
19
  }
20
  }
 
1
+ import cfg from "../../lib/config/config.js"
 
2
 
3
  export class friend extends plugin {
4
  constructor () {
5
  super({
6
+ name: "autoFriend",
7
+ dsc: "自动同意好友",
8
+ event: "request.friend"
9
  })
10
  }
11
 
12
  async accept() {
13
+ if (this.e.sub_type == "add" || this.e.sub_type == "single") {
14
  if (cfg.other.autoFriend == 1) {
15
  logger.mark(`[自动同意][添加好友] ${this.e.user_id}`)
16
+ await Bot.sleep(3000)
17
  this.e.approve(true)
18
  }
19
  }
Yunzai/plugins/system/invite.js CHANGED
@@ -1,11 +1,9 @@
1
- import cfg from '../../lib/config/config.js'
2
-
3
  export class invite extends plugin {
4
  constructor () {
5
  super({
6
- name: 'invite',
7
- dsc: '主人邀请自动进群',
8
- event: 'request.group.invite'
9
  })
10
  }
11
 
@@ -18,4 +16,4 @@ export class invite extends plugin {
18
  this.e.approve(true)
19
  this.e.bot.pickFriend(this.e.user_id).sendMsg(`已同意加群:${this.e.group_name}`)
20
  }
21
- }
 
 
 
1
  export class invite extends plugin {
2
  constructor () {
3
  super({
4
+ name: "invite",
5
+ dsc: "主人邀请自动进群",
6
+ event: "request.group.invite"
7
  })
8
  }
9
 
 
16
  this.e.approve(true)
17
  this.e.bot.pickFriend(this.e.user_id).sendMsg(`已同意加群:${this.e.group_name}`)
18
  }
19
+ }
Yunzai/plugins/system/master.js CHANGED
@@ -1,9 +1,9 @@
1
- import fs from "fs"
2
- import { randomUUID } from "crypto"
3
  let code = {}
4
  let file = "config/config/other.yaml"
5
  export class master extends plugin {
6
- constructor () {
7
  super({
8
  name: "设置主人",
9
  dsc: "设置主人",
@@ -17,8 +17,8 @@ export class master extends plugin {
17
  })
18
  }
19
 
20
- edit (file, key, value) {
21
- let data = fs.readFileSync(file, "utf8")
22
  if (data.match(RegExp(`- "?${value}"?`)))
23
  return
24
  value = `${key}:\n - "${value}"`
@@ -26,27 +26,27 @@ export class master extends plugin {
26
  data = data.replace(RegExp(`${key}:`), value)
27
  else
28
  data = `${data}\n${value}`
29
- fs.writeFileSync(file, data, "utf8")
30
  }
31
 
32
- async master () {
33
  if (this.e.isMaster) {
34
- await this.reply(`账号:${this.e.user_id} 已经为主人`, true)
35
  return false
36
  }
37
 
38
  code[this.e.user_id] = randomUUID()
39
  logger.mark(`${logger.cyan(`[${this.e.user_id}]`)} 设置主人验证码:${logger.green(code[this.e.user_id])}`)
40
  this.setContext("verify")
41
- await this.reply(`账号:${this.e.user_id} 请输入验证码`, true)
42
  }
43
 
44
- async verify () {
45
  this.finish("verify")
46
  if (this.e.msg.trim() == code[this.e.user_id]) {
47
- this.edit(file, "masterQQ", this.e.user_id)
48
- this.edit(file, "master", `${this.e.self_id}:${this.e.user_id}`)
49
- await this.reply(`账号:${this.e.user_id} 设置主人成功`, true)
50
  } else {
51
  await this.reply("验证码错误", true)
52
  return false
 
1
+ import fs from "node:fs/promises"
2
+ import { randomUUID } from "node:crypto"
3
  let code = {}
4
  let file = "config/config/other.yaml"
5
  export class master extends plugin {
6
+ constructor() {
7
  super({
8
  name: "设置主人",
9
  dsc: "设置主人",
 
17
  })
18
  }
19
 
20
+ async edit(file, key, value) {
21
+ let data = await fs.readFile(file, "utf8")
22
  if (data.match(RegExp(`- "?${value}"?`)))
23
  return
24
  value = `${key}:\n - "${value}"`
 
26
  data = data.replace(RegExp(`${key}:`), value)
27
  else
28
  data = `${data}\n${value}`
29
+ return fs.writeFile(file, data, "utf8")
30
  }
31
 
32
+ async master() {
33
  if (this.e.isMaster) {
34
+ await this.reply(`[${this.e.user_id}] 已经为主人`, true)
35
  return false
36
  }
37
 
38
  code[this.e.user_id] = randomUUID()
39
  logger.mark(`${logger.cyan(`[${this.e.user_id}]`)} 设置主人验证码:${logger.green(code[this.e.user_id])}`)
40
  this.setContext("verify")
41
+ await this.reply(`[${this.e.user_id}] 请输入验证码`, true)
42
  }
43
 
44
+ async verify() {
45
  this.finish("verify")
46
  if (this.e.msg.trim() == code[this.e.user_id]) {
47
+ await this.edit(file, "masterQQ", this.e.user_id)
48
+ await this.edit(file, "master", `${this.e.self_id}:${this.e.user_id}`)
49
+ await this.reply(`[${this.e.user_id}] 设置主人成功`, true)
50
  } else {
51
  await this.reply("验证码错误", true)
52
  return false