@zaofan/dsh-qqbot 1.5.1 → 1.5.4

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.
Files changed (56) hide show
  1. package/README_EN.md +7 -7
  2. package/client/qqbot-settings.js +36 -5
  3. package/dist/channel-tools.d.ts.map +1 -1
  4. package/dist/channel-tools.js +43 -1
  5. package/dist/channel-tools.js.map +1 -1
  6. package/dist/config.d.ts +34 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +17 -1
  9. package/dist/config.js.map +1 -1
  10. package/dist/features/agg-score.d.ts +1 -1
  11. package/dist/features/agg-score.js +1 -1
  12. package/dist/features/extension-store.d.ts.map +1 -1
  13. package/dist/features/extension-store.js +19 -4
  14. package/dist/features/extension-store.js.map +1 -1
  15. package/dist/features/four-source.d.ts +1 -1
  16. package/dist/features/four-source.js +1 -1
  17. package/dist/features/local-signals.js +2 -2
  18. package/dist/features/local-signals.js.map +1 -1
  19. package/dist/features/people-memo.js +1 -1
  20. package/dist/features/tendency-samples.js +1 -1
  21. package/dist/features/value-sample.d.ts +3 -1
  22. package/dist/features/value-sample.d.ts.map +1 -1
  23. package/dist/features/value-sample.js +3 -1
  24. package/dist/features/value-sample.js.map +1 -1
  25. package/dist/features/value-score.d.ts +27 -0
  26. package/dist/features/value-score.d.ts.map +1 -1
  27. package/dist/features/value-score.js +42 -3
  28. package/dist/features/value-score.js.map +1 -1
  29. package/dist/gateway/debounce.d.ts.map +1 -1
  30. package/dist/gateway/debounce.js +40 -6
  31. package/dist/gateway/debounce.js.map +1 -1
  32. package/dist/gateway/middleware-setup.d.ts.map +1 -1
  33. package/dist/gateway/middleware-setup.js +3 -0
  34. package/dist/gateway/middleware-setup.js.map +1 -1
  35. package/dist/middleware/media-history.d.ts +2 -0
  36. package/dist/middleware/media-history.d.ts.map +1 -1
  37. package/dist/middleware/media-history.js +32 -3
  38. package/dist/middleware/media-history.js.map +1 -1
  39. package/dist/session/session-manager.d.ts.map +1 -1
  40. package/dist/session/session-manager.js +13 -2
  41. package/dist/session/session-manager.js.map +1 -1
  42. package/dist/shared/mention-clean.d.ts +13 -0
  43. package/dist/shared/mention-clean.d.ts.map +1 -1
  44. package/dist/shared/mention-clean.js +19 -0
  45. package/dist/shared/mention-clean.js.map +1 -1
  46. package/dist/transport/inbound.d.ts +10 -0
  47. package/dist/transport/inbound.d.ts.map +1 -1
  48. package/dist/transport/inbound.js +131 -53
  49. package/dist/transport/inbound.js.map +1 -1
  50. package/dist/transport/markers.d.ts +59 -0
  51. package/dist/transport/markers.d.ts.map +1 -0
  52. package/dist/transport/markers.js +97 -0
  53. package/dist/transport/markers.js.map +1 -0
  54. package/docs/USER-GUIDE.md +461 -461
  55. package/package.json +124 -124
  56. package/settings-host.js +3095 -3071
