dsh-qq-stack 0.1.1 → 0.1.3
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 +29 -9
- package/package.json +4 -3
- 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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process'
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, statSync, readdirSync, unlinkSync } from 'node:fs'
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, statSync, readdirSync, unlinkSync, mkdirSync } from 'node:fs'
|
|
3
3
|
import { readFileSync as rf } from 'node:fs'
|
|
4
4
|
|
|
5
5
|
export const name = 'dsh-qq-stack'
|
|
@@ -102,6 +102,16 @@ async function probeLogin(port, token, force) {
|
|
|
102
102
|
return probeCache
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
|
|
106
|
+
function spawnSafe(cmd, args, opts) {
|
|
107
|
+
try {
|
|
108
|
+
const child = spawn(cmd, args, opts)
|
|
109
|
+
child.on('error', () => {})
|
|
110
|
+
child.unref()
|
|
111
|
+
return child
|
|
112
|
+
} catch { return null }
|
|
113
|
+
}
|
|
114
|
+
|
|
105
115
|
function alive(pid) {
|
|
106
116
|
if (!pid) return false
|
|
107
117
|
try { process.kill(pid, 0); return true } catch { return false }
|
|
@@ -113,6 +123,7 @@ export function apply(ctx, pluginConfig = {}) {
|
|
|
113
123
|
|
|
114
124
|
function applyInner(ctx, pluginConfig = {}) {
|
|
115
125
|
const cfg = resolveConfig(pluginConfig)
|
|
126
|
+
try { mkdirSync(cfg.root, { recursive: true }) } catch {}
|
|
116
127
|
const log = (msg) => { try { ctx.logger?.info?.(msg); if (process.env.DSH_QQ_DEBUG) console.log(msg) } catch {} }
|
|
117
128
|
log(`[qq-stack] 激活 root=${cfg.root} uin=${cfg.uin || '(空)'} autoInstall=${cfg.autoInstall}`)
|
|
118
129
|
|
|
@@ -131,8 +142,7 @@ function applyInner(ctx, pluginConfig = {}) {
|
|
|
131
142
|
if (alive(pid)) return
|
|
132
143
|
const script = `${cfg.root}/start-qq.sh`
|
|
133
144
|
if (!existsSync(script)) return
|
|
134
|
-
const child =
|
|
135
|
-
child.unref()
|
|
145
|
+
const child = spawnSafe('bash', [script], { cwd: cfg.root, detached: true, stdio: 'ignore', env })
|
|
136
146
|
log('[qq-stack] 拉起 QQ')
|
|
137
147
|
} catch (e) { log(`[qq-stack] 启动失败:${e?.message ?? e}`) }
|
|
138
148
|
}
|
|
@@ -144,11 +154,21 @@ function applyInner(ctx, pluginConfig = {}) {
|
|
|
144
154
|
if (!existsSync(installer)) { log('[qq-stack] 找不到 install.sh'); return }
|
|
145
155
|
log('[qq-stack] 首次安装开始(LinuxQQ + NapCat,约 250MB)')
|
|
146
156
|
try {
|
|
147
|
-
const child =
|
|
148
|
-
child.unref()
|
|
157
|
+
const child = spawnSafe('bash', [installer], { cwd: cfg.root, detached: true, stdio: 'ignore', env })
|
|
149
158
|
} catch (e) { log(`[qq-stack] 安装启动失败:${e?.message ?? e}`) }
|
|
150
159
|
}
|
|
151
160
|
|
|
161
|
+
ctx.effect(() => {
|
|
162
|
+
try {
|
|
163
|
+
const watchPid = `${cfg.root}/watch.pid`
|
|
164
|
+
let pid = 0
|
|
165
|
+
try { pid = Number(readFileSync(watchPid, 'utf8').trim()) || 0 } catch {}
|
|
166
|
+
if (alive(pid)) return
|
|
167
|
+
const child = spawnSafe(process.execPath, [`${SELF}watch.mjs`, SELF, cfg.root], { detached: true, stdio: 'ignore' })
|
|
168
|
+
try { writeFileSync(watchPid, String(child.pid)) } catch {}
|
|
169
|
+
} catch (e) { log(`[qq-stack] 卸载清理器启动失败:${e?.message ?? e}`) }
|
|
170
|
+
}, 'dsh-qq-stack: uninstall-watch')
|
|
171
|
+
|
|
152
172
|
ctx.effect(() => {
|
|
153
173
|
const timer = setInterval(async () => {
|
|
154
174
|
try {
|
|
@@ -165,7 +185,7 @@ function applyInner(ctx, pluginConfig = {}) {
|
|
|
165
185
|
if (healed && Date.now() - lastHealAt > 90000) {
|
|
166
186
|
lastHealAt = Date.now()
|
|
167
187
|
log('[qq-stack] 补齐 OneBot 配置,重启 QQ')
|
|
168
|
-
|
|
188
|
+
spawnSafe('bash', ['-c', 'bash stop-qq.sh; bash start-qq.sh'], { cwd: cfg.root, detached: true, stdio: 'ignore' })
|
|
169
189
|
}
|
|
170
190
|
} catch (e) { log(`[qq-stack] 巡检失败:${e?.message ?? e}`) }
|
|
171
191
|
}, 60000)
|
|
@@ -216,7 +236,7 @@ function applyInner(ctx, pluginConfig = {}) {
|
|
|
216
236
|
const healed = ensureOnebot(cfg.root, portNum, tokenVal)
|
|
217
237
|
if (healed && !login.loggedIn && Date.now() - lastHealAt > 90000) {
|
|
218
238
|
lastHealAt = Date.now()
|
|
219
|
-
|
|
239
|
+
spawnSafe('bash', ['-c', 'bash stop-qq.sh; bash start-qq.sh'], { cwd: cfg.root, detached: true, stdio: 'ignore' })
|
|
220
240
|
}
|
|
221
241
|
const probeFresh = Date.now() - (probeCache.at || 0) < 6000
|
|
222
242
|
if (login.loggedIn && login.uin && probeFresh && envFile.DSH_QQ_UIN !== login.uin) {
|
|
@@ -271,11 +291,11 @@ function applyInner(ctx, pluginConfig = {}) {
|
|
|
271
291
|
if (existsSync(auth)) unlinkSync(auth)
|
|
272
292
|
} catch {}
|
|
273
293
|
probeCache = { at: 0, loggedIn: false, nickname: '', uin: '' }
|
|
274
|
-
|
|
294
|
+
spawnSafe('bash', ['-c', 'bash stop-qq.sh; bash start-qq.sh'], { cwd: cfg.root, detached: true, stdio: 'ignore' })
|
|
275
295
|
return json({ ok: true })
|
|
276
296
|
}
|
|
277
297
|
if (req.method === 'POST' && p === '/restart') {
|
|
278
|
-
|
|
298
|
+
spawnSafe('bash', ['-c', 'bash stop-qq.sh; bash start-qq.sh'], { cwd: cfg.root, detached: true, stdio: 'ignore' })
|
|
279
299
|
return json({ ok: true })
|
|
280
300
|
}
|
|
281
301
|
json({ ok: false }, 404)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-qq-stack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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",
|
|
@@ -44,4 +45,4 @@
|
|
|
44
45
|
"scripts": {
|
|
45
46
|
"test": "node --input-type=module -e \"await import('./index.js')\""
|
|
46
47
|
}
|
|
47
|
-
}
|
|
48
|
+
}
|
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)
|