Upload 73 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- Yunzai/.eslintrc.cjs +3 -1
- Yunzai/CHANGELOG.md +1 -1
- Yunzai/README.md +110 -82
- Yunzai/app.js +22 -3
- Yunzai/config/default_config/bot.yaml +24 -11
- Yunzai/config/default_config/group.yaml +1 -1
- Yunzai/config/default_config/other.yaml +6 -4
- Yunzai/config/pm2.yaml +7 -0
- Yunzai/lib/bot.js +333 -67
- Yunzai/lib/common/common.js +3 -6
- Yunzai/lib/config/config.js +19 -18
- Yunzai/lib/config/init.js +22 -35
- Yunzai/lib/config/log.js +40 -62
- Yunzai/lib/config/redis.js +38 -49
- Yunzai/lib/events/message.js +2 -2
- Yunzai/lib/events/notice.js +2 -2
- Yunzai/lib/events/online.js +3 -4
- Yunzai/lib/events/request.js +2 -2
- Yunzai/lib/listener/listener.js +2 -2
- Yunzai/lib/listener/loader.js +6 -6
- Yunzai/lib/modules/md5/index.js +4 -0
- Yunzai/lib/modules/md5/package.json +5 -0
- Yunzai/lib/modules/node-fetch/index.js +2 -0
- Yunzai/lib/modules/node-fetch/package.json +5 -0
- Yunzai/lib/modules/oicq/index.js +10 -44
- Yunzai/lib/plugins/config.js +20 -0
- Yunzai/lib/plugins/loader.js +294 -454
- Yunzai/lib/plugins/plugin.js +41 -47
- Yunzai/lib/plugins/runtime.js +65 -72
- Yunzai/lib/plugins/stdin.js +7 -8
- Yunzai/lib/renderer/loader.js +16 -11
- Yunzai/lib/tools/web.js +28 -28
- Yunzai/package.json +21 -21
- Yunzai/plugins/adapter/ComWeChat.js +52 -86
- Yunzai/plugins/adapter/GSUIDCore.js +101 -49
- Yunzai/plugins/adapter/OPQBot.js +327 -0
- Yunzai/plugins/adapter/OneBotv11.js +989 -0
- Yunzai/plugins/example/主动复读.js +13 -14
- Yunzai/plugins/example/进群退群通知.js +9 -12
- Yunzai/plugins/other/install.js +10 -20
- Yunzai/plugins/other/restart.js +74 -95
- Yunzai/plugins/other/sendLog.js +5 -7
- Yunzai/plugins/other/update.js +69 -81
- Yunzai/plugins/other/version.js +31 -23
- Yunzai/plugins/system/add.js +36 -56
- Yunzai/plugins/system/botOperate.js +6 -3
- Yunzai/plugins/system/disablePrivate.js +7 -3
- Yunzai/plugins/system/friend.js +6 -7
- Yunzai/plugins/system/invite.js +4 -6
- 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 应用端,支持多账号,支持协议端:
|
6 |
|
7 |
[](https://github.com/TimeRainStarSky/Yunzai)
|
8 |
[](../../stargazers)
|
@@ -13,7 +13,7 @@ Yunzai 应用端,支持多账号,支持协议端:go-cqhttp、ComWeChat、G
|
|
13 |
|
14 |
</div>
|
15 |
|
16 |
-
- 基于 [Miao-Yunzai](../../../../yoimiya-kokomi/Miao-Yunzai)
|
17 |
- 开发文档:[docs 分支](../../tree/docs)
|
18 |
|
19 |
## TRSS-Yunzai 后续计划
|
@@ -36,151 +36,179 @@ Yunzai 应用端,支持多账号,支持协议端:go-cqhttp、ComWeChat、G
|
|
36 |
|
37 |
### 手动安装
|
38 |
|
39 |
-
> 环境准备:
|
|
|
40 |
|
41 |
-
1
|
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 |
-
|
62 |
|
63 |
-
```
|
64 |
-
npm
|
65 |
-
```
|
66 |
-
|
67 |
-
3.安装依赖
|
68 |
-
|
69 |
-
```
|
70 |
pnpm i
|
71 |
```
|
72 |
|
73 |
-
4
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
|
|
78 |
|
79 |
-
5
|
80 |
|
81 |
-
<details><summary>
|
82 |
|
83 |
-
|
84 |
|
85 |
-
|
86 |
-
uin: 账号
|
87 |
-
password: '密码'
|
88 |
-
post-format: array
|
89 |
-
universal: ws://localhost:2536/go-cqhttp
|
90 |
-
```
|
91 |
|
92 |
-
|
93 |
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
95 |
|
96 |
-
|
97 |
|
98 |
-
|
99 |
-
websocekt_type = "Backward"
|
100 |
-
websocket_url = ["ws://localhost:2536/ComWeChat"]
|
101 |
-
```
|
102 |
|
103 |
-
|
104 |
|
105 |
-
|
|
|
|
|
106 |
|
107 |
-
|
108 |
|
109 |
-
|
110 |
-
ws://localhost:2536/GSUIDCore
|
111 |
-
```
|
112 |
|
113 |
-
|
114 |
|
115 |
-
|
|
|
|
|
116 |
|
117 |
-
|
118 |
|
119 |
-
|
120 |
-
|
121 |
-
<details><summary>QQBot</summary>
|
122 |
|
123 |
-
[
|
124 |
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
-
|
128 |
|
129 |
-
|
130 |
|
131 |
-
|
132 |
|
133 |
-
|
134 |
|
135 |
-
|
|
|
|
|
|
|
136 |
|
137 |
-
|
138 |
|
139 |
-
<details><summary
|
140 |
|
141 |
-
[
|
142 |
|
143 |
-
|
|
|
|
|
144 |
|
145 |
-
<details
|
146 |
|
147 |
-
|
148 |
|
149 |
-
|
150 |
|
151 |
-
|
|
|
|
|
152 |
|
153 |
-
|
154 |
|
155 |
-
</details>
|
156 |
|
157 |
-
<details><summary
|
158 |
|
159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
|
161 |
</details>
|
162 |
|
163 |
-
|
164 |
-
|
165 |
-
下载运行 [OPQBot](https://opqbot.com),启动参数添加:
|
166 |
|
167 |
-
|
168 |
-
-wsserver ws://localhost:2536/OPQBot
|
169 |
-
```
|
170 |
|
171 |
-
|
|
|
|
|
|
|
|
|
172 |
|
173 |
-
|
174 |
|
175 |
-
|
|
|
|
|
|
|
|
|
176 |
|
177 |
-
|
178 |
|
179 |
-
|
|
|
|
|
180 |
|
181 |
## 致谢
|
182 |
|
183 |
-
|
|
184 |
-
|
|
185 |
-
|
|
186 |
-
|
|
|
|
2 |
|
3 |
# TRSS-Yunzai
|
4 |
|
5 |
+
Yunzai 应用端,支持多账号,支持协议端:OneBotv11、ComWeChat、GSUIDCore、ICQQ、QQBot、QQ频道、微信、KOOK、Telegram、Discord、OPQBot、Lagrange
|
6 |
|
7 |
[](https://github.com/TimeRainStarSky/Yunzai)
|
8 |
[](../../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 |
-
|
2 |
-
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
#
|
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 |
-
|
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 |
-
|
33 |
-
|
34 |
}
|
35 |
|
36 |
wsConnect(req, socket, head) {
|
37 |
this.wss.handleUpgrade(req, socket, head, conn => {
|
38 |
-
conn.
|
39 |
-
|
40 |
-
|
41 |
-
conn.on("
|
42 |
-
conn.on("
|
|
|
43 |
conn.sendMsg = msg => {
|
44 |
if (!Buffer.isBuffer(msg)) msg = this.String(msg)
|
45 |
-
this.makeLog("debug", `${
|
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 |
-
|
54 |
-
this.
|
55 |
-
|
56 |
-
|
57 |
-
})
|
|
|
|
|
|
|
|
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
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 |
-
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
85 |
}
|
86 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
98 |
-
} else if (
|
99 |
if (opts.file) return data
|
100 |
-
return Buffer.from(fs.
|
101 |
}
|
102 |
return data
|
103 |
}
|
104 |
|
105 |
-
async fileType(data,
|
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 |
-
|
119 |
-
|
120 |
-
file.name = name || `${Date.now()}-path.basename(file.buffer)`
|
121 |
}
|
122 |
} catch (err) {
|
123 |
-
|
124 |
}
|
|
|
|
|
125 |
return file
|
126 |
}
|
127 |
|
128 |
async fileToUrl(file, opts = {}) {
|
129 |
-
const {
|
|
|
|
|
|
|
|
|
130 |
|
131 |
-
file = await this.fileType(file, name)
|
132 |
if (!Buffer.isBuffer(file.buffer)) return file.buffer
|
133 |
-
|
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
|
148 |
else file = this.fs.timeout
|
149 |
}
|
150 |
|
151 |
if (file.type?.mime)
|
152 |
req.res.setHeader("Content-Type", file.type.mime)
|
153 |
|
154 |
-
|
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", `[
|
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 |
-
|
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 |
-
|
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 |
-
|
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 |
-
|
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 |
-
|
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 |
-
|
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
|
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))
|
|
|
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
|
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
|
123 |
return this.getYaml("default_config", name)
|
124 |
}
|
125 |
|
126 |
/** 用户配置 */
|
127 |
-
getConfig
|
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
|
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
|
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 |
-
|
|
|
36 |
|
37 |
-
/**
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
45 |
|
46 |
-
|
|
|
47 |
|
48 |
-
|
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
|
2 |
-
import chalk from
|
3 |
-
import cfg from
|
4 |
-
import fs from
|
5 |
|
6 |
/**
|
7 |
* 设置日志样式
|
8 |
*/
|
9 |
-
export default function setLog
|
10 |
-
let file =
|
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:
|
23 |
layout: {
|
24 |
-
type:
|
25 |
-
pattern:
|
26 |
}
|
27 |
},
|
28 |
command: {
|
29 |
-
type:
|
30 |
-
filename:
|
31 |
-
pattern:
|
32 |
numBackups: 15,
|
33 |
alwaysIncludePattern: true,
|
34 |
layout: {
|
35 |
-
type:
|
36 |
-
pattern:
|
37 |
}
|
38 |
},
|
39 |
error: {
|
40 |
-
type:
|
41 |
-
filename:
|
42 |
alwaysIncludePattern: true,
|
43 |
layout: {
|
44 |
-
type:
|
45 |
-
pattern:
|
46 |
}
|
47 |
}
|
48 |
},
|
49 |
categories: {
|
50 |
-
default: { appenders: [
|
51 |
-
command: { appenders: [
|
52 |
-
error: { appenders: [
|
53 |
}
|
54 |
})
|
55 |
|
56 |
-
const defaultLogger = log4js.getLogger(
|
57 |
-
const commandLogger = log4js.getLogger(
|
58 |
-
const errorLogger = log4js.getLogger(
|
59 |
|
60 |
-
/* eslint-disable no-useless-call */
|
61 |
/** 全局变量 logger */
|
62 |
global.logger = {
|
63 |
-
trace ()
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
try {
|
19 |
-
|
20 |
-
await
|
21 |
} catch (err) {
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
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 |
-
|
40 |
-
|
41 |
-
|
42 |
-
logger.error(`请先启动 Redis:${cmd}`)
|
43 |
-
process.exit()
|
44 |
})
|
45 |
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
}
|
52 |
|
53 |
async function aarch64() {
|
54 |
-
if (process.platform == "win32")
|
55 |
return ""
|
56 |
-
/** 判断
|
57 |
-
|
58 |
-
if (
|
59 |
-
|
60 |
-
|
61 |
-
if (v
|
62 |
-
|
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
|
2 |
|
3 |
/**
|
4 |
* 监听群聊消息
|
5 |
*/
|
6 |
export default class messageEvent extends EventListener {
|
7 |
constructor () {
|
8 |
-
super({ event:
|
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
|
2 |
|
3 |
/**
|
4 |
* 监听群聊消息
|
5 |
*/
|
6 |
export default class noticeEvent extends EventListener {
|
7 |
constructor () {
|
8 |
-
super({ event:
|
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
|
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:
|
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
|
2 |
|
3 |
/**
|
4 |
* 监听群聊消息
|
5 |
*/
|
6 |
export default class requestEvent extends EventListener {
|
7 |
constructor () {
|
8 |
-
super({ event:
|
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
|
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
|
2 |
-
import lodash from
|
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.
|
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 ?
|
22 |
|
23 |
if (lodash.isArray(listener.event)) {
|
24 |
listener.event.forEach((type) => {
|
25 |
-
const e = listener[type] ? type :
|
26 |
Bot[on](listener.prefix + type, event => listener[e](event))
|
27 |
})
|
28 |
} else {
|
29 |
-
const e = listener[listener.event] ? listener.event :
|
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
|
17 |
}
|
18 |
raw(data) {
|
19 |
-
return
|
20 |
}
|
21 |
button(...data) {
|
22 |
-
return
|
23 |
}
|
24 |
markdown(data) {
|
25 |
-
return
|
26 |
}
|
27 |
image(file, name) {
|
28 |
-
return
|
29 |
}
|
30 |
at(qq, name) {
|
31 |
-
return
|
32 |
}
|
33 |
record(file, name) {
|
34 |
-
return
|
35 |
}
|
36 |
video(file, name) {
|
37 |
-
return
|
38 |
}
|
39 |
file(file, name) {
|
40 |
-
return
|
41 |
}
|
42 |
reply(id, text, qq, time, seq) {
|
43 |
-
return
|
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
|
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 = "
|
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.
|
48 |
-
if (
|
49 |
-
|
50 |
-
const files = this.getPlugins()
|
51 |
|
52 |
logger.info("-----------")
|
53 |
logger.info("加载插件中...")
|
54 |
|
55 |
-
|
|
|
|
|
56 |
|
57 |
-
|
58 |
-
|
59 |
-
|
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.
|
105 |
|
106 |
logger.info(`加载定时任务[${this.task.length}个]`)
|
107 |
-
logger.info(`加载插件[${
|
108 |
|
109 |
/** 优先级排序 */
|
110 |
this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
|
111 |
}
|
112 |
|
113 |
-
async
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
}
|
116 |
|
117 |
-
|
118 |
-
if (!
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
})
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
194 |
-
|
|
|
195 |
p.e = e
|
196 |
-
/**
|
197 |
-
if (
|
198 |
-
|
199 |
-
|
200 |
-
priority.push(p)
|
201 |
-
})
|
202 |
|
203 |
-
for (
|
204 |
/** 上下文hook */
|
205 |
-
if (plugin.getContext)
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
plugin[fnc](context[fnc])
|
210 |
-
}
|
211 |
-
return
|
212 |
-
}
|
213 |
}
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
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 (
|
247 |
-
/** accept hook */
|
248 |
if (plugin.accept) {
|
249 |
-
|
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 |
-
|
260 |
-
a: for (let plugin of priority) {
|
261 |
/** 正则匹配 */
|
262 |
-
if (plugin.rule) {
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
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 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
|
|
|
|
|
|
311 |
}
|
312 |
-
|
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 |
-
|
361 |
-
if (
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
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 |
-
|
386 |
if (e.message) for (const i of e.message) {
|
387 |
switch (i.type) {
|
388 |
case "text":
|
389 |
-
|
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 |
-
|
438 |
-
|
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 |
-
|
450 |
-
nickname:
|
|
|
451 |
}
|
452 |
}
|
453 |
|
454 |
-
if (!e.group_name
|
|
|
455 |
|
456 |
-
e.logText = `[${e.group_name ? `${e.group_name}(${e.group_id})` : e.group_id}, ${e.sender?.
|
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 |
-
|
483 |
-
e.
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
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
|
531 |
} catch (err) {
|
532 |
-
Bot.makeLog("error",
|
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 |
-
|
558 |
-
|
559 |
-
|
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(
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
577 |
}
|
578 |
|
579 |
-
|
580 |
-
|
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 |
-
|
599 |
-
|
600 |
-
|
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 |
-
|
614 |
-
|
615 |
-
|
616 |
-
val.job = schedule.scheduleJob(val.cron, async () => {
|
617 |
try {
|
618 |
-
if (
|
619 |
-
logger.mark(`开始定时任务:${
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
if (val.log === true) {
|
624 |
-
logger.mark(`定时任务完成:${val.name}`)
|
625 |
-
}
|
626 |
} catch (error) {
|
627 |
-
logger.error(`定时任务报错:${
|
628 |
logger.error(error)
|
629 |
}
|
630 |
})
|
631 |
-
})
|
632 |
}
|
633 |
|
634 |
/** 检查命令冷却cd */
|
635 |
checkLimit(e) {
|
636 |
/** 禁言中 */
|
637 |
-
if (e.isGroup && e
|
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 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
684 |
|
685 |
/** at机器人 */
|
686 |
if (e.atBot) return true
|
@@ -693,50 +608,54 @@ class PluginsLoader {
|
|
693 |
|
694 |
/** 判断黑白名单 */
|
695 |
checkBlack(e) {
|
696 |
-
|
697 |
-
|
698 |
-
if (e.test) return true
|
699 |
|
700 |
-
/**
|
701 |
-
if (other.
|
|
|
|
|
|
|
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(
|
722 |
-
|
723 |
-
if (
|
724 |
-
if (groupCfg.enable.includes(p.name)) {
|
725 |
-
return true
|
726 |
-
}
|
727 |
-
// logger.debug(`${e.logText}[${p.name}]功能已禁用`)
|
728 |
return false
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
// logger.debug(`${e.logText}[${p.name}]功能已禁用`)
|
734 |
-
return false
|
735 |
-
}
|
736 |
|
737 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
748 |
const watcher = chokidar.watch(file)
|
749 |
-
|
750 |
|
751 |
/** 监听修改 */
|
752 |
-
watcher.on("change",
|
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 |
-
|
795 |
-
|
|
|
|
|
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 |
-
|
816 |
/** 新增文件 */
|
817 |
watcher.on("add", async PluPath => {
|
818 |
-
|
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 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
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 |
-
}
|
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 |
-
|
|
|
|
|
|
|
2 |
|
3 |
-
|
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 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
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
|
70 |
-
if (!this.e
|
71 |
return this.e.reply(msg, quote, data)
|
72 |
}
|
73 |
|
74 |
-
conKey
|
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
|
88 |
-
|
89 |
if (!stateArr[key]) stateArr[key] = {}
|
90 |
stateArr[key][type] = this.e
|
91 |
-
if (time) {
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
}
|
98 |
-
}, time * 1000)
|
99 |
-
}
|
100 |
-
}
|
101 |
-
|
102 |
-
getContext () {
|
103 |
-
let key = this.conKey()
|
104 |
-
return stateArr[key]
|
105 |
}
|
106 |
|
107 |
-
|
108 |
-
|
109 |
-
return stateArr[
|
110 |
}
|
111 |
|
112 |
/**
|
113 |
* @param type 执行方法
|
114 |
* @param isGroup 是否群聊
|
115 |
*/
|
116 |
-
finish
|
117 |
-
|
118 |
-
|
|
|
|
|
119 |
}
|
120 |
}
|
121 |
|
122 |
-
async renderImg
|
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
|
8 |
-
import fs from
|
9 |
-
import
|
10 |
-
import
|
11 |
-
import
|
12 |
-
import
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
19 |
|
20 |
/**
|
21 |
* 常用的处理方法
|
22 |
*/
|
23 |
|
24 |
export default class Runtime {
|
25 |
-
constructor
|
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
|
77 |
-
await MysInfo.initCache()
|
78 |
-
|
79 |
-
e.runtime
|
80 |
-
|
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:
|
93 |
-
uidList:
|
94 |
-
mysUser:
|
95 |
-
ckUidList:
|
96 |
}
|
97 |
if (fnMap[key]) {
|
98 |
return self[fnMap[key]](game)
|
99 |
}
|
100 |
-
if (key ===
|
101 |
-
return self.getUidData(
|
102 |
}
|
103 |
-
if ([
|
104 |
return (_game, arg2) => {
|
105 |
return self[key](_game || game, arg2)
|
106 |
}
|
107 |
}
|
108 |
-
if ([
|
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
|
126 |
if (!this._mysInfo[targetType]) {
|
127 |
-
this._mysInfo[targetType] = await MysInfo.init(this.e, targetType ===
|
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
|
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
|
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
|
176 |
// 处理传入的path
|
177 |
-
path = path.replace(/.html$/,
|
178 |
-
let paths = lodash.filter(path.split(
|
179 |
-
path = paths.join(
|
180 |
// 创建目录
|
181 |
-
|
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(
|
194 |
-
let miaoResPath = `../../../${lodash.repeat(
|
195 |
-
const layoutPath = process.cwd() +
|
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() +
|
206 |
-
defaultLayout: layoutPath +
|
207 |
-
elemLayout: layoutPath +
|
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:
|
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(
|
227 |
// debug下保存当前页面的渲染数据,方便模板编写与调试
|
228 |
// 由于只用于调试,开发者只关注自己当时开发的文件即可,暂不考虑app及plugin的命名冲突
|
229 |
-
let saveDir = mkdir(`ViewData/${plugin}`)
|
230 |
-
let file = `${saveDir}/${data._htmlPath.split(
|
231 |
-
fs.
|
232 |
}
|
233 |
// 截图
|
234 |
let base64 = await puppeteer.screenshot(`${plugin}/${path}`, data)
|
235 |
-
if (cfg.retType ===
|
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 ===
|
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
|
23 |
if (Buffer.isBuffer(file.buffer)) {
|
24 |
file.path = `${this.path}${file.name || Date.now()}`
|
25 |
-
fs.
|
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.
|
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
|
2 |
-
import yaml from
|
3 |
-
import lodash from
|
4 |
-
import cfg from
|
5 |
-
import
|
6 |
-
|
|
|
|
|
|
|
|
|
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 =
|
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,
|
36 |
let renderer = rendererFn(rendererCfg)
|
37 |
if (!renderer.id || !renderer.type || !renderer.render || !lodash.isFunction(renderer.render)) {
|
38 |
-
logger.warn(
|
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 ||
|
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
|
2 |
-
import template from
|
3 |
-
import fs from
|
4 |
-
import lodash from
|
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(
|
21 |
-
app.set(
|
22 |
-
app.set(
|
23 |
-
app.use(express.static(_path +
|
24 |
-
app.use(
|
25 |
|
26 |
-
app.get(
|
27 |
-
let pluginList = fs.
|
28 |
let html = [
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
]
|
33 |
let li = {}
|
34 |
for (let pIdx in pluginList) {
|
35 |
const plugin = pluginList[pIdx]
|
36 |
-
let fileList = fs.
|
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(
|
42 |
}
|
43 |
}
|
44 |
}
|
45 |
-
res.send(html.join(
|
46 |
})
|
47 |
|
48 |
-
app.get(
|
49 |
-
let [plugin, app, ...page] = req.params.page.split(
|
50 |
-
page = page.join(
|
51 |
-
if (plugin ==
|
52 |
-
return res.send(
|
53 |
}
|
54 |
-
let data = JSON.parse(fs.
|
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(
|
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(
|
|
|
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
|
12 |
-
"
|
13 |
-
"
|
14 |
-
"
|
15 |
-
"
|
16 |
-
"log": "node ./lib/tools/log.js"
|
17 |
},
|
18 |
"dependencies": {
|
19 |
"art-template": "^4.13.2",
|
20 |
"chalk": "^5.3.0",
|
21 |
-
"chokidar": "^3.
|
22 |
-
"express": "^4.
|
23 |
-
"file-type": "^
|
24 |
-
"https-proxy-agent": "7.0.
|
25 |
-
"image-size": "^1.
|
|
|
26 |
"lodash": "^4.17.21",
|
27 |
"log4js": "^6.9.1",
|
28 |
-
"md5": "
|
29 |
"moment": "^2.30.1",
|
30 |
-
"node-fetch": "
|
31 |
"node-schedule": "^2.1.1",
|
32 |
"oicq": "link:lib/modules/oicq",
|
33 |
-
"pm2": "^5.3.
|
34 |
-
"puppeteer": "
|
35 |
-
"redis": "^4.6.
|
36 |
-
"sequelize": "^6.
|
37 |
-
"sqlite3": "
|
38 |
"ws": "^8.16.0",
|
39 |
-
"yaml": "^2.
|
40 |
},
|
41 |
"devDependencies": {
|
42 |
-
"eslint": "^8.
|
43 |
"eslint-config-standard": "^17.1.0",
|
44 |
"eslint-plugin-import": "^2.29.1",
|
45 |
-
"eslint-plugin-n": "^16.
|
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
|
30 |
}
|
31 |
|
32 |
sendApi(ws, action, params = {}) {
|
33 |
const echo = randomUUID()
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
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
|
57 |
-
|
|
|
58 |
|
59 |
-
if (file.
|
60 |
-
opts.type = "url"
|
61 |
-
opts.url = file
|
62 |
-
} else if (file.match(/^base64:\/\//)) {
|
63 |
opts.type = "data"
|
64 |
-
opts.data = file.
|
65 |
-
} else if (
|
66 |
-
opts.type = "
|
67 |
-
opts.
|
68 |
} else {
|
69 |
opts.type = "path"
|
70 |
-
opts.path = file
|
71 |
}
|
72 |
|
73 |
-
|
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
|
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 |
-
|
118 |
-
|
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 |
-
|
131 |
-
|
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 |
-
|
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 |
-
|
352 |
break
|
353 |
case "group":
|
354 |
-
|
355 |
break
|
356 |
default:
|
357 |
-
|
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 |
-
|
373 |
data.sub_type = "recall"
|
374 |
break
|
375 |
case "group_message_delete":
|
376 |
-
|
377 |
data.sub_type = "recall"
|
378 |
break
|
379 |
case "wx.get_private_file":
|
380 |
-
|
381 |
break
|
382 |
case "wx.get_group_file":
|
383 |
-
|
384 |
break
|
385 |
case "wx.get_private_redbag":
|
386 |
-
|
387 |
break
|
388 |
case "wx.get_group_redbag":
|
389 |
-
|
390 |
break
|
391 |
case "wx.get_private_poke":
|
392 |
data.operator_id = data.from_user_id
|
393 |
data.target_id = data.user_id
|
394 |
-
|
395 |
break
|
396 |
case "wx.get_group_poke":
|
397 |
data.operator_id = data.from_user_id
|
398 |
data.target_id = data.user_id
|
399 |
-
|
400 |
break
|
401 |
case "wx.get_private_card":
|
402 |
-
|
403 |
break
|
404 |
case "wx.get_group_card":
|
405 |
-
|
406 |
break
|
407 |
default:
|
408 |
-
|
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 |
-
|
426 |
data.sub_type = "add"
|
427 |
break
|
428 |
default:
|
429 |
-
|
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 |
-
|
448 |
}
|
449 |
}
|
450 |
|
451 |
message(data, ws) {
|
452 |
try {
|
453 |
-
data =
|
|
|
|
|
|
|
454 |
} catch (err) {
|
455 |
-
return
|
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 |
-
|
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 |
-
|
486 |
}
|
487 |
} else if (data.echo) {
|
488 |
Bot.emit(data.echo, data)
|
489 |
} else {
|
490 |
-
|
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 |
-
|
13 |
-
|
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 |
-
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: "
|
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 |
-
|
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 |
-
|
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 |
-
|
166 |
Bot.em(`connect.${data.self_id}`, data)
|
167 |
}
|
168 |
|
169 |
-
message(
|
170 |
try {
|
171 |
-
|
172 |
} catch (err) {
|
173 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
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 (
|
229 |
data.message_type = "private"
|
230 |
-
|
231 |
} else {
|
232 |
data.message_type = "group"
|
233 |
-
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
10 |
priority: 5000,
|
11 |
rule: [
|
12 |
{
|
13 |
/** 命令正则匹配 */
|
14 |
-
reg:
|
15 |
/** 执行方法 */
|
16 |
-
fnc:
|
|
|
17 |
}
|
18 |
]
|
19 |
})
|
20 |
}
|
21 |
|
22 |
/** 复读 */
|
23 |
-
async repeat
|
24 |
/** 设置上下文,后续接收到内容会执行doRep方法 */
|
25 |
-
this.setContext(
|
26 |
/** 回复 */
|
27 |
-
await this.reply(
|
28 |
}
|
29 |
|
30 |
/** 接受内容 */
|
31 |
-
doRep
|
32 |
/** 复读内容 */
|
33 |
this.reply(this.e.message, false, { recallMsg: 5 })
|
34 |
/** 结束上下文 */
|
35 |
-
this.finish(
|
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 |
-
|
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,
|
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:
|
41 |
-
event:
|
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 |
-
"
|
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 |
-
"
|
|
|
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 (!
|
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 (
|
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
|
92 |
-
if (
|
93 |
-
await
|
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
|
2 |
-
import {
|
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:
|
13 |
priority: 10,
|
14 |
-
rule: [
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
23 |
})
|
24 |
|
25 |
if (e) this.e = e
|
|
|
|
|
26 |
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
29 |
|
30 |
-
async
|
31 |
let restart = await redis.get(this.key)
|
32 |
-
if (restart)
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
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 |
-
|
45 |
}
|
|
|
46 |
}
|
47 |
|
48 |
-
async restart
|
49 |
-
await this.e.reply(
|
50 |
-
|
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 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
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 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
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
|
52 |
}
|
53 |
|
54 |
-
getLog(logFile) {
|
55 |
-
let log = fs.
|
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
|
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 |
-
|
48 |
if (plugin === false) return false
|
49 |
|
50 |
-
/** 执行更新 */
|
51 |
await this.runUpdate(plugin)
|
52 |
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
}
|
58 |
}
|
59 |
|
60 |
-
getPlugin(plugin = '') {
|
61 |
if (!plugin) {
|
62 |
plugin = this.e.msg.replace(/#(强制)?更新(日志)?/, '')
|
63 |
if (!plugin) return ''
|
64 |
}
|
65 |
|
66 |
-
if (!
|
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
|
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 |
-
|
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 |
-
|
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(
|
146 |
const msg = '更新失败!'
|
147 |
-
const errMsg = err.toString()
|
148 |
-
stdout = stdout.toString()
|
149 |
|
150 |
-
if (
|
151 |
-
const remote =
|
152 |
return this.reply(`${msg}\n连接超时:${remote}`)
|
153 |
}
|
154 |
|
155 |
-
if (/Failed to connect|unable to access/g.test(
|
156 |
-
const remote =
|
157 |
return this.reply(`${msg}\n连接失败:${remote}`)
|
158 |
}
|
159 |
|
160 |
-
if (
|
161 |
-
return this.reply(`${msg}\n存在冲突:\n${
|
162 |
}
|
163 |
|
164 |
if (stdout.includes('CONFLICT')) {
|
165 |
-
return this.reply(`${msg}\n存在冲突:\n${
|
166 |
}
|
167 |
|
168 |
-
return this.reply([
|
169 |
}
|
170 |
|
171 |
async updateAll() {
|
172 |
-
const dirs = fs.
|
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.
|
184 |
-
|
185 |
-
|
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 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
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 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
logger.error(error.toString())
|
229 |
-
await this.reply(error.toString())
|
230 |
}
|
231 |
|
232 |
-
return
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
let
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
8 |
|
9 |
-
app.reg({
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
23 |
}
|
24 |
-
}
|
25 |
-
})
|
26 |
|
27 |
-
|
|
|
|
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
|
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 |
-
|
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()
|
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.
|
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
|
222 |
-
if (
|
223 |
-
|
|
|
|
|
|
|
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 &&
|
264 |
-
msg[i] = { ...msg[i], file:
|
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 (!
|
281 |
|
282 |
try {
|
283 |
-
const message = JSON.parse(fs.
|
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 (!
|
298 |
|
299 |
try {
|
300 |
-
const message = JSON.parse(fs.
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
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: "
|
5 |
dsc: "Bot 操作",
|
6 |
event: "message",
|
7 |
rule: [
|
@@ -20,7 +20,10 @@ export class botOperate extends plugin {
|
|
20 |
}
|
21 |
|
22 |
Verify() {
|
23 |
-
const data = {
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
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
|
2 |
-
import common from '../../lib/common/common.js'
|
3 |
|
4 |
export class friend extends plugin {
|
5 |
constructor () {
|
6 |
super({
|
7 |
-
name:
|
8 |
-
dsc:
|
9 |
-
event:
|
10 |
})
|
11 |
}
|
12 |
|
13 |
async accept() {
|
14 |
-
if (this.e.sub_type ==
|
15 |
if (cfg.other.autoFriend == 1) {
|
16 |
logger.mark(`[自动同意][添加好友] ${this.e.user_id}`)
|
17 |
-
await
|
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:
|
7 |
-
dsc:
|
8 |
-
event:
|
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
|
21 |
-
let data = fs.
|
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.
|
30 |
}
|
31 |
|
32 |
-
async master
|
33 |
if (this.e.isMaster) {
|
34 |
-
await this.reply(
|
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(
|
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(
|
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
|