Upload 75 files
Browse files- Yunzai/.gitignore +5 -146
- Yunzai/.puppeteerrc.cjs +1 -1
- Yunzai/README.md +71 -34
- Yunzai/app.js +1 -1
- Yunzai/config/default_config/bot.yaml +9 -0
- Yunzai/config/default_config/redis.yaml +7 -5
- Yunzai/config/pm2.yaml +1 -1
- Yunzai/lib/bot.js +330 -203
- Yunzai/lib/common/common.js +12 -21
- Yunzai/lib/config/config.js +31 -48
- Yunzai/lib/config/init.js +27 -10
- Yunzai/lib/config/log.js +23 -26
- Yunzai/lib/config/redis.js +90 -47
- Yunzai/lib/listener/listener.js +1 -1
- Yunzai/lib/listener/loader.js +13 -15
- Yunzai/lib/modules/oicq/index.js +18 -11
- Yunzai/lib/plugins/config.js +2 -2
- Yunzai/lib/plugins/loader.js +90 -87
- Yunzai/lib/plugins/plugin.js +25 -22
- Yunzai/lib/plugins/runtime.js +21 -17
- Yunzai/lib/plugins/stdin.js +39 -25
- Yunzai/lib/puppeteer/puppeteer.js +10 -12
- Yunzai/lib/renderer/loader.js +7 -14
- Yunzai/lib/tools/docker.sh +109 -0
- Yunzai/package.json +9 -7
- Yunzai/plugins/.gitignore +1 -0
- Yunzai/plugins/adapter/ComWeChat.js +40 -17
- Yunzai/plugins/adapter/GSUIDCore.js +12 -9
- Yunzai/plugins/adapter/OPQBot.js +32 -9
- Yunzai/plugins/adapter/OneBotv11.js +78 -46
- Yunzai/plugins/example/package.json +4 -0
- Yunzai/plugins/example/主动复读.js +3 -3
- Yunzai/plugins/other/install.js +29 -29
- Yunzai/plugins/other/restart.js +109 -52
- Yunzai/plugins/other/sendLog.js +1 -1
- Yunzai/plugins/other/update.js +152 -127
- Yunzai/plugins/other/version.js +1 -1
- Yunzai/plugins/system/master.js +5 -5
- Yunzai/plugins/system/status.js +1 -1
- Yunzai/renderers/puppeteer/config_default.yaml +5 -0
- Yunzai/renderers/puppeteer/lib/puppeteer.js +12 -9
Yunzai/.gitignore
CHANGED
@@ -1,148 +1,7 @@
|
|
1 |
-
|
|
|
2 |
logs
|
3 |
-
|
4 |
-
|
5 |
-
yarn-debug.log*
|
6 |
-
yarn-error.log*
|
7 |
-
lerna-debug.log*
|
8 |
-
.pnpm-debug.log*
|
9 |
-
|
10 |
-
# Diagnostic reports (https://nodejs.org/api/report.html)
|
11 |
-
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
12 |
-
|
13 |
-
# Runtime data
|
14 |
-
pids
|
15 |
-
*.pid
|
16 |
-
*.seed
|
17 |
-
*.pid.lock
|
18 |
-
|
19 |
-
# Directory for instrumented libs generated by jscoverage/JSCover
|
20 |
-
lib-cov
|
21 |
-
|
22 |
-
# Coverage directory used by tools like istanbul
|
23 |
-
coverage
|
24 |
-
*.lcov
|
25 |
-
|
26 |
-
# nyc test coverage
|
27 |
-
.nyc_output
|
28 |
-
|
29 |
-
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
30 |
-
.grunt
|
31 |
-
|
32 |
-
# Bower dependency directory (https://bower.io/)
|
33 |
-
bower_components
|
34 |
-
|
35 |
-
# node-waf configuration
|
36 |
-
.lock-wscript
|
37 |
-
|
38 |
-
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
39 |
-
build/Release
|
40 |
-
|
41 |
-
# Dependency directories
|
42 |
-
node_modules/
|
43 |
-
jspm_packages/
|
44 |
-
|
45 |
-
# Snowpack dependency directory (https://snowpack.dev/)
|
46 |
-
web_modules/
|
47 |
-
|
48 |
-
# TypeScript cache
|
49 |
-
*.tsbuildinfo
|
50 |
-
|
51 |
-
# Optional npm cache directory
|
52 |
-
.npm
|
53 |
-
|
54 |
-
# Optional eslint cache
|
55 |
-
.eslintcache
|
56 |
-
|
57 |
-
# Optional stylelint cache
|
58 |
-
.stylelintcache
|
59 |
-
|
60 |
-
# Microbundle cache
|
61 |
-
.rpt2_cache/
|
62 |
-
.rts2_cache_cjs/
|
63 |
-
.rts2_cache_es/
|
64 |
-
.rts2_cache_umd/
|
65 |
-
|
66 |
-
# Optional REPL history
|
67 |
-
.node_repl_history
|
68 |
-
|
69 |
-
# Output of 'npm pack'
|
70 |
-
*.tgz
|
71 |
-
|
72 |
-
# Yarn Integrity file
|
73 |
-
.yarn-integrity
|
74 |
-
|
75 |
-
# dotenv environment variable files
|
76 |
-
.env
|
77 |
-
.env.development.local
|
78 |
-
.env.test.local
|
79 |
-
.env.production.local
|
80 |
-
.env.local
|
81 |
-
|
82 |
-
# parcel-bundler cache (https://parceljs.org/)
|
83 |
-
.cache
|
84 |
-
.parcel-cache
|
85 |
-
|
86 |
-
# Next.js build output
|
87 |
-
.next
|
88 |
-
out
|
89 |
-
|
90 |
-
# Nuxt.js build / generate output
|
91 |
-
.nuxt
|
92 |
-
dist
|
93 |
-
|
94 |
-
# Gatsby files
|
95 |
-
.cache/
|
96 |
-
# Comment in the public line in if your project uses Gatsby and not Next.js
|
97 |
-
# https://nextjs.org/blog/next-9-1#public-directory-support
|
98 |
-
# public
|
99 |
-
|
100 |
-
# vuepress build output
|
101 |
-
.vuepress/dist
|
102 |
-
|
103 |
-
# vuepress v2.x temp and cache directory
|
104 |
-
.temp
|
105 |
-
.cache
|
106 |
-
|
107 |
-
# Docusaurus cache and generated files
|
108 |
-
.docusaurus
|
109 |
-
|
110 |
-
# Serverless directories
|
111 |
-
.serverless/
|
112 |
-
|
113 |
-
# FuseBox cache
|
114 |
-
.fusebox/
|
115 |
-
|
116 |
-
# DynamoDB Local files
|
117 |
-
.dynamodb/
|
118 |
-
|
119 |
-
# TernJS port file
|
120 |
-
.tern-port
|
121 |
-
|
122 |
-
# Stores VSCode versions used for testing VSCode extensions
|
123 |
-
.vscode-test
|
124 |
-
.vscode/
|
125 |
-
|
126 |
-
# yarn v2
|
127 |
-
.yarn/cache
|
128 |
-
.yarn/unplugged
|
129 |
-
.yarn/build-state.yml
|
130 |
-
.yarn/install-state.gz
|
131 |
-
.pnp.*
|
132 |
-
|
133 |
-
# Yunzai data
|
134 |
dump.rdb
|
135 |
-
|
136 |
-
config/test/*
|
137 |
-
data/
|
138 |
-
!config/test/default.yaml
|
139 |
-
logs/
|
140 |
-
|
141 |
-
# Docker file
|
142 |
-
redis
|
143 |
-
yunzai
|
144 |
-
/.idea/
|
145 |
-
/data/
|
146 |
-
/temp/
|
147 |
-
/pnpm-lock.yaml
|
148 |
-
/entrypoint.sh
|
|
|
1 |
+
config/*.yaml
|
2 |
+
data
|
3 |
logs
|
4 |
+
node_modules
|
5 |
+
temp
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
dump.rdb
|
7 |
+
pnpm-lock.yaml
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Yunzai/.puppeteerrc.cjs
CHANGED
@@ -33,7 +33,7 @@ if (!executablePath) for (const item of [
|
|
33 |
}
|
34 |
|
35 |
if (executablePath || arch == "arm64" || arch == "aarch64") {
|
36 |
-
(typeof logger
|
37 |
skipDownload = true
|
38 |
}
|
39 |
|
|
|
33 |
}
|
34 |
|
35 |
if (executablePath || arch == "arm64" || arch == "aarch64") {
|
36 |
+
(typeof logger != "undefined" ? logger : console).info(`[Chromium] ${executablePath}`)
|
37 |
skipDownload = true
|
38 |
}
|
39 |
|
Yunzai/README.md
CHANGED
@@ -25,16 +25,18 @@ Yunzai 应用端,支持多账号,支持协议端:OneBotv11、ComWeChat、G
|
|
25 |
|
26 |
项目仅供学习交流使用,严禁用于任何商业用途和非法行为
|
27 |
|
28 |
-
##
|
29 |
|
30 |
-
|
31 |
|
32 |
- [🌌 TRSS](https://TRSS.me)
|
33 |
- [🔼 Vercel](https://TRSS-Script.Vercel.app)
|
34 |
- [🐱 GitHub](https://TimeRainStarSky.GitHub.io/TRSS_Script)
|
35 |
- [🇬 Gitee](https://Gitee.com/TimeRainStarSky/TRSS_Script)
|
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)
|
@@ -49,35 +51,86 @@ git clone --depth 1 https://gitee.com/TimeRainStarSky/Yunzai
|
|
49 |
cd Yunzai
|
50 |
```
|
51 |
|
52 |
-
2.
|
53 |
|
54 |
```sh
|
55 |
-
|
56 |
-
|
57 |
-
git clone --depth 1 https://github.com/TimeRainStarSky/TRSS-Plugin plugins/TRSS-Plugin
|
58 |
```
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
```sh
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
```
|
65 |
|
66 |
-
|
|
|
|
|
67 |
|
68 |
```sh
|
69 |
-
|
70 |
-
|
71 |
```
|
72 |
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
| 操作 | 命令 |
|
76 |
| ---- | ---- |
|
77 |
-
|
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
|
82 |
<details><summary>WebSocket</summary><blockquote>
|
83 |
|
@@ -182,23 +235,7 @@ ws://localhost:2536/GSUIDCore
|
|
182 |
|
183 |
</details>
|
184 |
|
185 |
-
|
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 |
|
|
|
25 |
|
26 |
项目仅供学习交流使用,严禁用于任何商业用途和非法行为
|
27 |
|
28 |
+
## 安装教程
|
29 |
|
30 |
+
<details><summary>脚本安装</summary>
|
31 |
|
32 |
- [🌌 TRSS](https://TRSS.me)
|
33 |
- [🔼 Vercel](https://TRSS-Script.Vercel.app)
|
34 |
- [🐱 GitHub](https://TimeRainStarSky.GitHub.io/TRSS_Script)
|
35 |
- [🇬 Gitee](https://Gitee.com/TimeRainStarSky/TRSS_Script)
|
36 |
|
37 |
+
</details>
|
38 |
+
|
39 |
+
<details><summary>手动安装</summary>
|
40 |
|
41 |
> 环境准备:Windows/Linux/MacOS/Android
|
42 |
> [Node.js(>=v21)](https://nodejs.org), [Redis](https://redis.io), [Git](https://git-scm.com), [Chrome(可选)](https://google.cn/chrome)
|
|
|
51 |
cd Yunzai
|
52 |
```
|
53 |
|
54 |
+
2. 安装 [pnpm](https://pnpm.io/zh/installation) 和依赖
|
55 |
|
56 |
```sh
|
57 |
+
npm i -g pnpm
|
58 |
+
pnpm i
|
|
|
59 |
```
|
60 |
|
61 |
+
3. 前台运行
|
62 |
+
|
63 |
+
| 操作 | 命令 |
|
64 |
+
| ---- | ---- |
|
65 |
+
| 启动 | node . |
|
66 |
+
| 停止 | node . stop |
|
67 |
+
|
68 |
+
4. 使用 [pm2](https://pm2.keymetrics.io) 后台运行
|
69 |
+
|
70 |
+
| 操作 | 命令 |
|
71 |
+
| ---- | ---- |
|
72 |
+
| 启动 | pnpm start |
|
73 |
+
| 停止 | pnpm stop |
|
74 |
+
| 日志 | pnpm log |
|
75 |
+
|
76 |
+
5. 开机自启
|
77 |
+
|
78 |
```sh
|
79 |
+
pnpm start
|
80 |
+
pnpm pm2 save
|
81 |
+
pnpm pm2 startup
|
82 |
```
|
83 |
|
84 |
+
</details>
|
85 |
+
|
86 |
+
<details><summary>Docker 安装</summary>
|
87 |
|
88 |
```sh
|
89 |
+
bash <(curl -L https://github.com/TimeRainStarSky/Yunzai/raw/main/lib/tools/docker.sh)
|
90 |
+
bash <(curl -L https://gitee.com/TimeRainStarSky/Yunzai/raw/main/lib/tools/docker.sh)
|
91 |
```
|
92 |
|
93 |
+
| 参数 | 描述 | 默认值 |
|
94 |
+
| ---- | ---- | ------ |
|
95 |
+
| DIR | 安装文件夹 | $HOME/Yunzai |
|
96 |
+
| CMD | 启动命令 | tsyz |
|
97 |
+
| CMDPATH | 命令文件夹 | /usr/local/bin |
|
98 |
+
| DKNAME | 容器名 | Yunzai |
|
99 |
+
| DKURL | Docker 源 | docker.nju.edu.cn |
|
100 |
+
| GITURL | GIT 源 | https://gitee.com/TimeRainStarSky/Yunzai |
|
101 |
+
| APTURL | APT 源 | mirrors.ustc.edu.cn |
|
102 |
+
| APTDEP | APT 依赖 | chromium fonts-lxgw-wenkai fonts-noto-color-emoji |
|
103 |
+
| NPMURL | NPM 源 | https://registry.npmmirror.com |
|
104 |
+
|
105 |
+
- 参数修改方法
|
106 |
+
|
107 |
+
```sh
|
108 |
+
参数1="值1" 参数2="值2" bash <(x)
|
109 |
+
```
|
110 |
|
111 |
| 操作 | 命令 |
|
112 |
| ---- | ---- |
|
113 |
+
| 连接 | tsyz |
|
114 |
+
| 断开 | Ctrl+P+Q |
|
115 |
+
| 启动 | tsyz start |
|
116 |
+
| 重启 | tsyz restart |
|
117 |
+
| 停止 | tsyz stop |
|
118 |
+
| 日志 | tsyz log 行数 |
|
119 |
+
| 命令 | tsyz 命令 |
|
120 |
+
|
121 |
+
</details>
|
122 |
|
123 |
+
## 使用教程
|
124 |
+
|
125 |
+
1. 推荐安装插件(可选)
|
126 |
+
|
127 |
+
```
|
128 |
+
#安装genshin
|
129 |
+
#安装miao-plugin
|
130 |
+
#安装TRSS-Plugin
|
131 |
+
```
|
132 |
+
|
133 |
+
2. 启动协议端
|
134 |
|
135 |
<details><summary>WebSocket</summary><blockquote>
|
136 |
|
|
|
235 |
|
236 |
</details>
|
237 |
|
238 |
+
3. 设置主人:发送 `#设置主人`,日志获取验证码并发送
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
|
240 |
## 班级群(¿
|
241 |
|
Yunzai/app.js
CHANGED
@@ -9,7 +9,7 @@ switch (process.env.app_type || process.argv[2]) {
|
|
9 |
const fetch = (await import("node-fetch")).default
|
10 |
try {
|
11 |
await fetch(`http://localhost:${cfg.bot.port}/exit`)
|
12 |
-
} catch
|
13 |
process.exit()
|
14 |
} default: {
|
15 |
const { spawnSync } = await import("node:child_process")
|
|
|
9 |
const fetch = (await import("node-fetch")).default
|
10 |
try {
|
11 |
await fetch(`http://localhost:${cfg.bot.port}/exit`)
|
12 |
+
} catch {}
|
13 |
process.exit()
|
14 |
} default: {
|
15 |
const { spawnSync } = await import("node:child_process")
|
Yunzai/config/default_config/bot.yaml
CHANGED
@@ -14,6 +14,15 @@ port: 2536
|
|
14 |
update_time: 1440
|
15 |
# 自动重启时间
|
16 |
restart_time: 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
# 上线推送通知的冷却时间
|
18 |
online_msg_exp: 1440
|
19 |
# 文件保存时间
|
|
|
14 |
update_time: 1440
|
15 |
# 自动重启时间
|
16 |
restart_time: 0
|
17 |
+
# 定时更新cron表达式
|
18 |
+
update_cron:
|
19 |
+
# 定时重启cron表达式
|
20 |
+
restart_cron:
|
21 |
+
# 定时关机cron表达式
|
22 |
+
stop_cron:
|
23 |
+
# 定时开机cron表达式
|
24 |
+
start_cron:
|
25 |
+
|
26 |
# 上线推送通知的冷却时间
|
27 |
online_msg_exp: 1440
|
28 |
# 文件保存时间
|
Yunzai/config/default_config/redis.yaml
CHANGED
@@ -1,10 +1,12 @@
|
|
1 |
-
#
|
|
|
|
|
2 |
host: 127.0.0.1
|
3 |
-
#
|
4 |
port: 6379
|
5 |
-
#
|
6 |
username:
|
7 |
-
#
|
8 |
password:
|
9 |
-
#
|
10 |
db: 0
|
|
|
1 |
+
# Redis 命令路径
|
2 |
+
path: redis-server
|
3 |
+
# Redis 地址
|
4 |
host: 127.0.0.1
|
5 |
+
# Redis 端口
|
6 |
port: 6379
|
7 |
+
# Redis 用户名
|
8 |
username:
|
9 |
+
# Redis 密码
|
10 |
password:
|
11 |
+
# Redis 数据库
|
12 |
db: 0
|
Yunzai/config/pm2.yaml
CHANGED
@@ -2,6 +2,6 @@ apps:
|
|
2 |
- name: TRSS-Yunzai
|
3 |
script: ./app.js
|
4 |
max_memory_restart: 512M
|
5 |
-
|
6 |
env:
|
7 |
app_type: pm2
|
|
|
2 |
- name: TRSS-Yunzai
|
3 |
script: ./app.js
|
4 |
max_memory_restart: 512M
|
5 |
+
exp_backoff_restart_delay: 1000
|
6 |
env:
|
7 |
app_type: pm2
|
Yunzai/lib/bot.js
CHANGED
@@ -1,58 +1,107 @@
|
|
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 {
|
16 |
-
import { exec } from "node:child_process"
|
17 |
import { fileTypeFromBuffer } from "file-type"
|
18 |
import md5 from "md5"
|
19 |
-
import {
|
20 |
|
21 |
export default class Yunzai extends EventEmitter {
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
38 |
-
if (["::1", "::ffff:127.0.0.1"].includes(req.ip) || req.hostname
|
39 |
process.exit(1)
|
40 |
})
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
44 |
return this[`server${err.code}`](err)
|
45 |
this.makeLog("error", err, "Server")
|
46 |
})
|
|
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
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) {
|
@@ -77,7 +126,7 @@ export default class Yunzai extends EventEmitter {
|
|
77 |
this.makeLog("error", ["监听端口", cfg.bot.port, "错误", err], "Server")
|
78 |
try {
|
79 |
await fetch(`http://localhost:${cfg.bot.port}/exit`)
|
80 |
-
} catch
|
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)
|
@@ -90,7 +139,7 @@ export default class Yunzai extends EventEmitter {
|
|
90 |
}
|
91 |
|
92 |
async run() {
|
93 |
-
await
|
94 |
await this.serverLoad()
|
95 |
await import("./plugins/stdin.js")
|
96 |
await PluginsLoader.load()
|
@@ -101,63 +150,60 @@ export default class Yunzai extends EventEmitter {
|
|
101 |
this.emit("online", this)
|
102 |
}
|
103 |
|
104 |
-
sleep(time) {
|
|
|
105 |
return new Promise(resolve => setTimeout(resolve, time))
|
106 |
}
|
107 |
|
108 |
-
async fsStat(path) { try {
|
109 |
-
|
110 |
-
return stat
|
111 |
} catch (err) {
|
112 |
this.makeLog("trace", ["获取", path, "状态错误", err])
|
113 |
return false
|
114 |
}}
|
115 |
|
116 |
-
async mkdir(dir) { try {
|
117 |
-
|
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
|
127 |
-
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
@@ -170,7 +216,7 @@ export default class Yunzai extends EventEmitter {
|
|
170 |
const set = map.set.bind(map)
|
171 |
Object.defineProperty(map, "set", {
|
172 |
value: async (key, value) => {
|
173 |
-
if (JSON.stringify(map.get(key))
|
174 |
set(key, value)
|
175 |
await save()
|
176 |
}
|
@@ -193,7 +239,7 @@ export default class Yunzai extends EventEmitter {
|
|
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))
|
197 |
set(key, value)
|
198 |
await map.db.put(key, value)
|
199 |
}
|
@@ -219,7 +265,7 @@ export default class Yunzai extends EventEmitter {
|
|
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, "
|
223 |
} catch (err) {
|
224 |
this.makeLog("error", ["读取", path, "错误", err])
|
225 |
}
|
@@ -230,12 +276,13 @@ export default class Yunzai extends EventEmitter {
|
|
230 |
}
|
231 |
|
232 |
async getMap(dir) {
|
233 |
-
const map = new Map
|
234 |
-
const db = new
|
|
|
235 |
try {
|
236 |
await db.open()
|
237 |
for await (let [key, value] of db.iterator()) {
|
238 |
-
if (typeof value
|
239 |
value = this.makeMap(map, key, new Map(value.map_array))
|
240 |
map.set(key, value)
|
241 |
}
|
@@ -259,29 +306,72 @@ export default class Yunzai extends EventEmitter {
|
|
259 |
return map
|
260 |
}
|
261 |
|
262 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
271 |
}
|
272 |
-
return JSON.stringify(data)
|
273 |
-
}
|
274 |
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
return "[object null]"
|
282 |
|
283 |
-
|
284 |
-
|
|
|
|
|
|
|
|
|
285 |
colors: true,
|
286 |
showHidden: true,
|
287 |
showProxy: true,
|
@@ -289,23 +379,25 @@ export default class Yunzai extends EventEmitter {
|
|
289 |
breakLength: 100,
|
290 |
maxArrayLength: 100,
|
291 |
maxStringLength: 1000,
|
292 |
-
...
|
293 |
})
|
|
|
|
|
|
|
|
|
|
|
294 |
}
|
295 |
|
296 |
async Buffer(data, opts = {}) {
|
297 |
if (Buffer.isBuffer(data)) return data
|
298 |
data = this.String(data)
|
299 |
|
300 |
-
if (data.
|
301 |
-
return Buffer.from(data.replace(
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
if (opts.file) return data
|
307 |
-
return Buffer.from(await fs.readFile(data.replace(/^file:\/\//, "")))
|
308 |
-
}
|
309 |
return data
|
310 |
}
|
311 |
|
@@ -322,14 +414,12 @@ export default class Yunzai extends EventEmitter {
|
|
322 |
if (Buffer.isBuffer(file.buffer)) {
|
323 |
file.type = await fileTypeFromBuffer(file.buffer)
|
324 |
file.md5 = md5(file.buffer)
|
325 |
-
|
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 |
-
|
332 |
-
file.name = `${Date.now()}-${path.basename(file.url)}`
|
333 |
return file
|
334 |
}
|
335 |
|
@@ -342,9 +432,9 @@ export default class Yunzai extends EventEmitter {
|
|
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) :
|
346 |
|
347 |
-
if (typeof times
|
348 |
this.fs[file.name] = file
|
349 |
if (time) setTimeout(() => this.fs[file.name] = this.fs.timeout, time)
|
350 |
return `${cfg.bot.url}/File/${file.name}`
|
@@ -352,10 +442,9 @@ export default class Yunzai extends EventEmitter {
|
|
352 |
|
353 |
fileSend(req) {
|
354 |
const url = req.url.replace(/^\//, "")
|
355 |
-
let file = this.fs[url]
|
356 |
-
if (!file) file = this.fs[404]
|
357 |
|
358 |
-
if (typeof file.times
|
359 |
if (file.times > 0) file.times--
|
360 |
else file = this.fs.timeout
|
361 |
}
|
@@ -367,30 +456,44 @@ export default class Yunzai extends EventEmitter {
|
|
367 |
req.res.send(file.buffer)
|
368 |
}
|
369 |
|
370 |
-
async exec(cmd) {
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
379 |
}
|
380 |
|
381 |
makeLog(level, msg, id) {
|
382 |
const log = []
|
383 |
-
if (id
|
|
|
384 |
for (const i of Array.isArray(msg) ? msg : [msg])
|
385 |
-
log.push(
|
386 |
-
logger[level](...log)
|
387 |
}
|
388 |
|
389 |
-
|
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", {
|
@@ -411,24 +514,20 @@ export default class Yunzai extends EventEmitter {
|
|
411 |
data.adapter_name = data.bot.adapter.name
|
412 |
|
413 |
for (const i of [data.friend, data.group, data.member]) {
|
414 |
-
if (typeof i
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
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.
|
428 |
while (true) {
|
429 |
this.emit(name, data)
|
430 |
const i = name.lastIndexOf(".")
|
431 |
-
if (i
|
432 |
name = name.slice(0, i)
|
433 |
}
|
434 |
}
|
@@ -436,7 +535,7 @@ export default class Yunzai extends EventEmitter {
|
|
436 |
getFriendArray() {
|
437 |
const array = []
|
438 |
for (const bot_id of this.uin)
|
439 |
-
for (const [id, i] of this[bot_id].fl || [])
|
440 |
array.push({ ...i, bot_id })
|
441 |
return array
|
442 |
}
|
@@ -444,15 +543,14 @@ export default class Yunzai extends EventEmitter {
|
|
444 |
getFriendList() {
|
445 |
const array = []
|
446 |
for (const bot_id of this.uin)
|
447 |
-
|
448 |
-
array.push(id)
|
449 |
return array
|
450 |
}
|
451 |
|
452 |
getFriendMap() {
|
453 |
const map = new Map
|
454 |
for (const bot_id of this.uin)
|
455 |
-
for (const [id, i] of this[bot_id].fl || [])
|
456 |
map.set(id, { ...i, bot_id })
|
457 |
return map
|
458 |
}
|
@@ -461,7 +559,7 @@ export default class Yunzai extends EventEmitter {
|
|
461 |
getGroupArray() {
|
462 |
const array = []
|
463 |
for (const bot_id of this.uin)
|
464 |
-
for (const [id, i] of this[bot_id].gl || [])
|
465 |
array.push({ ...i, bot_id })
|
466 |
return array
|
467 |
}
|
@@ -469,15 +567,14 @@ export default class Yunzai extends EventEmitter {
|
|
469 |
getGroupList() {
|
470 |
const array = []
|
471 |
for (const bot_id of this.uin)
|
472 |
-
|
473 |
-
array.push(id)
|
474 |
return array
|
475 |
}
|
476 |
|
477 |
getGroupMap() {
|
478 |
const map = new Map
|
479 |
for (const bot_id of this.uin)
|
480 |
-
for (const [id, i] of this[bot_id].gl || [])
|
481 |
map.set(id, { ...i, bot_id })
|
482 |
return map
|
483 |
}
|
@@ -485,100 +582,130 @@ export default class Yunzai extends EventEmitter {
|
|
485 |
get gml() {
|
486 |
const map = new Map
|
487 |
for (const bot_id of this.uin)
|
488 |
-
for (const [id, i] of this[bot_id].gml || [])
|
489 |
-
map.set(id, i)
|
490 |
return map
|
491 |
}
|
492 |
|
493 |
-
pickFriend(user_id) {
|
494 |
-
user_id = Number(user_id) ||
|
495 |
-
|
496 |
-
if (user)
|
497 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
498 |
}
|
499 |
get pickUser() { return this.pickFriend }
|
500 |
|
501 |
-
pickGroup(group_id) {
|
502 |
-
group_id = Number(group_id) ||
|
503 |
const group = this.gl.get(group_id)
|
504 |
-
if (group) return this[group.bot_id].pickGroup(group_id)
|
505 |
-
|
|
|
|
|
506 |
}
|
507 |
|
508 |
pickMember(group_id, user_id) {
|
509 |
-
|
510 |
-
if (group) return group.pickMember(user_id)
|
511 |
}
|
512 |
|
513 |
-
sendFriendMsg(bot_id, user_id,
|
514 |
-
|
515 |
-
|
516 |
-
return this.pickFriend(user_id).sendMsg(msg)
|
517 |
|
518 |
-
|
519 |
-
|
520 |
|
521 |
-
|
522 |
-
|
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 |
-
}
|
529 |
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
534 |
|
535 |
-
|
536 |
-
|
|
|
537 |
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
this.
|
543 |
-
|
544 |
-
return
|
545 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
546 |
|
547 |
-
|
548 |
-
if (typeof fnc
|
549 |
const { self_id, user_id } = fnc
|
550 |
fnc = data => data.self_id == self_id && data.user_id == user_id
|
551 |
}
|
552 |
|
553 |
-
|
554 |
-
const
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
}}
|
571 |
}
|
572 |
|
573 |
getMasterMsg() {
|
574 |
-
return this.getTextMsg(data =>
|
575 |
-
cfg.master[data.self_id]?.includes(String(data.user_id)))
|
576 |
}
|
577 |
|
578 |
-
sendMasterMsg(msg) {
|
579 |
-
|
580 |
-
|
581 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
582 |
}
|
583 |
|
584 |
makeForwardMsg(msg) { return { type: "node", data: msg } }
|
@@ -597,16 +724,16 @@ export default class Yunzai extends EventEmitter {
|
|
597 |
return messages
|
598 |
}
|
599 |
|
600 |
-
getTimeDiff(time1 = this.stat.start_time, time2 = Date.now()
|
601 |
-
const time = time2
|
602 |
let ret = ""
|
603 |
-
const day = Math.floor(time
|
604 |
if (day) ret += `${day}天`
|
605 |
-
const hour = Math.floor((time
|
606 |
if (hour) ret += `${hour}时`
|
607 |
-
const min = Math.floor((time
|
608 |
if (min) ret += `${min}分`
|
609 |
-
const sec =
|
610 |
if (sec) ret += `${sec}秒`
|
611 |
return ret || "0秒"
|
612 |
}
|
|
|
1 |
+
import init from "./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 "node:http"
|
8 |
import { WebSocketServer } from "ws"
|
|
|
9 |
import fs from "node:fs/promises"
|
10 |
import path from "node:path"
|
11 |
import util from "node:util"
|
12 |
import fetch from "node-fetch"
|
13 |
+
import { exec, execFile } from "node:child_process"
|
|
|
14 |
import { fileTypeFromBuffer } from "file-type"
|
15 |
import md5 from "md5"
|
16 |
+
import { ulid } from "ulid"
|
17 |
|
18 |
export default class Yunzai extends EventEmitter {
|
19 |
+
stat = { start_time: Date.now()/1000 }
|
20 |
+
bot = this
|
21 |
+
bots = {}
|
22 |
+
uin = Object.assign([], {
|
23 |
+
toJSON() {
|
24 |
+
if (!this.now) {
|
25 |
+
switch (this.length) {
|
26 |
+
case 0:
|
27 |
+
return ""
|
28 |
+
case 1:
|
29 |
+
case 2:
|
30 |
+
return this[this.length-1]
|
31 |
+
}
|
32 |
+
const array = this.slice(1)
|
33 |
+
this.now = array[Math.floor(Math.random()*array.length)]
|
34 |
+
setTimeout(() => delete this.now, 60000)
|
35 |
+
}
|
36 |
+
return this.now
|
37 |
+
},
|
38 |
+
toString(raw, ...args) {
|
39 |
+
return raw === true ?
|
40 |
+
this.__proto__.toString.apply(this, args) :
|
41 |
+
this.toJSON().toString(raw, ...args)
|
42 |
+
},
|
43 |
+
includes(value) {
|
44 |
+
return this.some(i => i == value)
|
45 |
+
},
|
46 |
+
})
|
47 |
+
adapter = []
|
48 |
+
|
49 |
+
express = Object.assign(express(), { quiet: [] })
|
50 |
+
.use(express.urlencoded({ extended: false }))
|
51 |
+
.use(express.json())
|
52 |
+
.use(express.raw())
|
53 |
+
.use(express.text())
|
54 |
+
.use("/status", req => req.res.send(process.memoryUsage()))
|
55 |
+
.use(req => {
|
56 |
+
let quiet = false
|
57 |
+
for (const i of req.app.quiet)
|
58 |
+
if (req.originalUrl.startsWith(i)) {
|
59 |
+
quiet = true
|
60 |
+
break
|
61 |
+
}
|
62 |
req.rid = `${req.ip}:${req.socket.remotePort}`
|
63 |
req.sid = `${req.protocol}://${req.hostname}:${req.socket.localPort}${req.originalUrl}`
|
64 |
+
this.makeLog(quiet?"debug":"mark", ["HTTP", req.method, "请求", req.headers, req.query, req.body], `${req.sid} <= ${req.rid}`)
|
65 |
req.next()
|
66 |
})
|
67 |
+
.use("/exit", req => {
|
68 |
+
if (["::1", "::ffff:127.0.0.1"].includes(req.ip) || req.hostname === "localhost")
|
69 |
process.exit(1)
|
70 |
})
|
71 |
+
.use("/File", (...args) => this.fileSend(...args))
|
72 |
+
|
73 |
+
server = http.createServer(this.express)
|
74 |
+
.on("error", err => {
|
75 |
+
if (typeof this[`server${err.code}`] === "function")
|
76 |
return this[`server${err.code}`](err)
|
77 |
this.makeLog("error", err, "Server")
|
78 |
})
|
79 |
+
.on("upgrade", (...args) => this.wsConnect(...args))
|
80 |
|
81 |
+
wss = new WebSocketServer({ noServer: true })
|
82 |
+
wsf = Object.create(null)
|
83 |
+
fs = Object.create(null)
|
84 |
+
|
85 |
+
constructor() {
|
86 |
+
super()
|
87 |
|
|
|
|
|
88 |
for (const name of [404, "timeout"])
|
89 |
this.fileToUrl(`resources/http/File/${name}.jpg`, { name, time: false, times: false })
|
90 |
+
|
91 |
+
return new Proxy(this.bots, {
|
92 |
+
get: (target, prop) => {
|
93 |
+
const value = this[prop] ?? target[prop]
|
94 |
+
if (value !== undefined) return value
|
95 |
+
for (const i of [this.uin.toString(), ...this.uin])
|
96 |
+
if (target[i]?.[prop] !== undefined) {
|
97 |
+
this.makeLog("trace", `因不存在 Bot.${prop} 而重定向到 Bot.${i}.${prop}`)
|
98 |
+
if (typeof target[i][prop]?.bind === "function")
|
99 |
+
return target[i][prop].bind(target[i])
|
100 |
+
return target[i][prop]
|
101 |
+
}
|
102 |
+
this.makeLog("trace", `不存在 Bot.${prop}`)
|
103 |
+
}
|
104 |
+
})
|
105 |
}
|
106 |
|
107 |
wsConnect(req, socket, head) {
|
|
|
126 |
this.makeLog("error", ["监听端口", cfg.bot.port, "错误", err], "Server")
|
127 |
try {
|
128 |
await fetch(`http://localhost:${cfg.bot.port}/exit`)
|
129 |
+
} catch {}
|
130 |
this.server_listen_time = (this.server_listen_time || 0) + 1
|
131 |
await this.sleep(this.server_listen_time * 1000)
|
132 |
this.server.listen(cfg.bot.port)
|
|
|
139 |
}
|
140 |
|
141 |
async run() {
|
142 |
+
await init()
|
143 |
await this.serverLoad()
|
144 |
await import("./plugins/stdin.js")
|
145 |
await PluginsLoader.load()
|
|
|
150 |
this.emit("online", this)
|
151 |
}
|
152 |
|
153 |
+
sleep(time, promise) {
|
154 |
+
if (promise) return Promise.race([promise, this.sleep(time)])
|
155 |
return new Promise(resolve => setTimeout(resolve, time))
|
156 |
}
|
157 |
|
158 |
+
async fsStat(path, opts) { try {
|
159 |
+
return await fs.stat(path, opts)
|
|
|
160 |
} catch (err) {
|
161 |
this.makeLog("trace", ["获取", path, "状态错误", err])
|
162 |
return false
|
163 |
}}
|
164 |
|
165 |
+
async mkdir(dir, opts) { try {
|
166 |
+
await fs.mkdir(dir, { recursive: true, ...opts })
|
|
|
|
|
167 |
return true
|
168 |
} catch (err) {
|
169 |
this.makeLog("error", ["创建", dir, "错误", err])
|
170 |
return false
|
171 |
}}
|
172 |
|
173 |
+
async rm(file, opts) { try {
|
174 |
+
await fs.rm(file, { force: true, recursive: true, ...opts })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
return true
|
176 |
} catch (err) {
|
177 |
this.makeLog("error", ["删除", file, "错误", err])
|
178 |
return false
|
179 |
}}
|
180 |
|
181 |
+
async glob(path, opts = {}) {
|
182 |
+
if (!opts.force && await this.fsStat(path))
|
183 |
+
return [path]
|
184 |
+
if (!fs.glob) return []
|
185 |
+
const array = []
|
186 |
+
try {
|
187 |
+
for await (const i of fs.glob(path, opts))
|
188 |
+
array.push(i)
|
189 |
+
} catch (err) {
|
190 |
+
this.makeLog("error", ["匹配", path, "错误", err])
|
191 |
+
}
|
192 |
+
return array
|
193 |
+
}
|
194 |
+
|
195 |
+
async download(url, file, opts) {
|
196 |
let buffer
|
197 |
if (!file || (await this.fsStat(file))?.isDirectory?.()) {
|
198 |
+
const type = await this.fileType(url, opts)
|
199 |
file = file ? path.join(file, type.name) : type.name
|
200 |
buffer = type.buffer
|
201 |
} else {
|
202 |
await this.mkdir(path.dirname(file))
|
203 |
+
buffer = await this.Buffer(url, opts)
|
204 |
}
|
205 |
await fs.writeFile(file, buffer)
|
206 |
+
return { url, file, buffer }
|
207 |
}
|
208 |
|
209 |
makeMap(parent_map, parent_key, map) {
|
|
|
216 |
const set = map.set.bind(map)
|
217 |
Object.defineProperty(map, "set", {
|
218 |
value: async (key, value) => {
|
219 |
+
if (JSON.stringify(map.get(key)) !== JSON.stringify(value)) {
|
220 |
set(key, value)
|
221 |
await save()
|
222 |
}
|
|
|
239 |
if (value instanceof Map) {
|
240 |
set(key, this.makeMap(map, key, value))
|
241 |
await map.db.put(key, { map_array: Array.from(value) })
|
242 |
+
} else if (JSON.stringify(map.get(key)) !== JSON.stringify(value)) {
|
243 |
set(key, value)
|
244 |
await map.db.put(key, value)
|
245 |
}
|
|
|
265 |
try {
|
266 |
await map.set(i, (await this.fsStat(path)).isDirectory() ?
|
267 |
await this.importMap(path, new Map) :
|
268 |
+
JSON.parse(await fs.readFile(path, "utf8")))
|
269 |
} catch (err) {
|
270 |
this.makeLog("error", ["读取", path, "错误", err])
|
271 |
}
|
|
|
276 |
}
|
277 |
|
278 |
async getMap(dir) {
|
279 |
+
const map = new Map
|
280 |
+
const db = new (await import("level"))
|
281 |
+
.Level(`${dir}-leveldb`, { valueEncoding: "json" })
|
282 |
try {
|
283 |
await db.open()
|
284 |
for await (let [key, value] of db.iterator()) {
|
285 |
+
if (typeof value === "object" && value.map_array)
|
286 |
value = this.makeMap(map, key, new Map(value.map_array))
|
287 |
map.set(key, value)
|
288 |
}
|
|
|
306 |
return map
|
307 |
}
|
308 |
|
309 |
+
StringOrNull(data) {
|
310 |
+
if (typeof data === "object" && typeof data.toString !== "function")
|
311 |
+
return "[object null]"
|
312 |
+
return String(data)
|
313 |
+
}
|
314 |
+
|
315 |
+
StringOrBuffer(data, base64) {
|
316 |
+
const string = String(data)
|
317 |
+
return string.includes("\ufffd") ? (base64 ? `base64://${data.toString("base64")}` : data) : string
|
318 |
+
}
|
319 |
+
|
320 |
+
getCircularReplacer() {
|
321 |
+
const _this_ = this, ancestors = []
|
322 |
+
return function (key, value) {
|
323 |
+
switch (typeof value) {
|
324 |
+
case "function":
|
325 |
+
return String(value)
|
326 |
+
case "object":
|
327 |
+
if (value === null)
|
328 |
+
return null
|
329 |
+
if (value instanceof Map || value instanceof Set)
|
330 |
+
return Array.from(value)
|
331 |
+
if (value instanceof Error)
|
332 |
+
return value.stack
|
333 |
+
if (value.type === "Buffer" && Array.isArray(value.data)) try {
|
334 |
+
return _this_.StringOrBuffer(Buffer.from(value), true)
|
335 |
+
} catch {}
|
336 |
+
break
|
337 |
+
default:
|
338 |
+
return value
|
339 |
+
}
|
340 |
+
while (ancestors.length > 0 && ancestors.at(-1) !== this)
|
341 |
+
ancestors.pop()
|
342 |
+
if (ancestors.includes(value))
|
343 |
+
return `[Circular ${_this_.StringOrNull(value)}]`
|
344 |
+
ancestors.push(value)
|
345 |
+
return value
|
346 |
+
}
|
347 |
+
}
|
348 |
+
|
349 |
+
String(data, opts) {
|
350 |
switch (typeof data) {
|
351 |
case "string":
|
352 |
return data
|
353 |
+
case "function":
|
354 |
+
return String(data)
|
355 |
case "object":
|
356 |
if (data instanceof Error)
|
357 |
return data.stack
|
358 |
if (Buffer.isBuffer(data))
|
359 |
+
return this.StringOrBuffer(data, true)
|
360 |
}
|
|
|
|
|
361 |
|
362 |
+
try {
|
363 |
+
return JSON.stringify(data, this.getCircularReplacer(), opts) || this.StringOrNull(data)
|
364 |
+
} catch (err) {
|
365 |
+
return this.StringOrNull(data)
|
366 |
+
}
|
367 |
+
}
|
|
|
368 |
|
369 |
+
Loging(data, opts = cfg.bot.log_object) {
|
370 |
+
if (typeof data === "string") {}
|
371 |
+
else if (!opts)
|
372 |
+
data = this.StringOrNull(data)
|
373 |
+
else data = util.inspect(data, {
|
374 |
+
depth: 10,
|
375 |
colors: true,
|
376 |
showHidden: true,
|
377 |
showProxy: true,
|
|
|
379 |
breakLength: 100,
|
380 |
maxArrayLength: 100,
|
381 |
maxStringLength: 1000,
|
382 |
+
...opts,
|
383 |
})
|
384 |
+
|
385 |
+
const length = opts.length || cfg.bot.log_length
|
386 |
+
if (data.length > length)
|
387 |
+
data = `${data.slice(0, length)}${logger.gray(`... ${data.length-length} more characters`)}`
|
388 |
+
return data
|
389 |
}
|
390 |
|
391 |
async Buffer(data, opts = {}) {
|
392 |
if (Buffer.isBuffer(data)) return data
|
393 |
data = this.String(data)
|
394 |
|
395 |
+
if (data.startsWith("base64://"))
|
396 |
+
return Buffer.from(data.replace("base64://", ""), "base64")
|
397 |
+
else if (data.match(/^https?:\/\//))
|
398 |
+
return opts.http ? data : Buffer.from(await (await fetch(data, opts)).arrayBuffer())
|
399 |
+
else if (await this.fsStat(data.replace(/^file:\/\//, "")))
|
400 |
+
return opts.file ? data : Buffer.from(await fs.readFile(data.replace(/^file:\/\//, "")))
|
|
|
|
|
|
|
401 |
return data
|
402 |
}
|
403 |
|
|
|
414 |
if (Buffer.isBuffer(file.buffer)) {
|
415 |
file.type = await fileTypeFromBuffer(file.buffer)
|
416 |
file.md5 = md5(file.buffer)
|
417 |
+
file.name ??= `${Date.now().toString(36)}.${file.md5.slice(0,8)}.${file.type.ext}`
|
|
|
418 |
}
|
419 |
} catch (err) {
|
420 |
this.makeLog("error", ["文件类型检测错误", file, err])
|
421 |
}
|
422 |
+
file.name ??= `${Date.now().toString(36)}-${path.basename(file.url)}`
|
|
|
423 |
return file
|
424 |
}
|
425 |
|
|
|
432 |
|
433 |
file = await this.fileType({ file, name }, { http: true })
|
434 |
if (!Buffer.isBuffer(file.buffer)) return file.buffer
|
435 |
+
file.name = file.name ? encodeURIComponent(file.name) : ulid()
|
436 |
|
437 |
+
if (typeof times === "number") file.times = times
|
438 |
this.fs[file.name] = file
|
439 |
if (time) setTimeout(() => this.fs[file.name] = this.fs.timeout, time)
|
440 |
return `${cfg.bot.url}/File/${file.name}`
|
|
|
442 |
|
443 |
fileSend(req) {
|
444 |
const url = req.url.replace(/^\//, "")
|
445 |
+
let file = this.fs[url] || this.fs[404]
|
|
|
446 |
|
447 |
+
if (typeof file.times === "number") {
|
448 |
if (file.times > 0) file.times--
|
449 |
else file = this.fs.timeout
|
450 |
}
|
|
|
456 |
req.res.send(file.buffer)
|
457 |
}
|
458 |
|
459 |
+
async exec(cmd, opts = {}) { return new Promise(resolve => {
|
460 |
+
const name = logger.cyan(this.String(cmd))
|
461 |
+
this.makeLog(opts.quiet?"debug":"mark", name, "执行命令")
|
462 |
+
opts.encoding ??= "buffer"
|
463 |
+
const callback = (error, stdout, stderr) => {
|
464 |
+
const raw = { stdout, stderr }
|
465 |
+
stdout = String(stdout).trim()
|
466 |
+
stderr = String(stderr).trim()
|
467 |
+
resolve({ error, stdout, stderr, raw })
|
468 |
+
this.makeLog(opts.quiet?"debug":"mark", `${name} ${logger.green(`[完成${this.getTimeDiff(start_time)}]`)} ${stdout?`\n${stdout}`:""}${stderr?logger.red(`\n${stderr}`):""}`, "执行命令")
|
469 |
+
if (error) this.makeLog(opts.quiet?"debug":"error", error, "执行命令")
|
470 |
+
}
|
471 |
+
const start_time = Date.now()
|
472 |
+
if (Array.isArray(cmd))
|
473 |
+
execFile(cmd.shift(), cmd, opts, callback)
|
474 |
+
else
|
475 |
+
exec(cmd, opts, callback)
|
476 |
+
})}
|
477 |
+
|
478 |
+
async cmdPath(cmd, opts = {}) {
|
479 |
+
const ret = await this.exec(`${process.platform === "win32" ? "where" : "command -v"} "${cmd}"`, { quiet: true, ...opts })
|
480 |
+
return ret.error ? false : ret.stdout
|
481 |
}
|
482 |
|
483 |
makeLog(level, msg, id) {
|
484 |
const log = []
|
485 |
+
if (id !== false)
|
486 |
+
log.push(logger.blue(`[${id || "TRSSYz"}]`))
|
487 |
for (const i of Array.isArray(msg) ? msg : [msg])
|
488 |
+
log.push(this.Loging(i))
|
489 |
+
logger.logger[level](...log)
|
490 |
}
|
491 |
|
492 |
+
prepareEvent(data) {
|
493 |
+
if (!this.bots[data.self_id]) return
|
494 |
if (!data.bot)
|
495 |
Object.defineProperty(data, "bot", {
|
496 |
+
value: this.bots[data.self_id],
|
497 |
})
|
498 |
if (!data.friend && data.user_id)
|
499 |
Object.defineProperty(data, "friend", {
|
|
|
514 |
data.adapter_name = data.bot.adapter.name
|
515 |
|
516 |
for (const i of [data.friend, data.group, data.member]) {
|
517 |
+
if (typeof i !== "object") continue
|
518 |
+
i.sendFile ??= (file, name) => i.sendMsg(segment.file(file, name))
|
519 |
+
i.makeForwardMsg ??= this.makeForwardMsg
|
520 |
+
i.sendForwardMsg ??= msg => this.sendForwardMsg(msg => i.sendMsg(msg), msg)
|
521 |
+
i.getInfo ??= () => i.info || i
|
|
|
|
|
|
|
|
|
522 |
}
|
523 |
}
|
524 |
|
525 |
em(name = "", data = {}) {
|
526 |
+
this.prepareEvent(data)
|
527 |
while (true) {
|
528 |
this.emit(name, data)
|
529 |
const i = name.lastIndexOf(".")
|
530 |
+
if (i === -1) break
|
531 |
name = name.slice(0, i)
|
532 |
}
|
533 |
}
|
|
|
535 |
getFriendArray() {
|
536 |
const array = []
|
537 |
for (const bot_id of this.uin)
|
538 |
+
for (const [id, i] of this.bots[bot_id].fl || [])
|
539 |
array.push({ ...i, bot_id })
|
540 |
return array
|
541 |
}
|
|
|
543 |
getFriendList() {
|
544 |
const array = []
|
545 |
for (const bot_id of this.uin)
|
546 |
+
array.push(...(this.bots[bot_id].fl?.keys() || []))
|
|
|
547 |
return array
|
548 |
}
|
549 |
|
550 |
getFriendMap() {
|
551 |
const map = new Map
|
552 |
for (const bot_id of this.uin)
|
553 |
+
for (const [id, i] of this.bots[bot_id].fl || [])
|
554 |
map.set(id, { ...i, bot_id })
|
555 |
return map
|
556 |
}
|
|
|
559 |
getGroupArray() {
|
560 |
const array = []
|
561 |
for (const bot_id of this.uin)
|
562 |
+
for (const [id, i] of this.bots[bot_id].gl || [])
|
563 |
array.push({ ...i, bot_id })
|
564 |
return array
|
565 |
}
|
|
|
567 |
getGroupList() {
|
568 |
const array = []
|
569 |
for (const bot_id of this.uin)
|
570 |
+
array.push(...(this.bots[bot_id].gl?.keys() || []))
|
|
|
571 |
return array
|
572 |
}
|
573 |
|
574 |
getGroupMap() {
|
575 |
const map = new Map
|
576 |
for (const bot_id of this.uin)
|
577 |
+
for (const [id, i] of this.bots[bot_id].gl || [])
|
578 |
map.set(id, { ...i, bot_id })
|
579 |
return map
|
580 |
}
|
|
|
582 |
get gml() {
|
583 |
const map = new Map
|
584 |
for (const bot_id of this.uin)
|
585 |
+
for (const [id, i] of this.bots[bot_id].gml || [])
|
586 |
+
map.set(id, Object.assign(new Map(i), { bot_id }))
|
587 |
return map
|
588 |
}
|
589 |
|
590 |
+
pickFriend(user_id, strict) {
|
591 |
+
user_id = Number(user_id) || user_id
|
592 |
+
let user = this.fl.get(user_id)
|
593 |
+
if (!user) for (const [id, ml] of this.gml) {
|
594 |
+
user = ml.get(user_id)
|
595 |
+
if (user) {
|
596 |
+
user.bot_id = ml.bot_id
|
597 |
+
break
|
598 |
+
}
|
599 |
+
}
|
600 |
+
if (user) return this.bots[user.bot_id].pickFriend(user_id)
|
601 |
+
if (strict) return false
|
602 |
+
this.makeLog("debug", ["因不存在用户", user_id, "而随机选择Bot", this.uin.toJSON()])
|
603 |
+
return this.bots[this.uin].pickFriend(user_id)
|
604 |
}
|
605 |
get pickUser() { return this.pickFriend }
|
606 |
|
607 |
+
pickGroup(group_id, strict) {
|
608 |
+
group_id = Number(group_id) || group_id
|
609 |
const group = this.gl.get(group_id)
|
610 |
+
if (group) return this.bots[group.bot_id].pickGroup(group_id)
|
611 |
+
if (strict) return false
|
612 |
+
this.makeLog("debug", ["因不存在群", group_id, "而随机选择Bot", this.uin.toJSON()])
|
613 |
+
return this.bots[this.uin].pickGroup(group_id)
|
614 |
}
|
615 |
|
616 |
pickMember(group_id, user_id) {
|
617 |
+
return this.pickGroup(group_id).pickMember(user_id)
|
|
|
618 |
}
|
619 |
|
620 |
+
sendFriendMsg(bot_id, user_id, ...args) { try {
|
621 |
+
if (!bot_id)
|
622 |
+
return this.pickFriend(user_id).sendMsg(...args)
|
|
|
623 |
|
624 |
+
if (this.uin.includes(bot_id) && this.bots[bot_id])
|
625 |
+
return this.bots[bot_id].pickFriend(user_id).sendMsg(...args)
|
626 |
|
627 |
+
if (this.pickFriend(bot_id, true))
|
628 |
+
return this.pickFriend(bot_id).sendMsg(user_id, ...args)
|
|
|
|
|
|
|
|
|
|
|
|
|
629 |
|
630 |
+
return new Promise((resolve, reject) => {
|
631 |
+
const listener = data => {
|
632 |
+
resolve(data.bot.pickFriend(user_id).sendMsg(...args))
|
633 |
+
clearTimeout(timeout)
|
634 |
+
}
|
635 |
+
const timeout = setTimeout(() => {
|
636 |
+
reject(Object.assign(Error("等待 Bot 上线超时"), { bot_id, user_id, args }))
|
637 |
+
this.off(`connect.${bot_id}`, listener)
|
638 |
+
}, 300000)
|
639 |
+
this.once(`connect.${bot_id}`, listener)
|
640 |
+
})
|
641 |
+
} catch (err) {
|
642 |
+
this.makeLog("error", ["发送好友消息错误", args, err], `${bot_id} => ${user_id}`)
|
643 |
+
}}
|
644 |
|
645 |
+
sendGroupMsg(bot_id, group_id, ...args) { try {
|
646 |
+
if (!bot_id)
|
647 |
+
return this.pickGroup(group_id).sendMsg(...args)
|
648 |
|
649 |
+
if (this.uin.includes(bot_id) && this.bots[bot_id])
|
650 |
+
return this.bots[bot_id].pickGroup(group_id).sendMsg(...args)
|
651 |
+
|
652 |
+
if (this.pickGroup(bot_id, true))
|
653 |
+
return this.pickGroup(bot_id).sendMsg(group_id, ...args)
|
654 |
+
|
655 |
+
return new Promise((resolve, reject) => {
|
656 |
+
const listener = data => {
|
657 |
+
resolve(data.bot.pickGroup(group_id).sendMsg(...args))
|
658 |
+
clearTimeout(timeout)
|
659 |
+
}
|
660 |
+
const timeout = setTimeout(() => {
|
661 |
+
reject(Object.assign(Error("等待 Bot 上线超时"), { bot_id, group_id, args }))
|
662 |
+
this.off(`connect.${bot_id}`, listener)
|
663 |
+
}, 300000)
|
664 |
+
this.once(`connect.${bot_id}`, listener)
|
665 |
+
})
|
666 |
+
} catch (err) {
|
667 |
+
this.makeLog("error", ["发送群消息错误", args, err], `${bot_id} => ${group_id}`)
|
668 |
+
}}
|
669 |
|
670 |
+
getTextMsg(fnc = () => true) {
|
671 |
+
if (typeof fnc !== "function") {
|
672 |
const { self_id, user_id } = fnc
|
673 |
fnc = data => data.self_id == self_id && data.user_id == user_id
|
674 |
}
|
675 |
|
676 |
+
return new Promise(resolve => {
|
677 |
+
const listener = data => { try {
|
678 |
+
if (!fnc(data)) return
|
679 |
+
|
680 |
+
let msg = ""
|
681 |
+
for (const i of data.message)
|
682 |
+
if (i.type === "text" && i.text)
|
683 |
+
msg += i.text.trim()
|
684 |
+
if (!msg) return
|
685 |
+
|
686 |
+
resolve(msg)
|
687 |
+
this.off("message", listener)
|
688 |
+
} catch (err) {
|
689 |
+
this.makeLog("error", err, data.self_id)
|
690 |
+
}}
|
691 |
+
this.on("message", listener)
|
692 |
+
})
|
|
|
693 |
}
|
694 |
|
695 |
getMasterMsg() {
|
696 |
+
return this.getTextMsg(data => cfg.master[data.self_id]?.includes(String(data.user_id)))
|
|
|
697 |
}
|
698 |
|
699 |
+
async sendMasterMsg(msg, bot_array = Object.keys(cfg.master), sleep = 5000) {
|
700 |
+
const ret = {}
|
701 |
+
await Promise.allSettled((Array.isArray(bot_array) ? bot_array : [bot_array]).map(async bot_id => {
|
702 |
+
ret[bot_id] = {}
|
703 |
+
for (const user_id of cfg.master[bot_id] || []) {
|
704 |
+
ret[bot_id][user_id] = this.sendFriendMsg(bot_id, user_id, msg)
|
705 |
+
if (sleep) await this.sleep(sleep, ret[bot_id][user_id])
|
706 |
+
}
|
707 |
+
}))
|
708 |
+
return ret
|
709 |
}
|
710 |
|
711 |
makeForwardMsg(msg) { return { type: "node", data: msg } }
|
|
|
724 |
return messages
|
725 |
}
|
726 |
|
727 |
+
getTimeDiff(time1 = this.stat.start_time*1000, time2 = Date.now()) {
|
728 |
+
const time = (time2-time1)/1000
|
729 |
let ret = ""
|
730 |
+
const day = Math.floor(time/3600/24)
|
731 |
if (day) ret += `${day}天`
|
732 |
+
const hour = Math.floor((time/3600)%24)
|
733 |
if (hour) ret += `${hour}时`
|
734 |
+
const min = Math.floor((time/60)%60)
|
735 |
if (min) ret += `${min}分`
|
736 |
+
const sec = (time%60).toFixed(3)
|
737 |
if (sec) ret += `${sec}秒`
|
738 |
return ret || "0秒"
|
739 |
}
|
Yunzai/lib/common/common.js
CHANGED
@@ -1,16 +1,13 @@
|
|
1 |
-
import
|
2 |
-
import { promisify } from 'util'
|
3 |
-
import fetch from 'node-fetch'
|
4 |
-
import fs from 'node:fs'
|
5 |
-
import path from 'node:path'
|
6 |
|
7 |
/**
|
8 |
* 发送私聊消息
|
9 |
* @param user_id 账号
|
10 |
* @param msg 消息
|
|
|
11 |
*/
|
12 |
-
function relpyPrivate(
|
13 |
-
return Bot.
|
14 |
}
|
15 |
|
16 |
/**
|
@@ -18,22 +15,18 @@ function relpyPrivate(userId, msg) {
|
|
18 |
* @param ms 毫秒
|
19 |
*/
|
20 |
function sleep(ms) {
|
21 |
-
return new Promise(
|
22 |
}
|
23 |
|
24 |
/**
|
25 |
* 下载保存文件
|
26 |
-
* @param
|
27 |
-
* @param
|
|
|
28 |
*/
|
29 |
-
async function downFile(
|
30 |
try {
|
31 |
-
|
32 |
-
logger.debug(`[下载文件] ${fileUrl}`)
|
33 |
-
const response = await fetch(fileUrl,param)
|
34 |
-
const streamPipeline = promisify(pipeline)
|
35 |
-
await streamPipeline(response.body, fs.createWriteStream(savePath))
|
36 |
-
return true
|
37 |
} catch (err) {
|
38 |
logger.error(`下载文件错误:${err}`)
|
39 |
return false
|
@@ -42,10 +35,8 @@ async function downFile(fileUrl, savePath,param = {}) {
|
|
42 |
|
43 |
function mkdirs(dirname) {
|
44 |
if (fs.existsSync(dirname)) return true
|
45 |
-
|
46 |
-
|
47 |
-
return true
|
48 |
-
}
|
49 |
}
|
50 |
|
51 |
/**
|
|
|
1 |
+
import fs from "node:fs"
|
|
|
|
|
|
|
|
|
2 |
|
3 |
/**
|
4 |
* 发送私聊消息
|
5 |
* @param user_id 账号
|
6 |
* @param msg 消息
|
7 |
+
* @param bot_id 机器人账号
|
8 |
*/
|
9 |
+
function relpyPrivate(user_id, msg, bot_id) {
|
10 |
+
return Bot.sendFriendMsg(bot_id, user_id, msg)
|
11 |
}
|
12 |
|
13 |
/**
|
|
|
15 |
* @param ms 毫秒
|
16 |
*/
|
17 |
function sleep(ms) {
|
18 |
+
return new Promise(resolve => setTimeout(resolve, ms))
|
19 |
}
|
20 |
|
21 |
/**
|
22 |
* 下载保存文件
|
23 |
+
* @param url 下载地址
|
24 |
+
* @param file 保存路径
|
25 |
+
* @param opts 下载参数
|
26 |
*/
|
27 |
+
async function downFile(url, file, opts) {
|
28 |
try {
|
29 |
+
return await Bot.download(url, file, opts)
|
|
|
|
|
|
|
|
|
|
|
30 |
} catch (err) {
|
31 |
logger.error(`下载文件错误:${err}`)
|
32 |
return false
|
|
|
35 |
|
36 |
function mkdirs(dirname) {
|
37 |
if (fs.existsSync(dirname)) return true
|
38 |
+
fs.mkdirSync(dirname, { recursive: true })
|
39 |
+
return true
|
|
|
|
|
40 |
}
|
41 |
|
42 |
/**
|
Yunzai/lib/config/config.js
CHANGED
@@ -6,11 +6,12 @@ import chokidar from "chokidar"
|
|
6 |
class Cfg {
|
7 |
constructor() {
|
8 |
this.config = {}
|
9 |
-
|
10 |
/** 监听文件 */
|
11 |
this.watcher = { config: {}, defSet: {} }
|
12 |
-
|
13 |
this.initCfg()
|
|
|
|
|
|
|
14 |
}
|
15 |
|
16 |
/** 初始化配置 */
|
@@ -26,43 +27,22 @@ class Cfg {
|
|
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 }
|
34 |
-
|
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 |
-
|
46 |
-
get renderer() {
|
47 |
-
return this.getConfig("renderer");
|
48 |
-
}
|
49 |
-
|
50 |
/** 主人账号 */
|
51 |
get masterQQ() {
|
52 |
-
let masterQQ = this.
|
53 |
|
54 |
if (!Array.isArray(masterQQ))
|
55 |
masterQQ = [masterQQ]
|
56 |
|
57 |
const masters = []
|
58 |
for (const i of masterQQ)
|
59 |
-
masters.push(Number(i) ||
|
60 |
return masters
|
61 |
}
|
62 |
|
63 |
/** Bot账号:[主人帐号] */
|
64 |
get master() {
|
65 |
-
let master = this.
|
66 |
|
67 |
if (!Array.isArray(master))
|
68 |
master = [master]
|
@@ -98,10 +78,8 @@ class Cfg {
|
|
98 |
|
99 |
/** 群配置 */
|
100 |
getGroup(bot_id = "", group_id = "") {
|
101 |
-
const config = this.
|
102 |
-
const defCfg = this.getdefSet("group")
|
103 |
return {
|
104 |
-
...defCfg.default,
|
105 |
...config.default,
|
106 |
...config[`${bot_id}:default`],
|
107 |
...config[group_id],
|
@@ -111,9 +89,7 @@ class Cfg {
|
|
111 |
|
112 |
/** other配置 */
|
113 |
getOther() {
|
114 |
-
|
115 |
-
let config = this.getConfig("other")
|
116 |
-
return { ...def, ...config }
|
117 |
}
|
118 |
|
119 |
/**
|
@@ -129,39 +105,47 @@ class Cfg {
|
|
129 |
return this.getYaml("config", name)
|
130 |
}
|
131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
/**
|
133 |
* 获取配置yaml
|
134 |
* @param type 默认跑配置-defSet,用户配置-config
|
135 |
* @param name 名称
|
136 |
*/
|
137 |
getYaml(type, name) {
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
fs.readFileSync(file, "utf8")
|
144 |
-
)
|
|
|
|
|
|
|
145 |
|
|
|
146 |
this.watch(file, name, type)
|
147 |
-
|
148 |
return this.config[key]
|
149 |
}
|
150 |
|
151 |
/** 监听配置文件 */
|
152 |
watch(file, name, type = "default_config") {
|
153 |
-
|
154 |
|
155 |
if (this.watcher[key]) return
|
156 |
-
|
157 |
const watcher = chokidar.watch(file)
|
158 |
watcher.on("change", path => {
|
159 |
delete this.config[key]
|
160 |
-
if (typeof Bot
|
161 |
-
|
162 |
-
if (
|
163 |
this[`change_${name}`]()
|
164 |
-
}
|
165 |
})
|
166 |
|
167 |
this.watcher[key] = watcher
|
@@ -169,8 +153,7 @@ class Cfg {
|
|
169 |
|
170 |
async change_bot() {
|
171 |
/** 修改日志等级 */
|
172 |
-
|
173 |
-
log.default()
|
174 |
}
|
175 |
}
|
176 |
|
|
|
6 |
class Cfg {
|
7 |
constructor() {
|
8 |
this.config = {}
|
|
|
9 |
/** 监听文件 */
|
10 |
this.watcher = { config: {}, defSet: {} }
|
|
|
11 |
this.initCfg()
|
12 |
+
return new Proxy(this, {
|
13 |
+
get: (target, prop) => target[prop] ?? target.getAllCfg(String(prop)),
|
14 |
+
})
|
15 |
}
|
16 |
|
17 |
/** 初始化配置 */
|
|
|
27 |
fs.mkdirSync(i)
|
28 |
}
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
/** 主人账号 */
|
31 |
get masterQQ() {
|
32 |
+
let masterQQ = this.getAllCfg("other").masterQQ || []
|
33 |
|
34 |
if (!Array.isArray(masterQQ))
|
35 |
masterQQ = [masterQQ]
|
36 |
|
37 |
const masters = []
|
38 |
for (const i of masterQQ)
|
39 |
+
masters.push(Number(i) || i)
|
40 |
return masters
|
41 |
}
|
42 |
|
43 |
/** Bot账号:[主人帐号] */
|
44 |
get master() {
|
45 |
+
let master = this.getAllCfg("other").master || []
|
46 |
|
47 |
if (!Array.isArray(master))
|
48 |
master = [master]
|
|
|
78 |
|
79 |
/** 群配置 */
|
80 |
getGroup(bot_id = "", group_id = "") {
|
81 |
+
const config = this.getAllCfg("group")
|
|
|
82 |
return {
|
|
|
83 |
...config.default,
|
84 |
...config[`${bot_id}:default`],
|
85 |
...config[group_id],
|
|
|
89 |
|
90 |
/** other配置 */
|
91 |
getOther() {
|
92 |
+
return this.getAllCfg("other")
|
|
|
|
|
93 |
}
|
94 |
|
95 |
/**
|
|
|
105 |
return this.getYaml("config", name)
|
106 |
}
|
107 |
|
108 |
+
getAllCfg(name) {
|
109 |
+
return {
|
110 |
+
...this.getdefSet(name),
|
111 |
+
...this.getConfig(name),
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
/**
|
116 |
* 获取配置yaml
|
117 |
* @param type 默认跑配置-defSet,用户配置-config
|
118 |
* @param name 名称
|
119 |
*/
|
120 |
getYaml(type, name) {
|
121 |
+
const key = `${type}.${name}`
|
122 |
+
if (key in this.config) return this.config[key]
|
123 |
+
const file = `config/${type}/${name}.yaml`
|
124 |
+
|
125 |
+
try {
|
126 |
+
this.config[key] = fs.readFileSync(file, "utf8")
|
127 |
+
} catch (err) {
|
128 |
+
Bot.makeLog("trace", ["读取配置文件", file, "错误", err], "Config")
|
129 |
+
return this.config[key] = undefined
|
130 |
+
}
|
131 |
|
132 |
+
this.config[key] = YAML.parse(this.config[key])
|
133 |
this.watch(file, name, type)
|
|
|
134 |
return this.config[key]
|
135 |
}
|
136 |
|
137 |
/** 监听配置文件 */
|
138 |
watch(file, name, type = "default_config") {
|
139 |
+
const key = `${type}.${name}`
|
140 |
|
141 |
if (this.watcher[key]) return
|
|
|
142 |
const watcher = chokidar.watch(file)
|
143 |
watcher.on("change", path => {
|
144 |
delete this.config[key]
|
145 |
+
if (typeof Bot !== "object") return
|
146 |
+
Bot.makeLog("mark", `[修改配置文件][${type}][${name}]`, "Config")
|
147 |
+
if (`change_${name}` in this)
|
148 |
this[`change_${name}`]()
|
|
|
149 |
})
|
150 |
|
151 |
this.watcher[key] = watcher
|
|
|
153 |
|
154 |
async change_bot() {
|
155 |
/** 修改日志等级 */
|
156 |
+
(await import("./log.js")).default()
|
|
|
157 |
}
|
158 |
}
|
159 |
|
Yunzai/lib/config/init.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import setLog from "./log.js"
|
2 |
import cfg from "./config.js"
|
|
|
3 |
|
4 |
/** 设置标题 */
|
5 |
process.title = `TRSS Yunzai v${cfg.package.version} © 2023 - 2024 TimeRainStarSky`
|
@@ -7,7 +8,8 @@ process.title = `TRSS Yunzai v${cfg.package.version} © 2023 - 2024 TimeRainStar
|
|
7 |
/** 设置时区 */
|
8 |
process.env.TZ = "Asia/Shanghai"
|
9 |
|
10 |
-
|
|
|
11 |
|
12 |
/** 日志设置 */
|
13 |
setLog()
|
@@ -23,14 +25,29 @@ for (const i of ["uncaughtException", "unhandledRejection"])
|
|
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"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import setLog from "./log.js"
|
2 |
import cfg from "./config.js"
|
3 |
+
import redisInit from "./redis.js"
|
4 |
|
5 |
/** 设置标题 */
|
6 |
process.title = `TRSS Yunzai v${cfg.package.version} © 2023 - 2024 TimeRainStarSky`
|
|
|
8 |
/** 设置时区 */
|
9 |
process.env.TZ = "Asia/Shanghai"
|
10 |
|
11 |
+
for (const i of ["SIGHUP", "SIGTERM"])
|
12 |
+
process.on(i, (signal, code) => process.exit(code))
|
13 |
|
14 |
/** 日志设置 */
|
15 |
setLog()
|
|
|
25 |
}
|
26 |
})
|
27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
logger.mark("----^_^----")
|
29 |
logger.mark(logger.yellow(`TRSS-Yunzai v${cfg.package.version} 启动中...`))
|
30 |
+
logger.mark(logger.cyan("https://github.com/TimeRainStarSky/Yunzai"))
|
31 |
+
|
32 |
+
let stack
|
33 |
+
export default async function init() {
|
34 |
+
if (stack !== undefined) return
|
35 |
+
stack = ""
|
36 |
+
|
37 |
+
const redis = await redisInit()
|
38 |
+
const exit = process.exit
|
39 |
+
process.exit = async code => {
|
40 |
+
stack = Error().stack
|
41 |
+
if (redis.process) {
|
42 |
+
await Bot.sleep(5000, redis.save())
|
43 |
+
redis.process.kill()
|
44 |
+
}
|
45 |
+
exit(code)
|
46 |
+
}
|
47 |
+
|
48 |
+
/** 退出事件 */
|
49 |
+
process.on("exit", code => {
|
50 |
+
Bot.makeLog("mark", logger.magenta(`TRSS-Yunzai 已停止运行,本次运行时长:${Bot.getTimeDiff()} (${code})`), "exit")
|
51 |
+
Bot.makeLog("trace", (stack || Error().stack), "exit")
|
52 |
+
})
|
53 |
+
}
|
Yunzai/lib/config/log.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import log4js from "log4js"
|
2 |
-
import
|
3 |
import cfg from "./config.js"
|
4 |
import fs from "node:fs"
|
5 |
|
@@ -7,9 +7,8 @@ import fs from "node:fs"
|
|
7 |
* 设置日志样式
|
8 |
*/
|
9 |
export default function setLog() {
|
10 |
-
|
11 |
-
|
12 |
-
fs.mkdirSync(file)
|
13 |
|
14 |
/** 调整error日志等级 */
|
15 |
// log4js.levels.levels[5].level = Number.MAX_VALUE
|
@@ -21,7 +20,7 @@ export default function setLog() {
|
|
21 |
type: "console",
|
22 |
layout: {
|
23 |
type: "pattern",
|
24 |
-
pattern: "%[[
|
25 |
}
|
26 |
},
|
27 |
command: {
|
@@ -32,7 +31,7 @@ export default function setLog() {
|
|
32 |
alwaysIncludePattern: true,
|
33 |
layout: {
|
34 |
type: "pattern",
|
35 |
-
pattern: "[%d{hh:mm:ss.SSS}][%4.4p]
|
36 |
}
|
37 |
},
|
38 |
error: {
|
@@ -41,7 +40,7 @@ export default function setLog() {
|
|
41 |
alwaysIncludePattern: true,
|
42 |
layout: {
|
43 |
type: "pattern",
|
44 |
-
pattern: "[%d{hh:mm:ss.SSS}][%4.4p]
|
45 |
}
|
46 |
}
|
47 |
},
|
@@ -52,25 +51,23 @@ export default function setLog() {
|
|
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 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
blue: chalk.blue,
|
73 |
-
magenta: chalk.magenta,
|
74 |
-
cyan: chalk.cyan,
|
75 |
}
|
|
|
|
|
|
|
|
|
|
|
76 |
}
|
|
|
1 |
import log4js from "log4js"
|
2 |
+
import { Chalk } from "chalk"
|
3 |
import cfg from "./config.js"
|
4 |
import fs from "node:fs"
|
5 |
|
|
|
7 |
* 设置日志样式
|
8 |
*/
|
9 |
export default function setLog() {
|
10 |
+
if (!fs.existsSync("logs"))
|
11 |
+
fs.mkdirSync("logs")
|
|
|
12 |
|
13 |
/** 调整error日志等级 */
|
14 |
// log4js.levels.levels[5].level = Number.MAX_VALUE
|
|
|
20 |
type: "console",
|
21 |
layout: {
|
22 |
type: "pattern",
|
23 |
+
pattern: "%[[%d{hh:mm:ss.SSS}][%4.4p]%]%m"
|
24 |
}
|
25 |
},
|
26 |
command: {
|
|
|
31 |
alwaysIncludePattern: true,
|
32 |
layout: {
|
33 |
type: "pattern",
|
34 |
+
pattern: "[%d{hh:mm:ss.SSS}][%4.4p]%m"
|
35 |
}
|
36 |
},
|
37 |
error: {
|
|
|
40 |
alwaysIncludePattern: true,
|
41 |
layout: {
|
42 |
type: "pattern",
|
43 |
+
pattern: "[%d{hh:mm:ss.SSS}][%4.4p]%m"
|
44 |
}
|
45 |
}
|
46 |
},
|
|
|
51 |
}
|
52 |
})
|
53 |
|
|
|
|
|
|
|
|
|
54 |
/** 全局变量 logger */
|
55 |
+
const chalk = new Chalk({ level: 3 })
|
56 |
+
chalk.logger = {
|
57 |
+
defaultLogger: log4js.getLogger("message"),
|
58 |
+
commandLogger: log4js.getLogger("command"),
|
59 |
+
errorLogger: log4js.getLogger("error"),
|
60 |
+
trace(...args) { return this.defaultLogger.trace(...args) },
|
61 |
+
debug(...args) { return this.defaultLogger.debug(...args) },
|
62 |
+
info(...args) { return this.defaultLogger.info(...args) },
|
63 |
+
warn(...args) { return this.commandLogger.warn(...args) },
|
64 |
+
error(...args) { return this.errorLogger.error(...args) },
|
65 |
+
fatal(...args) { return this.errorLogger.fatal(...args) },
|
66 |
+
mark(...args) { return this.commandLogger.mark(...args) },
|
|
|
|
|
|
|
67 |
}
|
68 |
+
const defid = chalk.blue(`[TRSSYz]`)
|
69 |
+
for (const i in chalk.logger)
|
70 |
+
if (typeof chalk.logger[i] == "function")
|
71 |
+
chalk[i] = (...args) => chalk.logger[i](defid, ...args)
|
72 |
+
global.logger = chalk
|
73 |
}
|
Yunzai/lib/config/redis.js
CHANGED
@@ -1,66 +1,109 @@
|
|
1 |
import cfg from "./config.js"
|
2 |
import { createClient } from "redis"
|
3 |
-
import {
|
4 |
|
5 |
-
let lock = false
|
6 |
/**
|
7 |
* 初始化全局redis客户端
|
8 |
*/
|
9 |
export default async function redisInit() {
|
10 |
const rc = cfg.redis
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
|
|
18 |
}
|
19 |
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
-
|
25 |
-
|
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 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
38 |
|
39 |
-
|
40 |
-
|
41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
64 |
}
|
65 |
-
return ""
|
66 |
}
|
|
|
1 |
import cfg from "./config.js"
|
2 |
import { createClient } from "redis"
|
3 |
+
import { spawn } from "node:child_process"
|
4 |
|
|
|
5 |
/**
|
6 |
* 初始化全局redis客户端
|
7 |
*/
|
8 |
export default async function redisInit() {
|
9 |
const rc = cfg.redis
|
10 |
+
return global.redis = await new Redis({
|
11 |
+
socket: {
|
12 |
+
host: rc.host,
|
13 |
+
port: rc.port,
|
14 |
+
},
|
15 |
+
username: rc.username,
|
16 |
+
password: rc.password,
|
17 |
+
database: rc.db,
|
18 |
+
}, true).connect()
|
19 |
}
|
20 |
|
21 |
+
export class Redis {
|
22 |
+
constructor(opts, exit) {
|
23 |
+
Bot.makeLog("info", `正在连接 ${logger.cyan(`redis://${opts.socket.host}:${opts.socket.port}/${opts.database}`)}`, "Redis")
|
24 |
+
this.opts = opts
|
25 |
+
this.redis = createClient(opts)
|
26 |
+
this.redis.class = this
|
27 |
+
this.exit = exit
|
28 |
+
}
|
29 |
+
|
30 |
+
async connect(force) {
|
31 |
+
if (this.lock && !force) return
|
32 |
+
this.lock = true
|
33 |
+
|
34 |
+
try {
|
35 |
+
await this.redis.connect()
|
36 |
+
} catch (err) {
|
37 |
+
await this.redis.disconnect()
|
38 |
+
.catch(err => Bot.makeLog("error", err, "Redis"))
|
39 |
+
this.err = err
|
40 |
+
if (!force) return this.start()
|
41 |
+
return false
|
42 |
+
}
|
43 |
|
44 |
+
this.lock = false
|
45 |
+
return this.redis.once("error", this.onerror)
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
}
|
47 |
|
48 |
+
async start() {
|
49 |
+
if (this.opts.socket.host !== "127.0.0.1") {
|
50 |
+
Bot.makeLog("error", [`连接错误,请确认连接地址正确`, this.err], "Redis")
|
51 |
+
if (this.exit) process.exit(1)
|
52 |
+
return false
|
53 |
+
}
|
54 |
|
55 |
+
const cmd = [
|
56 |
+
cfg.redis.path,
|
57 |
+
"--port", this.opts.socket.port,
|
58 |
+
...await this.aarch64(),
|
59 |
+
]
|
60 |
+
Bot.makeLog("info", ["正在启动", logger.cyan(cmd.join(" "))], "Redis")
|
61 |
+
const redisProcess = spawn(cmd[0], cmd.slice(1))
|
62 |
+
.on("error", err => {
|
63 |
+
Bot.makeLog("error", ["启动错误", err], "Redis")
|
64 |
+
redisProcess.exit = true
|
65 |
+
})
|
66 |
+
.on("exit", () => redisProcess.exit = true)
|
67 |
+
redisProcess.stdout.on("data", data => {
|
68 |
+
Bot.makeLog("info", Bot.String(data).trim(), "Redis")
|
69 |
+
})
|
70 |
+
redisProcess.stderr.on("data", data => {
|
71 |
+
Bot.makeLog("error", Bot.String(data).trim(), "Redis")
|
72 |
+
})
|
73 |
+
this.redis.process = redisProcess
|
74 |
|
75 |
+
for (let i=0; i<15; i++) {
|
76 |
+
await Bot.sleep(1000)
|
77 |
+
if (redisProcess.exit) break
|
78 |
+
const ret = await this.connect(true)
|
79 |
+
if (ret) return ret
|
80 |
+
}
|
81 |
+
|
82 |
+
Bot.makeLog("error", ["连接错误", this.err], "Redis")
|
83 |
+
redisProcess.kill()
|
84 |
+
if (this.exit) process.exit(1)
|
85 |
+
return false
|
86 |
+
}
|
87 |
+
|
88 |
+
onerror = async (err) => {
|
89 |
+
Bot.makeLog("error", err, "Redis")
|
90 |
+
await this.redis.disconnect()
|
91 |
+
.catch(err => Bot.makeLog("error", err, "Redis"))
|
92 |
+
if (this.redis.process) this.redis.process.kill()
|
93 |
+
return this.connect()
|
94 |
}
|
|
|
|
|
|
|
|
|
|
|
95 |
|
96 |
+
async aarch64() {
|
97 |
+
if (process.platform === "win32" || process.arch !== "arm64")
|
98 |
+
return []
|
99 |
+
/** 判断redis版本 */
|
100 |
+
let v = await Bot.exec("redis-server -v")
|
101 |
+
if (v.stdout?.match) {
|
102 |
+
v = v.stdout.match(/v=(\d)./)
|
103 |
+
/** 忽略arm警告 */
|
104 |
+
if (v && v[1] >= 6)
|
105 |
+
return ["--ignore-warnings", "ARM64-COW-BUG"]
|
106 |
+
}
|
107 |
+
return []
|
108 |
}
|
|
|
109 |
}
|
Yunzai/lib/listener/listener.js
CHANGED
@@ -7,7 +7,7 @@ export default class EventListener {
|
|
7 |
* @param data.event 监听的事件
|
8 |
* @param data.once 是否只监听一次
|
9 |
*/
|
10 |
-
constructor
|
11 |
this.prefix = data.prefix || ""
|
12 |
this.event = data.event
|
13 |
this.once = data.once || false
|
|
|
7 |
* @param data.event 监听的事件
|
8 |
* @param data.once 是否只监听一次
|
9 |
*/
|
10 |
+
constructor(data) {
|
11 |
this.prefix = data.prefix || ""
|
12 |
this.event = data.event
|
13 |
this.once = data.once || false
|
Yunzai/lib/listener/loader.js
CHANGED
@@ -8,12 +8,12 @@ class ListenerLoader {
|
|
8 |
/**
|
9 |
* 监听事件加载
|
10 |
*/
|
11 |
-
async load
|
12 |
-
|
13 |
-
|
14 |
let eventCount = 0
|
15 |
for (const file of (await fs.readdir("./lib/events")).filter(file => file.endsWith(".js"))) {
|
16 |
-
|
17 |
try {
|
18 |
let listener = await import(`../events/${file}`)
|
19 |
if (!listener.default) continue
|
@@ -30,27 +30,25 @@ class ListenerLoader {
|
|
30 |
Bot[on](listener.prefix + listener.event, event => listener[e](event))
|
31 |
}
|
32 |
eventCount++
|
33 |
-
} catch (
|
34 |
-
|
35 |
-
logger.error(e)
|
36 |
}
|
37 |
}
|
38 |
-
|
39 |
|
40 |
-
|
41 |
-
|
42 |
let adapterCount = 0
|
43 |
for (const adapter of Bot.adapter) {
|
44 |
try {
|
45 |
-
|
46 |
await adapter.load()
|
47 |
adapterCount++
|
48 |
-
} catch (
|
49 |
-
|
50 |
-
logger.error(e)
|
51 |
}
|
52 |
}
|
53 |
-
|
54 |
}
|
55 |
}
|
56 |
|
|
|
8 |
/**
|
9 |
* 监听事件加载
|
10 |
*/
|
11 |
+
async load() {
|
12 |
+
Bot.makeLog("info", "-----------", "Listener")
|
13 |
+
Bot.makeLog("info", "加载监听事件中...", "Listener")
|
14 |
let eventCount = 0
|
15 |
for (const file of (await fs.readdir("./lib/events")).filter(file => file.endsWith(".js"))) {
|
16 |
+
Bot.makeLog("debug", [`加载监听事件 ${file}`], "Listener")
|
17 |
try {
|
18 |
let listener = await import(`../events/${file}`)
|
19 |
if (!listener.default) continue
|
|
|
30 |
Bot[on](listener.prefix + listener.event, event => listener[e](event))
|
31 |
}
|
32 |
eventCount++
|
33 |
+
} catch (err) {
|
34 |
+
Bot.makeLog("error", [`监听事件加载错误 ${file}`, err], "Listener")
|
|
|
35 |
}
|
36 |
}
|
37 |
+
Bot.makeLog("info", `加载监听事件[${eventCount}个]`, "Listener")
|
38 |
|
39 |
+
Bot.makeLog("info", "-----------", "Adapter")
|
40 |
+
Bot.makeLog("info", "加载适配器中...", "Adapter")
|
41 |
let adapterCount = 0
|
42 |
for (const adapter of Bot.adapter) {
|
43 |
try {
|
44 |
+
Bot.makeLog("debug", [`加载适配器 ${adapter.name}(${adapter.id})`], "Adapter")
|
45 |
await adapter.load()
|
46 |
adapterCount++
|
47 |
+
} catch (err) {
|
48 |
+
Bot.makeLog("error", [`适配器加载错误 ${adapter.name}(${adapter.id})`, err], "Adapter")
|
|
|
49 |
}
|
50 |
}
|
51 |
+
Bot.makeLog("info", `加载适配器[${adapterCount}个]`, "Adapter")
|
52 |
}
|
53 |
}
|
54 |
|
Yunzai/lib/modules/oicq/index.js
CHANGED
@@ -1,34 +1,41 @@
|
|
1 |
-
const 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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
export { segment }
|
|
|
1 |
+
const 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 |
|
34 |
+
try {
|
35 |
+
const { segment: icqq_segment } = await import(`file://${process.cwd()}/plugins/ICQQ-Plugin/node_modules/icqq/lib/message/elements.js`)
|
36 |
+
const { deprecate } = await import("node:util")
|
37 |
+
for (const i in icqq_segment) if (!segment[i])
|
38 |
+
segment[i] = deprecate(icqq_segment[i], `segment.${i} 仅在 icqq 上可用`)
|
39 |
+
} catch {}
|
40 |
+
|
41 |
export { segment }
|
Yunzai/lib/plugins/config.js
CHANGED
@@ -3,11 +3,11 @@ 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), "
|
7 |
|
8 |
let configData
|
9 |
try {
|
10 |
-
configData = YAML.parse(await fs.readFile(configFile, "
|
11 |
_.merge(config, configData)
|
12 |
} catch (err) {
|
13 |
logger.debug("配置文件", configFile, "读取失败", err)
|
|
|
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), "utf8")
|
7 |
|
8 |
let configData
|
9 |
try {
|
10 |
+
configData = YAML.parse(await fs.readFile(configFile, "utf8"))
|
11 |
_.merge(config, configData)
|
12 |
} catch (err) {
|
13 |
logger.debug("配置文件", configFile, "读取失败", err)
|
Yunzai/lib/plugins/loader.js
CHANGED
@@ -19,29 +19,27 @@ global.segment = segment
|
|
19 |
* 加载插件
|
20 |
*/
|
21 |
class PluginsLoader {
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
}
|
39 |
|
40 |
-
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
}
|
45 |
|
46 |
async getPlugins() {
|
47 |
const files = await fs.readdir(this.dir, { withFileTypes: true })
|
@@ -82,8 +80,8 @@ class PluginsLoader {
|
|
82 |
if (isRefresh) this.priority = []
|
83 |
if (this.priority.length) return
|
84 |
|
85 |
-
|
86 |
-
|
87 |
|
88 |
const files = await this.getPlugins()
|
89 |
this.pluginCount = 0
|
@@ -96,8 +94,8 @@ class PluginsLoader {
|
|
96 |
this.packageTips(packageErr)
|
97 |
this.createTask()
|
98 |
|
99 |
-
|
100 |
-
|
101 |
|
102 |
/** 优先级排序 */
|
103 |
this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
|
@@ -112,16 +110,14 @@ class PluginsLoader {
|
|
112 |
pluginArray.push(this.loadPlugin(file, p))
|
113 |
)
|
114 |
for (const i of await Promise.allSettled(pluginArray))
|
115 |
-
if (i?.status && i.status
|
116 |
-
|
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 |
-
|
124 |
-
logger.error(decodeURI(error.stack))
|
125 |
}
|
126 |
}
|
127 |
}
|
@@ -130,11 +126,11 @@ class PluginsLoader {
|
|
130 |
if (!p?.prototype) return
|
131 |
this.pluginCount++
|
132 |
const plugin = new p
|
133 |
-
|
134 |
/** 执行初始化,返回 return 则跳过加载 */
|
135 |
-
if (plugin.init && await plugin.init()
|
136 |
/** 初始化定时任务 */
|
137 |
-
this.collectTask(plugin.task)
|
138 |
this.priority.push({
|
139 |
class: p,
|
140 |
key: file.name,
|
@@ -156,14 +152,14 @@ class PluginsLoader {
|
|
156 |
|
157 |
packageTips(packageErr) {
|
158 |
if (!packageErr.length) return
|
159 |
-
|
160 |
for (const i of packageErr) {
|
161 |
const pack = i.error.stack.match(/'(.+?)'/g)[0].replace(/'/g, "")
|
162 |
-
|
163 |
}
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
}
|
168 |
|
169 |
/**
|
@@ -202,8 +198,10 @@ class PluginsLoader {
|
|
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 |
}
|
@@ -230,38 +228,35 @@ class PluginsLoader {
|
|
230 |
for (const plugin of priority)
|
231 |
if (plugin.accept) {
|
232 |
const res = await plugin.accept(e)
|
233 |
-
if (res
|
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}
|
245 |
|
246 |
-
|
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
|
254 |
const res = plugin[v.fnc] && (await plugin[v.fnc](e))
|
255 |
if (res !== false) {
|
256 |
/** 设置冷却cd */
|
257 |
this.setLimit(e)
|
258 |
-
|
259 |
-
logger.mark(`${e.logFnc} ${lodash.truncate(e.msg, { length: 100 })} 处理完成 ${Date.now() - start}ms`)
|
260 |
break a
|
261 |
}
|
262 |
-
} catch (
|
263 |
-
|
264 |
-
logger.error(error.stack)
|
265 |
break a
|
266 |
}
|
267 |
}
|
@@ -275,19 +270,19 @@ class PluginsLoader {
|
|
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
|
284 |
}
|
285 |
|
286 |
/** 判断权限 */
|
287 |
filtPermission(e, v) {
|
288 |
-
if (v.permission
|
289 |
|
290 |
-
if (v.permission
|
291 |
if (e.isMaster) {
|
292 |
return true
|
293 |
} else {
|
@@ -297,13 +292,13 @@ class PluginsLoader {
|
|
297 |
}
|
298 |
|
299 |
if (e.isGroup) {
|
300 |
-
if (v.permission
|
301 |
if (!e.member.is_owner) {
|
302 |
e.reply("暂无权限,只有群主才能操作")
|
303 |
return false
|
304 |
}
|
305 |
}
|
306 |
-
if (v.permission
|
307 |
if (!e.member.is_admin) {
|
308 |
e.reply("暂无权限,只有管理员才能操作")
|
309 |
return false
|
@@ -349,7 +344,7 @@ class PluginsLoader {
|
|
349 |
e.img = [i.url]
|
350 |
break
|
351 |
case "at":
|
352 |
-
if (i.qq
|
353 |
e.atBot = true
|
354 |
else
|
355 |
e.at = i.qq
|
@@ -366,14 +361,14 @@ class PluginsLoader {
|
|
366 |
break
|
367 |
case "xml":
|
368 |
case "json":
|
369 |
-
e.msg = (e.msg || "") + (typeof i.data
|
370 |
break
|
371 |
}
|
372 |
}
|
373 |
|
374 |
e.logText = ""
|
375 |
|
376 |
-
if (e.message_type
|
377 |
e.isPrivate = true
|
378 |
|
379 |
if (e.sender) {
|
@@ -387,7 +382,7 @@ class PluginsLoader {
|
|
387 |
}
|
388 |
|
389 |
e.logText = `[${e.sender?.nickname ? `${e.sender.nickname}(${e.user_id})` : e.user_id}]`
|
390 |
-
} else if (e.message_type
|
391 |
e.isGroup = true
|
392 |
|
393 |
if (e.sender) {
|
@@ -407,6 +402,8 @@ class PluginsLoader {
|
|
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))) {
|
411 |
e.isMaster = true
|
412 |
}
|
@@ -476,6 +473,7 @@ class PluginsLoader {
|
|
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) {
|
@@ -523,27 +521,35 @@ class PluginsLoader {
|
|
523 |
}
|
524 |
|
525 |
/** 收集定时任务 */
|
526 |
-
collectTask(task) {
|
527 |
for (const i of Array.isArray(task) ? task : [task])
|
528 |
-
if (i.cron && i.
|
|
|
529 |
this.task.push(i)
|
|
|
530 |
}
|
531 |
|
532 |
/** 创建定时任务 */
|
533 |
createTask() {
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
547 |
}
|
548 |
|
549 |
/** 检查命令冷却cd */
|
@@ -592,10 +598,10 @@ class PluginsLoader {
|
|
592 |
let groupCfg = cfg.getGroup(e.self_id, e.group_id)
|
593 |
|
594 |
/** 模式0,未开启前缀 */
|
595 |
-
if (groupCfg.onlyReplyAt
|
596 |
|
597 |
/** 模式2,非主人开启 */
|
598 |
-
if (groupCfg.onlyReplyAt
|
599 |
|
600 |
/** at机器人 */
|
601 |
if (e.atBot) return true
|
@@ -646,15 +652,14 @@ class PluginsLoader {
|
|
646 |
lodash.forEach(app, p => {
|
647 |
const plugin = new p
|
648 |
for (const i in this.priority)
|
649 |
-
if (this.priority[i].key
|
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 (
|
656 |
-
|
657 |
-
logger.error(decodeURI(error.stack))
|
658 |
}
|
659 |
}
|
660 |
|
@@ -669,18 +674,16 @@ class PluginsLoader {
|
|
669 |
|
670 |
/** 监听修改 */
|
671 |
watcher.on("change", path => {
|
672 |
-
|
673 |
this.changePlugin(key)
|
674 |
})
|
675 |
|
676 |
/** 监听删除 */
|
677 |
watcher.on("unlink", async path => {
|
678 |
-
|
679 |
/** 停止更新监听 */
|
680 |
this.watcher[`${dirName}.${appName}`].removeAllListeners("change")
|
681 |
-
|
682 |
-
if (this.priority[i].key == key)
|
683 |
-
this.priority.splice(i, 1)
|
684 |
})
|
685 |
this.watcher[`${dirName}.${appName}`] = watcher
|
686 |
}
|
@@ -695,7 +698,7 @@ class PluginsLoader {
|
|
695 |
watcher.on("add", async PluPath => {
|
696 |
const appName = path.basename(PluPath)
|
697 |
if (!appName.endsWith(".js")) return
|
698 |
-
|
699 |
const key = `${dirName}/${appName}`
|
700 |
await this.importPlugin({
|
701 |
name: key,
|
|
|
19 |
* 加载插件
|
20 |
*/
|
21 |
class PluginsLoader {
|
22 |
+
priority = []
|
23 |
+
handler = {}
|
24 |
+
task = []
|
25 |
+
dir = "plugins"
|
26 |
+
|
27 |
+
/** 命令冷却cd */
|
28 |
+
groupCD = {}
|
29 |
+
singleCD = {}
|
30 |
+
|
31 |
+
/** 插件监听 */
|
32 |
+
watcher = {}
|
33 |
+
eventMap = {
|
34 |
+
message: ["post_type", "message_type", "sub_type"],
|
35 |
+
notice: ["post_type", "notice_type", "sub_type"],
|
36 |
+
request: ["post_type", "request_type", "sub_type"],
|
37 |
+
}
|
|
|
38 |
|
39 |
+
msgThrottle = {}
|
40 |
|
41 |
+
/** 星铁命令前缀 */
|
42 |
+
srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/
|
|
|
43 |
|
44 |
async getPlugins() {
|
45 |
const files = await fs.readdir(this.dir, { withFileTypes: true })
|
|
|
80 |
if (isRefresh) this.priority = []
|
81 |
if (this.priority.length) return
|
82 |
|
83 |
+
Bot.makeLog("info", "-----------", "Plugin")
|
84 |
+
Bot.makeLog("info", "加载插件中...", "Plugin")
|
85 |
|
86 |
const files = await this.getPlugins()
|
87 |
this.pluginCount = 0
|
|
|
94 |
this.packageTips(packageErr)
|
95 |
this.createTask()
|
96 |
|
97 |
+
Bot.makeLog("info", `加载定时任务[${this.task.length}个]`, "Plugin")
|
98 |
+
Bot.makeLog("info", `加载插件[${this.pluginCount}个]`, "Plugin")
|
99 |
|
100 |
/** 优先级排序 */
|
101 |
this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
|
|
|
110 |
pluginArray.push(this.loadPlugin(file, p))
|
111 |
)
|
112 |
for (const i of await Promise.allSettled(pluginArray))
|
113 |
+
if (i?.status && i.status !== "fulfilled") {
|
114 |
+
Bot.makeLog("error", [`插件加载错误 ${logger.red(file.name)}`, i], "Plugin")
|
|
|
115 |
}
|
116 |
} catch (error) {
|
117 |
if (packageErr && error.stack.includes("Cannot find package")) {
|
118 |
packageErr.push({ error, file })
|
119 |
} else {
|
120 |
+
Bot.makeLog("error", [`插件加载错误 ${logger.red(file.name)}`, error], "Plugin")
|
|
|
121 |
}
|
122 |
}
|
123 |
}
|
|
|
126 |
if (!p?.prototype) return
|
127 |
this.pluginCount++
|
128 |
const plugin = new p
|
129 |
+
Bot.makeLog("debug", `加载插件 [${file.name}][${plugin.name}]`, "Plugin")
|
130 |
/** 执行初始化,返回 return 则跳过加载 */
|
131 |
+
if (plugin.init && await plugin.init() === "return") return
|
132 |
/** 初始化定时任务 */
|
133 |
+
this.collectTask(plugin.task, plugin.name)
|
134 |
this.priority.push({
|
135 |
class: p,
|
136 |
key: file.name,
|
|
|
152 |
|
153 |
packageTips(packageErr) {
|
154 |
if (!packageErr.length) return
|
155 |
+
Bot.makeLog("error", "--------- 插件加载错误 ---------", "Plugin")
|
156 |
for (const i of packageErr) {
|
157 |
const pack = i.error.stack.match(/'(.+?)'/g)[0].replace(/'/g, "")
|
158 |
+
Bot.makeLog("error", `${logger.cyan(i.file.name)} 缺少依赖 ${logger.red(pack)}`, "Plugin")
|
159 |
}
|
160 |
+
Bot.makeLog("error", `安装插件后请 ${logger.red("pnpm i")} 安装依赖`, "Plugin")
|
161 |
+
Bot.makeLog("error", `仍报错${logger.red("进入插件目录")} pnpm add 依赖`, "Plugin")
|
162 |
+
Bot.makeLog("error", "--------------------------------", "Plugin")
|
163 |
}
|
164 |
|
165 |
/**
|
|
|
198 |
...plugin.getContext(false, true),
|
199 |
}
|
200 |
if (!lodash.isEmpty(context)) {
|
201 |
+
let ret
|
202 |
for (const fnc in context)
|
203 |
+
ret ||= await plugin[fnc](context[fnc])
|
204 |
+
if (ret === "continue") continue
|
205 |
return
|
206 |
}
|
207 |
}
|
|
|
228 |
for (const plugin of priority)
|
229 |
if (plugin.accept) {
|
230 |
const res = await plugin.accept(e)
|
231 |
+
if (res === "return") return
|
232 |
if (res) break
|
233 |
}
|
234 |
|
235 |
a: for (const plugin of priority) {
|
|
|
236 |
if (plugin.rule) for (const v of plugin.rule) {
|
237 |
/** 判断事件 */
|
238 |
if (v.event && !this.filtEvent(e, v)) continue
|
239 |
|
240 |
+
/** 正则匹配 */
|
241 |
if (!new RegExp(v.reg).test(e.msg)) continue
|
242 |
+
e.logFnc = `${logger.blue(`[${plugin.name}(${v.fnc})]`)}`
|
243 |
|
244 |
+
Bot.makeLog(v.log === false ? "debug" : "info", `${e.logText}${e.logFnc}${logger.yellow("[开始处理]")}`, false)
|
|
|
245 |
|
246 |
/** 判断权限 */
|
247 |
if (!this.filtPermission(e, v)) break a
|
248 |
|
249 |
try {
|
250 |
+
const start_time = Date.now()
|
251 |
const res = plugin[v.fnc] && (await plugin[v.fnc](e))
|
252 |
if (res !== false) {
|
253 |
/** 设置冷却cd */
|
254 |
this.setLimit(e)
|
255 |
+
Bot.makeLog(v.log === false ? "debug" : "mark", `${e.logText}${e.logFnc}${logger.green(`[完成${Bot.getTimeDiff(start_time)}]`)}`, false)
|
|
|
256 |
break a
|
257 |
}
|
258 |
+
} catch (err) {
|
259 |
+
Bot.makeLog("error", [`${e.logFnc}${e.logText}`, err], false)
|
|
|
260 |
break a
|
261 |
}
|
262 |
}
|
|
|
270 |
const eventMap = this.eventMap[e.post_type] || []
|
271 |
const newEvent = []
|
272 |
for (const i in event) {
|
273 |
+
if (event[i] === "*")
|
274 |
newEvent.push(event[i])
|
275 |
else
|
276 |
newEvent.push(e[eventMap[i]])
|
277 |
}
|
278 |
+
return v.event === newEvent.join(".")
|
279 |
}
|
280 |
|
281 |
/** 判断权限 */
|
282 |
filtPermission(e, v) {
|
283 |
+
if (v.permission === "all" || !v.permission) return true
|
284 |
|
285 |
+
if (v.permission === "master") {
|
286 |
if (e.isMaster) {
|
287 |
return true
|
288 |
} else {
|
|
|
292 |
}
|
293 |
|
294 |
if (e.isGroup) {
|
295 |
+
if (v.permission === "owner") {
|
296 |
if (!e.member.is_owner) {
|
297 |
e.reply("暂无权限,只有群主才能操作")
|
298 |
return false
|
299 |
}
|
300 |
}
|
301 |
+
if (v.permission === "admin") {
|
302 |
if (!e.member.is_admin) {
|
303 |
e.reply("暂无权限,只有管理员才能操作")
|
304 |
return false
|
|
|
344 |
e.img = [i.url]
|
345 |
break
|
346 |
case "at":
|
347 |
+
if (i.qq === e.self_id)
|
348 |
e.atBot = true
|
349 |
else
|
350 |
e.at = i.qq
|
|
|
361 |
break
|
362 |
case "xml":
|
363 |
case "json":
|
364 |
+
e.msg = (e.msg || "") + (typeof i.data === "string" ? i.data : JSON.stringify(i.data))
|
365 |
break
|
366 |
}
|
367 |
}
|
368 |
|
369 |
e.logText = ""
|
370 |
|
371 |
+
if (e.message_type === "private" || e.notice_type === "friend") {
|
372 |
e.isPrivate = true
|
373 |
|
374 |
if (e.sender) {
|
|
|
382 |
}
|
383 |
|
384 |
e.logText = `[${e.sender?.nickname ? `${e.sender.nickname}(${e.user_id})` : e.user_id}]`
|
385 |
+
} else if (e.message_type === "group" || e.notice_type === "group") {
|
386 |
e.isGroup = true
|
387 |
|
388 |
if (e.sender) {
|
|
|
402 |
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}]`
|
403 |
}
|
404 |
|
405 |
+
e.logText = `${logger.cyan(e.logText)}${logger.red(`[${lodash.truncate(e.msg || e.raw_message || Bot.String(e), { length: 100 })}]`)}`
|
406 |
+
|
407 |
if (e.user_id && cfg.master[e.self_id]?.includes(String(e.user_id))) {
|
408 |
e.isMaster = true
|
409 |
}
|
|
|
473 |
res = await reply(msg)
|
474 |
} catch (err) {
|
475 |
Bot.makeLog("error", ["发送消息错误", msg, err], e.self_id)
|
476 |
+
res = { error: [err] }
|
477 |
}
|
478 |
|
479 |
if (recallMsg > 0 && res?.message_id) {
|
|
|
521 |
}
|
522 |
|
523 |
/** 收集定时任务 */
|
524 |
+
collectTask(task, name) {
|
525 |
for (const i of Array.isArray(task) ? task : [task])
|
526 |
+
if (i.cron && i.fnc) {
|
527 |
+
i.name ??= name
|
528 |
this.task.push(i)
|
529 |
+
}
|
530 |
}
|
531 |
|
532 |
/** 创建定时任务 */
|
533 |
createTask() {
|
534 |
+
const created = []
|
535 |
+
for (const i of this.task) {
|
536 |
+
if (i.job?.cancel) i.job.cancel()
|
537 |
+
const name = `${logger.blue(`[${i.name}(${i.cron})]`)}`
|
538 |
+
if (created.includes(name)) {
|
539 |
+
Bot.makeLog("warn", `重复定时任务 ${name} 已跳过`, "Task")
|
540 |
+
continue
|
541 |
+
}
|
542 |
+
created.push(name)
|
543 |
+
Bot.makeLog("debug", `加载定时任务 ${name}`, "Task")
|
544 |
+
i.job = schedule.scheduleJob(i.cron, async () => { try {
|
545 |
+
const start_time = Date.now()
|
546 |
+
Bot.makeLog(i.log === false ? "debug" : "mark", `${name}${logger.yellow("[开始处理]")}`, false)
|
547 |
+
await i.fnc()
|
548 |
+
Bot.makeLog(i.log === false ? "debug" : "mark", `${name}${logger.green(`[完成${Bot.getTimeDiff(start_time)}]`)}`, false)
|
549 |
+
} catch (err) {
|
550 |
+
Bot.makeLog("error", [name, err], false)
|
551 |
+
}})
|
552 |
+
}
|
553 |
}
|
554 |
|
555 |
/** 检查命令冷却cd */
|
|
|
598 |
let groupCfg = cfg.getGroup(e.self_id, e.group_id)
|
599 |
|
600 |
/** 模式0,未开启前缀 */
|
601 |
+
if (groupCfg.onlyReplyAt === 0 || !groupCfg.botAlias) return true
|
602 |
|
603 |
/** 模式2,非主人开启 */
|
604 |
+
if (groupCfg.onlyReplyAt === 2 && e.isMaster) return true
|
605 |
|
606 |
/** at机器人 */
|
607 |
if (e.atBot) return true
|
|
|
652 |
lodash.forEach(app, p => {
|
653 |
const plugin = new p
|
654 |
for (const i in this.priority)
|
655 |
+
if (this.priority[i].key === key && this.priority[i].name === plugin.name) {
|
656 |
this.priority[i].class = p
|
657 |
this.priority[i].priority = plugin.priority
|
658 |
}
|
659 |
})
|
660 |
this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"])
|
661 |
+
} catch (err) {
|
662 |
+
Bot.makeLog("error", [`插件加载错误 ${logger.red(key)}`, err], "Plugin")
|
|
|
663 |
}
|
664 |
}
|
665 |
|
|
|
674 |
|
675 |
/** 监听修改 */
|
676 |
watcher.on("change", path => {
|
677 |
+
Bot.makeLog("mark", `[修改插件][${dirName}][${appName}]`, "Plugin")
|
678 |
this.changePlugin(key)
|
679 |
})
|
680 |
|
681 |
/** 监听删除 */
|
682 |
watcher.on("unlink", async path => {
|
683 |
+
Bot.makeLog("mark", `[卸载插件][${dirName}][${appName}]`, "Plugin")
|
684 |
/** 停止更新监听 */
|
685 |
this.watcher[`${dirName}.${appName}`].removeAllListeners("change")
|
686 |
+
this.priority = this.priority.filter(i => i.key !== key)
|
|
|
|
|
687 |
})
|
688 |
this.watcher[`${dirName}.${appName}`] = watcher
|
689 |
}
|
|
|
698 |
watcher.on("add", async PluPath => {
|
699 |
const appName = path.basename(PluPath)
|
700 |
if (!appName.endsWith(".js")) return
|
701 |
+
Bot.makeLog("mark", `[新增插件][${dirName}][${appName}]`, "Plugin")
|
702 |
const key = `${dirName}/${appName}`
|
703 |
await this.importPlugin({
|
704 |
name: key,
|
Yunzai/lib/plugins/plugin.js
CHANGED
@@ -1,9 +1,11 @@
|
|
1 |
let Common
|
2 |
try {
|
3 |
Common = (await import("#miao")).Common
|
4 |
-
} catch
|
5 |
|
6 |
const stateArr = {}
|
|
|
|
|
7 |
|
8 |
export default class plugin {
|
9 |
/**
|
@@ -34,7 +36,7 @@ export default class plugin {
|
|
34 |
namespace,
|
35 |
event = "message",
|
36 |
priority = 5000,
|
37 |
-
task = { fnc: "", cron: "" },
|
38 |
rule = []
|
39 |
}) {
|
40 |
/** 插件名称 */
|
@@ -46,14 +48,7 @@ export default class plugin {
|
|
46 |
/** 优先级 */
|
47 |
this.priority = priority
|
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 |
|
@@ -76,27 +71,30 @@ export default class plugin {
|
|
76 |
|
77 |
conKey(isGroup = false) {
|
78 |
if (isGroup) {
|
79 |
-
return `${this.name}.${this.e.group_id}`
|
80 |
} else {
|
81 |
-
return `${this.name}.${this.userId || this.e.user_id}`
|
82 |
}
|
83 |
}
|
84 |
|
85 |
/**
|
86 |
* @param type 执行方法
|
87 |
* @param isGroup 是否群聊
|
88 |
-
* @param time
|
|
|
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]
|
95 |
if (stateArr[key][type]) {
|
|
|
96 |
delete stateArr[key][type]
|
97 |
-
this.reply(
|
98 |
}
|
99 |
}, time * 1000)
|
|
|
100 |
}
|
101 |
|
102 |
getContext(type, isGroup) {
|
@@ -104,18 +102,23 @@ export default class plugin {
|
|
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]
|
114 |
-
clearTimeout(stateArr[key][type]
|
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 |
}
|
|
|
1 |
let Common
|
2 |
try {
|
3 |
Common = (await import("#miao")).Common
|
4 |
+
} catch {}
|
5 |
|
6 |
const stateArr = {}
|
7 |
+
const SymbolTimeout = Symbol("Timeout")
|
8 |
+
const SymbolResolve = Symbol("Resolve")
|
9 |
|
10 |
export default class plugin {
|
11 |
/**
|
|
|
36 |
namespace,
|
37 |
event = "message",
|
38 |
priority = 5000,
|
39 |
+
task = { name: "", fnc: "", cron: "" },
|
40 |
rule = []
|
41 |
}) {
|
42 |
/** 插件名称 */
|
|
|
48 |
/** 优先级 */
|
49 |
this.priority = priority
|
50 |
/** 定时任务,可以是数组 */
|
51 |
+
this.task = task
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
/** 命令规则 */
|
53 |
this.rule = rule
|
54 |
|
|
|
71 |
|
72 |
conKey(isGroup = false) {
|
73 |
if (isGroup) {
|
74 |
+
return `${this.name}.${this.group_id || this.groupId || this.e.group_id}`
|
75 |
} else {
|
76 |
+
return `${this.name}.${this.user_id || this.userId || this.e.user_id}`
|
77 |
}
|
78 |
}
|
79 |
|
80 |
/**
|
81 |
* @param type 执行方法
|
82 |
* @param isGroup 是否群聊
|
83 |
+
* @param time 操作时间
|
84 |
+
* @param timeout 操作超时回复
|
85 |
*/
|
86 |
+
setContext(type, isGroup, time = 120, timeout = "操作超时已取消") {
|
87 |
const key = this.conKey(isGroup)
|
88 |
if (!stateArr[key]) stateArr[key] = {}
|
89 |
stateArr[key][type] = this.e
|
90 |
+
if (time) stateArr[key][type][SymbolTimeout] = setTimeout(() => {
|
91 |
if (stateArr[key][type]) {
|
92 |
+
const resolve = stateArr[key][type][SymbolResolve]
|
93 |
delete stateArr[key][type]
|
94 |
+
resolve ? resolve(false) : this.reply(timeout, true)
|
95 |
}
|
96 |
}, time * 1000)
|
97 |
+
return stateArr[key][type]
|
98 |
}
|
99 |
|
100 |
getContext(type, isGroup) {
|
|
|
102 |
return stateArr[this.conKey(isGroup)]
|
103 |
}
|
104 |
|
|
|
|
|
|
|
|
|
105 |
finish(type, isGroup) {
|
106 |
const key = this.conKey(isGroup)
|
107 |
+
if (stateArr[key]?.[type]) {
|
108 |
+
clearTimeout(stateArr[key][type][SymbolTimeout])
|
109 |
delete stateArr[key][type]
|
110 |
}
|
111 |
}
|
112 |
|
113 |
+
awaitContext(...args) {
|
114 |
+
return new Promise(resolve => this.setContext("resolveContext", ...args)[SymbolResolve] = resolve)
|
115 |
+
}
|
116 |
+
|
117 |
+
resolveContext(context) {
|
118 |
+
this.finish("resolveContext")
|
119 |
+
context[SymbolResolve](this.e)
|
120 |
+
}
|
121 |
+
|
122 |
async renderImg(plugin, tpl, data, cfg) {
|
123 |
return Common.render(plugin, tpl, data, { ...cfg, e: this.e })
|
124 |
}
|
Yunzai/lib/plugins/runtime.js
CHANGED
@@ -18,8 +18,10 @@ try {
|
|
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
|
23 |
|
24 |
/**
|
25 |
* 常用的处理方法
|
@@ -77,13 +79,6 @@ export default class Runtime {
|
|
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)
|
@@ -141,12 +136,13 @@ export default class Runtime {
|
|
141 |
*
|
142 |
* @param targetType all: 所有用户均可, cookie:查询用户必须具备Cookie
|
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)
|
150 |
}
|
151 |
return false
|
152 |
}
|
@@ -156,10 +152,11 @@ export default class Runtime {
|
|
156 |
* @param uid
|
157 |
* @param ck
|
158 |
* @param option
|
|
|
159 |
* @returns {Promise<MysApi>}
|
160 |
*/
|
161 |
-
async createMysApi(uid, ck, option) {
|
162 |
-
return new MysApi(uid, ck, option)
|
163 |
}
|
164 |
|
165 |
/**
|
@@ -192,7 +189,7 @@ export default class Runtime {
|
|
192 |
scale: 1
|
193 |
},
|
194 |
/** miao 相关参数 **/
|
195 |
-
copyright: `Created By TRSS-Yunzai<span class="version">${Version
|
196 |
_res_path: pluResPath,
|
197 |
_miao_path: miaoResPath,
|
198 |
_tpl_path: process.cwd() + "/plugins/miao-plugin/resources/common/tpl/",
|
@@ -206,10 +203,7 @@ export default class Runtime {
|
|
206 |
_htmlPath: path,
|
207 |
pluResPath,
|
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
|
215 |
if (cfg.beforeRender) {
|
@@ -238,4 +232,14 @@ export default class Runtime {
|
|
238 |
}
|
239 |
return cfg.retType === "msgId" ? ret : true
|
240 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
}
|
|
|
|
|
|
|
|
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 |
+
} catch {}
|
22 |
+
try {
|
23 |
Version = (await import("#miao")).Version
|
24 |
+
} catch {}
|
25 |
|
26 |
/**
|
27 |
* 常用的处理方法
|
|
|
79 |
return MysUser
|
80 |
}
|
81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
async initUser() {
|
83 |
let e = this.e
|
84 |
let user = await NoteUser.create(e)
|
|
|
136 |
*
|
137 |
* @param targetType all: 所有用户均可, cookie:查询用户必须具备Cookie
|
138 |
* @param option MysApi option
|
139 |
+
* @param isSr 是否为星穹铁道
|
140 |
* @returns {Promise<boolean|MysApi>}
|
141 |
*/
|
142 |
+
async getMysApi(targetType = "all", option = {}, isSr = false) {
|
143 |
let mys = await this.getMysInfo(targetType)
|
144 |
if (mys.uid && mys?.ckInfo?.ck) {
|
145 |
+
return new MysApi(mys.uid, mys.ckInfo.ck, option, isSr)
|
146 |
}
|
147 |
return false
|
148 |
}
|
|
|
152 |
* @param uid
|
153 |
* @param ck
|
154 |
* @param option
|
155 |
+
* @param isSr 是否为星穹铁道
|
156 |
* @returns {Promise<MysApi>}
|
157 |
*/
|
158 |
+
async createMysApi(uid, ck, option, isSr = false) {
|
159 |
+
return new MysApi(uid, ck, option, isSr)
|
160 |
}
|
161 |
|
162 |
/**
|
|
|
189 |
scale: 1
|
190 |
},
|
191 |
/** miao 相关参数 **/
|
192 |
+
copyright: `Created By TRSS-Yunzai<span class="version">${Version?.yunzai}</span> `,
|
193 |
_res_path: pluResPath,
|
194 |
_miao_path: miaoResPath,
|
195 |
_tpl_path: process.cwd() + "/plugins/miao-plugin/resources/common/tpl/",
|
|
|
203 |
_htmlPath: path,
|
204 |
pluResPath,
|
205 |
tplFile: `./plugins/${plugin}/resources/${path}.html`,
|
206 |
+
saveId: data.saveId || data.save_id || paths[paths.length - 1]
|
|
|
|
|
|
|
207 |
}
|
208 |
// 处理beforeRender
|
209 |
if (cfg.beforeRender) {
|
|
|
232 |
}
|
233 |
return cfg.retType === "msgId" ? ret : true
|
234 |
}
|
235 |
+
|
236 |
+
static async init(e) {
|
237 |
+
await MysInfo.initCache()
|
238 |
+
e.runtime = new Runtime(e)
|
239 |
+
await e.runtime.initUser()
|
240 |
+
return e.runtime
|
241 |
+
}
|
242 |
}
|
243 |
+
|
244 |
+
if (!MysInfo || !NoteUser)
|
245 |
+
Runtime.init = async e => e.runtime = new Runtime(e)
|
Yunzai/lib/plugins/stdin.js
CHANGED
@@ -1,11 +1,18 @@
|
|
|
|
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) {
|
@@ -19,7 +26,7 @@ Bot.adapter.push(new class stdinAdapter {
|
|
19 |
if (i.file) {
|
20 |
file = await Bot.fileType(i)
|
21 |
if (Buffer.isBuffer(file.buffer)) {
|
22 |
-
file.path = `${this.path}${file.name
|
23 |
await fs.writeFile(file.path, file.buffer)
|
24 |
}
|
25 |
}
|
@@ -27,17 +34,18 @@ Bot.adapter.push(new class stdinAdapter {
|
|
27 |
switch (i.type) {
|
28 |
case "text":
|
29 |
if (i.text.match("\n"))
|
30 |
-
i.text =
|
31 |
-
|
32 |
break
|
33 |
case "image":
|
34 |
-
|
|
|
35 |
break
|
36 |
case "record":
|
37 |
-
|
38 |
break
|
39 |
case "video":
|
40 |
-
|
41 |
break
|
42 |
case "reply":
|
43 |
break
|
@@ -47,44 +55,39 @@ Bot.adapter.push(new class stdinAdapter {
|
|
47 |
Bot.sendForwardMsg(msg => this.sendMsg(msg), i.data)
|
48 |
break
|
49 |
default:
|
50 |
-
i
|
51 |
-
if (i.match("\n"))
|
52 |
-
i = `\n${i}`
|
53 |
-
logger.info(`${logger.blue(`[${this.id}]`)} 发送消息:${i}`)
|
54 |
}
|
55 |
}
|
56 |
-
return { message_id: Date.now() }
|
57 |
}
|
58 |
|
59 |
recallMsg(message_id) {
|
60 |
-
|
61 |
}
|
62 |
|
63 |
async sendFile(file, name = path.basename(file)) {
|
64 |
const buffer = await Bot.Buffer(file)
|
65 |
if (!Buffer.isBuffer(buffer)) {
|
66 |
-
|
67 |
return false
|
68 |
}
|
69 |
|
70 |
-
const files = `${this.path}${Date.now()}-${name}`
|
71 |
-
|
72 |
return fs.writeFile(files, buffer)
|
73 |
}
|
74 |
|
75 |
pickFriend() {
|
76 |
return {
|
77 |
-
user_id: this.id,
|
78 |
-
nickname: this.name,
|
79 |
-
group_id: this.id,
|
80 |
-
group_name: this.name,
|
81 |
sendMsg: msg => this.sendMsg(msg),
|
82 |
recallMsg: message_id => this.recallMsg(message_id),
|
83 |
sendFile: (file, name) => this.sendFile(file, name),
|
|
|
84 |
}
|
85 |
}
|
86 |
|
87 |
message(msg) {
|
|
|
88 |
const data = {
|
89 |
bot: Bot[this.id],
|
90 |
self_id: this.id,
|
@@ -94,20 +97,27 @@ Bot.adapter.push(new class stdinAdapter {
|
|
94 |
sender: { user_id: this.id, nickname: this.name },
|
95 |
message: [{ type: "text", text: msg }],
|
96 |
raw_message: msg,
|
97 |
-
friend: this.pickFriend(),
|
98 |
}
|
99 |
-
|
100 |
-
|
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 },
|
@@ -125,9 +135,13 @@ Bot.adapter.push(new class stdinAdapter {
|
|
125 |
}
|
126 |
Bot[this.id].gml.set(this.id, Bot[this.id].fl)
|
127 |
|
128 |
-
|
|
|
|
|
|
|
|
|
129 |
|
130 |
-
|
131 |
Bot.em(`connect.${this.id}`, { self_id: this.id })
|
132 |
}
|
133 |
})
|
|
|
1 |
+
import readline from "node:readline/promises"
|
2 |
import fs from "node:fs/promises"
|
3 |
import path from "node:path"
|
4 |
+
import { spawn } from "node:child_process"
|
5 |
|
6 |
Bot.adapter.push(new class stdinAdapter {
|
7 |
constructor() {
|
8 |
this.id = "stdin"
|
9 |
this.name = "标准输入"
|
10 |
this.path = "data/stdin/"
|
11 |
+
this.catimg = file => new Promise(resolve =>
|
12 |
+
spawn("catimg", ["-l0", file], { stdio: "inherit" })
|
13 |
+
.on("error", () => this.catimg = () => {})
|
14 |
+
.on("close", resolve)
|
15 |
+
)
|
16 |
}
|
17 |
|
18 |
async sendMsg(msg) {
|
|
|
26 |
if (i.file) {
|
27 |
file = await Bot.fileType(i)
|
28 |
if (Buffer.isBuffer(file.buffer)) {
|
29 |
+
file.path = `${this.path}${file.name}`
|
30 |
await fs.writeFile(file.path, file.buffer)
|
31 |
}
|
32 |
}
|
|
|
34 |
switch (i.type) {
|
35 |
case "text":
|
36 |
if (i.text.match("\n"))
|
37 |
+
i.text = `发送文本:\n${i.text}`
|
38 |
+
Bot.makeLog("info", i.text, this.id)
|
39 |
break
|
40 |
case "image":
|
41 |
+
await this.catimg(file.path)
|
42 |
+
Bot.makeLog("info", `发送图片:${file.url}\n文件已保存到:${logger.cyan(file.path)}`, this.id)
|
43 |
break
|
44 |
case "record":
|
45 |
+
Bot.makeLog("info", `发送音频:${file.url}\n文件已保存到:${logger.cyan(file.path)}`, this.id)
|
46 |
break
|
47 |
case "video":
|
48 |
+
Bot.makeLog("info", `发送视频:${file.url}\n文件已保存到:${logger.cyan(file.path)}`, this.id)
|
49 |
break
|
50 |
case "reply":
|
51 |
break
|
|
|
55 |
Bot.sendForwardMsg(msg => this.sendMsg(msg), i.data)
|
56 |
break
|
57 |
default:
|
58 |
+
Bot.makeLog("info", i, this.id)
|
|
|
|
|
|
|
59 |
}
|
60 |
}
|
61 |
+
return { message_id: Date.now().toString(36) }
|
62 |
}
|
63 |
|
64 |
recallMsg(message_id) {
|
65 |
+
Bot.makeLog("info", `撤回消息:${message_id}`, this.id)
|
66 |
}
|
67 |
|
68 |
async sendFile(file, name = path.basename(file)) {
|
69 |
const buffer = await Bot.Buffer(file)
|
70 |
if (!Buffer.isBuffer(buffer)) {
|
71 |
+
Bot.makeLog("error", `发送文件错误:找不到文件 ${logger.red(file)}`, this.id)
|
72 |
return false
|
73 |
}
|
74 |
|
75 |
+
const files = `${this.path}${Date.now().toString(36)}-${name}`
|
76 |
+
Bot.makeLog("info", `发送文件:${file}\n文件已保存到:${logger.cyan(files)}`, this.id)
|
77 |
return fs.writeFile(files, buffer)
|
78 |
}
|
79 |
|
80 |
pickFriend() {
|
81 |
return {
|
|
|
|
|
|
|
|
|
82 |
sendMsg: msg => this.sendMsg(msg),
|
83 |
recallMsg: message_id => this.recallMsg(message_id),
|
84 |
sendFile: (file, name) => this.sendFile(file, name),
|
85 |
+
pickMember: function() { return this },
|
86 |
}
|
87 |
}
|
88 |
|
89 |
message(msg) {
|
90 |
+
fs.appendFile(`${this.path}history`, `${Date.now().toString(36)}:${msg}\n`, "utf8")
|
91 |
const data = {
|
92 |
bot: Bot[this.id],
|
93 |
self_id: this.id,
|
|
|
97 |
sender: { user_id: this.id, nickname: this.name },
|
98 |
message: [{ type: "text", text: msg }],
|
99 |
raw_message: msg,
|
|
|
100 |
}
|
101 |
+
Bot.makeLog("info", `系统消息:${data.raw_message}`, this.id)
|
|
|
102 |
Bot.em(`${data.post_type}.${data.message_type}`, data)
|
103 |
}
|
104 |
|
105 |
+
async load(force) {
|
106 |
+
if (!(process.stdin.isTTY || process.env.FORCE_TTY || force)) return
|
107 |
+
|
108 |
await Bot.mkdir(this.path)
|
109 |
Bot[this.id] = {
|
110 |
adapter: this,
|
111 |
+
sdk: readline.createInterface({
|
112 |
+
input: process.stdin,
|
113 |
+
output: process.stderr,
|
114 |
+
}).on("line", data => this.message(String(data)))
|
115 |
+
.on("close", () => process.exit(1)),
|
116 |
+
|
117 |
uin: this.id,
|
118 |
nickname: this.name,
|
119 |
version: { id: this.id, name: this.name },
|
120 |
+
|
121 |
pickFriend: () => this.pickFriend(),
|
122 |
get stat() { return Bot.stat },
|
123 |
get pickUser() { return this.pickFriend },
|
|
|
135 |
}
|
136 |
Bot[this.id].gml.set(this.id, Bot[this.id].fl)
|
137 |
|
138 |
+
try {
|
139 |
+
Bot[this.id].sdk.history = (await fs.readFile(`${this.path}history`, "utf8")).split("\n").slice(-Bot[this.id].sdk.historySize-1, -1).map(i => i.replace(/^[0-9a-z]+?:/, "")).reverse()
|
140 |
+
} catch (err) {
|
141 |
+
Bot.makeLog("trace", err, this.id)
|
142 |
+
}
|
143 |
|
144 |
+
Bot.makeLog("mark", `${this.name}(${this.id}) 已连接`, this.id)
|
145 |
Bot.em(`connect.${this.id}`, { self_id: this.id })
|
146 |
}
|
147 |
})
|
Yunzai/lib/puppeteer/puppeteer.js
CHANGED
@@ -1,23 +1,21 @@
|
|
1 |
-
import Renderer from
|
2 |
|
3 |
/**
|
4 |
* 暂时保留对手工引用puppeteer.js的兼容
|
5 |
* 后期会逐步废弃
|
6 |
* 只提供截图及分片截图功能
|
7 |
*/
|
8 |
-
|
9 |
renderer.screenshot = async (name, data) => {
|
10 |
-
|
11 |
-
|
12 |
}
|
13 |
renderer.screenshots = async (name, data) => {
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
return ret.length > 0 ? ret : false
|
21 |
}
|
22 |
-
|
23 |
export default renderer
|
|
|
1 |
+
import Renderer from "../renderer/loader.js"
|
2 |
|
3 |
/**
|
4 |
* 暂时保留对手工引用puppeteer.js的兼容
|
5 |
* 后期会逐步废弃
|
6 |
* 只提供截图及分片截图功能
|
7 |
*/
|
8 |
+
const renderer = Renderer.getRenderer()
|
9 |
renderer.screenshot = async (name, data) => {
|
10 |
+
const img = await renderer.render(name, data)
|
11 |
+
return img ? segment.image(img) : img
|
12 |
}
|
13 |
renderer.screenshots = async (name, data) => {
|
14 |
+
data.multiPage = true
|
15 |
+
const imgs = await renderer.render(name, data) || []
|
16 |
+
const ret = []
|
17 |
+
for (const img of imgs)
|
18 |
+
ret.push(img ? segment.image(img) : img)
|
19 |
+
return ret.length > 0 ? ret : false
|
|
|
20 |
}
|
|
|
21 |
export default renderer
|
Yunzai/lib/renderer/loader.js
CHANGED
@@ -4,11 +4,6 @@ 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
|
14 |
|
@@ -18,7 +13,7 @@ global.Renderer = Renderer
|
|
18 |
class RendererLoader {
|
19 |
constructor() {
|
20 |
this.renderers = new Map()
|
21 |
-
this.dir = "
|
22 |
// TODO 渲染器热加载
|
23 |
this.watcher = {}
|
24 |
}
|
@@ -30,15 +25,14 @@ class RendererLoader {
|
|
30 |
}
|
31 |
|
32 |
async load() {
|
33 |
-
if (!Data) return
|
34 |
const subFolders = fs.readdirSync(this.dir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory())
|
35 |
-
for (
|
36 |
-
|
37 |
try {
|
38 |
-
const rendererFn = await
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
if (!renderer.id || !renderer.type || !renderer.render || !lodash.isFunction(renderer.render)) {
|
43 |
logger.warn("渲染后端 " + (renderer.id || subFolder.name) + " 不可用")
|
44 |
}
|
@@ -57,5 +51,4 @@ class RendererLoader {
|
|
57 |
}
|
58 |
}
|
59 |
|
60 |
-
|
61 |
export default await RendererLoader.init()
|
|
|
4 |
import cfg from "../config/config.js"
|
5 |
import Renderer from "./Renderer.js"
|
6 |
|
|
|
|
|
|
|
|
|
|
|
7 |
/** 全局变量 Renderer */
|
8 |
global.Renderer = Renderer
|
9 |
|
|
|
13 |
class RendererLoader {
|
14 |
constructor() {
|
15 |
this.renderers = new Map()
|
16 |
+
this.dir = "renderers"
|
17 |
// TODO 渲染器热加载
|
18 |
this.watcher = {}
|
19 |
}
|
|
|
25 |
}
|
26 |
|
27 |
async load() {
|
|
|
28 |
const subFolders = fs.readdirSync(this.dir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory())
|
29 |
+
for (const subFolder of subFolders) {
|
30 |
+
const name = subFolder.name
|
31 |
try {
|
32 |
+
const rendererFn = (await import(`../../${this.dir}/${name}/index.js`)).default
|
33 |
+
const configFile = `${this.dir}/${name}/config.yaml`
|
34 |
+
const rendererCfg = fs.existsSync(configFile) ? yaml.parse(fs.readFileSync(configFile, "utf8")) : {}
|
35 |
+
const renderer = rendererFn(rendererCfg)
|
36 |
if (!renderer.id || !renderer.type || !renderer.render || !lodash.isFunction(renderer.render)) {
|
37 |
logger.warn("渲染后端 " + (renderer.id || subFolder.name) + " 不可用")
|
38 |
}
|
|
|
51 |
}
|
52 |
}
|
53 |
|
|
|
54 |
export default await RendererLoader.init()
|
Yunzai/lib/tools/docker.sh
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#TRSS Yunzai Docker 安装脚本 作者:时雨🌌星空
|
2 |
+
NAME=v1.0.0;VERSION=202405290
|
3 |
+
R="[1;31m" G="[1;32m" Y="[1;33m" C="[1;36m" B="[1;m" O="[m"
|
4 |
+
echo "$B———————————————————————————
|
5 |
+
$R TRSS$Y Yunzai$G Docker$C Script$O
|
6 |
+
$G$NAME$C ($VERSION)$O
|
7 |
+
$B———————————————————————————
|
8 |
+
$G作者:$C时雨🌌星空$O
|
9 |
+
|
10 |
+
$Y- 正在检查环境$O
|
11 |
+
"
|
12 |
+
DIR="${DIR:-$HOME/Yunzai}"
|
13 |
+
CMD="${CMD:-tsyz}"
|
14 |
+
CMDPATH="${CMDPATH:-/usr/local/bin}"
|
15 |
+
DKNAME="${DKNAME:-Yunzai}"
|
16 |
+
DKURL="${DKURL:-docker.nju.edu.cn}"
|
17 |
+
GITURL="${GITURL:-https://gitee.com/TimeRainStarSky/Yunzai}"
|
18 |
+
APTURL="${APTURL:-mirrors.ustc.edu.cn}"
|
19 |
+
APTDEP="${APTDEP:-chromium fonts-lxgw-wenkai fonts-noto-color-emoji}"
|
20 |
+
NPMURL="${NPMURL:-https://registry.npmmirror.com}"
|
21 |
+
abort(){ echo "
|
22 |
+
$R! $@$O";exit 1;}
|
23 |
+
mktmp(){ TMP="$DIR/tmp"&&rm -rf "$TMP"&&mkdir -p "$TMP"||abort "缓存目录创建失败";}
|
24 |
+
if type docker;then
|
25 |
+
echo "
|
26 |
+
$G- Docker 已安装$O
|
27 |
+
"
|
28 |
+
elif type pacman &>/dev/null;then
|
29 |
+
echo "
|
30 |
+
$Y- 正在使用 pacman 安装 Docker$O
|
31 |
+
"
|
32 |
+
pacman -Syu --noconfirm --needed --overwrite "*" docker||abort "Docker 安装失败"
|
33 |
+
elif type apt &>/dev/null;then
|
34 |
+
echo "
|
35 |
+
$Y- 正在使用 apt 安装 Docker$O
|
36 |
+
"
|
37 |
+
apt update&&apt install -y docker.io||abort "Docker 安装失败"
|
38 |
+
else
|
39 |
+
echo "
|
40 |
+
$Y- 正在使用 官方脚本 安装 Docker$O
|
41 |
+
"
|
42 |
+
DOWNLOAD_URL="https://$APTURL/docker-ce" bash <(curl -L get.docker.com)||abort "官方脚本 执行失败,请自行安装 Docker 后重试:https://docker.com"
|
43 |
+
fi
|
44 |
+
docker info||{ systemctl enable --now docker||service docker start&&docker info;}&&echo "
|
45 |
+
$G- Docker 已启动$O"||abort "Docker 启动失败"
|
46 |
+
N=1
|
47 |
+
until echo "
|
48 |
+
$Y- 正在从 $C$DKURL$Y 下载 Docker 容器$O
|
49 |
+
"
|
50 |
+
docker pull "$DKURL/library/node:slim";do
|
51 |
+
echo "
|
52 |
+
$R! 下载失败,5秒后切换镜像源$O"
|
53 |
+
sleep 5
|
54 |
+
((N++))
|
55 |
+
case "$N" in
|
56 |
+
1)DKURL="docker.nju.edu.cn";;
|
57 |
+
2)DKURL="mirror.ccs.tencentyun.com";;
|
58 |
+
3)DKURL="mirror.baidubce.com";;
|
59 |
+
4)DKURL="dockerproxy.com";;
|
60 |
+
5)DKURL="docker.m.daocloud.io";;
|
61 |
+
*)DKURL="docker.io";N=0
|
62 |
+
esac
|
63 |
+
done
|
64 |
+
echo "
|
65 |
+
$Y- 正在构建 Docker 容器$O
|
66 |
+
"
|
67 |
+
mktmp
|
68 |
+
cd "$TMP"
|
69 |
+
echo "FROM $DKURL"'/library/node:slim
|
70 |
+
RUN sed -i "s|deb.debian.org|'"$APTURL"'|g" /etc/apt/sources.list.d/debian.sources\
|
71 |
+
&& apt update\
|
72 |
+
&& apt install -y ca-certificates\
|
73 |
+
&& sed -i "s|http://'"$APTURL"'|https://'"$APTURL"'|g" /etc/apt/sources.list.d/debian.sources\
|
74 |
+
&& apt update\
|
75 |
+
&& apt full-upgrade -y\
|
76 |
+
&& apt install -y curl git redis-server '"$APTDEP"'\
|
77 |
+
&& apt autoremove --purge\
|
78 |
+
&& apt clean\
|
79 |
+
&& git config --global --add safe.directory "*"\
|
80 |
+
&& npm install -g pnpm --registry "'"$NPMURL"'"\
|
81 |
+
&& rm -rf /var/cache/* /var/log/* /var/lib/apt /root/.npm\
|
82 |
+
&& echo -n "[ -s .git ]||git clone --depth 1 --single-branch \"'"$GITURL"'\" .&&pnpm install --force&&echo -n \"exec node . start\">/start&&exec node . start">/start
|
83 |
+
HEALTHCHECK CMD curl -s http://localhost:2536/status||exit 1
|
84 |
+
WORKDIR /root/Yunzai
|
85 |
+
ENTRYPOINT []
|
86 |
+
CMD ["sh","/start"]
|
87 |
+
EXPOSE 2536'>Dockerfile
|
88 |
+
docker build -t trss:yunzai .||abort "Docker 容器构建失败"
|
89 |
+
echo "
|
90 |
+
$Y- 正在启动 Docker 容器$O
|
91 |
+
"
|
92 |
+
docker rm -f $DKNAME 2>/dev/null
|
93 |
+
docker image prune -f
|
94 |
+
docker run -itd -h Yunzai --name $DKNAME -v "$DIR":/root/Yunzai --restart always $([ $DKNAME = Yunzai ]&&echo "-p2536:2536"||echo "-P") trss:yunzai||abort "Docker 容器启动失败"
|
95 |
+
mkdir -vp "$CMDPATH"&&
|
96 |
+
echo -n 'if [ -n "$1" ];then case "$1" in
|
97 |
+
s|start)exec docker start '$DKNAME';;
|
98 |
+
st|stop)exec docker stop '$DKNAME';;
|
99 |
+
rs|restart)exec docker restart '$DKNAME';;
|
100 |
+
l|log)exec docker logs -fn"${2:-100}" '$DKNAME';;
|
101 |
+
*)exec docker exec -it '$DKNAME' "$@";;
|
102 |
+
esac;else
|
103 |
+
docker logs -n100 '$DKNAME'
|
104 |
+
exec docker attach '$DKNAME'
|
105 |
+
fi'>"$CMDPATH/$CMD"&&
|
106 |
+
chmod 755 "$CMDPATH/$CMD"||abort "脚本执行命令 $CMDPATH/$CMD 设置失败,手动执行命令:docker attach $DKNAME"
|
107 |
+
echo "
|
108 |
+
$G- Docker 容器安装完成,启动命令:$C$CMD$O"
|
109 |
+
rm -rf "$TMP"
|
Yunzai/package.json
CHANGED
@@ -18,7 +18,7 @@
|
|
18 |
"art-template": "^4.13.2",
|
19 |
"chalk": "^5.3.0",
|
20 |
"chokidar": "^3.6.0",
|
21 |
-
"express": "^4.19.
|
22 |
"file-type": "^19.0.0",
|
23 |
"https-proxy-agent": "7.0.4",
|
24 |
"image-size": "^1.1.1",
|
@@ -30,20 +30,22 @@
|
|
30 |
"node-fetch": "link:lib/modules/node-fetch",
|
31 |
"node-schedule": "^2.1.1",
|
32 |
"oicq": "link:lib/modules/oicq",
|
33 |
-
"pm2": "^5.
|
34 |
"puppeteer": "*",
|
35 |
-
"redis": "^4.6.
|
36 |
-
"sequelize": "^6.37.
|
37 |
"sqlite3": "5.1.6",
|
38 |
-
"
|
39 |
-
"
|
|
|
|
|
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.
|
47 |
},
|
48 |
"imports": {
|
49 |
"#miao": "./plugins/miao-plugin/components/index.js",
|
|
|
18 |
"art-template": "^4.13.2",
|
19 |
"chalk": "^5.3.0",
|
20 |
"chokidar": "^3.6.0",
|
21 |
+
"express": "^4.19.2",
|
22 |
"file-type": "^19.0.0",
|
23 |
"https-proxy-agent": "7.0.4",
|
24 |
"image-size": "^1.1.1",
|
|
|
30 |
"node-fetch": "link:lib/modules/node-fetch",
|
31 |
"node-schedule": "^2.1.1",
|
32 |
"oicq": "link:lib/modules/oicq",
|
33 |
+
"pm2": "^5.4.0",
|
34 |
"puppeteer": "*",
|
35 |
+
"redis": "^4.6.14",
|
36 |
+
"sequelize": "^6.37.3",
|
37 |
"sqlite3": "5.1.6",
|
38 |
+
"strip-ansi": "^7.1.0",
|
39 |
+
"ulid": "^2.3.0",
|
40 |
+
"ws": "^8.17.0",
|
41 |
+
"yaml": "^2.4.3"
|
42 |
},
|
43 |
"devDependencies": {
|
44 |
"eslint": "^8.57.0",
|
45 |
"eslint-config-standard": "^17.1.0",
|
46 |
"eslint-plugin-import": "^2.29.1",
|
47 |
"eslint-plugin-n": "^16.6.2",
|
48 |
+
"eslint-plugin-promise": "^6.2.0"
|
49 |
},
|
50 |
"imports": {
|
51 |
"#miao": "./plugins/miao-plugin/components/index.js",
|
Yunzai/plugins/.gitignore
CHANGED
@@ -6,6 +6,7 @@
|
|
6 |
!system/**
|
7 |
!other
|
8 |
!other/**
|
|
|
9 |
!example/一言.js
|
10 |
!example/主动复读.js
|
11 |
!example/进群退群通知.js
|
|
|
6 |
!system/**
|
7 |
!other
|
8 |
!other/**
|
9 |
+
!example/package.json
|
10 |
!example/一言.js
|
11 |
!example/主动复读.js
|
12 |
!example/进群退群通知.js
|
Yunzai/plugins/adapter/ComWeChat.js
CHANGED
@@ -1,23 +1,34 @@
|
|
1 |
-
import { randomUUID } from "node:crypto"
|
2 |
import path from "node:path"
|
|
|
3 |
|
4 |
Bot.adapter.push(new class ComWeChatAdapter {
|
5 |
constructor() {
|
6 |
this.id = "WeChat"
|
7 |
this.name = "ComWeChat"
|
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 =
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
22 |
|
23 |
async uploadFile(data, file) {
|
@@ -44,7 +55,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
44 |
msg = [msg]
|
45 |
const msgs = []
|
46 |
for (let i of msg) {
|
47 |
-
if (typeof i
|
48 |
i = { type: "text", data: { text: i }}
|
49 |
else if (!i.data)
|
50 |
i = { type: i.type, data: { ...i, type: undefined }}
|
@@ -63,7 +74,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
63 |
i.type = "file"
|
64 |
break
|
65 |
case "at":
|
66 |
-
if (i.data.qq
|
67 |
i = { type: "mention_all", data: {}}
|
68 |
else
|
69 |
i = { type: "mention", data: { user_id: i.data.qq }}
|
@@ -74,8 +85,11 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
74 |
case "node":
|
75 |
await Bot.sendForwardMsg(send, i.data)
|
76 |
continue
|
|
|
|
|
|
|
77 |
default:
|
78 |
-
i = { type: "text", data: { text:
|
79 |
}
|
80 |
msgs.push(i)
|
81 |
}
|
@@ -107,7 +121,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
107 |
for (const i of (await data.bot.sendApi("get_friend_list")).data)
|
108 |
array.push({
|
109 |
...i,
|
110 |
-
nickname: i.user_remark
|
111 |
})
|
112 |
return array
|
113 |
}
|
@@ -237,7 +251,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
237 |
Bot[data.self_id] = {
|
238 |
adapter: this,
|
239 |
ws: ws,
|
240 |
-
sendApi: (action, params) => this.sendApi(ws, action, params),
|
241 |
stat: { ...data.status, start_time: data.time },
|
242 |
|
243 |
info: {},
|
@@ -265,9 +279,9 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
265 |
if (!Bot.uin.includes(data.self_id))
|
266 |
Bot.uin.push(data.self_id)
|
267 |
|
268 |
-
data.bot.info = (await data.bot.sendApi("get_self_info")).data
|
269 |
data.bot.version = {
|
270 |
-
...(await data.bot.sendApi("get_version")).data,
|
271 |
id: this.id,
|
272 |
name: this.name,
|
273 |
}
|
@@ -428,7 +442,7 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
428 |
}
|
429 |
|
430 |
if (data.type) {
|
431 |
-
if (data.type
|
432 |
Bot.makeLog("warn", `找不到对应Bot,忽略消息:${logger.magenta(data.raw)}`, data.self_id)
|
433 |
return false
|
434 |
}
|
@@ -450,8 +464,17 @@ Bot.adapter.push(new class ComWeChatAdapter {
|
|
450 |
default:
|
451 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
452 |
}
|
453 |
-
} else if (data.echo) {
|
454 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
455 |
} else {
|
456 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
457 |
}
|
|
|
|
|
1 |
import path from "node:path"
|
2 |
+
import { ulid } from "ulid"
|
3 |
|
4 |
Bot.adapter.push(new class ComWeChatAdapter {
|
5 |
constructor() {
|
6 |
this.id = "WeChat"
|
7 |
this.name = "ComWeChat"
|
8 |
this.path = this.name
|
9 |
+
this.echo = {}
|
10 |
+
this.timeout = 60000
|
11 |
}
|
12 |
|
13 |
makeLog(msg) {
|
14 |
return Bot.String(msg).replace(/(base64:\/\/|"type":"data","data":").*?"/g, '$1..."')
|
15 |
}
|
16 |
|
17 |
+
sendApi(data, ws, action, params = {}) {
|
18 |
+
const echo = ulid()
|
19 |
+
const request = { action, params, echo }
|
20 |
+
ws.sendMsg(request)
|
21 |
+
return new Promise((resolve, reject) =>
|
22 |
+
this.echo[echo] = {
|
23 |
+
request, resolve, reject,
|
24 |
+
timeout: setTimeout(() => {
|
25 |
+
reject(Object.assign(request, { timeout: this.timeout }))
|
26 |
+
delete this.echo[echo]
|
27 |
+
Bot.makeLog("error", ["请求超时", request], data.self_id)
|
28 |
+
ws.terminate()
|
29 |
+
}, this.timeout),
|
30 |
+
}
|
31 |
+
)
|
32 |
}
|
33 |
|
34 |
async uploadFile(data, file) {
|
|
|
55 |
msg = [msg]
|
56 |
const msgs = []
|
57 |
for (let i of msg) {
|
58 |
+
if (typeof i !== "object")
|
59 |
i = { type: "text", data: { text: i }}
|
60 |
else if (!i.data)
|
61 |
i = { type: i.type, data: { ...i, type: undefined }}
|
|
|
74 |
i.type = "file"
|
75 |
break
|
76 |
case "at":
|
77 |
+
if (i.data.qq === "all")
|
78 |
i = { type: "mention_all", data: {}}
|
79 |
else
|
80 |
i = { type: "mention", data: { user_id: i.data.qq }}
|
|
|
85 |
case "node":
|
86 |
await Bot.sendForwardMsg(send, i.data)
|
87 |
continue
|
88 |
+
case "raw":
|
89 |
+
i = i.data
|
90 |
+
break
|
91 |
default:
|
92 |
+
i = { type: "text", data: { text: Bot.String(i) }}
|
93 |
}
|
94 |
msgs.push(i)
|
95 |
}
|
|
|
121 |
for (const i of (await data.bot.sendApi("get_friend_list")).data)
|
122 |
array.push({
|
123 |
...i,
|
124 |
+
nickname: i.user_remark === "null" ? i.user_displayname || i.user_name : i.user_remark,
|
125 |
})
|
126 |
return array
|
127 |
}
|
|
|
251 |
Bot[data.self_id] = {
|
252 |
adapter: this,
|
253 |
ws: ws,
|
254 |
+
sendApi: (action, params) => this.sendApi(data, ws, action, params),
|
255 |
stat: { ...data.status, start_time: data.time },
|
256 |
|
257 |
info: {},
|
|
|
279 |
if (!Bot.uin.includes(data.self_id))
|
280 |
Bot.uin.push(data.self_id)
|
281 |
|
282 |
+
data.bot.info = (await data.bot.sendApi("get_self_info").catch(i => i.error)).data
|
283 |
data.bot.version = {
|
284 |
+
...(await data.bot.sendApi("get_version").catch(i => i.error)).data,
|
285 |
id: this.id,
|
286 |
name: this.name,
|
287 |
}
|
|
|
442 |
}
|
443 |
|
444 |
if (data.type) {
|
445 |
+
if (data.type !== "meta" && !Bot.uin.includes(data.self_id)) {
|
446 |
Bot.makeLog("warn", `找不到对应Bot,忽略消息:${logger.magenta(data.raw)}`, data.self_id)
|
447 |
return false
|
448 |
}
|
|
|
464 |
default:
|
465 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
466 |
}
|
467 |
+
} else if (data.echo && this.echo[data.echo]) {
|
468 |
+
if (data.retcode !== 0)
|
469 |
+
this.echo[data.echo].reject(Object.assign(
|
470 |
+
this.echo[data.echo].request, { error: data }
|
471 |
+
))
|
472 |
+
else
|
473 |
+
this.echo[data.echo].resolve(data.data ? new Proxy(data, {
|
474 |
+
get: (target, prop) => target.data[prop] ?? target[prop],
|
475 |
+
}) : data)
|
476 |
+
clearTimeout(this.echo[data.echo].timeout)
|
477 |
+
delete this.echo[data.echo]
|
478 |
} else {
|
479 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
480 |
}
|
Yunzai/plugins/adapter/GSUIDCore.js
CHANGED
@@ -28,7 +28,7 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
|
|
28 |
} else return false
|
29 |
|
30 |
if (button.permission) {
|
31 |
-
if (button.permission
|
32 |
msg.permission = 1
|
33 |
} else {
|
34 |
msg.permission = 0
|
@@ -58,7 +58,7 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
|
|
58 |
msg = [msg]
|
59 |
const msgs = []
|
60 |
for (let i of msg) {
|
61 |
-
if (typeof i
|
62 |
i = { type: "text", text: i }
|
63 |
|
64 |
if (i.file) {
|
@@ -100,8 +100,11 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
|
|
100 |
array.push(...await this.makeMsg(message))
|
101 |
i.data = array
|
102 |
break
|
103 |
-
}
|
104 |
-
i =
|
|
|
|
|
|
|
105 |
}
|
106 |
msgs.push(i)
|
107 |
}
|
@@ -118,7 +121,7 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
|
|
118 |
target_id: data.user_id,
|
119 |
content,
|
120 |
})
|
121 |
-
return { message_id: Date.now() }
|
122 |
}
|
123 |
|
124 |
async sendGroupMsg(data, msg) {
|
@@ -132,7 +135,7 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
|
|
132 |
target_id: target[1],
|
133 |
content,
|
134 |
})
|
135 |
-
return { message_id: Date.now() }
|
136 |
}
|
137 |
|
138 |
pickFriend(id, user_id) {
|
@@ -260,15 +263,15 @@ Bot.adapter.push(new class GSUIDCoreAdapter {
|
|
260 |
break
|
261 |
case "node":
|
262 |
data.message.push({ type: "node", data: i.data })
|
263 |
-
data.raw_message += `[合并转发:${
|
264 |
break
|
265 |
default:
|
266 |
data.message.push(i)
|
267 |
-
data.raw_message +=
|
268 |
}
|
269 |
}
|
270 |
|
271 |
-
if (raw.user_type
|
272 |
data.message_type = "private"
|
273 |
Bot.makeLog("info", `好友消息:${data.raw_message}`, `${data.self_id} <= ${data.user_id}`)
|
274 |
} else {
|
|
|
28 |
} else return false
|
29 |
|
30 |
if (button.permission) {
|
31 |
+
if (button.permission === "admin") {
|
32 |
msg.permission = 1
|
33 |
} else {
|
34 |
msg.permission = 0
|
|
|
58 |
msg = [msg]
|
59 |
const msgs = []
|
60 |
for (let i of msg) {
|
61 |
+
if (typeof i !== "object")
|
62 |
i = { type: "text", text: i }
|
63 |
|
64 |
if (i.file) {
|
|
|
100 |
array.push(...await this.makeMsg(message))
|
101 |
i.data = array
|
102 |
break
|
103 |
+
} case "raw":
|
104 |
+
i = i.data
|
105 |
+
break
|
106 |
+
default:
|
107 |
+
i = { type: "text", data: Bot.String(i) }
|
108 |
}
|
109 |
msgs.push(i)
|
110 |
}
|
|
|
121 |
target_id: data.user_id,
|
122 |
content,
|
123 |
})
|
124 |
+
return { message_id: Date.now().toString(36) }
|
125 |
}
|
126 |
|
127 |
async sendGroupMsg(data, msg) {
|
|
|
135 |
target_id: target[1],
|
136 |
content,
|
137 |
})
|
138 |
+
return { message_id: Date.now().toString(36) }
|
139 |
}
|
140 |
|
141 |
pickFriend(id, user_id) {
|
|
|
263 |
break
|
264 |
case "node":
|
265 |
data.message.push({ type: "node", data: i.data })
|
266 |
+
data.raw_message += `[合并转发:${Bot.String(i.data)}]`
|
267 |
break
|
268 |
default:
|
269 |
data.message.push(i)
|
270 |
+
data.raw_message += Bot.String(i)
|
271 |
}
|
272 |
}
|
273 |
|
274 |
+
if (raw.user_type === "direct") {
|
275 |
data.message_type = "private"
|
276 |
Bot.makeLog("info", `好友消息:${data.raw_message}`, `${data.self_id} <= ${data.user_id}`)
|
277 |
} else {
|
Yunzai/plugins/adapter/OPQBot.js
CHANGED
@@ -3,6 +3,8 @@ Bot.adapter.push(new class OPQBotAdapter {
|
|
3 |
this.id = "QQ"
|
4 |
this.name = "OPQBot"
|
5 |
this.path = this.name
|
|
|
|
|
6 |
this.CommandId = {
|
7 |
FriendImage: 1,
|
8 |
GroupImage: 2,
|
@@ -13,9 +15,19 @@ Bot.adapter.push(new class OPQBotAdapter {
|
|
13 |
|
14 |
sendApi(id, CgiCmd, CgiRequest) {
|
15 |
const ReqId = Math.round(Math.random()*10**16)
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
}
|
20 |
|
21 |
makeLog(msg) {
|
@@ -46,7 +58,7 @@ Bot.adapter.push(new class OPQBotAdapter {
|
|
46 |
}
|
47 |
|
48 |
for (let i of msg) {
|
49 |
-
if (typeof i
|
50 |
i = { type: "text", text: i }
|
51 |
|
52 |
switch (i.type) {
|
@@ -71,8 +83,12 @@ Bot.adapter.push(new class OPQBotAdapter {
|
|
71 |
case "node":
|
72 |
await Bot.sendForwardMsg(msg => this.sendMsg(send, upload, msg), i.data)
|
73 |
continue
|
|
|
|
|
|
|
|
|
74 |
default:
|
75 |
-
message.Content +=
|
76 |
}
|
77 |
}
|
78 |
|
@@ -141,7 +157,7 @@ Bot.adapter.push(new class OPQBotAdapter {
|
|
141 |
return {
|
142 |
...i,
|
143 |
sendMsg: msg => this.sendFriendMsg(i, msg),
|
144 |
-
getAvatarUrl: () => `https://
|
145 |
}
|
146 |
}
|
147 |
|
@@ -267,7 +283,7 @@ Bot.adapter.push(new class OPQBotAdapter {
|
|
267 |
uin: id,
|
268 |
info: { id },
|
269 |
get nickname() { return this.info.nickname },
|
270 |
-
get avatar() { return `https://
|
271 |
|
272 |
version: {
|
273 |
id: this.id,
|
@@ -310,8 +326,15 @@ Bot.adapter.push(new class OPQBotAdapter {
|
|
310 |
this.makeBot(id, ws)
|
311 |
|
312 |
this.makeEvent(id, data.CurrentPacket)
|
313 |
-
} else if (data.ReqId) {
|
314 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
315 |
} else {
|
316 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, id)
|
317 |
}
|
|
|
3 |
this.id = "QQ"
|
4 |
this.name = "OPQBot"
|
5 |
this.path = this.name
|
6 |
+
this.echo = {}
|
7 |
+
this.timeout = 60000
|
8 |
this.CommandId = {
|
9 |
FriendImage: 1,
|
10 |
GroupImage: 2,
|
|
|
15 |
|
16 |
sendApi(id, CgiCmd, CgiRequest) {
|
17 |
const ReqId = Math.round(Math.random()*10**16)
|
18 |
+
const request = { BotUin: String(id), CgiCmd, CgiRequest, ReqId }
|
19 |
+
Bot[id].ws.sendMsg(request)
|
20 |
+
return new Promise((resolve, reject) =>
|
21 |
+
this.echo[ReqId] = {
|
22 |
+
request, resolve, reject,
|
23 |
+
timeout: setTimeout(() => {
|
24 |
+
reject(Object.assign(request, { timeout: this.timeout }))
|
25 |
+
delete this.echo[ReqId]
|
26 |
+
Bot.makeLog("error", ["请求超时", request], id)
|
27 |
+
Bot[id].ws.terminate()
|
28 |
+
}, this.timeout),
|
29 |
+
}
|
30 |
+
)
|
31 |
}
|
32 |
|
33 |
makeLog(msg) {
|
|
|
58 |
}
|
59 |
|
60 |
for (let i of msg) {
|
61 |
+
if (typeof i !== "object")
|
62 |
i = { type: "text", text: i }
|
63 |
|
64 |
switch (i.type) {
|
|
|
83 |
case "node":
|
84 |
await Bot.sendForwardMsg(msg => this.sendMsg(send, upload, msg), i.data)
|
85 |
continue
|
86 |
+
case "raw":
|
87 |
+
for (const i in i.data)
|
88 |
+
message[i] = i.data[i]
|
89 |
+
continue
|
90 |
default:
|
91 |
+
message.Content += Bot.String(i)
|
92 |
}
|
93 |
}
|
94 |
|
|
|
157 |
return {
|
158 |
...i,
|
159 |
sendMsg: msg => this.sendFriendMsg(i, msg),
|
160 |
+
getAvatarUrl: () => `https://q.qlogo.cn/g?b=qq&s=0&nk=${user_id}`,
|
161 |
}
|
162 |
}
|
163 |
|
|
|
283 |
uin: id,
|
284 |
info: { id },
|
285 |
get nickname() { return this.info.nickname },
|
286 |
+
get avatar() { return `https://q.qlogo.cn/g?b=qq&s=0&nk=${this.uin}` },
|
287 |
|
288 |
version: {
|
289 |
id: this.id,
|
|
|
326 |
this.makeBot(id, ws)
|
327 |
|
328 |
this.makeEvent(id, data.CurrentPacket)
|
329 |
+
} else if (data.ReqId && this.echo[data.ReqId]) {
|
330 |
+
if (data.CgiBaseResponse?.Ret !== 0)
|
331 |
+
this.echo[data.ReqId].reject(Object.assign(
|
332 |
+
this.echo[data.ReqId].request, { error: data }
|
333 |
+
))
|
334 |
+
else
|
335 |
+
this.echo[data.ReqId].resolve(data)
|
336 |
+
clearTimeout(this.echo[data.ReqId].timeout)
|
337 |
+
delete this.echo[data.ReqId]
|
338 |
} else {
|
339 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, id)
|
340 |
}
|
Yunzai/plugins/adapter/OneBotv11.js
CHANGED
@@ -1,28 +1,34 @@
|
|
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 =
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
26 |
}
|
27 |
|
28 |
async makeFile(file) {
|
@@ -38,7 +44,7 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
38 |
const msgs = []
|
39 |
const forward = []
|
40 |
for (let i of msg) {
|
41 |
-
if (typeof i
|
42 |
i = { type: "text", data: { text: i }}
|
43 |
else if (!i.data)
|
44 |
i = { type: i.type, data: { ...i, type: undefined }}
|
@@ -55,6 +61,9 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
55 |
case "node":
|
56 |
forward.push(...i.data)
|
57 |
continue
|
|
|
|
|
|
|
58 |
}
|
59 |
|
60 |
if (i.data.file)
|
@@ -79,7 +88,7 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
79 |
|
80 |
if (message.length)
|
81 |
ret.push(await send(message))
|
82 |
-
if (ret.length
|
83 |
|
84 |
const message_id = []
|
85 |
for (const i of ret) if (i?.message_id)
|
@@ -131,7 +140,7 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
131 |
parseMsg(msg) {
|
132 |
const array = []
|
133 |
for (const i of Array.isArray(msg) ? msg : [msg])
|
134 |
-
if (typeof i
|
135 |
array.push({ ...i.data, type: i.type })
|
136 |
else
|
137 |
array.push({ type: "text", text: String(i) })
|
@@ -368,6 +377,19 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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", {
|
@@ -386,10 +408,9 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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 |
|
@@ -574,13 +595,13 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
574 |
sendForwardMsg: msg => this.sendFriendForwardMsg(i, msg),
|
575 |
sendFile: (file, name) => this.sendFriendFile(i, file, name),
|
576 |
getInfo: () => this.getFriendInfo(i),
|
577 |
-
getAvatarUrl: () => `https://
|
578 |
thumbUp: times => this.sendLike(i, times),
|
579 |
}
|
580 |
}
|
581 |
|
582 |
pickMember(data, group_id, user_id) {
|
583 |
-
if (typeof group_id
|
584 |
const guild_id = group_id.split("-")
|
585 |
const i = {
|
586 |
...data,
|
@@ -607,17 +628,18 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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
|
615 |
-
get is_admin() { return i.role
|
616 |
}
|
617 |
}
|
618 |
|
619 |
pickGroup(data, group_id) {
|
620 |
-
if (typeof group_id
|
621 |
const guild_id = group_id.split("-")
|
622 |
const i = {
|
623 |
...data.bot.gl.get(group_id),
|
@@ -656,7 +678,7 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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),
|
@@ -674,8 +696,8 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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
|
678 |
-
get is_admin() { return data.bot.gml.get(group_id)?.get(data.self_id)?.role
|
679 |
}
|
680 |
}
|
681 |
|
@@ -683,7 +705,7 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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: {},
|
@@ -699,10 +721,11 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
699 |
info: {},
|
700 |
get uin() { return this.info.user_id },
|
701 |
get nickname() { return this.info.nickname },
|
702 |
-
get avatar() { return `https://
|
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 },
|
@@ -733,13 +756,13 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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() {
|
@@ -773,7 +796,7 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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}] ${
|
777 |
Object.defineProperty(data, "friend", { get() { return this.member || {}}})
|
778 |
break
|
779 |
default:
|
@@ -793,24 +816,24 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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
|
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
|
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
|
811 |
break
|
812 |
case "group_upload":
|
813 |
-
Bot.makeLog("info", `群文件上传:${
|
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}`)
|
@@ -846,10 +869,10 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
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", `离线文件:${
|
850 |
break
|
851 |
case "client_status":
|
852 |
-
Bot.makeLog("info", `客户端${data.online ? "上线" : "下线"}:${
|
853 |
data.clients = (await data.bot.sendApi("get_online_clients")).clients
|
854 |
data.bot.clients = data.clients
|
855 |
break
|
@@ -862,20 +885,20 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
862 |
break
|
863 |
case "message_reactions_updated":
|
864 |
data.notice_type = "guild_message_reactions_updated"
|
865 |
-
Bot.makeLog("info", `频道消息表情贴:${data.message_id} ${
|
866 |
break
|
867 |
case "channel_updated":
|
868 |
data.notice_type = "guild_channel_updated"
|
869 |
-
Bot.makeLog("info", `子频道更新:${
|
870 |
break
|
871 |
case "channel_created":
|
872 |
data.notice_type = "guild_channel_created"
|
873 |
-
Bot.makeLog("info", `子频道创建:${
|
874 |
data.bot.getGroupMap()
|
875 |
break
|
876 |
case "channel_destroyed":
|
877 |
data.notice_type = "guild_channel_destroyed"
|
878 |
-
Bot.makeLog("info", `子频道删除:${
|
879 |
data.bot.getGroupMap()
|
880 |
break
|
881 |
default:
|
@@ -944,7 +967,7 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
944 |
}
|
945 |
|
946 |
if (data.post_type) {
|
947 |
-
if (data.meta_event_type
|
948 |
Bot.makeLog("warn", `找不到对应Bot,忽略消息:${logger.magenta(data.raw)}`, data.self_id)
|
949 |
return false
|
950 |
}
|
@@ -970,8 +993,17 @@ Bot.adapter.push(new class OneBotv11Adapter {
|
|
970 |
default:
|
971 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
972 |
}
|
973 |
-
} else if (data.echo) {
|
974 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
975 |
} else {
|
976 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
977 |
}
|
|
|
|
|
1 |
import path from "node:path"
|
2 |
+
import { ulid } from "ulid"
|
3 |
|
4 |
Bot.adapter.push(new class OneBotv11Adapter {
|
5 |
constructor() {
|
6 |
this.id = "QQ"
|
7 |
this.name = "OneBotv11"
|
8 |
this.path = this.name
|
9 |
+
this.echo = {}
|
10 |
+
this.timeout = 60000
|
11 |
}
|
12 |
|
13 |
makeLog(msg) {
|
14 |
return Bot.String(msg).replace(/base64:\/\/.*?(,|]|")/g, "base64://...$1")
|
15 |
}
|
16 |
|
17 |
+
sendApi(data, ws, action, params = {}) {
|
18 |
+
const echo = ulid()
|
19 |
+
const request = { action, params, echo }
|
20 |
+
ws.sendMsg(request)
|
21 |
+
return new Promise((resolve, reject) =>
|
22 |
+
this.echo[echo] = {
|
23 |
+
request, resolve, reject,
|
24 |
+
timeout: setTimeout(() => {
|
25 |
+
reject(Object.assign(request, { timeout: this.timeout }))
|
26 |
+
delete this.echo[echo]
|
27 |
+
Bot.makeLog("error", ["请求超时", request], data.self_id)
|
28 |
+
ws.terminate()
|
29 |
+
}, this.timeout),
|
30 |
+
}
|
31 |
+
)
|
32 |
}
|
33 |
|
34 |
async makeFile(file) {
|
|
|
44 |
const msgs = []
|
45 |
const forward = []
|
46 |
for (let i of msg) {
|
47 |
+
if (typeof i !== "object")
|
48 |
i = { type: "text", data: { text: i }}
|
49 |
else if (!i.data)
|
50 |
i = { type: i.type, data: { ...i, type: undefined }}
|
|
|
61 |
case "node":
|
62 |
forward.push(...i.data)
|
63 |
continue
|
64 |
+
case "raw":
|
65 |
+
i = i.data
|
66 |
+
break
|
67 |
}
|
68 |
|
69 |
if (i.data.file)
|
|
|
88 |
|
89 |
if (message.length)
|
90 |
ret.push(await send(message))
|
91 |
+
if (ret.length === 1) return ret[0]
|
92 |
|
93 |
const message_id = []
|
94 |
for (const i of ret) if (i?.message_id)
|
|
|
140 |
parseMsg(msg) {
|
141 |
const array = []
|
142 |
for (const i of Array.isArray(msg) ? msg : [msg])
|
143 |
+
if (typeof i === "object")
|
144 |
array.push({ ...i.data, type: i.type })
|
145 |
else
|
146 |
array.push({ type: "text", text: String(i) })
|
|
|
377 |
})
|
378 |
}
|
379 |
|
380 |
+
|
381 |
+
setProfile(data, profile) {
|
382 |
+
Bot.makeLog("info", `设置资料:${Bot.String(profile)}`, data.self_id)
|
383 |
+
return data.bot.sendApi("set_qq_profile", profile)
|
384 |
+
}
|
385 |
+
|
386 |
+
async setAvatar(data, file) {
|
387 |
+
Bot.makeLog("info", `设置头像:${file}`, data.self_id)
|
388 |
+
return data.bot.sendApi("set_qq_avatar", {
|
389 |
+
file: await this.makeFile(file),
|
390 |
+
})
|
391 |
+
}
|
392 |
+
|
393 |
sendLike(data, times) {
|
394 |
Bot.makeLog("info", `点赞:${times}次`, `${data.self_id} => ${data.user_id}`)
|
395 |
return data.bot.sendApi("send_like", {
|
|
|
408 |
|
409 |
async setGroupAvatar(data, file) {
|
410 |
Bot.makeLog("info", `设置群头像:${file}`, `${data.self_id} => ${data.group_id}`)
|
|
|
411 |
return data.bot.sendApi("set_group_portrait", {
|
412 |
group_id: data.group_id,
|
413 |
+
file: await this.makeFile(file),
|
414 |
})
|
415 |
}
|
416 |
|
|
|
595 |
sendForwardMsg: msg => this.sendFriendForwardMsg(i, msg),
|
596 |
sendFile: (file, name) => this.sendFriendFile(i, file, name),
|
597 |
getInfo: () => this.getFriendInfo(i),
|
598 |
+
getAvatarUrl: () => i.avatar || `https://q.qlogo.cn/g?b=qq&s=0&nk=${user_id}`,
|
599 |
thumbUp: times => this.sendLike(i, times),
|
600 |
}
|
601 |
}
|
602 |
|
603 |
pickMember(data, group_id, user_id) {
|
604 |
+
if (typeof group_id === "string" && group_id.match("-")) {
|
605 |
const guild_id = group_id.split("-")
|
606 |
const i = {
|
607 |
...data,
|
|
|
628 |
...this.pickFriend(i, user_id),
|
629 |
...i,
|
630 |
getInfo: () => this.getMemberInfo(i),
|
631 |
+
getAvatarUrl: () => i.avatar || `https://q.qlogo.cn/g?b=qq&s=0&nk=${user_id}`,
|
632 |
poke: () => this.sendGroupMsg(i, { type: "poke", qq: user_id }),
|
633 |
mute: duration => this.setGroupBan(i, i.user_id, duration),
|
634 |
kick: reject_add_request => this.setGroupKick(i, i.user_id, reject_add_request),
|
635 |
get is_friend() { return data.bot.fl.has(user_id) },
|
636 |
+
get is_owner() { return i.role === "owner" },
|
637 |
+
get is_admin() { return i.role === "admin" },
|
638 |
}
|
639 |
}
|
640 |
|
641 |
pickGroup(data, group_id) {
|
642 |
+
if (typeof group_id === "string" && group_id.match("-")) {
|
643 |
const guild_id = group_id.split("-")
|
644 |
const i = {
|
645 |
...data.bot.gl.get(group_id),
|
|
|
678 |
sendForwardMsg: msg => this.sendGroupForwardMsg(i, msg),
|
679 |
sendFile: (file, name) => this.sendGroupFile(i, file, undefined, name),
|
680 |
getInfo: () => this.getGroupInfo(i),
|
681 |
+
getAvatarUrl: () => i.avatar || `https://p.qlogo.cn/gh/${group_id}/${group_id}/0`,
|
682 |
getChatHistory: (seq, cnt) => this.getGroupMsgHistory(i, seq, cnt),
|
683 |
getMemberArray: () => this.getMemberArray(i),
|
684 |
getMemberList: () => this.getMemberList(i),
|
|
|
696 |
kickMember: (user_id, reject_add_request) => this.setGroupKick(i, user_id, reject_add_request),
|
697 |
quit: is_dismiss => this.setGroupLeave(i, is_dismiss),
|
698 |
fs: this.getGroupFs(i),
|
699 |
+
get is_owner() { return data.bot.gml.get(group_id)?.get(data.self_id)?.role === "owner" },
|
700 |
+
get is_admin() { return data.bot.gml.get(group_id)?.get(data.self_id)?.role === "admin" },
|
701 |
}
|
702 |
}
|
703 |
|
|
|
705 |
Bot[data.self_id] = {
|
706 |
adapter: this,
|
707 |
ws: ws,
|
708 |
+
sendApi: (action, params) => this.sendApi(data, ws, action, params),
|
709 |
stat: {
|
710 |
start_time: data.time,
|
711 |
stat: {},
|
|
|
721 |
info: {},
|
722 |
get uin() { return this.info.user_id },
|
723 |
get nickname() { return this.info.nickname },
|
724 |
+
get avatar() { return `https://q.qlogo.cn/g?b=qq&s=0&nk=${this.uin}` },
|
725 |
|
726 |
setProfile: profile => this.setProfile(data, profile),
|
727 |
setNickname: nickname => this.setProfile(data, { nickname }),
|
728 |
+
setAvatar: file => this.setAvatar(data, file),
|
729 |
|
730 |
pickFriend: user_id => this.pickFriend(data, user_id),
|
731 |
get pickUser() { return this.pickFriend },
|
|
|
756 |
data.bot.sendApi("_set_model_show", {
|
757 |
model: data.bot.model,
|
758 |
model_show: data.bot.model,
|
759 |
+
}).catch(() => {})
|
760 |
|
761 |
+
data.bot.info = (await data.bot.sendApi("get_login_info").catch(i => i.error)).data
|
762 |
+
data.bot.guild_info = (await data.bot.sendApi("get_guild_service_profile").catch(i => i.error)).data
|
763 |
+
data.bot.clients = (await data.bot.sendApi("get_online_clients").catch(i => i.error)).clients
|
764 |
data.bot.version = {
|
765 |
+
...(await data.bot.sendApi("get_version_info").catch(i => i.error)).data,
|
766 |
id: this.id,
|
767 |
name: this.name,
|
768 |
get version() {
|
|
|
796 |
} case "guild":
|
797 |
data.message_type = "group"
|
798 |
data.group_id = `${data.guild_id}-${data.channel_id}`
|
799 |
+
Bot.makeLog("info", `频道消息:[${data.sender.nickname}] ${Bot.String(data.message)}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
|
800 |
Object.defineProperty(data, "friend", { get() { return this.member || {}}})
|
801 |
break
|
802 |
default:
|
|
|
816 |
break
|
817 |
case "group_increase":
|
818 |
Bot.makeLog("info", `群成员增加:${data.operator_id} => ${data.user_id} ${data.sub_type}`, `${data.self_id} <= ${data.group_id}`)
|
819 |
+
if (data.user_id === data.self_id)
|
820 |
data.bot.getGroupMemberMap()
|
821 |
else
|
822 |
data.bot.pickGroup(data.group_id).getMemberMap()
|
823 |
break
|
824 |
case "group_decrease":
|
825 |
Bot.makeLog("info", `群成员减少:${data.operator_id} => ${data.user_id} ${data.sub_type}`, `${data.self_id} <= ${data.group_id}`)
|
826 |
+
if (data.user_id === data.self_id)
|
827 |
data.bot.getGroupMemberMap()
|
828 |
else
|
829 |
data.bot.pickGroup(data.group_id).getMemberMap()
|
830 |
break
|
831 |
case "group_admin":
|
832 |
Bot.makeLog("info", `群管理员变动:${data.sub_type}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
|
833 |
+
data.set = data.sub_type === "set"
|
834 |
break
|
835 |
case "group_upload":
|
836 |
+
Bot.makeLog("info", `群文件上传:${Bot.String(data.file)}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
|
837 |
break
|
838 |
case "group_ban":
|
839 |
Bot.makeLog("info", `群禁言:${data.operator_id} => ${data.user_id} ${data.sub_type} ${data.duration}秒`, `${data.self_id} <= ${data.group_id}`)
|
|
|
869 |
Bot.makeLog("info", `群名片更新:${data.card_old} => ${data.card_new}`, `${data.self_id} <= ${data.group_id}, ${data.user_id}`)
|
870 |
break
|
871 |
case "offline_file":
|
872 |
+
Bot.makeLog("info", `离线文件:${Bot.String(data.file)}`, `${data.self_id} <= ${data.user_id}`)
|
873 |
break
|
874 |
case "client_status":
|
875 |
+
Bot.makeLog("info", `客户端${data.online ? "上线" : "下线"}:${Bot.String(data.client)}`, data.self_id)
|
876 |
data.clients = (await data.bot.sendApi("get_online_clients")).clients
|
877 |
data.bot.clients = data.clients
|
878 |
break
|
|
|
885 |
break
|
886 |
case "message_reactions_updated":
|
887 |
data.notice_type = "guild_message_reactions_updated"
|
888 |
+
Bot.makeLog("info", `频道消息表情贴:${data.message_id} ${Bot.String(data.current_reactions)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
|
889 |
break
|
890 |
case "channel_updated":
|
891 |
data.notice_type = "guild_channel_updated"
|
892 |
+
Bot.makeLog("info", `子频道更新:${Bot.String(data.old_info)} => ${Bot.String(data.new_info)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
|
893 |
break
|
894 |
case "channel_created":
|
895 |
data.notice_type = "guild_channel_created"
|
896 |
+
Bot.makeLog("info", `子频道创建:${Bot.String(data.channel_info)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
|
897 |
data.bot.getGroupMap()
|
898 |
break
|
899 |
case "channel_destroyed":
|
900 |
data.notice_type = "guild_channel_destroyed"
|
901 |
+
Bot.makeLog("info", `子频道删除:${Bot.String(data.channel_info)}`, `${data.self_id} <= ${data.guild_id}-${data.channel_id}, ${data.user_id}`)
|
902 |
data.bot.getGroupMap()
|
903 |
break
|
904 |
default:
|
|
|
967 |
}
|
968 |
|
969 |
if (data.post_type) {
|
970 |
+
if (data.meta_event_type !== "lifecycle" && !Bot.uin.includes(data.self_id)) {
|
971 |
Bot.makeLog("warn", `找不到对应Bot,忽略消息:${logger.magenta(data.raw)}`, data.self_id)
|
972 |
return false
|
973 |
}
|
|
|
993 |
default:
|
994 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
995 |
}
|
996 |
+
} else if (data.echo && this.echo[data.echo]) {
|
997 |
+
if (![0, 1].includes(data.retcode))
|
998 |
+
this.echo[data.echo].reject(Object.assign(
|
999 |
+
this.echo[data.echo].request, { error: data }
|
1000 |
+
))
|
1001 |
+
else
|
1002 |
+
this.echo[data.echo].resolve(data.data ? new Proxy(data, {
|
1003 |
+
get: (target, prop) => target.data[prop] ?? target[prop],
|
1004 |
+
}) : data)
|
1005 |
+
clearTimeout(this.echo[data.echo].timeout)
|
1006 |
+
delete this.echo[data.echo]
|
1007 |
} else {
|
1008 |
Bot.makeLog("warn", `未知消息:${logger.magenta(data.raw)}`, data.self_id)
|
1009 |
}
|
Yunzai/plugins/example/package.json
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "example",
|
3 |
+
"type": "module"
|
4 |
+
}
|
Yunzai/plugins/example/主动复读.js
CHANGED
@@ -23,14 +23,14 @@ export class example2 extends plugin {
|
|
23 |
/** 设置上下文,后续接收到内容会执行doRep方法 */
|
24 |
this.setContext("doRep")
|
25 |
/** 回复 */
|
26 |
-
|
27 |
}
|
28 |
|
29 |
/** 接受内容 */
|
30 |
doRep() {
|
31 |
-
/** 复读内容 */
|
32 |
-
this.reply(this.e.message, false, { recallMsg: 5 })
|
33 |
/** 结束上下文 */
|
34 |
this.finish("doRep")
|
|
|
|
|
35 |
}
|
36 |
}
|
|
|
23 |
/** 设置上下文,后续接收到内容会执行doRep方法 */
|
24 |
this.setContext("doRep")
|
25 |
/** 回复 */
|
26 |
+
return this.reply("请发送要复读的内容", false, { at: true })
|
27 |
}
|
28 |
|
29 |
/** 接受内容 */
|
30 |
doRep() {
|
|
|
|
|
31 |
/** 结束上下文 */
|
32 |
this.finish("doRep")
|
33 |
+
/** 复读内容 */
|
34 |
+
return this.reply(this.e.message, false, { recallMsg: 5 })
|
35 |
}
|
36 |
}
|
Yunzai/plugins/other/install.js
CHANGED
@@ -4,9 +4,11 @@ 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",
|
@@ -24,6 +26,9 @@ const list = {
|
|
24 |
"ICQQ-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin",
|
25 |
"KOOK-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-KOOK-Plugin",
|
26 |
}
|
|
|
|
|
|
|
27 |
|
28 |
export class install extends plugin {
|
29 |
constructor() {
|
@@ -33,7 +38,7 @@ export class install extends plugin {
|
|
33 |
event: "message",
|
34 |
rule: [
|
35 |
{
|
36 |
-
reg: `^#安装(插件|${Object.keys(
|
37 |
fnc: "install",
|
38 |
permission: "master"
|
39 |
}
|
@@ -43,16 +48,18 @@ export class install extends plugin {
|
|
43 |
|
44 |
async install() {
|
45 |
if (insing) {
|
46 |
-
await this.reply("
|
47 |
return false
|
48 |
}
|
49 |
|
50 |
-
|
|
|
|
|
51 |
if (name == "插件") {
|
52 |
let msg = "\n"
|
53 |
-
for (const
|
54 |
-
if (!await Bot.fsStat(`plugins/${
|
55 |
-
msg += `${
|
56 |
|
57 |
if (msg == "\n")
|
58 |
msg = "暂无可安装插件"
|
@@ -68,47 +75,40 @@ export class install extends plugin {
|
|
68 |
await this.reply(`${name} 插件已安装`)
|
69 |
return false
|
70 |
}
|
71 |
-
|
72 |
-
this.restart()
|
73 |
}
|
74 |
|
75 |
async runInstall(name, url, path) {
|
76 |
-
logger.mark(`${this.e.logFnc}
|
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(
|
82 |
if (await Bot.fsStat(`${path}/package.json`))
|
83 |
await Bot.exec("pnpm install")
|
84 |
insing = false
|
85 |
|
86 |
if (ret.error) {
|
87 |
-
logger.mark(`${this.e.logFnc}
|
88 |
-
this.gitErr(ret.error, ret.stdout)
|
89 |
return false
|
90 |
}
|
|
|
91 |
}
|
92 |
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
stdout = stdout.toString()
|
97 |
-
|
98 |
-
if (errMsg.includes('Timed out')) {
|
99 |
-
const remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
|
100 |
-
return this.reply(`${msg}\n连接超时:${remote}`)
|
101 |
-
}
|
102 |
-
|
103 |
-
if (/Failed to connect|unable to access/g.test(errMsg)) {
|
104 |
-
const remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
|
105 |
-
return this.reply(`${msg}\n连接失败:${remote}`)
|
106 |
-
}
|
107 |
|
108 |
-
|
|
|
|
|
|
|
|
|
|
|
109 |
}
|
110 |
|
111 |
restart() {
|
112 |
-
new Restart(this.e).restart()
|
113 |
}
|
114 |
}
|
|
|
4 |
const list = {
|
5 |
"Atlas":"https://gitee.com/Nwflower/atlas",
|
6 |
"genshin" :"https://gitee.com/TimeRainStarSky/Yunzai-genshin",
|
7 |
+
"DF-Plugin":"https://gitee.com/DenFengLai/DF-Plugin",
|
8 |
"ws-plugin":"https://gitee.com/xiaoye12123/ws-plugin",
|
9 |
"TRSS-Plugin" :"https://Yunzai.TRSS.me",
|
10 |
"miao-plugin" :"https://gitee.com/yoimiya-kokomi/miao-plugin",
|
11 |
+
"Guoba-Plugin" :"https://gitee.com/guoba-yunzai/guoba-plugin",
|
12 |
"yenai-plugin" :"https://gitee.com/yeyang52/yenai-plugin",
|
13 |
"flower-plugin" :"https://gitee.com/Nwflower/flower-plugin",
|
14 |
"xianyu-plugin" :"https://gitee.com/suancaixianyu/xianyu-plugin",
|
|
|
26 |
"ICQQ-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin",
|
27 |
"KOOK-Plugin" :"https://gitee.com/TimeRainStarSky/Yunzai-KOOK-Plugin",
|
28 |
}
|
29 |
+
const map = {}
|
30 |
+
for (const i in list)
|
31 |
+
map[i.replace(/-[Pp]lugin$/, "")] = i
|
32 |
|
33 |
export class install extends plugin {
|
34 |
constructor() {
|
|
|
38 |
event: "message",
|
39 |
rule: [
|
40 |
{
|
41 |
+
reg: `^#安装(插件|${Object.keys(map).join("|")})(-[Pp]lugin)?$`,
|
42 |
fnc: "install",
|
43 |
permission: "master"
|
44 |
}
|
|
|
48 |
|
49 |
async install() {
|
50 |
if (insing) {
|
51 |
+
await this.reply("正在安装,请稍候再试")
|
52 |
return false
|
53 |
}
|
54 |
|
55 |
+
let name = this.e.msg.replace(/^#安装(.+?)(-[Pp]lugin)?$/, "$1")
|
56 |
+
if (map[name]) name = map[name]
|
57 |
+
|
58 |
if (name == "插件") {
|
59 |
let msg = "\n"
|
60 |
+
for (const i in list)
|
61 |
+
if (!await Bot.fsStat(`plugins/${i}`))
|
62 |
+
msg += `${i}\n`
|
63 |
|
64 |
if (msg == "\n")
|
65 |
msg = "暂无可安装插件"
|
|
|
75 |
await this.reply(`${name} 插件已安装`)
|
76 |
return false
|
77 |
}
|
78 |
+
return this.runInstall(name, list[name], path)
|
|
|
79 |
}
|
80 |
|
81 |
async runInstall(name, url, path) {
|
82 |
+
logger.mark(`${this.e.logFnc} 开始安装 ${name} 插件`)
|
83 |
await this.reply(`开始安装 ${name} 插件`)
|
84 |
|
|
|
85 |
insing = true
|
86 |
+
const ret = await Bot.exec(`git clone --depth 1 --single-branch "${url}" "${path}"`)
|
87 |
if (await Bot.fsStat(`${path}/package.json`))
|
88 |
await Bot.exec("pnpm install")
|
89 |
insing = false
|
90 |
|
91 |
if (ret.error) {
|
92 |
+
logger.mark(`${this.e.logFnc} ${name} 插件安装错误`)
|
93 |
+
this.gitErr(name, ret.error.message, ret.stdout)
|
94 |
return false
|
95 |
}
|
96 |
+
return this.restart()
|
97 |
}
|
98 |
|
99 |
+
gitErrUrl(error) {
|
100 |
+
return error.replace(/(Cloning into|正克隆到)\s*'.+?'/g, "").match(/'(.+?)'/g)[0].replace(/'(.+?)'/, "$1")
|
101 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
|
103 |
+
async gitErr(name, error, stdout) {
|
104 |
+
if (/unable to access|无法访问/.test(error))
|
105 |
+
await this.reply(`远程仓库连接错误:${this.gitErrUrl(error)}`)
|
106 |
+
else if (/not found|未找到|does not (exist|appear)|不存在|Authentication failed|鉴权失败/.test(error))
|
107 |
+
await this.reply(`远程仓库地址错误:${this.gitErrUrl(error)}`)
|
108 |
+
else await this.reply(`${name} 插件安装错误\n${error}\n${stdout}`)
|
109 |
}
|
110 |
|
111 |
restart() {
|
112 |
+
return new Restart(this.e).restart()
|
113 |
}
|
114 |
}
|
Yunzai/plugins/other/restart.js
CHANGED
@@ -1,11 +1,38 @@
|
|
1 |
import cfg from "../../lib/config/config.js"
|
2 |
import { spawn } from "child_process"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
export class Restart extends plugin {
|
5 |
-
constructor
|
6 |
super({
|
7 |
-
name: "
|
8 |
-
dsc: "#重启",
|
9 |
event: "message",
|
10 |
priority: 10,
|
11 |
rule: [
|
@@ -15,87 +42,117 @@ export class Restart extends plugin {
|
|
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 |
-
|
32 |
-
|
33 |
-
|
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 |
-
|
50 |
-
else
|
51 |
-
msg.push(`重启成功,用时${time}秒`)
|
52 |
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
Bot.
|
60 |
-
|
61 |
-
|
|
|
|
|
62 |
}
|
63 |
|
64 |
-
async
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
bot_id: this.e.self_id,
|
70 |
msg_id: this.e.message_id,
|
71 |
time: Date.now(),
|
72 |
}))
|
|
|
73 |
|
74 |
-
|
|
|
|
|
75 |
const ret = await Bot.exec("pnpm run restart")
|
76 |
if (!ret.error) process.exit()
|
77 |
-
await this.
|
78 |
Bot.makeLog("error", ["重启错误", ret])
|
79 |
-
}
|
80 |
-
process.exit()
|
81 |
}
|
82 |
|
83 |
-
async stop() {
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
time: Date.now(),
|
92 |
-
}))
|
93 |
|
94 |
-
|
|
|
|
|
95 |
const ret = await Bot.exec("pnpm stop")
|
96 |
-
await this.
|
97 |
-
Bot.makeLog("error", ["
|
98 |
-
}
|
99 |
-
process.exit(1)
|
100 |
}
|
101 |
}
|
|
|
1 |
import cfg from "../../lib/config/config.js"
|
2 |
import { spawn } from "child_process"
|
3 |
+
import PluginsLoader from "../../lib/plugins/loader.js"
|
4 |
+
|
5 |
+
const temp = {}
|
6 |
+
class Start extends plugin {
|
7 |
+
constructor(e) {
|
8 |
+
super({
|
9 |
+
name: "开机",
|
10 |
+
dsc: "#开机",
|
11 |
+
event: "message",
|
12 |
+
rule: [
|
13 |
+
{
|
14 |
+
reg: "^#开机$",
|
15 |
+
fnc: "start"
|
16 |
+
}
|
17 |
+
]
|
18 |
+
})
|
19 |
+
if (e) this.e = e
|
20 |
+
}
|
21 |
+
|
22 |
+
async start() {
|
23 |
+
if (!this.e.isMaster || !temp.priority) return false
|
24 |
+
PluginsLoader.priority = temp.priority
|
25 |
+
delete temp.priority
|
26 |
+
temp.start_time = Date.now()
|
27 |
+
return this.reply(`开机成功,距离上次关机${Bot.getTimeDiff(temp.stop_time)}`)
|
28 |
+
}
|
29 |
+
}
|
30 |
|
31 |
export class Restart extends plugin {
|
32 |
+
constructor(e) {
|
33 |
super({
|
34 |
+
name: "进程管理",
|
35 |
+
dsc: "#重启 #关机 #停止",
|
36 |
event: "message",
|
37 |
priority: 10,
|
38 |
rule: [
|
|
|
42 |
permission: "master"
|
43 |
},
|
44 |
{
|
45 |
+
reg: "^#关机$",
|
46 |
fnc: "stop",
|
47 |
permission: "master"
|
48 |
+
},
|
49 |
+
{
|
50 |
+
reg: "^#停(机|止)$",
|
51 |
+
fnc: "exit",
|
52 |
+
permission: "master"
|
53 |
}
|
54 |
]
|
55 |
})
|
|
|
56 |
if (e) this.e = e
|
|
|
57 |
}
|
58 |
+
key = "Yz:restart"
|
59 |
|
60 |
init() {
|
61 |
Bot.once("online", () => this.restartMsg())
|
62 |
+
this.e = {
|
63 |
+
reply: msg => Bot.sendMasterMsg(msg),
|
64 |
+
isMaster: true,
|
|
|
|
|
|
|
65 |
}
|
66 |
+
if (cfg.bot.restart_time)
|
67 |
+
setTimeout(() => this.restart(), cfg.bot.restart_time*60000)
|
68 |
+
|
69 |
+
this.task = []
|
70 |
+
if (cfg.bot.restart_cron)
|
71 |
+
for (const i of Array.isArray(cfg.bot.restart_cron) ? cfg.bot.restart_cron : [cfg.bot.restart_cron])
|
72 |
+
this.task.push({
|
73 |
+
name: "定时重启",
|
74 |
+
cron: i,
|
75 |
+
fnc: () => this.restart(),
|
76 |
+
})
|
77 |
+
if (cfg.bot.stop_cron)
|
78 |
+
for (const i of Array.isArray(cfg.bot.stop_cron) ? cfg.bot.stop_cron : [cfg.bot.stop_cron])
|
79 |
+
this.task.push({
|
80 |
+
name: "定时关机",
|
81 |
+
cron: i,
|
82 |
+
fnc: () => this.stop(),
|
83 |
+
})
|
84 |
+
if (cfg.bot.start_cron)
|
85 |
+
for (const i of Array.isArray(cfg.bot.start_cron) ? cfg.bot.start_cron : [cfg.bot.start_cron])
|
86 |
+
this.task.push({
|
87 |
+
name: "定时开机",
|
88 |
+
cron: i,
|
89 |
+
fnc: () => new Start(this.e).start(),
|
90 |
+
})
|
91 |
}
|
92 |
|
93 |
async restartMsg() {
|
94 |
let restart = await redis.get(this.key)
|
95 |
if (!restart) return
|
96 |
+
await redis.del(this.key)
|
97 |
restart = JSON.parse(restart)
|
|
|
|
|
|
|
|
|
98 |
if (restart.isStop)
|
99 |
+
return this.stop(restart.time)
|
|
|
|
|
100 |
|
101 |
+
const time = Bot.getTimeDiff(restart.time)
|
102 |
+
const msg = [restart.isExit ? `开机成功,距离上次停止${time}` : `重启成功,用时${time}`]
|
103 |
+
if (restart.msg_id)
|
104 |
+
msg.unshift(segment.reply(restart.msg_id))
|
105 |
+
|
106 |
+
if (restart.group_id)
|
107 |
+
await Bot.sendGroupMsg(restart.bot_id, restart.group_id, msg)
|
108 |
+
else if (restart.user_id)
|
109 |
+
await Bot.sendFriendMsg(restart.bot_id, restart.user_id, msg)
|
110 |
+
else
|
111 |
+
await Bot.sendMasterMsg(msg)
|
112 |
}
|
113 |
|
114 |
+
async set(isExit) {
|
115 |
+
if (temp.priority)
|
116 |
+
return redis.set(this.key, JSON.stringify({
|
117 |
+
isStop: true,
|
118 |
+
time: temp.stop_time,
|
119 |
+
}))
|
120 |
+
await this.reply(`开始${isExit ? "停止" : "重启"},本次运行时长${Bot.getTimeDiff()}`)
|
121 |
+
return redis.set(this.key, JSON.stringify({
|
122 |
+
isExit,
|
123 |
+
group_id: this.e.group_id,
|
124 |
+
user_id: this.e.user_id,
|
125 |
bot_id: this.e.self_id,
|
126 |
msg_id: this.e.message_id,
|
127 |
time: Date.now(),
|
128 |
}))
|
129 |
+
}
|
130 |
|
131 |
+
async restart() {
|
132 |
+
await this.set()
|
133 |
+
if (process.env.app_type === "pm2") {
|
134 |
const ret = await Bot.exec("pnpm run restart")
|
135 |
if (!ret.error) process.exit()
|
136 |
+
await this.reply(`重启错误\n${ret.error}\n${ret.stdout}\n${ret.stderr}`)
|
137 |
Bot.makeLog("error", ["重启错误", ret])
|
138 |
+
} else process.exit()
|
|
|
139 |
}
|
140 |
|
141 |
+
async stop(time) {
|
142 |
+
if (temp.priority) return false
|
143 |
+
temp.priority = PluginsLoader.priority
|
144 |
+
PluginsLoader.priority = [{ class: Start }]
|
145 |
+
if (typeof time === "number") return temp.stop_time = time
|
146 |
+
temp.stop_time = Date.now()
|
147 |
+
return this.reply(`关机成功,本次运行时长${Bot.getTimeDiff(temp.start_time)}`)
|
148 |
+
}
|
|
|
|
|
149 |
|
150 |
+
async exit() {
|
151 |
+
await this.set(true)
|
152 |
+
if (process.env.app_type === "pm2") {
|
153 |
const ret = await Bot.exec("pnpm stop")
|
154 |
+
await this.reply(`停止错误\n${ret.error}\n${ret.stdout}\n${ret.stderr}`)
|
155 |
+
Bot.makeLog("error", ["停止错误", ret])
|
156 |
+
} else process.exit(1)
|
|
|
157 |
}
|
158 |
}
|
Yunzai/plugins/other/sendLog.js
CHANGED
@@ -50,7 +50,7 @@ export class sendLog extends plugin {
|
|
50 |
}
|
51 |
|
52 |
async getLog(logFile) {
|
53 |
-
let log = await fs.readFile(logFile, "
|
54 |
log = log.split("\n")
|
55 |
|
56 |
if (this.keyWord) {
|
|
|
50 |
}
|
51 |
|
52 |
async getLog(logFile) {
|
53 |
+
let log = await fs.readFile(logFile, "utf8")
|
54 |
log = log.split("\n")
|
55 |
|
56 |
if (this.keyWord) {
|
Yunzai/plugins/other/update.js
CHANGED
@@ -1,47 +1,62 @@
|
|
1 |
-
import cfg from
|
2 |
-
import
|
3 |
-
import
|
4 |
-
import { Restart } from './restart.js'
|
5 |
|
6 |
let uping = false
|
7 |
|
8 |
export class update extends plugin {
|
9 |
constructor() {
|
10 |
super({
|
11 |
-
name:
|
12 |
-
dsc:
|
13 |
-
event:
|
14 |
priority: 4000,
|
15 |
rule: [
|
16 |
{
|
17 |
-
reg:
|
18 |
-
fnc:
|
19 |
},
|
20 |
{
|
21 |
-
reg:
|
22 |
-
fnc:
|
23 |
},
|
24 |
{
|
25 |
-
reg:
|
26 |
-
fnc:
|
27 |
-
permission:
|
28 |
}
|
29 |
]
|
30 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
-
|
|
|
|
|
33 |
}
|
34 |
|
35 |
init() {
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
reply: msg => Bot.sendMasterMsg(msg),
|
42 |
-
}
|
43 |
-
this.autoUpdate()
|
44 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
}
|
46 |
|
47 |
autoUpdate() {
|
@@ -53,65 +68,58 @@ export class update extends plugin {
|
|
53 |
|
54 |
async update() {
|
55 |
if (!this.e.isMaster) return false
|
56 |
-
if (uping)
|
57 |
-
|
58 |
-
|
|
|
59 |
|
60 |
/** 获取插件 */
|
61 |
-
|
62 |
if (plugin === false) return false
|
63 |
|
|
|
64 |
await this.runUpdate(plugin)
|
65 |
|
66 |
-
if (this.isPkgUp)
|
67 |
-
|
68 |
-
|
69 |
-
this.restart()
|
70 |
}
|
71 |
|
72 |
-
async getPlugin(plugin =
|
73 |
-
if (!plugin)
|
74 |
-
|
75 |
-
if (
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
this.typeName = plugin
|
81 |
-
return plugin
|
82 |
}
|
83 |
|
84 |
-
async runUpdate(plugin =
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
cm = `git reset --hard && git pull --rebase --allow-unrelated-histories`
|
93 |
}
|
94 |
-
|
95 |
-
|
96 |
-
this.oldCommitId = await this.getcommitId(plugin)
|
97 |
|
98 |
-
logger.mark(`${this.e.logFnc} 开始${type}
|
|
|
|
|
|
|
99 |
|
100 |
-
await this.
|
101 |
-
|
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
|
114 |
-
|
|
|
115 |
} else {
|
116 |
this.isUp = true
|
117 |
if (/package\.json/.test(ret.stdout))
|
@@ -124,100 +132,117 @@ export class update extends plugin {
|
|
124 |
return true
|
125 |
}
|
126 |
|
127 |
-
async
|
128 |
-
|
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(
|
135 |
-
|
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
|
142 |
-
|
|
|
143 |
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
}
|
148 |
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
|
|
|
|
|
|
153 |
|
154 |
-
|
155 |
-
|
|
|
|
|
156 |
}
|
157 |
|
158 |
-
|
159 |
-
|
|
|
|
|
|
|
|
|
160 |
}
|
|
|
|
|
161 |
|
162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
}
|
164 |
|
165 |
async updateAll() {
|
166 |
-
|
|
|
|
|
|
|
167 |
|
|
|
168 |
await this.runUpdate()
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
await this.runUpdate(plu)
|
174 |
}
|
175 |
|
176 |
-
if (this.isPkgUp)
|
177 |
-
|
178 |
-
|
179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
}
|
181 |
|
182 |
restart() {
|
183 |
new Restart(this.e).restart()
|
184 |
}
|
185 |
|
186 |
-
async getLog(plugin =
|
187 |
-
let cm = 'git log -100 --pretty="%h||[%cd] %s" --date=format:"%F %T"'
|
188 |
-
if (
|
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) {
|
201 |
-
str = str.split(
|
202 |
-
if (str[0]
|
203 |
-
if (str[1].includes(
|
204 |
log.push(str[1])
|
205 |
}
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
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(
|
221 |
}
|
222 |
|
223 |
async updateLog() {
|
@@ -225,4 +250,4 @@ export class update extends plugin {
|
|
225 |
if (plugin === false) return false
|
226 |
return this.reply(await this.getLog(plugin))
|
227 |
}
|
228 |
-
}
|
|
|
1 |
+
import cfg from "../../lib/config/config.js"
|
2 |
+
import fs from "node:fs/promises"
|
3 |
+
import { Restart } from "./restart.js"
|
|
|
4 |
|
5 |
let uping = false
|
6 |
|
7 |
export class update extends plugin {
|
8 |
constructor() {
|
9 |
super({
|
10 |
+
name: "更新",
|
11 |
+
dsc: "#更新 #强制更新",
|
12 |
+
event: "message",
|
13 |
priority: 4000,
|
14 |
rule: [
|
15 |
{
|
16 |
+
reg: "^#更新日志",
|
17 |
+
fnc: "updateLog"
|
18 |
},
|
19 |
{
|
20 |
+
reg: "^#(安?静)?(强制)?更新",
|
21 |
+
fnc: "update"
|
22 |
},
|
23 |
{
|
24 |
+
reg: "^#全部(安?静)?(强制)?更新$",
|
25 |
+
fnc: "updateAll",
|
26 |
+
permission: "master"
|
27 |
}
|
28 |
]
|
29 |
})
|
30 |
+
this.typeName = "TRSS-Yunzai"
|
31 |
+
}
|
32 |
+
|
33 |
+
get quiet() {
|
34 |
+
return /^#(全部)?(安?静)/.test(this.e.msg)
|
35 |
+
}
|
36 |
|
37 |
+
exec(cmd, plugin, opts = {}) {
|
38 |
+
if (plugin) opts.cwd = `plugins/${plugin}`
|
39 |
+
return Bot.exec(cmd, opts)
|
40 |
}
|
41 |
|
42 |
init() {
|
43 |
+
this.e = {
|
44 |
+
isMaster: true,
|
45 |
+
logFnc: "[自动更新]",
|
46 |
+
msg: "#全部静更新",
|
47 |
+
reply: msg => Bot.sendMasterMsg(msg),
|
|
|
|
|
|
|
48 |
}
|
49 |
+
if (cfg.bot.update_time)
|
50 |
+
this.autoUpdate()
|
51 |
+
|
52 |
+
this.task = []
|
53 |
+
if (cfg.bot.update_cron)
|
54 |
+
for (const i of Array.isArray(cfg.bot.update_cron) ? cfg.bot.update_cron : [cfg.bot.update_cron])
|
55 |
+
this.task.push({
|
56 |
+
name: "定时更新",
|
57 |
+
cron: i,
|
58 |
+
fnc: () => this.updateAll(),
|
59 |
+
})
|
60 |
}
|
61 |
|
62 |
autoUpdate() {
|
|
|
68 |
|
69 |
async update() {
|
70 |
if (!this.e.isMaster) return false
|
71 |
+
if (uping) {
|
72 |
+
await this.reply("正在更新,请稍候再试")
|
73 |
+
return false
|
74 |
+
}
|
75 |
|
76 |
/** 获取插件 */
|
77 |
+
const plugin = await this.getPlugin()
|
78 |
if (plugin === false) return false
|
79 |
|
80 |
+
uping = true
|
81 |
await this.runUpdate(plugin)
|
82 |
|
83 |
+
if (this.isPkgUp) await this.updatePackage()
|
84 |
+
if (this.isUp) this.restart()
|
85 |
+
uping = false
|
|
|
86 |
}
|
87 |
|
88 |
+
async getPlugin(plugin = this.e.msg.replace(/#(安?静)?(强制)?更新(日志)?/, "")) {
|
89 |
+
if (!plugin) return ""
|
90 |
+
for (const i of [plugin, `${plugin}-Plugin`, `${plugin}-plugin`])
|
91 |
+
if (await Bot.fsStat(`plugins/${i}/.git`)) {
|
92 |
+
this.typeName = i
|
93 |
+
return i
|
94 |
+
}
|
95 |
+
return false
|
|
|
|
|
96 |
}
|
97 |
|
98 |
+
async runUpdate(plugin = "") {
|
99 |
+
let cm = "git pull"
|
100 |
+
let type = "更新"
|
101 |
+
if (!plugin) cm = `git checkout package.json && ${cm}`
|
102 |
|
103 |
+
if (this.e.msg.includes("强制")) {
|
104 |
+
type = "强制更新"
|
105 |
+
cm = `git reset --hard ${await this.getRemoteBranch(true, plugin)} && git pull --rebase`
|
|
|
106 |
}
|
107 |
+
this.oldCommitId = await this.getCommitId(plugin)
|
|
|
|
|
108 |
|
109 |
+
logger.mark(`${this.e.logFnc} 开始${type} ${this.typeName}`)
|
110 |
+
if (!this.quiet)
|
111 |
+
await this.reply(`开始${type} ${this.typeName}`)
|
112 |
+
const ret = await this.exec(cm, plugin)
|
113 |
|
114 |
+
if (ret.error && !await this.gitErr(plugin, ret.stdout, ret.error.message)) {
|
115 |
+
logger.mark(`${this.e.logFnc} 更新失败 ${this.typeName}`)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
return false
|
117 |
}
|
118 |
|
119 |
const time = await this.getTime(plugin)
|
120 |
+
if (/Already up|已经是最新/.test(ret.stdout)) {
|
121 |
+
if (!this.quiet)
|
122 |
+
await this.reply(`${this.typeName} 已是最新\n最后更新时间:${time}`)
|
123 |
} else {
|
124 |
this.isUp = true
|
125 |
if (/package\.json/.test(ret.stdout))
|
|
|
132 |
return true
|
133 |
}
|
134 |
|
135 |
+
async getCommitId(...args) {
|
136 |
+
return (await this.exec("git rev-parse --short HEAD", ...args)).stdout
|
|
|
|
|
|
|
137 |
}
|
138 |
|
139 |
+
async getTime(...args) {
|
140 |
+
return (await this.exec('git log -1 --pretty=%cd --date=format:"%F %T"', ...args)).stdout
|
|
|
|
|
|
|
141 |
}
|
142 |
|
143 |
+
async getBranch(...args) {
|
144 |
+
return (await this.exec("git branch --show-current", ...args)).stdout
|
145 |
+
}
|
146 |
|
147 |
+
async getRemote(branch, ...args) {
|
148 |
+
return (await this.exec(`git config branch.${branch}.remote`, ...args)).stdout
|
149 |
+
}
|
|
|
150 |
|
151 |
+
async getRemoteBranch(string, ...args) {
|
152 |
+
const branch = await this.getBranch(...args)
|
153 |
+
if (!branch && string) return ""
|
154 |
+
const remote = await this.getRemote(branch, ...args)
|
155 |
+
if (!remote && string) return ""
|
156 |
+
return string ? `${remote}/${branch}` : { remote, branch }
|
157 |
+
}
|
158 |
|
159 |
+
async getRemoteUrl(branch, hide, ...args) {
|
160 |
+
if (branch) {
|
161 |
+
const url = (await this.exec(`git config remote.${branch}.url`, ...args)).stdout
|
162 |
+
return hide ? url.replace(/\/\/([^@]+)@/, "//") : url
|
163 |
}
|
164 |
|
165 |
+
const ret = await this.exec("git config -l", ...args)
|
166 |
+
const urls = {}
|
167 |
+
for (const i of ret.stdout.match(/remote\..*?\.url=.+/g) || []) {
|
168 |
+
const branch = i.replace(/remote\.(.*?)\.url=.+/g, "$1")
|
169 |
+
const url = i.replace(/remote\..*?\.url=/g, "")
|
170 |
+
urls[branch] = (hide ? url.replace(/\/\/([^@]+)@/, "//") : url)
|
171 |
}
|
172 |
+
return urls
|
173 |
+
}
|
174 |
|
175 |
+
gitErrUrl(error) {
|
176 |
+
return error.match(/'(.+?)'/g)[0].replace(/'(.+?)'/, "$1")
|
177 |
+
}
|
178 |
+
|
179 |
+
async gitErr(plugin, stdout, error) {
|
180 |
+
if (/unable to access|无法访问/.test(error))
|
181 |
+
await this.reply(`远程仓库连接错误:${this.gitErrUrl(error)}`)
|
182 |
+
else if (/not found|未找到|does not (exist|appear)|不存在|Authentication failed|鉴权失败/.test(error))
|
183 |
+
await this.reply(`远程仓库地址错误:${this.gitErrUrl(error)}`)
|
184 |
+
else if (/be overwritten by merge|被合并操作覆盖/.test(error) || /Merge conflict|合并冲突/.test(stdout))
|
185 |
+
await this.reply(`${error}\n${stdout}\n若修改过文件请手动更新,否则发送 #强制更新${plugin}`)
|
186 |
+
else if (/divergent branches|偏离的分支/.test(error)) {
|
187 |
+
const ret = await this.exec("git pull --rebase", plugin)
|
188 |
+
if (!ret.error && /Successfully rebased|成功变基/.test(ret.stdout+ret.stderr))
|
189 |
+
return true
|
190 |
+
await this.reply(`${error}\n${stdout}\n若修改过文件请手动更新,否则发送 #强制更新${plugin}`)
|
191 |
+
} else await this.reply(`${error}\n${stdout}\n未知错误,可尝试发送 #强制更新${plugin}`)
|
192 |
}
|
193 |
|
194 |
async updateAll() {
|
195 |
+
if (uping) {
|
196 |
+
await this.reply("正在更新,请稍候再试")
|
197 |
+
return false
|
198 |
+
}
|
199 |
|
200 |
+
uping = true
|
201 |
await this.runUpdate()
|
202 |
+
for (let plugin of await fs.readdir("plugins")) {
|
203 |
+
plugin = await this.getPlugin(plugin)
|
204 |
+
if (plugin === false) continue
|
205 |
+
await this.runUpdate(plugin)
|
|
|
206 |
}
|
207 |
|
208 |
+
if (this.isPkgUp) await this.updatePackage()
|
209 |
+
if (this.isUp) this.restart()
|
210 |
+
uping = false
|
211 |
+
}
|
212 |
+
|
213 |
+
async updatePackage() {
|
214 |
+
const cmd = "pnpm install"
|
215 |
+
if (process.platform === "win32")
|
216 |
+
return this.reply(`检测到依赖更新,请 #关机 后执行 ${cmd}`)
|
217 |
+
await this.reply("开始更新依赖")
|
218 |
+
return this.exec(cmd)
|
219 |
}
|
220 |
|
221 |
restart() {
|
222 |
new Restart(this.e).restart()
|
223 |
}
|
224 |
|
225 |
+
async getLog(plugin = "") {
|
226 |
+
let cm = await this.exec('git log -100 --pretty="%h||[%cd] %s" --date=format:"%F %T"', plugin)
|
227 |
+
if (cm.error) return this.reply(cm.error.message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
|
229 |
+
const logAll = cm.stdout.split("\n")
|
230 |
if (!logAll.length) return false
|
231 |
|
232 |
let log = []
|
233 |
for (let str of logAll) {
|
234 |
+
str = str.split("||")
|
235 |
+
if (str[0] === this.oldCommitId) break
|
236 |
+
if (str[1].includes("Merge branch")) continue
|
237 |
log.push(str[1])
|
238 |
}
|
239 |
+
if (log.length <= 0) return ""
|
240 |
+
|
241 |
+
const msg = [`${plugin || "TRSS-Yunzai"} 更新日志,共${log.length}条`, log.join("\n\n")]
|
242 |
+
const end = await this.getRemoteUrl((await this.getRemoteBranch(false, plugin)).remote, true, plugin)
|
243 |
+
if (end) msg.push(end)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
|
245 |
+
return Bot.makeForwardArray(msg)
|
246 |
}
|
247 |
|
248 |
async updateLog() {
|
|
|
250 |
if (plugin === false) return false
|
251 |
return this.reply(await this.getLog(plugin))
|
252 |
}
|
253 |
+
}
|
Yunzai/plugins/other/version.js
CHANGED
@@ -3,7 +3,7 @@ try {
|
|
3 |
App = (await import("#miao")).App
|
4 |
Common = (await import("#miao")).Common
|
5 |
Version = (await import("#miao")).Version
|
6 |
-
} catch
|
7 |
|
8 |
export let version = {}
|
9 |
if (App) {
|
|
|
3 |
App = (await import("#miao")).App
|
4 |
Common = (await import("#miao")).Common
|
5 |
Version = (await import("#miao")).Version
|
6 |
+
} catch {}
|
7 |
|
8 |
export let version = {}
|
9 |
if (App) {
|
Yunzai/plugins/system/master.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import fs from "node:fs/promises"
|
2 |
-
import {
|
3 |
let code = {}
|
4 |
let file = "config/config/other.yaml"
|
5 |
export class master extends plugin {
|
@@ -35,18 +35,18 @@ export class master extends plugin {
|
|
35 |
return false
|
36 |
}
|
37 |
|
38 |
-
code[this.e.user_id] =
|
39 |
-
logger.mark(`${logger.cyan(`[${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}]
|
50 |
} else {
|
51 |
await this.reply("验证码错误", true)
|
52 |
return false
|
|
|
1 |
import fs from "node:fs/promises"
|
2 |
+
import { ulid } from "ulid"
|
3 |
let code = {}
|
4 |
let file = "config/config/other.yaml"
|
5 |
export class master extends plugin {
|
|
|
35 |
return false
|
36 |
}
|
37 |
|
38 |
+
code[this.e.user_id] = ulid()
|
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().toUpperCase() == 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
|
Yunzai/plugins/system/status.js
CHANGED
@@ -37,7 +37,7 @@ export class status extends plugin {
|
|
37 |
let msg = "\n\n账号在线时长"
|
38 |
for (const i of Bot.uin)
|
39 |
if (Bot[i]?.stat?.start_time)
|
40 |
-
msg += `\n${Bot.getTimeDiff(Bot[i].stat.start_time)} ${i}`
|
41 |
return msg
|
42 |
}
|
43 |
|
|
|
37 |
let msg = "\n\n账号在线时长"
|
38 |
for (const i of Bot.uin)
|
39 |
if (Bot[i]?.stat?.start_time)
|
40 |
+
msg += `\n${Bot.getTimeDiff(Bot[i].stat.start_time*1000)} ${i}`
|
41 |
return msg
|
42 |
}
|
43 |
|
Yunzai/renderers/puppeteer/config_default.yaml
CHANGED
@@ -21,3 +21,8 @@ args:
|
|
21 |
|
22 |
# puppeteer截图超时时间
|
23 |
puppeteerTimeout:
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
# puppeteer截图超时时间
|
23 |
puppeteerTimeout:
|
24 |
+
|
25 |
+
# 页面goto时的参数
|
26 |
+
pageGotoParams:
|
27 |
+
timeout: 120000
|
28 |
+
waitUntil: networkidle2
|
Yunzai/renderers/puppeteer/lib/puppeteer.js
CHANGED
@@ -40,6 +40,10 @@ export default class Puppeteer extends Renderer {
|
|
40 |
this.config.wsEndpoint = config.puppeteerWS || cfg?.bot?.puppeteer_ws
|
41 |
/** puppeteer超时超时时间 */
|
42 |
this.puppeteerTimeout = config.puppeteerTimeout || cfg?.bot?.puppeteer_timeout || 0
|
|
|
|
|
|
|
|
|
43 |
}
|
44 |
|
45 |
/**
|
@@ -74,7 +78,7 @@ export default class Puppeteer extends Renderer {
|
|
74 |
await redis.del(this.browserMacKey)
|
75 |
}
|
76 |
}
|
77 |
-
} catch
|
78 |
|
79 |
if (!this.browser || !connectFlag) {
|
80 |
// 如果没有实例,初始化puppeteer
|
@@ -159,11 +163,11 @@ export default class Puppeteer extends Renderer {
|
|
159 |
return false
|
160 |
const pageHeight = data.multiPageHeight || 4000
|
161 |
|
162 |
-
|
163 |
if (!savePath) return false
|
164 |
|
165 |
let buff = ""
|
166 |
-
|
167 |
|
168 |
let ret = []
|
169 |
this.shoting.push(name)
|
@@ -183,20 +187,20 @@ export default class Puppeteer extends Renderer {
|
|
183 |
|
184 |
try {
|
185 |
const page = await this.browser.newPage()
|
186 |
-
|
187 |
await page.goto(`file://${_path}${lodash.trim(savePath, ".")}`, pageGotoParams)
|
188 |
-
|
189 |
|
190 |
// 计算页面高度
|
191 |
const boundingBox = await body.boundingBox()
|
192 |
// 分页数
|
193 |
let num = 1
|
194 |
|
195 |
-
|
196 |
type: data.imgType || "jpeg",
|
197 |
omitBackground: data.omitBackground || false,
|
198 |
quality: data.quality || 90,
|
199 |
-
path: data.path || ""
|
200 |
}
|
201 |
|
202 |
if (data.multiPage) {
|
@@ -204,9 +208,8 @@ export default class Puppeteer extends Renderer {
|
|
204 |
num = Math.round(boundingBox.height / pageHeight) || 1
|
205 |
}
|
206 |
|
207 |
-
if (data.imgType === "png")
|
208 |
delete randData.quality
|
209 |
-
}
|
210 |
|
211 |
if (!data.multiPage) {
|
212 |
buff = await body.screenshot(randData)
|
|
|
40 |
this.config.wsEndpoint = config.puppeteerWS || cfg?.bot?.puppeteer_ws
|
41 |
/** puppeteer超时超时时间 */
|
42 |
this.puppeteerTimeout = config.puppeteerTimeout || cfg?.bot?.puppeteer_timeout || 0
|
43 |
+
this.pageGotoParams = config.pageGotoParams || {
|
44 |
+
timeout: 120000,
|
45 |
+
waitUntil: "networkidle2",
|
46 |
+
}
|
47 |
}
|
48 |
|
49 |
/**
|
|
|
78 |
await redis.del(this.browserMacKey)
|
79 |
}
|
80 |
}
|
81 |
+
} catch {}
|
82 |
|
83 |
if (!this.browser || !connectFlag) {
|
84 |
// 如果没有实例,初始化puppeteer
|
|
|
163 |
return false
|
164 |
const pageHeight = data.multiPageHeight || 4000
|
165 |
|
166 |
+
const savePath = this.dealTpl(name, data)
|
167 |
if (!savePath) return false
|
168 |
|
169 |
let buff = ""
|
170 |
+
const start = Date.now()
|
171 |
|
172 |
let ret = []
|
173 |
this.shoting.push(name)
|
|
|
187 |
|
188 |
try {
|
189 |
const page = await this.browser.newPage()
|
190 |
+
const pageGotoParams = lodash.extend(this.pageGotoParams, data.pageGotoParams || {})
|
191 |
await page.goto(`file://${_path}${lodash.trim(savePath, ".")}`, pageGotoParams)
|
192 |
+
const body = await page.$("#container") || await page.$("body")
|
193 |
|
194 |
// 计算页面高度
|
195 |
const boundingBox = await body.boundingBox()
|
196 |
// 分页数
|
197 |
let num = 1
|
198 |
|
199 |
+
const randData = {
|
200 |
type: data.imgType || "jpeg",
|
201 |
omitBackground: data.omitBackground || false,
|
202 |
quality: data.quality || 90,
|
203 |
+
path: data.path || "",
|
204 |
}
|
205 |
|
206 |
if (data.multiPage) {
|
|
|
208 |
num = Math.round(boundingBox.height / pageHeight) || 1
|
209 |
}
|
210 |
|
211 |
+
if (data.imgType === "png")
|
212 |
delete randData.quality
|
|
|
213 |
|
214 |
if (!data.multiPage) {
|
215 |
buff = await body.screenshot(randData)
|