package/README_EN.md CHANGED
@@ -4,8 +4,8 @@ An **enhanced fork** of the QQ Bot IM plugin for [deepseek-harness](https://gith
4
4
 
5
5
  📦 Repo: [gcry13067381632-jpg/dsh-qqbot](https://github.com/gcry13067381632-jpg/dsh-qqbot) (forked from [tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot))
6
6
 
7
- > ⭐ **If it works well for you, please star the repo** — it is the only visible signal that someone is using it, and it keeps the project going.
8
-
7
+ > ⭐ **If it works well for you, please star the repo** — it is the only visible signal that someone is using it, and it keeps the project going.
8
+
9
9
  [中文文档](./README.md) | English
10
10
 
11
11
  ## 🐋 This fork (enhanced edition)
@@ -290,7 +290,7 @@ Please install the local small model used by the dsh qqbot plugin on this machin
290
290
  Every inbound message carries a **short message number** (e.g. `#0913a` — month/day + rolling id):
291
291
 
292
292
  ```
293
- [做早饭 (E9020753…) #0913a] take a look at this
293
+ [Alice (12AB34…) #0913a] take a look at this
294
294
  ```
295
295
 
296
296
  - To quote a message, the AI writes `[rf:0913a]` in its reply body → the reply is sent as a **quote bubble** (the other side sees "she quoted this message"). The tag itself is never displayed.
@@ -410,10 +410,10 @@ export QQBOT_APPID="xxx" QQBOT_SECRET="xxx"
410
410
  npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
411
411
  ```
412
412
 
413
- ## Support
414
-
415
- If this plugin saved you some tokens — or made your QQ bot feel alive — a **⭐ Star** is the most concrete way to say thanks. Bugs and feature requests: [Issues](https://github.com/gcry13067381632-jpg/dsh-qqbot/issues).
416
-
413
+ ## Support
414
+
415
+ If this plugin saved you some tokens — or made your QQ bot feel alive — a **⭐ Star** is the most concrete way to say thanks. Bugs and feature requests: [Issues](https://github.com/gcry13067381632-jpg/dsh-qqbot/issues).
416
+
417
417
  ## License
418
418
 
419
419
  [MIT](./LICENSE)
@@ -2125,7 +2125,9 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
2125
2125
  var q = state.ns ? '?' + outNsQ() : ''
2126
2126
  Promise.all([
2127
2127
  fetch('/api/qqbot-settings/affinity' + q).then(function (r) { return r.json() }).catch(function () { return {} }),
2128
- fetch('/api/qqbot-settings/attitude' + q).then(function (r) { return r.json() }).catch(function () { return {} })
2128
+ // all=1&limit=50: 这张表是"按 key 左连接好感度", 只要 top N 的话排在 N 后面的人永远显示 "—"
2129
+ // (2026-09-15 主人问"小明不是有好感度吗, 怎么显示 0" —— 他 a=0.064 排第 9, 被 top8 截掉)。
2130
+ fetch('/api/qqbot-settings/attitude' + q + (q ? '&' : '?') + 'all=1&limit=50').then(function (r) { return r.json() }).catch(function () { return {} })
2129
2131
  ]).then(function (rs) {
2130
2132
  var aff = (rs[0] && rs[0].items) || []
2131
2133
  var att = (rs[1] && rs[1].items) || []
@@ -2257,11 +2259,21 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
2257
2259
  valueGate: gateEl ? gateEl.value : 'log',
2258
2260
  valueMinScore: minEl ? (parseFloat(minEl.value) || 0.5) : 0.5,
2259
2261
  }
2262
+ var apNext = null // 附件唤醒新值(账号级): 保存成功后立刻写回本地缓存, 不等接口回读
2260
2263
  if (hint) hint.textContent = '保存中…'
2261
2264
  fetch(READ + (state.ns ? '?' + outNsQ() : '')).then(function (r) { return r.json() }).then(function (d) {
2262
2265
  var cur = (d && d.value) || {}
2263
2266
  var lm = Object.assign({}, cur.localModel || {})
2264
2267
  if (dirEl) lm.modelDir = String(dirEl.value || '').trim() // 账号级
2268
+ // 附件唤醒开关(账号级,2026-09-15):勾了 = 该类附件无视分数直接唤醒
2269
+ {
2270
+ var apEls = panel ? panel.querySelectorAll('.dk-lm-ap') : []
2271
+ if (apEls.length) {
2272
+ lm.attachmentPassthrough = {}
2273
+ apEls.forEach(function (el) { lm.attachmentPassthrough[el.getAttribute('data-kind')] = !!el.checked })
2274
+ apNext = lm.attachmentPassthrough
2275
+ }
2276
+ }
2265
2277
  lm.overrides = Object.assign({}, (lm.overrides || {}))
2266
2278
  lm.overrides[ovKey] = ovNext // 会话级
2267
2279
  var patch = Object.assign({}, cur, { localModel: lm })
@@ -2277,6 +2289,7 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
2277
2289
  lm2.overrides = Object.assign({}, (lm2.overrides || {}))
2278
2290
  lm2.overrides[ovKey] = ovNext
2279
2291
  if (dirEl) lm2.modelDir = String(dirEl.value || '').trim()
2292
+ if (apNext) lm2.attachmentPassthrough = apNext // 附件唤醒(账号级): 保存后立刻回填, 否则重绘又跳回默认勾选
2280
2293
  state.lmCfg = lm2
2281
2294
  paintBody()
2282
2295
  setTimeout(function () { loadLocalModel() }, 2000)
@@ -3559,6 +3572,24 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
3559
3572
  + '<span style="font-size:12px;color:#666">门槛:</span>'
3560
3573
  + '<input class="qqs-inp" id="dk-lm-min" type="number" step="0.01" min="0" max="1" style="width:78px" value="' + minV + '">'
3561
3574
  + '</div>'
3575
+ // 🎬 附件唤醒开关(2026-09-15 主人定):按类型决定"是否**无视分数**直接唤醒"
3576
+ // 勾上 = 该类附件不管分多低都唤醒;没勾 = 交给评分决定(没文字没分 → 不唤醒)
3577
+ {
3578
+ // ⚠️ 2026-09-15 修(主人反馈"关掉图片唤醒, 保存后又跳回勾选"):
3579
+ // 根因=这里只读 status 接口(lm)的字段, 而该接口的 bot.cfg 解析不到实例配置(实测一律
3580
+ // fallback: valueGate=log / 无 attachmentPassthrough) → 永远按默认渲染 → 看着像没保存。
3581
+ // 实际 settings 里早就是 image:false(读接口 ?ns= 已证实)。所以**以 lmCfg(直读配置)为准**,
3582
+ // status 只兜底 —— 与 09-13 修 valueGate 同一个教训: 前端别依赖需要重启才对的 host 字段。
3583
+ var apCfg = ((lmCfg && lmCfg.attachmentPassthrough) || lm.attachmentPassthrough || {})
3584
+ var apDef = { image: true, video: false, voice: false, file: false }
3585
+ body += '<div class="dk-row" style="gap:12px;flex-wrap:wrap;align-items:center;margin:4px 0 2px">'
3586
+ + '<span style="font-size:12px;color:#666" title="勾上的类型:不管分数都唤醒她;没勾的按评分判(没文字就没分 → 不唤醒)">附件唤醒:</span>'
3587
+ + [['image', '🖼 图片'], ['video', '🎬 视频'], ['voice', '🎤 语音'], ['file', '📎 文件']].map(function (k) {
3588
+ var on = apCfg[k[0]] === undefined ? apDef[k[0]] : apCfg[k[0]] === true
3589
+ return '<label style="display:inline-flex;align-items:center;gap:4px;font-size:12px;cursor:pointer"><input type="checkbox" class="dk-lm-ap" data-kind="' + k[0] + '"' + (on ? ' checked' : '') + '> ' + k[1] + '</label>'
3590
+ }).join('')
3591
+ + '</div>'
3592
+ }
3562
3593
  body += '<span class="dk-msg" id="dk-lm-hint" style="color:#2f9e44;margin:2px 0"></span>'
3563
3594
  // 📊 最近评分(观察期): 让主人直接看到"她是怎么被叫醒的"
3564
3595
  // 2026-09-13 二次修: 列表抽成 scoreListHtml + 独立 div, 由 refreshScoreList 做**局部刷新**
@@ -4266,11 +4297,11 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
4266
4297
  if (keepScrollOffset == null) box.scrollTop = box.scrollHeight
4267
4298
  else box.scrollTop = box.scrollHeight - keepScrollOffset
4268
4299
  }
4269
- // 聊天文本渲染(2026-09-13 引用消息功能): 把 [Quoted message begins]…[Quoted message ends] 块
4270
- // 渲染成"引用样式"气泡(左边框+灰底), 其余正文照常显示。没有引用块则整段照常。
4300
+ // 聊天文本渲染(2026-09-13 引用消息功能): 把引用块渲染成"引用样式"气泡(左边框+灰底), 其余正文照常。
4301
+ // 2026-09-15 标记改短([引]…[/引][当前]) —— **新旧都认**, 否则老会话里的英文标记会显示成裸文本。
4271
4302
  function chatRenderText(t) {
4272
- var src = String(t || '')
4273
- var re = /\[Quoted message begins\]([\s\S]*?)\[Quoted message ends\]/g
4303
+ var src = String(t || '').replace(/\[(?:当前|Current message)\]\s*/g, '')
4304
+ var re = /\[(?:引|Quoted message begins)\]([\s\S]*?)\[\/(?:引|Quoted message ends)\]/g
4274
4305
  var parts = []
4275
4306
  var m, last = 0
4276
4307
  while ((m = re.exec(src)) !== null) {
@@ -1 +1 @@
1
- {"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AA2ElE,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,eAAO,MAAM,IAAI,wBAAwB,CAAC;AAC1C,eAAO,MAAM,MAAM,UAAY,CAAC;AAShC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAI/D;AAkFD,mDAAmD;AACnD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAk1DvD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,GAAG,KAAK,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCvC"}
1
+ {"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AA2ElE,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,eAAO,MAAM,IAAI,wBAAwB,CAAC;AAC1C,eAAO,MAAM,MAAM,UAAY,CAAC;AAShC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAI/D;AAkFD,mDAAmD;AACnD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAk3DvD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,GAAG,KAAK,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCvC"}
@@ -2194,8 +2194,47 @@ export async function apply(ctx) {
2194
2194
  || process.cwd();
2195
2195
  const defs = await loadExtensionTools(extRoot, (ctx.logger ?? console));
2196
2196
  extDiag(`apply extRoot=${extRoot} defs=${defs.length} qqCh=${!!qqCh} bridge=${!!bridgeCh}`);
2197
+ // ── 同名扩展工具「热替换」(2026-09-15 主人踩坑后加) ──────────────────────
2198
+ // 症状: 改**已有**扩展工具的逻辑 → tools_reload 报"完成",但跑的还是旧实现
2199
+ // (dsh 的 registry 对重名直接抛 'already registered',这里只能 catch 跳过)。
2200
+ // 根因: 上一版只"新增",没有回收入口。而 `ctx.tools.register()` 其实**返回 disposer**
2201
+ // (dsh-tools 的 `register(definition): () => void`)→ 先 dispose 旧的再注册新的即可。
2202
+ // ⚠️ disposer 不能存在模块变量里: 热刷是用 `?hot=<ts>` 重新 import 本模块的,
2203
+ // 新模块的模块级 Map 是空的、认不出上次注册的东西 → 必须存在 **globalThis 上的
2204
+ // WeakMap(ctx)** 才跨模块实例可见(也顺带不污染 ctx 对象)。
2205
+ // 安全边界: 只 dispose"我们自己注册过、且名字对得上"的工具;别人注册的同名工具照旧走
2206
+ // 原来的 catch 跳过分支(行为不变)。
2207
+ const disposeStore = (() => {
2208
+ const g = globalThis;
2209
+ const KEY = Symbol.for('im-qqbot.extToolDisposers');
2210
+ let store = g[KEY];
2211
+ if (!store) {
2212
+ store = new WeakMap();
2213
+ g[KEY] = store;
2214
+ }
2215
+ return store;
2216
+ })();
2217
+ const myDisposers = (() => {
2218
+ const k = ctx;
2219
+ let m = disposeStore.get(k);
2220
+ if (!m) {
2221
+ m = new Map();
2222
+ disposeStore.set(k, m);
2223
+ }
2224
+ return m;
2225
+ })();
2197
2226
  for (const def of defs) {
2198
2227
  try {
2228
+ // 同一个 ctx 再来一遍(热刷)= 更新已有工具:先摘掉我们自己上次注册的那份
2229
+ const prev = myDisposers.get(def.name);
2230
+ if (prev) {
2231
+ try {
2232
+ prev();
2233
+ }
2234
+ catch { /* 旧句柄已失效,忽略 */ }
2235
+ myDisposers.delete(def.name);
2236
+ extDiag(`热替换: 先注销旧 ${def.name}`);
2237
+ }
2199
2238
  const tool = defineTool({
2200
2239
  name: def.name,
2201
2240
  description: def.description + ' (用户扩展工具)',
@@ -2227,7 +2266,10 @@ export async function apply(ctx) {
2227
2266
  }
2228
2267
  },
2229
2268
  });
2230
- ctx.tools.register(tool);
2269
+ const dispose = ctx.tools.register(tool);
2270
+ // 收下 disposer —— 下次(热刷/重装配)靠它做同名替换,不然只能重启宿主
2271
+ if (typeof dispose === 'function')
2272
+ myDisposers.set(def.name, dispose);
2231
2273
  ctx.logger?.info?.(`[channel-tools] 已注册扩展工具: ${def.name}`);
2232
2274
  diag(`扩展工具注册成功: ${def.name}`);
2233
2275
  extDiag(`注册成功: ${def.name}`);