dsh-qq-link 1.5.3 → 1.5.5

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 (3) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/lib/host.js +31 -14
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 1.5.5 — 2026-11-10
2
+
3
+ **QQ 空间「看单条说说」会把整轮对话弄崩:tid 被声明成了整数**
4
+
5
+ - 🐞 `qq_space_detail` 的 `tid` 参数类型写的是 `integer`,可它其实是 24 位十六进制的一串字符。
6
+ 模型照着「整数」去填就不加引号,写出来是个**非法 JSON**,整轮直接被判失败
7
+ (`DeepSeek Messages stream: tool input is invalid JSON`),重试几次都一样。
8
+ - 🐞 列表里输出 tid 时也没带引号,模型原样抄下来正好踩进这个坑。
9
+ - 🐞 执行时又拿 `Math.round(Number())` 去转:十六进制转出来是 NaN,长数字串还会丢精度。
10
+ - 现在:`tid` 声明成 `string`,列表输出带引号,执行时原样当字符串往下传
11
+ (模型还是给数字也照收,转成字符串再用)。
12
+
13
+ ## 1.5.4 — 2026-11-07
14
+
15
+ **宿主 0.2.2 起,QQ 空间的四个工具每 2 秒报一次 already registered,日志被刷爆**
16
+
17
+ - 🐞 issue #36:`空间工具心跳()` 注册失败时**不写 map**,于是每跳(2 秒)重试一次;
18
+ 而 0.2.2 起同一作用域不允许重名注册,第二次就抛 `already registered` ——
19
+ 一个中午 521 条,把宿主日志淹了。工具其实一直是挂着的,用户只看见「桥在报错」。
20
+ - 现在:`already registered` 不算失败,按「会话 + agent 实例」记下来,**实例换了才重挂**;
21
+ 真的挂不上时也只记一次,不再每 2 秒刷。
22
+
1
23
  ## 1.5.3 — 2026-11-07
2
24
 
3
25
  **别人发来的文件,以前一行都看不见**
