@zaofan/dsh-qqbot 1.11.8 → 1.11.10
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/client/qqbot-settings.js +120 -10
- package/dist/channel-tools.d.ts +18 -0
- package/dist/channel-tools.d.ts.map +1 -1
- package/dist/channel-tools.js +77 -0
- package/dist/channel-tools.js.map +1 -1
- package/dist/session/session-manager.d.ts +16 -0
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +88 -37
- package/dist/session/session-manager.js.map +1 -1
- package/package.json +145 -145
- package/settings-host.js +110 -32
package/package.json
CHANGED
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@zaofan/dsh-qqbot",
|
|
3
|
-
"version": "1.11.
|
|
4
|
-
"description": "QQ Bot IM channel plugin for deepseek-harness (dsh)",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./entry.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./entry.js"
|
|
12
|
-
},
|
|
13
|
-
"./channel-tools": {
|
|
14
|
-
"types": "./dist/channel-tools.d.ts",
|
|
15
|
-
"import": "./dist/channel-tools.js"
|
|
16
|
-
},
|
|
17
|
-
"./sticker-store": {
|
|
18
|
-
"types": "./dist/features/sticker-store.d.ts",
|
|
19
|
-
"import": "./dist/features/sticker-store.js"
|
|
20
|
-
},
|
|
21
|
-
"./schedule-store": {
|
|
22
|
-
"types": "./dist/features/schedule-store.d.ts",
|
|
23
|
-
"import": "./dist/features/schedule-store.js"
|
|
24
|
-
},
|
|
25
|
-
"./client": "./client/qqbot-settings.js",
|
|
26
|
-
"./session-registry": "./dist/features/session-registry.js"
|
|
27
|
-
},
|
|
28
|
-
"scripts": {
|
|
29
|
-
"build": "tsc",
|
|
30
|
-
"dev": "tsc --watch",
|
|
31
|
-
"prepublishOnly": "npm run build && node scripts/check-package.mjs",
|
|
32
|
-
"check:package": "node scripts/check-package.mjs",
|
|
33
|
-
"lint": "eslint src/",
|
|
34
|
-
"test": "vitest run",
|
|
35
|
-
"clean": "rm -rf dist",
|
|
36
|
-
"ci:prepare-test-pkg": "node scripts/prepare-test-pkg.js"
|
|
37
|
-
},
|
|
38
|
-
"repository": {
|
|
39
|
-
"type": "git",
|
|
40
|
-
"url": "git+https://github.com/zaofan-make/dsh-qqbot.git"
|
|
41
|
-
},
|
|
42
|
-
"homepage": "https://github.com/zaofan-make/dsh-qqbot#readme",
|
|
43
|
-
"bugs": {
|
|
44
|
-
"url": "https://github.com/zaofan-make/dsh-qqbot/issues"
|
|
45
|
-
},
|
|
46
|
-
"keywords": [
|
|
47
|
-
"deepseek-harness",
|
|
48
|
-
"dsh",
|
|
49
|
-
"dsh-plugin",
|
|
50
|
-
"qqbot",
|
|
51
|
-
"qq-bot",
|
|
52
|
-
"chatbot",
|
|
53
|
-
"ai-agent",
|
|
54
|
-
"cordis",
|
|
55
|
-
"plugin",
|
|
56
|
-
"llm"
|
|
57
|
-
],
|
|
58
|
-
"license": "MIT",
|
|
59
|
-
"files": [
|
|
60
|
-
"dist",
|
|
61
|
-
"docs/extension-contract.md",
|
|
62
|
-
"docs/USER-GUIDE.md",
|
|
63
|
-
"docs/botplay-contract.md",
|
|
64
|
-
"docs/host-services.md",
|
|
65
|
-
"examples",
|
|
66
|
-
"client",
|
|
67
|
-
"cordis.patch.yml",
|
|
68
|
-
"settings-host.js",
|
|
69
|
-
"README.md",
|
|
70
|
-
"README_EN.md",
|
|
71
|
-
"LICENSE",
|
|
72
|
-
"voice-convert.mjs",
|
|
73
|
-
"scripts/download-model.mjs",
|
|
74
|
-
"scripts/fix-dsh-016-presets.ps1",
|
|
75
|
-
"entry.js"
|
|
76
|
-
],
|
|
77
|
-
"dsh": {
|
|
78
|
-
"bundle": {
|
|
79
|
-
"patch": "./cordis.patch.yml"
|
|
80
|
-
},
|
|
81
|
-
"client": {
|
|
82
|
-
"platform": "web",
|
|
83
|
-
"inject": [
|
|
84
|
-
"@deepseek-ai/dsh-client-ui-settings",
|
|
85
|
-
"@deepseek-ai/dsh-client-ui-slots"
|
|
86
|
-
],
|
|
87
|
-
"immediately": true
|
|
88
|
-
},
|
|
89
|
-
"engines": {
|
|
90
|
-
"dsh": ">=0.1.5-rc.2"
|
|
91
|
-
},
|
|
92
|
-
"manifestVersion": 1
|
|
93
|
-
},
|
|
94
|
-
"dependencies": {
|
|
95
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
|
|
96
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
|
|
97
|
-
"@deepseek-ai/schemastery": "3.18.4",
|
|
98
|
-
"@tencent-connect/qqbot-connector": "1.2.0",
|
|
99
|
-
"@tencent-connect/qqbot-nodejs": "^1.0.4",
|
|
100
|
-
"js-yaml": "^4.1.0",
|
|
101
|
-
"lamejs-fixed": "^1.2.2",
|
|
102
|
-
"silk-wasm": "^3.7.1"
|
|
103
|
-
},
|
|
104
|
-
"optionalDependencies": {
|
|
105
|
-
"@huggingface/transformers": "^4.3.0"
|
|
106
|
-
},
|
|
107
|
-
"peerDependencies": {
|
|
108
|
-
"@deepseek-ai/cordis": ">=4.0.1",
|
|
109
|
-
"@deepseek-ai/dsh-agent": ">=0.1.0-rc.6",
|
|
110
|
-
"@deepseek-ai/dsh-llm": ">=0.1.0-rc.6",
|
|
111
|
-
"@deepseek-ai/dsh-session": ">=0.1.0-rc.6",
|
|
112
|
-
"@deepseek-ai/dsh-settings": "*",
|
|
113
|
-
"@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
|
|
114
|
-
"@tencent-connect/qqbot-connector": "1.2.0"
|
|
115
|
-
},
|
|
116
|
-
"peerDependenciesMeta": {
|
|
117
|
-
"@deepseek-ai/cordis": {
|
|
118
|
-
"optional": true
|
|
119
|
-
},
|
|
120
|
-
"@deepseek-ai/schemastery": {
|
|
121
|
-
"optional": true
|
|
122
|
-
},
|
|
123
|
-
"@deepseek-ai/dsh-agent": {
|
|
124
|
-
"optional": true
|
|
125
|
-
},
|
|
126
|
-
"@deepseek-ai/dsh-session": {
|
|
127
|
-
"optional": true
|
|
128
|
-
},
|
|
129
|
-
"@deepseek-ai/dsh-llm": {
|
|
130
|
-
"optional": true
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
"devDependencies": {
|
|
134
|
-
"@deepseek-ai/cordis": "4.0.1",
|
|
135
|
-
"@deepseek-ai/dsh-agent": "0.1.0-rc.6",
|
|
136
|
-
"@deepseek-ai/dsh-llm": "0.1.0-rc.6",
|
|
137
|
-
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
|
|
138
|
-
"@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
|
|
139
|
-
"@deepseek-ai/schemastery": "3.18.1",
|
|
140
|
-
"@types/js-yaml": "^4.0.9",
|
|
141
|
-
"@types/node": "^26.2.0",
|
|
142
|
-
"typescript": "^5.5.0",
|
|
143
|
-
"vitest": "^4.1.8"
|
|
144
|
-
}
|
|
145
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@zaofan/dsh-qqbot",
|
|
3
|
+
"version": "1.11.10",
|
|
4
|
+
"description": "QQ Bot IM channel plugin for deepseek-harness (dsh)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./entry.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./entry.js"
|
|
12
|
+
},
|
|
13
|
+
"./channel-tools": {
|
|
14
|
+
"types": "./dist/channel-tools.d.ts",
|
|
15
|
+
"import": "./dist/channel-tools.js"
|
|
16
|
+
},
|
|
17
|
+
"./sticker-store": {
|
|
18
|
+
"types": "./dist/features/sticker-store.d.ts",
|
|
19
|
+
"import": "./dist/features/sticker-store.js"
|
|
20
|
+
},
|
|
21
|
+
"./schedule-store": {
|
|
22
|
+
"types": "./dist/features/schedule-store.d.ts",
|
|
23
|
+
"import": "./dist/features/schedule-store.js"
|
|
24
|
+
},
|
|
25
|
+
"./client": "./client/qqbot-settings.js",
|
|
26
|
+
"./session-registry": "./dist/features/session-registry.js"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"dev": "tsc --watch",
|
|
31
|
+
"prepublishOnly": "npm run build && node scripts/check-package.mjs",
|
|
32
|
+
"check:package": "node scripts/check-package.mjs",
|
|
33
|
+
"lint": "eslint src/",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"clean": "rm -rf dist",
|
|
36
|
+
"ci:prepare-test-pkg": "node scripts/prepare-test-pkg.js"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/zaofan-make/dsh-qqbot.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/zaofan-make/dsh-qqbot#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/zaofan-make/dsh-qqbot/issues"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"deepseek-harness",
|
|
48
|
+
"dsh",
|
|
49
|
+
"dsh-plugin",
|
|
50
|
+
"qqbot",
|
|
51
|
+
"qq-bot",
|
|
52
|
+
"chatbot",
|
|
53
|
+
"ai-agent",
|
|
54
|
+
"cordis",
|
|
55
|
+
"plugin",
|
|
56
|
+
"llm"
|
|
57
|
+
],
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"files": [
|
|
60
|
+
"dist",
|
|
61
|
+
"docs/extension-contract.md",
|
|
62
|
+
"docs/USER-GUIDE.md",
|
|
63
|
+
"docs/botplay-contract.md",
|
|
64
|
+
"docs/host-services.md",
|
|
65
|
+
"examples",
|
|
66
|
+
"client",
|
|
67
|
+
"cordis.patch.yml",
|
|
68
|
+
"settings-host.js",
|
|
69
|
+
"README.md",
|
|
70
|
+
"README_EN.md",
|
|
71
|
+
"LICENSE",
|
|
72
|
+
"voice-convert.mjs",
|
|
73
|
+
"scripts/download-model.mjs",
|
|
74
|
+
"scripts/fix-dsh-016-presets.ps1",
|
|
75
|
+
"entry.js"
|
|
76
|
+
],
|
|
77
|
+
"dsh": {
|
|
78
|
+
"bundle": {
|
|
79
|
+
"patch": "./cordis.patch.yml"
|
|
80
|
+
},
|
|
81
|
+
"client": {
|
|
82
|
+
"platform": "web",
|
|
83
|
+
"inject": [
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-slots"
|
|
86
|
+
],
|
|
87
|
+
"immediately": true
|
|
88
|
+
},
|
|
89
|
+
"engines": {
|
|
90
|
+
"dsh": ">=0.1.5-rc.2"
|
|
91
|
+
},
|
|
92
|
+
"manifestVersion": 1
|
|
93
|
+
},
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
|
|
96
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
|
|
97
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
98
|
+
"@tencent-connect/qqbot-connector": "1.2.0",
|
|
99
|
+
"@tencent-connect/qqbot-nodejs": "^1.0.4",
|
|
100
|
+
"js-yaml": "^4.1.0",
|
|
101
|
+
"lamejs-fixed": "^1.2.2",
|
|
102
|
+
"silk-wasm": "^3.7.1"
|
|
103
|
+
},
|
|
104
|
+
"optionalDependencies": {
|
|
105
|
+
"@huggingface/transformers": "^4.3.0"
|
|
106
|
+
},
|
|
107
|
+
"peerDependencies": {
|
|
108
|
+
"@deepseek-ai/cordis": ">=4.0.1",
|
|
109
|
+
"@deepseek-ai/dsh-agent": ">=0.1.0-rc.6",
|
|
110
|
+
"@deepseek-ai/dsh-llm": ">=0.1.0-rc.6",
|
|
111
|
+
"@deepseek-ai/dsh-session": ">=0.1.0-rc.6",
|
|
112
|
+
"@deepseek-ai/dsh-settings": "*",
|
|
113
|
+
"@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
|
|
114
|
+
"@tencent-connect/qqbot-connector": "1.2.0"
|
|
115
|
+
},
|
|
116
|
+
"peerDependenciesMeta": {
|
|
117
|
+
"@deepseek-ai/cordis": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"@deepseek-ai/schemastery": {
|
|
121
|
+
"optional": true
|
|
122
|
+
},
|
|
123
|
+
"@deepseek-ai/dsh-agent": {
|
|
124
|
+
"optional": true
|
|
125
|
+
},
|
|
126
|
+
"@deepseek-ai/dsh-session": {
|
|
127
|
+
"optional": true
|
|
128
|
+
},
|
|
129
|
+
"@deepseek-ai/dsh-llm": {
|
|
130
|
+
"optional": true
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"devDependencies": {
|
|
134
|
+
"@deepseek-ai/cordis": "4.0.1",
|
|
135
|
+
"@deepseek-ai/dsh-agent": "0.1.0-rc.6",
|
|
136
|
+
"@deepseek-ai/dsh-llm": "0.1.0-rc.6",
|
|
137
|
+
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
|
|
138
|
+
"@deepseek-ai/dsh-tools": ">=0.1.1-rc.2",
|
|
139
|
+
"@deepseek-ai/schemastery": "3.18.1",
|
|
140
|
+
"@types/js-yaml": "^4.0.9",
|
|
141
|
+
"@types/node": "^26.2.0",
|
|
142
|
+
"typescript": "^5.5.0",
|
|
143
|
+
"vitest": "^4.1.8"
|
|
144
|
+
}
|
|
145
|
+
}
|
package/settings-host.js
CHANGED
|
@@ -2620,13 +2620,20 @@ export function apply(ctx) {
|
|
|
2620
2620
|
const hits = [];
|
|
2621
2621
|
const seen = new Set();
|
|
2622
2622
|
// 按实例台账补昵称(dataRoot/表情包/known-chats.jsonl)
|
|
2623
|
+
// ⚠️ 2026-10-28:加 30s 缓存 —— 每次 lookup 都重新扫台账是"判断命中慢"的另一半原因。
|
|
2623
2624
|
const nameOf = (ns, scope, peerId) => {
|
|
2624
2625
|
const bot = botByNs.get(ns);
|
|
2625
2626
|
if (!bot) return '';
|
|
2627
|
+
const nc = (globalThis.__qqsLedgerNameCache = globalThis.__qqsLedgerNameCache || new Map());
|
|
2628
|
+
const ck = ns + '|' + scope + '|' + peerId;
|
|
2629
|
+
const hitN = nc.get(ck);
|
|
2630
|
+
if (hitN && Date.now() - hitN.at < 30000) return hitN.name;
|
|
2626
2631
|
try {
|
|
2627
2632
|
const cwd = bot.cfg.dataRoot || bot.cfg.cwd;
|
|
2628
2633
|
const ld = chatLedgerNames(join(cwd, '表情包')).get(scope + ':' + peerId);
|
|
2629
|
-
|
|
2634
|
+
const name = (ld && ld.name) || '';
|
|
2635
|
+
nc.set(ck, { at: Date.now(), name });
|
|
2636
|
+
return name;
|
|
2630
2637
|
} catch { return '' }
|
|
2631
2638
|
};
|
|
2632
2639
|
const pushHit = (ns, scope, peerId, name, via) => {
|
|
@@ -2635,35 +2642,77 @@ export function apply(ctx) {
|
|
|
2635
2642
|
seen.add(key);
|
|
2636
2643
|
hits.push({ ns, scope, peerId, name, via });
|
|
2637
2644
|
};
|
|
2638
|
-
//
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2645
|
+
// ★ 2026-10-28(主人反馈"判断命中要两三秒、悬浮球显隐太慢"):
|
|
2646
|
+
// ① 结果缓存 15s(front 端 5s 轮一次,命中缓存基本零延迟);
|
|
2647
|
+
// ② 快路优先:prefs(单文件 JSON) → derive(台账/群注册表) → **最后**才 session-registry
|
|
2648
|
+
// (那个 `await import('./dist/...')` 冷启动要几百 ms 到数秒,是主要慢因);
|
|
2649
|
+
// 快路命中就直接返回,不再等它。
|
|
2650
|
+
const cache = (globalThis.__qqsLookupCache = globalThis.__qqsLookupCache || new Map());
|
|
2651
|
+
const cached = cache.get(sessionId);
|
|
2652
|
+
if (cached && Date.now() - cached.at < 15000) return writeJson(res, 200, cached.data);
|
|
2653
|
+
|
|
2654
|
+
// ① 快路 A:反查持久偏好({dataRoot}/.qqbot/model-prefs.json 的 sessionKey → 最新 sessionId)
|
|
2655
|
+
// 覆盖"宿主刚启动、会话记录还没恢复"以及"fork/新建后 sessionId 是 randomUUID、derive 推不出"。
|
|
2644
2656
|
for (const bot of bots) {
|
|
2645
2657
|
if (bot.disabled || !bot.cfg?.appId || !bot.cfg?.cwd) continue;
|
|
2646
|
-
const appId = bot.cfg.appId;
|
|
2647
2658
|
const cwd = bot.cfg.dataRoot || bot.cfg.cwd;
|
|
2648
2659
|
const ns = bot.id;
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
if (!
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2660
|
+
try {
|
|
2661
|
+
const raw = readFileSync(join(cwd, '.qqbot', 'model-prefs.json'), 'utf8');
|
|
2662
|
+
const prefs = JSON.parse(raw);
|
|
2663
|
+
const ids = prefs && prefs.sessionIds;
|
|
2664
|
+
if (!ids || typeof ids !== 'object') continue;
|
|
2665
|
+
for (const [sk0, sid] of Object.entries(ids)) {
|
|
2666
|
+
if (!sid || String(sid) !== sessionId) continue;
|
|
2667
|
+
// 兼容 "sessionKey@sessionId" 形态(overrides 的键是那个形状,sessionIds 一般不带)
|
|
2668
|
+
const sk = String(sk0).split('@')[0];
|
|
2669
|
+
const i = sk.indexOf(':');
|
|
2670
|
+
if (i <= 0) continue;
|
|
2671
|
+
const scope = sk.slice(0, i);
|
|
2672
|
+
const peerId = sk.slice(i + 1);
|
|
2673
|
+
if (!peerId || (scope !== 'group' && scope !== 'c2c')) continue;
|
|
2674
|
+
pushHit(ns, scope, peerId, nameOf(ns, scope, peerId), 'prefs');
|
|
2675
|
+
}
|
|
2676
|
+
} catch { /* 没有偏好文件 / JSON 坏了:跳过 */ }
|
|
2677
|
+
}
|
|
2678
|
+
// ② 快路 B:确定性推导(会话记录未建立/被回收时,从群注册表 + 台账推导)
|
|
2679
|
+
if (hits.length === 0) {
|
|
2680
|
+
for (const bot of bots) {
|
|
2681
|
+
if (bot.disabled || !bot.cfg?.appId || !bot.cfg?.cwd) continue;
|
|
2682
|
+
const appId = bot.cfg.appId;
|
|
2683
|
+
const cwd = bot.cfg.dataRoot || bot.cfg.cwd;
|
|
2684
|
+
const ns = bot.id;
|
|
2685
|
+
const ledger = chatLedgerNames(join(cwd, '表情包'));
|
|
2686
|
+
const groups = readGroupsJson(cwd);
|
|
2687
|
+
const gids = new Set([...Object.keys(groups), ...[...ledger.keys()].filter((k) => k.startsWith('group:')).map((k) => k.slice(6))]);
|
|
2688
|
+
for (const gid of gids) {
|
|
2689
|
+
if (!gid) continue;
|
|
2690
|
+
if (deriveSessionIdOf(appId, 'group', gid) !== sessionId) continue;
|
|
2691
|
+
const regMeta = groups[gid];
|
|
2692
|
+
const ld = ledger.get('group:' + gid);
|
|
2693
|
+
pushHit(ns, 'group', gid, (regMeta && regMeta.name) || (ld && ld.name) || '', 'derive');
|
|
2694
|
+
}
|
|
2695
|
+
for (const [key2, ld] of ledger) {
|
|
2696
|
+
if (!key2.startsWith('c2c:')) continue;
|
|
2697
|
+
const openid = key2.slice(4);
|
|
2698
|
+
if (!openid || deriveSessionIdOf(appId, 'c2c', openid) !== sessionId) continue;
|
|
2699
|
+
pushHit(ns, 'c2c', openid, ld.name || '', 'derive');
|
|
2700
|
+
}
|
|
2664
2701
|
}
|
|
2665
2702
|
}
|
|
2666
|
-
|
|
2703
|
+
// ③ 慢路(只在快路全落空时才走):真实会话记录,权威但需要动态 import
|
|
2704
|
+
if (hits.length === 0) {
|
|
2705
|
+
try {
|
|
2706
|
+
const sessReg = await import('./dist/features/session-registry.js');
|
|
2707
|
+
for (const r of sessReg.findSessionBySessionIdWeb(sessionId)) {
|
|
2708
|
+
pushHit(r.ns, r.scope, r.peerId, nameOf(r.ns, r.scope, r.peerId), 'session');
|
|
2709
|
+
}
|
|
2710
|
+
} catch { /* 模块不可用:按未命中处理 */ }
|
|
2711
|
+
}
|
|
2712
|
+
const data = { ok: true, sessionId, hits };
|
|
2713
|
+
cache.set(sessionId, { at: Date.now(), data });
|
|
2714
|
+
if (cache.size > 300) { let n = 0; for (const k of cache.keys()) { cache.delete(k); if (++n >= 150) break } }
|
|
2715
|
+
writeJson(res, 200, data);
|
|
2667
2716
|
} catch (e) { writeJson(res, 500, { error: String((e && e.message) || e) }); }
|
|
2668
2717
|
});
|
|
2669
2718
|
|
|
@@ -5067,16 +5116,45 @@ const WHY_MAP = { busy: '目标会话回合活跃,已等待至回合结束仍超
|
|
|
5067
5116
|
if (bot.disabled || !bot.cfg?.appId || !bot.cfg?.appSecret) continue;
|
|
5068
5117
|
try {
|
|
5069
5118
|
const ns = bot.id;
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5119
|
+
// ★ 2026-10-28(主人定):这一条要同时满足三件事 —— 准确、快、和面板一致。
|
|
5120
|
+
// ① 数据根用 cfg.dataRoot(cwd 只是工作区,join-pending/groups 都在 <cwd>/<数据根> 下);
|
|
5121
|
+
// ② 群集合与面板 accounts 同源:registry(groups.json) + pending + 表情包台账(known-chats);
|
|
5122
|
+
// ③ 结果进 **30 秒内存缓存**:球每 10s 轮一次基本全部命中,只有过期那次才真去打官方接口
|
|
5123
|
+
// (6 个群串行调用很慢,这正是"打开面板才有、球上迟迟不亮"的另一半原因)。
|
|
5124
|
+
const root = String(bot.cfg.dataRoot || '').trim() || String(bot.cwd || '');
|
|
5125
|
+
const reg = readGroupsJson(root);
|
|
5126
|
+
const pendM = readPendingJson(root);
|
|
5127
|
+
const gids = new Set([...Object.keys(reg), ...Object.keys(pendM)]);
|
|
5128
|
+
try {
|
|
5129
|
+
const st = getStickerStore(join(root, '表情包'));
|
|
5130
|
+
const rawKnown = readFileSync(join(st.dataDir, 'known-chats.jsonl'), 'utf8');
|
|
5131
|
+
for (const ln of rawKnown.split(String.fromCharCode(10))) {
|
|
5132
|
+
const t = ln.trim(); if (!t) continue;
|
|
5133
|
+
try {
|
|
5134
|
+
const o = JSON.parse(t);
|
|
5135
|
+
if (o && o.scope === 'group' && typeof o.id === 'string' && o.id) gids.add(o.id);
|
|
5136
|
+
} catch { /* 坏行跳过 */ }
|
|
5137
|
+
}
|
|
5138
|
+
} catch { /* 无台账 */ }
|
|
5139
|
+
const cache = (globalThis.__qqsJoinSummaryCache = globalThis.__qqsJoinSummaryCache || new Map());
|
|
5140
|
+
const hit = cache.get(ns);
|
|
5073
5141
|
let pending = 0;
|
|
5074
|
-
|
|
5075
|
-
|
|
5142
|
+
if (hit && Date.now() - hit.at < 30000) {
|
|
5143
|
+
pending = hit.pending;
|
|
5144
|
+
} else {
|
|
5076
5145
|
try {
|
|
5077
|
-
const
|
|
5078
|
-
if (
|
|
5079
|
-
|
|
5146
|
+
const gc = await groupClientOf(ns);
|
|
5147
|
+
if (gc) {
|
|
5148
|
+
for (const gid of gids) {
|
|
5149
|
+
if (!gid) continue;
|
|
5150
|
+
try {
|
|
5151
|
+
const r = await gc.client.listJoinRequests(gid);
|
|
5152
|
+
if (r.ok && Array.isArray(r.data?.list)) pending += r.data.list.length;
|
|
5153
|
+
} catch { /* 单群失败不阻断 */ }
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
} catch { /* 实例异常按 0 处理 */ }
|
|
5157
|
+
cache.set(ns, { at: Date.now(), pending });
|
|
5080
5158
|
}
|
|
5081
5159
|
out.push({ ns, pending });
|
|
5082
5160
|
} catch { /* 实例失败跳过 */ }
|