dsh-qq-stack 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/index.js +39 -3
- package/install.sh +7 -1
- package/package.json +3 -2
- package/watch.mjs +22 -0
package/README.md
CHANGED
|
@@ -54,3 +54,13 @@ dsh plugin --profile web add dsh-qq-stack
|
|
|
54
54
|
## 许可
|
|
55
55
|
|
|
56
56
|
MIT
|
|
57
|
+
|
|
58
|
+
## 卸载
|
|
59
|
+
|
|
60
|
+
插件卸载后,后台会有一个清理器(20 秒内)自动停掉 QQ、删掉 `~/.dsh/qq-bot`(含 700MB+ 的客户端和登录数据)。
|
|
61
|
+
|
|
62
|
+
如果想立刻清,也可以在卸载前手动执行:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
bash ~/.dsh/qq-bot/stop-qq.sh && rm -rf ~/.dsh/qq-bot
|
|
66
|
+
```
|
package/index.js
CHANGED
|
@@ -74,8 +74,8 @@ function resolveConfig(config = {}) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
let probeCache = { at: 0, loggedIn: false, nickname: '', uin: '' }
|
|
77
|
-
async function probeLogin(port, token) {
|
|
78
|
-
if (Date.now() - probeCache.at < 15000) return probeCache
|
|
77
|
+
async function probeLogin(port, token, force) {
|
|
78
|
+
if (!force && Date.now() - probeCache.at < 15000) return probeCache
|
|
79
79
|
const result = await new Promise((resolve) => {
|
|
80
80
|
let done = false
|
|
81
81
|
const finish = (v) => { if (!done) { done = true; resolve(v) } }
|
|
@@ -149,6 +149,41 @@ function applyInner(ctx, pluginConfig = {}) {
|
|
|
149
149
|
} catch (e) { log(`[qq-stack] 安装启动失败:${e?.message ?? e}`) }
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
ctx.effect(() => {
|
|
153
|
+
try {
|
|
154
|
+
const watchPid = `${cfg.root}/watch.pid`
|
|
155
|
+
let pid = 0
|
|
156
|
+
try { pid = Number(readFileSync(watchPid, 'utf8').trim()) || 0 } catch {}
|
|
157
|
+
if (alive(pid)) return
|
|
158
|
+
const child = spawn(process.execPath, [`${SELF}watch.mjs`, SELF, cfg.root], { detached: true, stdio: 'ignore' })
|
|
159
|
+
child.unref()
|
|
160
|
+
try { writeFileSync(watchPid, String(child.pid)) } catch {}
|
|
161
|
+
} catch (e) { log(`[qq-stack] 卸载清理器启动失败:${e?.message ?? e}`) }
|
|
162
|
+
}, 'dsh-qq-stack: uninstall-watch')
|
|
163
|
+
|
|
164
|
+
ctx.effect(() => {
|
|
165
|
+
const timer = setInterval(async () => {
|
|
166
|
+
try {
|
|
167
|
+
const envFile = readEnvFile(cfg.root)
|
|
168
|
+
const port = Number(envFile.DSH_QQ_WS_PORT || cfg.wsPort || 3001)
|
|
169
|
+
const token = envFile.DSH_QQ_TOKEN || cfg.token
|
|
170
|
+
const login = await probeLogin(port, token, true)
|
|
171
|
+
if (login.loggedIn && login.uin && envFile.DSH_QQ_UIN !== login.uin) {
|
|
172
|
+
const next = { ...envFile, DSH_QQ_UIN: login.uin }
|
|
173
|
+
writeFileSync(`${cfg.root}/config.env`, Object.entries(next).map(([k, x]) => `${k}=${x}`).join('\n') + '\n')
|
|
174
|
+
log(`[qq-stack] 记住快登号 ${login.uin}`)
|
|
175
|
+
}
|
|
176
|
+
const healed = ensureOnebot(cfg.root, port, token)
|
|
177
|
+
if (healed && Date.now() - lastHealAt > 90000) {
|
|
178
|
+
lastHealAt = Date.now()
|
|
179
|
+
log('[qq-stack] 补齐 OneBot 配置,重启 QQ')
|
|
180
|
+
try { spawn('bash', ['-c', 'bash stop-qq.sh; bash start-qq.sh'], { cwd: cfg.root, detached: true, stdio: 'ignore' }).unref() } catch {}
|
|
181
|
+
}
|
|
182
|
+
} catch (e) { log(`[qq-stack] 巡检失败:${e?.message ?? e}`) }
|
|
183
|
+
}, 60000)
|
|
184
|
+
return () => clearInterval(timer)
|
|
185
|
+
}, 'dsh-qq-stack: config-check')
|
|
186
|
+
|
|
152
187
|
ctx.effect(() => {
|
|
153
188
|
const t = setTimeout(() => { try { ensureInstalled() } catch (e) { log(`[qq-stack] ${e?.message ?? e}`) } }, 8000)
|
|
154
189
|
return () => clearTimeout(t)
|
|
@@ -195,7 +230,8 @@ function applyInner(ctx, pluginConfig = {}) {
|
|
|
195
230
|
lastHealAt = Date.now()
|
|
196
231
|
try { spawn('bash', ['-c', `bash stop-qq.sh; bash start-qq.sh`], { cwd: cfg.root, detached: true, stdio: 'ignore' }).unref() } catch {}
|
|
197
232
|
}
|
|
198
|
-
|
|
233
|
+
const probeFresh = Date.now() - (probeCache.at || 0) < 6000
|
|
234
|
+
if (login.loggedIn && login.uin && probeFresh && envFile.DSH_QQ_UIN !== login.uin) {
|
|
199
235
|
try {
|
|
200
236
|
const e2 = { ...envFile, DSH_QQ_UIN: login.uin }
|
|
201
237
|
writeFileSync(`${cfg.root}/config.env`, Object.entries(e2).map(([k, x]) => `${k}=${x}`).join('\n') + '\n')
|
package/install.sh
CHANGED
|
@@ -54,7 +54,13 @@ QQ_APP="$ROOT/opt/opt/QQ/resources/app"
|
|
|
54
54
|
|
|
55
55
|
if [ ! -f "$QQ_APP/app_launcher/napcat/napcat.mjs" ]; then
|
|
56
56
|
say "下载 NapCat"
|
|
57
|
-
|
|
57
|
+
if [ -f /tmp/NapCat.Shell.zip ] && unzip -t /tmp/NapCat.Shell.zip >/dev/null 2>&1; then
|
|
58
|
+
say "复用已下载的 NapCat 包"
|
|
59
|
+
else
|
|
60
|
+
curl -fL --retry 2 -o /tmp/NapCat.Shell.zip "$NAPCAT_ZIP" \
|
|
61
|
+
|| curl -fL --retry 2 -o /tmp/NapCat.Shell.zip "https://gh-proxy.com/$NAPCAT_ZIP" \
|
|
62
|
+
|| { say "NapCat 下载失败"; exit 1; }
|
|
63
|
+
fi
|
|
58
64
|
rm -rf /tmp/napcat-unzip && mkdir -p /tmp/napcat-unzip
|
|
59
65
|
unzip -q -o /tmp/NapCat.Shell.zip -d /tmp/napcat-unzip || { say "NapCat 解压失败"; exit 1; }
|
|
60
66
|
mkdir -p "$QQ_APP/app_launcher"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-qq-stack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"description": "DeepSeek Harness 的 QQ 协议端底座:LinuxQQ + NapCat 随引擎自启,扫码一次后自动快登。",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"install.sh",
|
|
23
23
|
"cordis.patch.yml",
|
|
24
24
|
"README.md",
|
|
25
|
-
"LICENSE"
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"watch.mjs"
|
|
26
27
|
],
|
|
27
28
|
"exports": {
|
|
28
29
|
".": "./index.js",
|
package/watch.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { spawnSync, spawn } from 'node:child_process'
|
|
3
|
+
|
|
4
|
+
const [pkgDir, root] = process.argv.slice(2)
|
|
5
|
+
if (!pkgDir || !root) process.exit(0)
|
|
6
|
+
|
|
7
|
+
const alive = (pid) => {
|
|
8
|
+
try { process.kill(Number(pid), 0); return true } catch { return false }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function cleanup() {
|
|
12
|
+
try { spawnSync('bash', [`${root}/stop-qq.sh`], { timeout: 30000 }) } catch {}
|
|
13
|
+
try { spawnSync('bash', ['-c', `rm -rf "${root}"`], { timeout: 120000 }) } catch {}
|
|
14
|
+
try {
|
|
15
|
+
const pids = spawnSync('bash', ['-c', "ps -eo pid=,comm= | awk '$2==\"Xvfb\"{print $1}'"], { encoding: 'utf8' }).stdout
|
|
16
|
+
for (const p of String(pids).split(/\s+/).filter(Boolean)) { try { process.kill(Number(p), 'SIGKILL') } catch {} }
|
|
17
|
+
} catch {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setInterval(() => {
|
|
21
|
+
if (!existsSync(`${pkgDir}/package.json`)) { cleanup(); process.exit(0) }
|
|
22
|
+
}, 20000)
|