package/lib/host.js CHANGED
@@ -2954,7 +2954,7 @@ export function apply(ctx) {
2954
2954
  if (!m) continue
2955
2955
  const 图数 = Array.isArray(m.images) ? m.images.length : 0
2956
2956
  行们.push((起 + i + 1) + '. ' + 空间时间(m.time)
2957
- + (m.tid != null ? ' tid ' + String(m.tid) : '')
2957
+ + (m.tid != null ? ' tid ' + JSON.stringify(String(m.tid)) : '')
2958
2958
  + ' 评论 ' + (Number(m.comment_num) || 0)
2959
2959
  + (m.is_private ? ' 私密' : '')
2960
2960
  + ' 图 ' + 图数 + ' 张'
@@ -2972,7 +2972,7 @@ export function apply(ctx) {
2972
2972
  parameters: {
2973
2973
  type: 'object',
2974
2974
  properties: {
2975
- tid: { type: 'integer', description: '说说的 tid(qq_space_list 里每条前面那个)' },
2975
+ tid: { type: 'string', description: '说说的 tid:qq_space_list 里 tid 后面那一串,原样照抄' },
2976
2976
  qq: { type: 'string', description: '这条说说属于谁的 QQ 号。不填 = 机器人自己的' },
2977
2977
  num: { type: 'integer', minimum: 1, maximum: 50, description: '评论最多看几条,默认 20' },
2978
2978
  pos: { type: 'integer', minimum: 0, description: '评论从第几条开始,默认 0' },
@@ -2983,8 +2983,12 @@ export function apply(ctx) {
2983
2983
  output: 空间输出(),
2984
2984
  async execute(args = {}) {
2985
2985
  const 配置 = 读配置()
2986
- const tid = Math.round(Number(args.tid))
2987
- if (!Number.isFinite(tid) || tid <= 0) return { ok: false, 说: '要给一个说说的 tid(数字,从 qq_space_list 里拿)' }
2986
+ // ⭐ 1.5.5:tid 是 24 位十六进制的一串字符,不是数字。
2987
+ // 以前这里写的是 integer + Math.round(Number()):模型照着「整数」去填就不加引号,
2988
+ // 写出来是个非法 JSON,整轮直接被判失败;就算写成数字串,Number() 也会丢精度。
2989
+ // 现在一律当字符串原样往下传。
2990
+ const tid = String(args.tid == null ? '' : args.tid).trim()
2991
+ if (!tid) return { ok: false, 说: '要给一个说说的 tid(从 qq_space_list 里拿,原样照抄)' }
2988
2992
  const 条数 = Math.max(1, Math.min(Math.round(Number(args.num) || 20), 50))
2989
2993
  const 起 = Math.max(0, Math.round(Number(args.pos) || 0))
2990
2994
  const 体 = { tid, pos: 起, num: 条数 }
@@ -3120,27 +3124,40 @@ export function apply(ctx) {
3120
3124
  // 宿主没给 tools 服务(或者那间会话没开)就跳过 —— 下一跳再看
3121
3125
  if (!agent || !agent.ctx || !agent.ctx.tools || typeof agent.ctx.tools.register !== 'function') continue
3122
3126
  const sid = 会话键((agent.session && agent.session.id) || id)
3123
- if (空间挂着.has(sid)) continue
3127
+ // ⭐ 1.5.4(issue #36):按「sid + agent 实例」记 —— 实例换了才重挂(会话重开、插件重载)。
3128
+ // 以前只按 sid 记,而且注册全失败时不写 map ⇒ 每 2 秒重试一次,
3129
+ // 0.2.2 起同一作用域不许重名注册,于是每次都抛 already registered,日志被刷爆。
3130
+ const 已挂 = 空间挂着.get(sid)
3131
+ if (已挂 && 已挂.agent === agent) continue
3124
3132
  const 卸们 = []
3133
+ let 已经有过 = 0
3125
3134
  for (const 工具 of 做空间工具()) {
3126
3135
  try {
3127
3136
  const off = agent.ctx.tools.register(工具)
3128
3137
  if (typeof off === 'function') 卸们.push(off)
3129
3138
  } catch (error) {
3130
- 记账('QQ 空间的工具挂不上(' + 短id(sid) + ',' + 工具.name + '):' + String((error && error.message) || error))
3139
+ const 因 = String((error && error.message) || error)
3140
+ // 已经注册过 = 上一次其实挂上了(只是我们手里没留 off)—— 记下来,别再试
3141
+ if (/already registered/i.test(因)) { 已经有过 += 1; continue }
3142
+ if (!已挂 || !已挂.报过) {
3143
+ 记账('QQ 空间的工具挂不上(' + 短id(sid) + ',' + 工具.name + '):' + 因)
3144
+ }
3131
3145
  }
3132
3146
  }
3133
- if (!卸们.length) continue
3134
- 空间挂着.set(sid, () => {
3135
- for (const off of 卸们) {
3136
- try { off() } catch (error) {}
3137
- }
3147
+ if (!卸们.length && !已经有过) {
3148
+ // 一个都没成:记一笔「报过」,下一跳别再刷同样的错
3149
+ 空间挂着.set(sid, { agent, off: null, 报过: true })
3150
+ continue
3151
+ }
3152
+ 空间挂着.set(sid, {
3153
+ agent,
3154
+ off: () => { for (const off of 卸们) { try { off() } catch (error) {} } },
3138
3155
  })
3139
- 记账('QQ 空间的工具挂上了(' + 短id(sid) + ')')
3156
+ 记账('QQ 空间的工具挂上了(' + 短id(sid) + (卸们.length < 4 ? ',其中 ' + 已经有过 + ' 个本来就在' : '') + ')')
3140
3157
  }
3141
- for (const [sid, off] of 空间挂着) {
3158
+ for (const [sid, 记] of 空间挂着) {
3142
3159
  if ([...该挂].some((id) => 会话键(id) === sid)) continue
3143
- try { off() } catch (error) {}
3160
+ try { if (记 && typeof 记.off === 'function') 记.off() } catch (error) {}
3144
3161
  空间挂着.delete(sid)
3145
3162
  记账('QQ 空间的工具卸下了(' + 短id(sid) + ')')
3146
3163
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-qq-link",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "通到 QQ:DSH 里的 AI 伙伴和 QQ 双向互通 —— 它说完话自动发到 QQ,你在 QQ 里说的话自动转进 DSH。走 OneBot 11 HTTP(SnowLuma / NapCat 都行)。",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "engines": {