qq-bot-roost 0.9.5 → 0.9.6

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 CHANGED
@@ -10,7 +10,7 @@
10
10
  <img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="零依赖">
11
11
  <img src="https://img.shields.io/badge/tests-700%2B%20passed-brightgreen" alt="测试 700+ 项">
12
12
  <img src="https://img.shields.io/badge/plugins-JS%20%7C%20Python%20%7C%20Go%20%7C%20any-blue" alt="多语言插件">
13
- <img src="https://img.shields.io/badge/version-0.9.5-blueviolet" alt="版本 0.9.5">
13
+ <img src="https://img.shields.io/badge/version-0.9.6-blueviolet" alt="版本 0.9.6">
14
14
  <img src="https://img.shields.io/badge/license-Non--Commercial-red" alt="禁止商用">
15
15
  <a href="https://github.com/tdcq9594/qq-bot-roost/actions"><img src="https://img.shields.io/github/actions/workflow/status/tdcq9594/qq-bot-roost/ci.yml?branch=master" alt="CI"></a>
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qq-bot-roost",
3
- "version": "0.9.5",
3
+ "version": "0.9.6",
4
4
  "type": "module",
5
5
  "description": "栖 Roost:零依赖的 QQ 官方机器人框架,填 AppID/AppSecret 即连,插件可写任意语言",
6
6
  "license": "SEE LICENSE IN LICENSE",
package/src/outbound.js CHANGED
@@ -211,6 +211,34 @@ class QuotaLedger {
211
211
  }
212
212
  }
213
213
 
214
+ /**
215
+ * 从出站报文里挑出「人看得懂的那部分」,喂给 `digestOf`。
216
+ *
217
+ * 报文不止一种形状,只认顶层 `content` 会漏掉一大半:
218
+ *
219
+ * · 纯文本 `{ content }`
220
+ * · markdown `{ markdown: { content } }` ← ai-chat 默认就走这条
221
+ * · 卡片 `{ markdown, keyboard, ark }`
222
+ * · 富媒体 `{ msgType: 7, media }`,正文只有一个占位空格
223
+ *
224
+ * 漏掉的后果不是发不出去(账本的状态机不看摘要),而是摘要列全变成
225
+ * `…(0)#da39a3ee`——所有记录长得一模一样,「人工核对」这条路等于堵死,
226
+ * 真出现 `unknown` 的时候只能靠猜。
227
+ */
228
+ export function digestTextOf(payload) {
229
+ const p = payload ?? {};
230
+ const md = p.markdown;
231
+ const text = p.content ?? p.text ?? (typeof md === 'string' ? md : md?.content);
232
+ if (typeof text === 'string' && text.trim()) return text;
233
+ /**
234
+ * 没有正文的报文给个占位。一串空摘要里冒出 `[媒体]` / `[卡片]`,
235
+ * 至少能分辨「这条本来就没有文字」,而不是「摘要功能又坏了」。
236
+ */
237
+ if (p.media) return '[媒体]';
238
+ if (p.ark) return '[卡片]';
239
+ return '';
240
+ }
241
+
214
242
  export class Outbound {
215
243
  /**
216
244
  * @param {object} opts
@@ -296,7 +324,7 @@ export class Outbound {
296
324
  chatId: opts.target,
297
325
  replyTo: opts.payload?.msgId,
298
326
  msgSeq: opts.payload?.msgSeq,
299
- digest: digestOf(opts.payload?.content ?? opts.payload?.text ?? ''),
327
+ digest: digestOf(digestTextOf(opts.payload)),
300
328
  });
301
329
 
302
330
  if (c.action === 'reuse') {
package/web/app.js CHANGED
@@ -3095,11 +3095,17 @@ function renderDelivery(d) {
3095
3095
  const st = DELIVERY_LABEL[x.state] ?? { text: x.state, tip: '' };
3096
3096
  const when = x.created_at ? new Date(Number(x.created_at)).toLocaleString('zh-CN', { hour12: false }) : '-';
3097
3097
  const warn = x.state === 'unknown' || x.state === 'failed';
3098
+ /**
3099
+ * 摘要空的老记录(修 `digestTextOf` 之前发的)一律是 `…(0)#da39a3ee`,
3100
+ * 一列看下来完全分不清谁是谁。显示成一句人话,原文挂在 title 上备查。
3101
+ */
3102
+ const digest = String(x.digest ?? '');
3103
+ const digestText = /^…\(0\)#/.test(digest) ? '(无正文)' : digest.slice(0, 60);
3098
3104
  return `<tr>
3099
3105
  <td>${escapeHtml(when)}</td>
3100
3106
  <td class="mono">${escapeHtml(String(x.chat_id ?? '').slice(0, 12))}</td>
3101
3107
  <td${warn ? ' style="color:var(--warn-text)"' : ''} title="${escapeHtml(st.tip)}">${escapeHtml(st.text)}</td>
3102
- <td>${escapeHtml(String(x.digest ?? '').slice(0, 60))}</td>
3108
+ <td title="${escapeHtml(digest)}">${escapeHtml(digestText)}</td>
3103
3109
  <td class="num">${Number(x.attempts ?? 0)}</td>
3104
3110
  <td class="mono">${escapeHtml(String(x.code ?? ''))}</td>
3105
3111
  </tr>`;