dsh-qq-link 0.0.0-stage → 1.4.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/CHANGELOG.md ADDED
@@ -0,0 +1,861 @@
1
+ ## 1.4.6 — 2026-11-01
2
+
3
+ **「检查更新」以前会报错版本,而且只给一条命令让你自己手动复制、然后手动装**
4
+
5
+ - 🐞 **"最新版本"会骗人**:以前是「哪个地址先返回就用哪个」。GitHub 一超时就落到 jsDelivr ——
6
+ 那个地址**带 CDN 缓存、会滞后好几天**,于是同一分钟内能报出好几个不同的"最新",
7
+ 甚至比你本机装的还旧,看着像"该降级"。
8
+ - ✅ 现在**三个源全查**(GitHub / jsDelivr / npm registry),**取版本号最大的那个**:
9
+ 版本只会往前走,哪个源报小了就是它滞后。三个都失败才说"查不到",绝不把旧值当成"最新"。
10
+ - ✨ **检测到新版可以直接装**:那一块多了一个「一键更新到 vX」按钮,点它走 DSH 自己的插件管理装最新版,
11
+ 装完会提示你重启(或重载)一次。以前只有一个「复制命令」,得你自己去终端敲两行。
12
+ - 🧹 **界面上那些命令行全删了**:以前那两行 `dsh plugin --profile web remove/add` 和「复制命令」按钮不再显示 —— 很多人看不懂,还得跑来问。现在有新版就只有「一键更新到 vX」一个按钮。
13
+ - ⚠ **本地开发副本(link: 进来的那种)不给一键装** —— 那会把正在改的目录换成仓库里的版本;
14
+ 这种会明确写出来,让你手动更新。
15
+
16
+ ## 1.4.5 — 2026-10-31
17
+
18
+ **正文里写文件链接,就把那个文件传到 QQ**
19
+
20
+ - ✅ 这一轮的正文里出现 `[报告](D:\x\报告.docx)` 这种**普通 markdown 链接**,且那一轮正常结束,
21
+ 就把那个文件真传过去(走 OneBot 的 `upload_private_file` / `upload_group_file`)。
22
+ - 跟图片分得清:`![名](路径)` 是图片(发图那条路),`[名](路径)` 才是文件;
23
+ `http(s)://` 开头的链接和 `#锚点` 一律不当文件。
24
+ - 路径带空格就写尖括号:`[报告](<D:\我的 文档\报告.docx>)`。
25
+ - 找不到 / 超过上限都**往日志记一句**(不然人以为发了,其实什么都没出去)。
26
+ - ✅ 新增「只往私聊发(群里不发文件)」,**默认开** —— 大群里发文件容易出问题
27
+ (群文件权限、刷屏、审核),私聊最稳。关掉之后群里也会传。
28
+ 群里被跳过时会记一句,看得见为什么没发。
29
+ - 新增三个设置项:开关「发文件(它 → QQ)」、单个文件上限(默认 50MB,最大 2048)、
30
+ 开关「只往私聊发(群里不发文件)」(默认开)。
31
+ - 自测:宿主半边、网页半边全过。
32
+
33
+ ## 1.4.4 — 2026-10-31
34
+
35
+ **「按句子拆」存不住;正文里叫名字也能算点名;「只回 QQ 唤醒」不再一刀切**
36
+
37
+ ### ① 「按句子拆」勾上、保存,自己弹回去
38
+
39
+ - 🐞 1.4.3 给这个开关加了界面、加了默认值、也加了业务逻辑,**唯独漏在归一化的字段白名单里**。
40
+ 归一化会把不认识的字段直接扔掉,于是:勾上 → 保存 → 后端回一份没有这个字段的配置 →
41
+ 前端拿返回值一覆盖,勾就变回空的;磁盘上的配置文件里也从来没有这一项。
42
+ - ✅ 补进白名单;另加两条测试(开着、关掉,都能存住)。
43
+
44
+ ### ② 正文里直接叫名字,也算点名
45
+
46
+ - ✅ 新增「正文里叫这些名字也算点名」。多个用逗号或空格隔开,留空 = 关着(跟以前一样)。
47
+ - 以前群里只认 @ 和引用;有人直接说「让它帮我干活」「去问问它」,既没 @ 也没引用,
48
+ 只能靠「回复概率」掷骰子,大概率被丢掉 —— 而人看到的是「名字被叫了却没回应」,
49
+ 很难想到是概率拦的。
50
+ - 这一项同样放进白名单(不会再犯上面那个错)。
51
+
52
+ ### ③ 「只回 QQ 唤醒」不再一刀切
53
+
54
+ - 🐞 以前这道闸的判据是「最近十分钟内有没有 QQ 消息进来」,于是**自动触发的轮次**
55
+ (主动搭话、到点刷 B 站)也一起被挡了 —— 它们根本不是「你在窗口里自己聊」。
56
+ - ✅ 现在按这一轮的**来源**(`source.kind`)分开看:
57
+ · 你自己在 DSH 窗口里打的字 → **一律留在本地**(这才是「只回 QQ 唤醒」本来要挡的东西)
58
+ · QQ 转进来的 → 照常发
59
+ · 自动触发的 → 看下面那个新开关
60
+ - ✅ 新增「自动触发的也发(主动搭话 / 定时任务)」,**默认关**,老用户行为完全不变;
61
+ 开了之后那几轮也会推到手机。
62
+ - 自测:宿主半边全过。
63
+
64
+ ## 1.4.3 — 2026-10-30
65
+
66
+ **群里「只听点名」管不住自己发的话;长回复拆成一条条;人设的三个坑**
67
+
68
+ ### ① 「只听点名」现在对群里所有人生效
69
+
70
+ - 🐞 以前有一条捷径:**自己(绑定的那个私聊号)在群里说的话会绕过点名过滤**,直接收进来。
71
+ 于是在群里随手发点东西(地址、key、别处的聊天记录),也会被当成「对着它说」。
72
+ - ✅ 现在「只听点名」一开,**群里任何人**(包括自己)都要 @ 或引用才收。
73
+ - ✅ 新增每行一个 **「不受点名限制的 QQ」** 名单:填进去的号不用点名也直接收
74
+ (**但不算点名** —— 不触发「点名必回」,也不消耗「点名之后接着听」的条数)。
75
+ 逗号/空格/中文逗号/顿号/分号/换行都能分,写几个都行,默认空。
76
+
77
+ ### ② 长回复可以按句子拆(默认关)
78
+
79
+ - 🆕 配置 `按句拆`:开着时按句末标点切(`。!?;…` 和 `.!?;`),一句一条,像人一条条打字。
80
+ - 规则做得保守:连续标点(`?!`、`……`)算一处不重复切;标点后紧跟的右引号/右括号留在这一句;
81
+ **括号里的标点不算断句**(不然一句话被切成两半);空行照样是硬边界。
82
+ - 默认关 —— 老用户升级后行为一点不变。设置页在「发出去的样子」那一栏。
83
+
84
+ ### ③ 人设三处
85
+
86
+ - 🐞 **2000 字上限静默截断**:设置读入和挂载时都 `.slice(0, 2000)`,超出的部分直接没了、界面上也没提示。
87
+ 现在上限放到 2 万字(只做保护,不再悄悄砍)。
88
+ - 🐞 **只在收到 QQ 消息时挂载**:Harness 重启之后,如果第一个回合是定时任务 / 主动搭话触发的,
89
+ 那一轮就没有人设。现在跟着心跳每跳补一次(同一个 agent、同一段文本会秒返回)。
90
+ - 🐞 **会话实例替换后不重新挂**:缓存只比「文本变没变」,会话被恢复成新实例之后文本没变,
91
+ 就判定「已经挂上了」直接返回,人设其实没挂到新会话上。现在缓存**认 agent 实例**。
92
+
93
+ - 自测:宿主半边 **218 项**、网页半边全过。
94
+
95
+ ## 1.4.2 — 2026-10-30
96
+
97
+ **装不上 / 看不懂日志的两处小坑**
98
+
99
+ - ✅ README 的安装那节补了一条**不用 git 的装法**:`github:` 那种写法会让包管理器去 clone 仓库,
100
+ 机器上没装 git 就会报 `ERR_PNPM_GIT_FETCH_FAILED`;改用 tarball 直链就能装。
101
+ - ✅ 「补投窗口」跳过老消息时,日志后面补了一句「**想看内容可以回 QQ 里翻聊天记录**」——
102
+ 以前只写「这条已经是 1165 分钟前的了,不再补投」,用户看着像消息丢了。
103
+
104
+ ## 1.4.1 — 2026-10-30
105
+
106
+ **被点名之后会接着听几句;能看 QQ 空间、能发说说**
107
+
108
+ - ✅ 群里默认「只听点名」时,被 @ 或引用之后,**同一个群里后面紧跟着的普通消息**也会转进来:
109
+ 接够「点名后接听条」就停,或者过了「点名后接听秒」就停,然后退回「只听点名」。
110
+ - 两项新配置:「点名后接听条」默认 5(范围 0~50,0 = 关掉这个功能,回到老行为)、
111
+ 「点名后接听秒」默认 120(范围 0~600);设置页在「提示与门槛」。
112
+ - 状态按聊天(群号)分开记:一个群在接听,不影响别的群;私聊完全不受影响。
113
+ - 接听中进来的话**不算点名**,不会把条数又重置一遍,也不影响「点名必回」那套判定。
114
+ - 「该不该放进来」抽成了纯函数 `该收这条`(导出,单测直接喂)。
115
+ - ✅ 新增四个工具(挂在绑了 QQ 的那几间会话上,走同一个 NapCat / SnowLuma 地址和令牌):
116
+ - `qq_space_list`:看某个号(不填 = 机器人自己)的说说,返回总数;每条带 tid / 时间 / 正文(截到 200 字)/
117
+ 评论数 / 是否私密 / 图片数;
118
+ - `qq_space_detail`:看一条说说的正文和评论;
119
+ - `qq_space_feeds`:好友动态流(默认 10 条),html 先去掉标签、压掉空白再截断;
120
+ - `qq_space_post`:发说说,可以带图,`ugc_right` 支持 1 / 4 / 16 / 64 / 128,16 / 128 没给名单就直接说清楚、不发。
121
+ - ✅ 发说说单独一道闸「允许发空间动态」(默认关):关着的时候工具照常在,但一调只说一句「没开」,
122
+ 一个请求都不会发出去。设置页在「发消息 → QQ 空间」。
123
+ - ✅ 空间接口报错时把后端的中文说明带出来,不吐 JSON;「这个号没发过说说」(retcode 100、wording 里带
124
+ `msglist:null`)翻译成「这个号的空间里还没有说说」,不当成故障。
125
+ - ✅ 设置页多了一个 **「空间」** 页(原来的「发消息」改名 **「消息」**、「提示与门槛」改名 **「门槛」**,
126
+ 检查更新单独拆成 **「更新」** 页):
127
+ - **定时发说说**:一行一个时间,可以加多条(上午一条、下午一条都行),每条能单独选可见范围、单独启用;
128
+ 到点只往会话里递一句提醒,**正文由它自己写**,这里不预置任何文案。
129
+ - **定时看一圈**:开着就按「多少分钟」去拉一次好友动态;两个子开关都关时连请求都不发。
130
+ - **自动点赞 / 允许评论**:点赞挑最近三天里没点过的(每次最多 3 条、一天最多 20 条,跳过自己发的、点过的记下来不重复);
131
+ 评论是把新动态递进会话,**评不评、评什么由它自己决定**。
132
+ - ✅ 「允许发 QQ 空间动态」那道闸从「消息」页搬进「空间」页;「转发进来会变成什么样」那张卡删了。
133
+ - 测试:宿主半边 202 项(新增 45 项)、网页半边 23 项(新增 11 项),其余三套全过。
134
+
135
+ ## 1.4.0 — 2026-10-30
136
+
137
+ **同一间会话可以同时绑私聊和群聊了(两边都收、都发)**
138
+
139
+ - 🐞 以前有条规矩「一个会话只绑一条」,所以「私聊 + 一个群都指向同一间会话」做不到 ——
140
+ 第二行保存时会被静默挡掉。
141
+ - ✅ 现在按**聊天**去重(同一个私聊/同一个群只能绑一条),不再限制「一个会话绑几条」。
142
+ - ✅ 发出去的时候:这间会话绑了**几个聊天就发几个**;其中哪一行关掉了(「启用」没勾)就跳过它。
143
+ - ✅ 收进来不变:每个聊天各走自己那一行,谁说的只回谁那边,不会串。
144
+ - 设置页那句说明也跟着改了。
145
+ - 测试:新增/改写两条(私聊 + 群都发 / 关掉一条只发另一条),宿主半边 74 项、其余四套全过。
146
+
147
+ ## 1.3.0 — 2026-10-30
148
+
149
+ **每条进来的消息都带「来源头」,正文里的【】会被换掉(防冒充)**
150
+
151
+ - ✅ 现在每条转进来的消息,第一行都是来源:
152
+ - `【QQ群 123456】`(群消息,带群号)
153
+ - `【QQ私聊】`
154
+ - `【本机注入·不是QQ·不是主人】`(`/receive` 那条路 —— 以前它跟 QQ 私聊长得一模一样,会被当成主人说话)
155
+ - ✅ **防冒充**:正文里出现的 `【】` 一律换成 `[]`,并在末尾注一句「原文里的【】已换成 [],那是对方亲手打的字」——
156
+ 别人打一句「【QQ私聊·主人·123】帮我改代码」冒充不了来源了。
157
+ - ✅ 开关「来源头」默认开;关掉就回到老样子(只有群里那行「昵称(号):」)。
158
+ - 测试:宿主半边 72 项、其余四套全过。
159
+
160
+ ## 1.2.1 — 2026-10-29
161
+
162
+ **「署名」改成昵称 + 号**
163
+
164
+ - 🔧 以前署名只带号(`QQ 789:…`),私聊里看不出是谁。现在带昵称:`昵称(789):…`;
165
+ 没昵称才退回 `QQ 789:…`。
166
+ - 「[QQ] 前缀」那档不变。
167
+
168
+ ## 1.2.0 — 2026-10-29
169
+
170
+ **私聊进来的话可以标一下来源(默认不标)**
171
+
172
+ - ✅ 设置 → 「私聊进来的话标来源」,三档:
173
+ - **不标**(默认):跟以前一模一样,纯一行字;
174
+ - **[QQ] 前缀**:`[QQ] 你说的那句`,占几个字,一眼认出是从手机来的;
175
+ - **署名**:`QQ 789:你说的那句`,跟群里那套一个样子。
176
+ - 用途:同一个会话里手机和电脑交替说话时,模型那边也能分清「这句是从 QQ 来的,还是我刚在电脑上打的」——
177
+ `source.kind` 在事件里是有的,但它不进模型的上下文,所以只能靠正文里带。
178
+ - 默认关,对现有用法零影响。
179
+ - 测试:新增三条(不标 / 前缀 / 署名),宿主半边与四套自测全过。
180
+
181
+ ## 1.1.9 — 2026-10-29
182
+
183
+ **补上商城要的「兼容范围」和权限说明**
184
+
185
+ - ✅ `package.json` 补了 `engines`:Node `>=22`、DSH `>=0.2.1-rc.2`;
186
+ 另外加了 `dsh.compatibility.dshReleases`,只声明实际跑过的那个版本(`0.2.1-rc.3`),不批发。
187
+ - ✅ README 新增一节「它要什么权限,边界在哪」:网络(只连你填的后端)、文件(只碰 `~/.dsh/` 两个文件 + 收图目录)、
188
+ 命令(只有你自己填了转写命令才跑)、凭据(令牌只用来连自己的后端),以及外部依赖和失败边界。
189
+ - 没有行为变化。
190
+
191
+ ## 1.1.8 — 2026-10-29
192
+
193
+ **群聊:能 @ 人了,冷却真的生效了,绑定能单独开关**
194
+
195
+ - ✅ **能 @ 别人**:回复里写到的 `@` 现在会变成真正的 @(OneBot 的 `at` 段),不再只是几个字。
196
+ - `@123456789`(数字 QQ 号)→ 直接 @ 那个号;
197
+ - `@昵称` → 去群成员表里找同名(缓存十分钟),找到且只有一个人叫这名才真 @,找不到就当普通文字,不会乱 @ 人;
198
+ - `@全体成员` / `@所有人` → 默认**不认**(怕一次把全群炸起来),要的话在设置里打开「能 @ 全体」。
199
+ - 🔧 **群聊冷却、每分钟上限以前对 @ 的话不生效** —— 群里一有人 @ 它,那两个数就等于没有,看起来就是
200
+ 「没有冷却、发言频率特别高」。现在 **@ 的话也排队**:不丢、不拒,只是晚几秒(多条会合并)再回。
201
+ - ✅ **每一条绑定有自己的开关**(设置 → 绑定,每行下面两个勾):
202
+ - **启用**:取消 = 这个群/私聊整个静音(不收也不发),配置留着,随时勾回来 —— 不用再删掉重建;
203
+ - **只听 @ 我的**(群才有):勾上 = 这个群平时不读,只有 @ / 引用它才进来。
204
+ - 测试:宿主半边 + 四套自测全过(新增 @ 拆段 7 条、冷却 1 条、绑定开关 2 条、界面 1 条)。
205
+
206
+ ## 1.1.6 — 2026-10-29
207
+
208
+ **群里的自我回声:机器人自己说的话又被转进去了**
209
+
210
+ - 🔧 开着「群里也听别人的话」时,机器人**自己发出去的回话**也被当成群友的话收了进来 ——
211
+ 它回一句、这句又触发一轮、再回一句,能一直套下去(issue #14)。
212
+ - ✅ 现在先拿自己的 QQ 号,群消息里**自己发的直接跳过**(不回、不攒、不占上下文)。
213
+ - 测试:宿主半边 143 条、其余四套全过。
214
+
215
+ ## 1.1.5 — 2026-10-29
216
+
217
+ **一条取不到的「合并转发的聊天记录」,把日志刷爆、还一直打 NapCat**
218
+
219
+ - 🔧 取不到内容时,整批转不进去 → 水位不推进 → 每 2 秒重来一遍,每一遍都再拉一次 `get_forward_msg`,
220
+ NapCat 一直报错、日志被同一个 id 刷屏。
221
+ - ✅ 现在按 id **记住「取不到」**(成功的照样每次重读,不拿旧文本冒充新内容),十分钟内不再重拉;
222
+ 同时那条消息照样带着一句 `[合并转发的聊天记录(内容取不到)]` 转进去,不会被静默丢掉。
223
+ - 测试:新增三条(第一次拉、下一跳不再拉、取不到也有话说),宿主半边 143 条全过。
224
+
225
+ ## 1.1.4 — 2026-10-29
226
+
227
+ **会话清单列不全:没在标签页里打开过的会话,列表里根本看不到**
228
+
229
+ - 🔧 `ctx.get('sessions').list` 在新版 DSH 里是**快照对象**(有 `.getSnapshot()`、本身不是函数),
230
+ 以前只认 `typeof list === 'function'`,于是那条路整个不执行,只剩「活着的 agent」兜底 ——
231
+ 一直用、但从没在这台机器上打开过的会话就永远列不出来。
232
+ - ✅ 现在三路都试:内存里的会话(老的 `list()` / 新的 `getSnapshot()`)、
233
+ 盘上的 `storages/session_projcache/sessions/*.json`(关掉标签页也还在)、最后才是 agent 兜底。
234
+ - ✅ 盘上那份顺手把**标题**也读出来;**子智能体的会话滤掉**(不然设置页会被几十个 subagent 淹掉)。
235
+ - 测试:新增三条(读得到盘上的、滤掉子会话、标题读出来),全部通过。
236
+
237
+ ## 1.1.3 — 2026-10-29
238
+
239
+ **「回复概率」以前对 @ 它的话根本不生效**
240
+
241
+ - 🔧 原因:默认「群里只听点了名的」= 能进来的几乎都是 @ / 引用它的消息,而这类批次**从来不掷骰子**,
242
+ 所以概率填 50、填 20,实际表现都是照回(只有 100 才"没差别")。
243
+ - ✅ 新增开关「@ 我的一定回」(默认开 = 保持原来行为)。关掉之后,「概率」连 @ / 引用 的话一起管 ——
244
+ 想让它少说话的人就能真的少说了。
245
+ - 测试:新增两条(开着必回 / 关掉也掷骰子),全部通过。
246
+
247
+ ## 1.1.2 — 2026-10-28
248
+
249
+ **人设加了个「现在就生效」按钮:改完不用等 QQ 来信**
250
+
251
+ - 🆕 以前人设是在「把 QQ 消息转进 DSH」那一步才挂上去 —— 所以改完开关/文字,得先去 QQ 发一句话才看得到效果。
252
+ 现在设置页有一个 **「现在就生效」**:把每条绑定的会话挨个挂一遍(开关关着 = 顺手撤掉),
253
+ 下面会写清楚哪几间生效、哪几间没开着。
254
+ - ✅ 新增路由 `POST /dsh-qq-bridge/persona-apply`(按钮打的就是它)。
255
+
256
+ ## 1.1.1 — 2026-10-28
257
+
258
+ **人设只走系统提示词:挂不上就不带,不再塞进聊天里**
259
+
260
+ - 🔧 1.1.0 留了一条兜底:挂不上系统提示词就把人设塞进消息最前面。这条**删了** ——
261
+ 那段字会留在对话历史里越堆越多,而且只对 QQ 转进来的消息生效(自己打字时没有),
262
+ 同一个会话两个渠道口吻会不一样。
263
+ - ✅ 现在只有一条路:挂到那间会话的**系统提示词**上(挂完会验一次装配里看不看得见,
264
+ 带 `scope` 才看得见 —— 这是这个 DSH 版本的正确用法)。挂不上就一个字都不带,记录里写一句。
265
+ - ✅ 界面上那句「每轮都要多花这些(1 个字 = 1 个 token)」也删了 —— 走系统提示词时它是稳定前缀,
266
+ 命中缓存,不是每轮全价,写那句会误导人。改成「几句话就够,太长会把它注意力拽到这些字上」。
267
+ - 测试:攒条里那两条人设用例改成「不管挂不挂得上,消息里都不出现人设字」。
268
+
269
+ ## 1.1.0 — 2026-10-28
270
+
271
+ **人设(选填):可以让某个群 / 某个私聊单独带一份**
272
+
273
+ - 🆕 「绑定」页最上面多了「人设(选填)」:一个开关 + 一段字,**关着就完全不带**。
274
+ - 🆕 每一行绑定还能**单独写一份**(表格里「单独」那一列)—— 那个聊天用自己的,没写的用上面那份。
275
+ - 做法:优先挂到那间会话的**系统提示词**上(不占对话、能走缓存、也不像每轮都在下命令);
276
+ 拿不到那个服务时退回来,把这段字放进转进去的消息最前面,并在记录里写一句是哪种方式。
277
+ - 改人设随时生效:撤掉旧的、挂上新的,下一轮就按新的说话(代价是那一段缓存要重算一次)。
278
+ - 新增诊断路由 `/dsh-qq-bridge/persona-probe`:只看「到底能不能挂上系统提示词」。
279
+ - 测试:网页半边点进「绑定」页也验了(人设卡 + 那一列在);攒条新增两条(人设随消息带上 / 关掉就不带)。
280
+
281
+ ## 1.0.9 — 2026-10-28
282
+
283
+ **群聊不再一句一句回:先攒一攒,再给个「隔多久 / 一分钟几条」**
284
+
285
+ - 🐛 群里每来一条就转进去起一轮,人多的时候像刷屏,也一直在烧上下文。
286
+ - ✅ 群里**换人也照攒**:一波人连着说的几句合成一条送进去,每行前面写清「昵称(QQ号):」,
287
+ 不会认错人;攒够「一次最多攒几条」立刻送,不用干等窗口。
288
+ - ✅ 节奏只有**两个数**(旋钮多了反而没人看得懂):
289
+ · **两条之间至少隔几秒**(默认 5)—— 时间里群里新的话先攒着,到点一起回;
290
+ · **一分钟最多回几条**(默认 4)—— 撞上了这批就等着,等一分钟窗口腾出位置再送,**等不丢**。
291
+ - ✅ @、引用 不受这两个数限制。
292
+ - ✅ **不是每句都接(概率 %)**:群里的闲聊不一定每句都理。**没掷中的那批根本不送进来** ——
293
+ 不进 DSH、不占上下文、不花 token,跟「送进来再判不接」(模型想一遍再停)是两回事;
294
+ 大群里上下文不会被人家的闲聊塞满。默认 100(每批都接),@ 和引用照旧必接。
295
+ - ✅ 设置放在「绑定」页「+ 加一条」下面,单独一张「群里怎么回(对所有群生效)」。
296
+ - 测试:宿主半边、网页半边、攒条、生命周期、单飞全过(新增 5 条:冷却挂住不丢、一分钟上限挂着等窗口、
297
+ 跨人合并且名字都在、概率 0 整批跳过、概率 100 照接)。
298
+
299
+
300
+ ## 1.0.8 — 2026-10-27
301
+
302
+ **入站消息改成一条道:不会重复投、不会乱序、停用也停得干净**
303
+
304
+ - 🐛 以前「普通轮询」和「攒条补跑」是**两个入口**,可能同时在跑:同一时刻两条 followup 挤进去,
305
+ 消息顺序会乱,同一条也可能被投两次。
306
+ - ✅ 现在收消息是**单飞**(同一时刻只有一个在跑),两个入口共用同一个起点。
307
+ - 🐛 插件停用、或者关掉总开关时,在途的那一轮只在「还没受理」时被中止;
308
+ 已经交给宿主的 followup **不给读超时**——给超时就会半路解锁,同一条消息再投一遍。
309
+ - ✅ 卸载时的「迟到成功」只追加自己那批已转 id,不覆盖新实例的进度;水位按磁盘上最近一次成功记录合并。
310
+ - ✅ 顺手读回自己的 QQ 号(`get_login_info`),给「自己发的消息被当成别人说的」留出过滤依据。
311
+ - 测试:新增 `tests/inbound-singleflight.mjs` / `tests/inbound-lifecycle.mjs` / `tests/inbound-batching.mjs`;
312
+ 语音回执那条测试改成**等事件**而不是固定延时(`QQ_TEST_ASR_DELAY_MS` 可调),慢机器上不飘。
313
+
314
+ > 这一版来自 [@saigyujikingyo-png](https://github.com/saigyujikingyo-png) 的 PR #10 和 PR #11,谢谢。
315
+
316
+ ## 1.0.7 — 2026-10-26
317
+
318
+ **QQ 消息不会再「卡一分钟」才进来**
319
+
320
+ - 🐞 收消息这一路原来有**两个请求没有超时**(拉历史、拉合并转发内容)。QQ 端(NapCat / SnowLuma)
321
+ 掉线重连、或者接口一时卡住时,这两个请求会一直挂着,**整条轮询也跟着挂在那儿**,
322
+ 要等它回来才轮到下一跳 —— 表现就是「QQ 发过去要等一分钟,DSH 才收得到」。
323
+ - ✅ 现在超过 **8 秒**就掐掉,当「这次没数据」,下一跳(默认 2 秒)接着拉;
324
+ 读「登录号」那个请求用 5 秒超时。
325
+ - ✅ 顺手记一笔:一次收消息超过 **3 秒**就在运行记录里写一行,能看出「QQ 端回得慢」到什么程度。
326
+ - 测试:宿主半边 / 网页半边全过。
327
+
328
+ ## 1.0.6 — 2026-10-26
329
+
330
+ **连不上时的提示改成「QQ 机器人(NapCat / SnowLuma)」**
331
+
332
+ - 设置页那个 `⚠ 连不上 NapCat` 只写了 NapCat,换成 SnowLuma 之后看着像在说别人 ——
333
+ 现在两个名字都写上,后面照旧跟具体原因(`fetch failed` 这类)。
334
+ - 「多久拉一次新消息」下面那句说明也改成「请求 QQ 机器人更勤」。
335
+
336
+ ## 1.0.5 — 2026-10-26
337
+
338
+ **重启之后「看到哪儿了」终于读得回来了**
339
+
340
+ - 🐛 启动时先调「读水位」,而它用的那张表要到下面几行才声明 —— 直接报错,又被外层空 `catch` 吞掉。
341
+ 结果磁盘上记着的「每个聊天看到哪儿了 / 哪些消息已经转过」**从来没被读回来过**:
342
+ 重启后要么从很早的位置重新补,要么把已经转过的消息再转一遍。
343
+ - ✅ 把表的声明提到读取之前,恢复照常进行(解读失败的旧文件也不会影响启动)。
344
+ - 测试:`node tests/宿主半边.mjs` 新增 3 项「模拟重启」断言(收到启动前未处理的新消息 / 同水位的已转 ID 不重投 /
345
+ 水位之前的旧消息不重投),全过。
346
+
347
+ ## 1.0.4 — 2026-10-26
348
+
349
+ **`[QQ]` 标记不会再发到手机上**
350
+
351
+ - 🐛 手机上收到过一条开头写着 `[QQ]` 的消息。以前只有开着「只发标记内」时才剥离标记;
352
+ 关着的时候,正文里出现的 `[QQ]` / `[/QQ]` 会原样发出去。现在**不管开关如何,标记都剥掉**。
353
+ - 🐛 开着「只发标记内」、但只写了 `[QQ]` 忘了写 `[/QQ]` 时,以前这一轮**一条都不发**(手机什么都收不到);
354
+ 现在按「`[QQ]` 之后全是想发的」发出去,并在日志里说一句。
355
+ - 🔧 抽成纯函数 `抠QQ标记(文字, 只发标记内)`,自测能直接断言(5 项:开关关着剥标记 / 完整块只发块内 /
356
+ 忘了闭合照发 / 没写块不发 / 带空格的标记也剥)。
357
+ - 自测:宿主半边 **134 项**全过。
358
+
359
+ ## 1.0.3 — 2026-10-26
360
+
361
+ **两处「转进来就看不全」修好了:嵌套转发能看更深、大表情不再整条消失**
362
+
363
+ - 🐛 转发里套转发:以前**只展开两层**,第三层起只剩一句「[里面还套了一层转发(不再展开)]」。
364
+ 现在展开到第 4 层;实在套得太深时,会写清「[里面还有 N 条转发,套得太深没展开]」,不再干截断。
365
+ - 🐛 大表情(表情商店 / 收藏里的大图表情)没有 `url` 时,以前整条会被当成「没字也没图」丢掉。
366
+ 现在至少留一句 `[表情:…]`,让人看得出对方发了个表情。
367
+ - 🐛 有图但一张都没取到(格式不支持 / 下载失败)时,也会说一句「发来 N 张图,但一张都没取到」——
368
+ 以前这种情况整条消息也会消失。
369
+ - ✅ 部分图失败时会补一句「另有 N 张没取到」,不再悄悄少几张。
370
+ - 自测:宿主半边 **129 项**全过(新增 6 项:三层转发可见 / 套太深带条数 / 大表情不丢 / 大表情留占位 / 图全失败不丢 / 说清没取到)。
371
+
372
+ ## 1.0.2 — 2026-10-26
373
+
374
+ **连着发的几条攒成一条再转进来(发一串图不会一张一张回)+ 收消息更快**
375
+
376
+ - 🆕 新增 `合并窗口秒`(默认 **1**,0 = 关):同一个人在这段时间内连着发的消息(比如一次发好几张图、或者连着打三四句),
377
+ 会在 DSH 这边**合成一条**再转进去 —— 它只回一次,不再一条一条回。
378
+ - 🆕 新增 `合并最多秒`(默认 20):有人一直发个不停时,到点也先转进来,不会无限等。
379
+ - 🆕 新增 `轮询间隔毫秒`(默认 **2000**,原来写死 4 秒):新消息进来得更快。
380
+ - ✅ 攒消息时**不会干等**:发现「最后一条刚发完」,会补跑一跳(约 +1.2 秒内落地),不等下一轮。
381
+ 实际感受:一串图 → 只回一次;单条消息 → 一般 1~3 秒内进来。
382
+ - ✅ 只有「刚发完的那一批」才等:已经过去一会儿的消息**立刻转**,不会因为开了合并就每条都慢半拍。
383
+ - ✅ 私聊、群都管用;群里**换人说话就不合并**(各人各一条),免得把两个人的话搅在一起。
384
+ - 🆕 设置页「收进来的样子」里能调这三个数。
385
+ - 自测:宿主半边 **123 项**全过(新增 5 项:三条攒成一条 / 内容都在 / 刚发完先不转 / 窗口过了补转 / 0 = 不攒)。
386
+
387
+ ## 1.0.1 — 2026-10-25
388
+
389
+ **「只回 QQ 唤醒」不会再漏掉同一次触发的后半段(issue #6)**
390
+
391
+ - 🐛 用户报的:一次 QQ 消息触发后,如果它在同一次触发里说了**两段话**,第二段只留在 DSH、QQ 收不到。
392
+ - 🔍 真因:唤醒标记在**第一轮 `turn/end` 就消费掉了**,所以只有紧跟着的那一轮能往外发。
393
+ - ✅ 改成「按一次触发算」:真被 QQ 唤醒的那一轮发完,还留一条**尾巴**(默认 90 秒,`唤醒尾巴秒` 可配,0 = 关掉)——
394
+ 尾巴内的后续轮次也算「QQ 起的头」,照常往外发。
395
+ - ✅ 老坑堵住:**你自己在这间会话里打了字**(`source.kind = 'user'`)→ 尾巴立刻清掉,
396
+ 不会出现「群里一条消息叫醒一次,后面十轮全推到手机」。
397
+ - 自测:宿主半边 **31 项** 仍全过;新增专项测试 5 项(第一段/第二段/清尾巴后不发/真人输入清尾巴/尾巴关闭回老行为)。
398
+
399
+ ## 1.0.0 — 2026-10-25
400
+
401
+ **设置页不再可能「永远读取中…」**
402
+
403
+ - 🆕 连 NapCat 探活带超时(默认 6 秒),超时按「没连上」处理,不会再把整个 `/state` 挂死。
404
+ - 🆕 `/state` 出任何意外都兜底返回:版本、配置、日志照给,界面不会空白;同时写一行日志(`设置页读状态时出错(已用兜底数据):…`),错误不被静默吞掉。
405
+ - ✅ 收发消息那条路一行没改。
406
+ - 自测:宿主半边 **117 项**(新增 4 项:模拟 NapCat 不回应、`/state` 必须 1 秒内回来、兜底带版本与配置、兜底说明原因)+ 网页 4 项。
407
+ - 📌 版本规矩照旧:每次改版 +0.0.1,满十进位 —— `0.9.9` 的下一个就是 **1.0.0**。
408
+
409
+ ## 0.9.9 — 2026-10-25
410
+
411
+ **「群里 @ 它 不理人」修好了;@ 和引用一律直接回,不再让模型自己犹豫**
412
+
413
+ - 🐛 用户反馈:在群里 @ 大肥鱼(或引用它说的话),它不理人。
414
+ - 🔍 真因有两个:
415
+ 1. **群里别人的消息根本没转进来** —— 以前只收「你自己发的」,别人 @ 它、引用它的消息在过滤那一步就被丢了,门槛连判的机会都没有。
416
+ 2. 就算转进来了,门槛还要模型自己判「是不是在跟我说话」,它一犹豫就判 false → 整轮停掉。
417
+ - ✅ 现在:
418
+ - 群里别人的消息,**只要点了名就收**(@ 了我 / 引用的是我发过的消息;@全体不算);
419
+ - **点了名的那一轮,门槛直接放行**,不让模型犹豫;
420
+ - **私聊永远不判**(调了也直接放行,门槛只对群有效);
421
+ - 想收全群的话,新开关「群里也听别人的话」(默认关)打开,交给门槛判。
422
+ - ✅ 门槛提示词也改了:@ 你 / 引用你 → 不用调工具直接回;拿不准就按「回」走。
423
+ - 自测:宿主半边 **113 项**(新增 @ / 引用 / 私聊放行 / 群里闲聊不转 等)+ 网页 4 项。
424
+
425
+ ## 0.9.8 — 2026-10-25
426
+
427
+ **⚠️ 严重:门槛工具的 parameters 写错格式,导致整间会话每一轮都 INVALID_REQUEST**
428
+
429
+ - 🐛 她 20:04 发来的截图:`Invalid schema for function 'qq_reply_gate': schema must be a JSON Schema of 'type: "object"', got 'type: null'`,连着好几轮「本轮运行失败」——**一句话都说不出去**。
430
+ - 🔍 真因:0.9.2 补 `output` 的时候,`parameters` 还是「属性表」写法(`{ reply: {...}, reason: {...} }`)。别的插件没踩,是因为它们用 `hostDeps.defineTool` 会把属性表转成 JSON Schema;这里直连 `ctx.tools.register`,**属性表被原样交给上游,schema 就成了 null**。
431
+ - ✅ 改成完整 JSON Schema:`type: 'object'` + `properties` + `required: ['reply']` + `additionalProperties: false`。
432
+ - ✅ 自测加 4 项,专门盯这个:parameters 必须是 object schema、属性齐、required 是数组、属性上不许再写 `required: true`。
433
+ - 自测:宿主半边 **103 项** + 网页 4 项。
434
+
435
+ ## 0.9.7 — 2026-10-25
436
+
437
+ **设置页「试一句」老是说「这间会话没绑任何 QQ 聊天」—— 修好了**
438
+
439
+ - 🐛 用户反馈(也有人提过):点「试一句」,日志里永远是「这间会话没绑任何 QQ 聊天,这一轮不发出去」,一条测试消息都发不出去,看着像绑定坏了。
440
+ - 🔍 真因:0.6.0 之后发送要按「方向 = 哪间会话」去绑定里反查聊天,而**手动测试这条路根本没有会话可反查** —— 方向是空的 → 反查不到 → 直接判「没绑」,返回空。绑定本身没错。
441
+ - ✅ 修法:「试一句」改成**按绑定直接发**:没指定就发第一条绑定,回话里说清发给了谁(「测试句已经发到 私聊 123 了」)。
442
+ - ✅ 没绑定时不再报那句吓人的话,改成「还没绑定任何聊天 —— 去「绑定」页加一条」。
443
+ - ✅ 真实转发(会话里说话 → QQ)一直是好的,这次只动手动测试那条路。
444
+ - 自测:宿主半边 **99 项**(新增 4 项)+ 网页 4 项。
445
+
446
+ ## 0.9.6 — 2026-10-25
447
+
448
+ **「一次回一堆消息」变成可配置:拆段开关 + 间隔**
449
+
450
+ - 🐛 用户反馈:它一条回复会在几秒内连着发好几条。真因不是 streaming(本插件没有),而是**长回复按空行拆段连发**,而且**拆段和 450ms 间隔都是写死的**。
451
+ - ✅ 设置页「发消息」多一块「发出去的样子」:
452
+ - **长回复拆成几条发**(默认开):关掉就**一整段只发一条**,不会刷屏;
453
+ - **每段之间隔(毫秒)**(默认 450,可 0–5000):只在拆段开着时有效。
454
+ - 自测:宿主半边 **95 项**(新增 2 项:关掉只发一条、开着拆成 3 条)+ 网页 4 项。
455
+
456
+ ## 0.9.5 — 2026-10-25
457
+
458
+ **门槛给了你一个选择:「只拦话」还是「真停轮」**
459
+
460
+ - 背景(作者在手机上实测):`Error: tool call aborted` **正是「停掉这一轮」的表现** —— 它一出现,这一轮思考就结束了。所以那不是 bug,是机制。
461
+ - 但她那边只要一触发就出问题(只能关插件才好),所以现在做成**两个都给你,默认用不折腾的那个**:
462
+ - **默认(关)**:判定「不接」→ 只把这一轮的话拦住、不发 QQ,界面干干净净,模型会把这一轮想完(费一点 token)。
463
+ - **打开「判定不接时:真把这一轮停掉」**:调 `agent.cancel()` 真停 —— 省 token,但界面上会显示一次 `Error: tool call aborted`(那是停轮的表现)。
464
+ - 设置位置:**提示与门槛** 页签。
465
+ - 自测:宿主半边 **93 项**(新增 2 项:真停轮开着会 cancel、关着不会)+ 网页 4 项。
466
+
467
+ ## 0.9.4 — 2026-10-25
468
+
469
+ **门槛不再「停轮」,改成「这轮不往外发」—— 手机上看不到 `tool call aborted` 了**
470
+
471
+ - 🐛 手机实测(作者截图):判定「不接」时会调 `agent.cancel()`,结果界面上是
472
+ **「工具调用 · Error: tool call aborted」+「已重试模型请求 (1/5)」**,看着像插件坏了、还白发一次模型请求。
473
+ - ✅ 现在**不取消**:工具只记下「这一轮不往 QQ 发」,返回一句「这轮不接,到此为止」;等这一轮正常结束,出站那一步直接跳过。
474
+ - 界面上不再有 aborted / 重试;
475
+ - 效果一样:**一个字都不会发到 QQ**,会话里照常留着记录。
476
+ - 自测:宿主半边 **90 项**(门槛那几项改成验「不 cancel + 那一轮零发送」)+ 网页 4 项。
477
+
478
+ ## 0.9.3 — 2026-10-25
479
+
480
+ **门槛:`reply` 传成字符串 `"false"` 也认(以前会当成"正常回")**
481
+
482
+ - 🐛 实测踩到:模型有时把布尔传成字符串(`"false"`),而代码是严格 `args.reply !== false` —— 于是明明判了「不接」,却走了「正常回」那一支,门槛等于没生效。
483
+ - ✅ 现在 `false` / `"false"` / `0` / `"0"` 都算「不接」。
484
+ - 自测:宿主半边 **90 项**(新增 1 项:字符串 "false" 也能停掉这一轮)。
485
+
486
+ ## 0.9.2 — 2026-10-25
487
+
488
+ **门槛工具终于真挂上了(缺 `output` 被 DSH 拒绝);群消息标「谁发的」**
489
+
490
+ - 🐛 **回复门槛一直没生效**:`qq_reply_gate` 缺了 `output` 声明,DSH 直接拒绝注册
491
+ (日志里是那句 `tool "qq_reply_gate" must declare output { schema, render, presentationMeta? }`),
492
+ 于是面板上看着门槛开着,实际它连工具都没有。现在补上了。
493
+ - ✅ **群消息标明是谁发的**:以前转进来的只有正文,看不出哪个人说的。现在群里会写成
494
+ `昵称(QQ号):正文`(私聊不用,就对面一个人)。
495
+ - 自测:宿主半边 **89 项**(新增 2 项:群消息带署名、门槛工具声明了 output)。
496
+
497
+ ## 0.9.1 — 2026-10-25
498
+
499
+ **修两个「明明绑了群,群里却收不到、门槛像没生效」的暗坑**
500
+
501
+ - 🐛 **群里收不到它的回复**:老的两个开关「转私聊 / 转群」(界面早就不显示了)还在拦发送,而它们默认是 `false` —— 于是加了群绑定、但群里一条都收不到。现在**有那一行就代表要发**:这两个值从绑定反推。
502
+ - 🐛 **「是不是 QQ 叫醒的」原来是一个全局时间戳**:A 群刚被叫醒,B 群那一轮也会被当成 QQ 叫的(10 分钟窗口内一直成立)。现在改成**按会话记 + 那一轮结束就消费掉**,一轮只算一次。
503
+ - 说明:门槛(`qq_reply_gate`)只在「这条明显不是冲它来的」时候才会被调用 —— 你在群里发「测试测试」这种,它判断是跟自己说话,就不会关闸,**这是正常的**。
504
+ - 自测:宿主半边 **87 项**(新增 1 项:有群绑定就能发,老开关不再拦)+ 网页 4 项。
505
+
506
+ ## 0.9.0 — 2026-10-25
507
+
508
+ **文案改成大白话:「手机只收人话(不发代码/路径)」**
509
+
510
+ - 上一版的说明是「只发 [QQ] 块里的内容」,作者自己看了都说看不懂 —— 改成直接讲**会发生什么**:只有 `[QQ]…[/QQ]` 中间那句会到手机,代码/路径/分析都留在电脑上。
511
+ - 开关名也换了:**「手机只收『人话』(不发代码/路径)」**。
512
+ - 行为没变(默认仍然关)。
513
+
514
+ ## 0.8.9 — 2026-10-25
515
+
516
+ **「只发 [QQ] 块」补上一句约定说明(以前开了它会一条都发不出去)**
517
+
518
+ - 🐛 这个开关以前只做「过滤」,**没告诉它要写 `[QQ]…[/QQ]`** —— 结果一打开,它根本不写块,于是什么都不发(日志里只有一句「没写块,这轮不往 QQ 发」)。开着的人只会觉得"坏了"。
519
+ - ✅ 现在只要开着这个开关,转进去的消息末尾会带一句:只把正文里 `[QQ]…[/QQ]` 中间那段发到 QQ,块外的只在本机看。
520
+ - 自测:宿主半边 **86 项**(新增 1 项:开着时消息里带这个约定)。
521
+
522
+ ## 0.8.8 — 2026-10-25
523
+
524
+ **语音模型从「999MB 整包」改成「只下要用的两个文件」(约 230MB)**
525
+
526
+ - 🐛 上一版让用户下了 **999MB**:官方那个 `tar.bz2` 把 fp32 模型也打包进去了,而我们只需要 int8 的 `model.int8.onnx` + `tokens.txt`。
527
+ - ✅ 现在按文件下(HuggingFace 单文件,另配 `hf-mirror.com` 兜底):
528
+ - `model.int8.onnx`(约 230MB)
529
+ - `tokens.txt`(约 1MB)
530
+ 下完其中一个就跳过它(**断了下再点会接着**,不重下已完成的);先写 `.part` 再改名,避免半截文件被当成完整的。
531
+ - ✅ 进度条上会写明**正在下哪个文件**:「正在下 model.int8.onnx:已下 87MB / 230MB(38%)约 1.2MB/s」。
532
+ - 自测:宿主半边 **85 项** + 网页 4 项。
533
+
534
+ ## 0.8.7 — 2026-10-25
535
+
536
+ **下载语音模型有进度条了(下了多少、多快,一眼看见)**
537
+
538
+ - 🐛 上一版下 230MB 模型时**只有一个「开始下载」** —— 用户不知道下了多少、卡没卡、还要多久。
539
+ - ✅ 改成**边下边写 + 实时报进度**:插件页显示进度条 + 「已下 87MB / 231MB(38%)约 1.2MB/s」;每 16MB 还会往安装日志里记一行。
540
+ - ✅ 状态接口多了 `下载`(已收/总/百分比/速度)和 `模型就绪 / 模型目录` 两个字段。
541
+ - 自测:宿主半边 **85 项**(新增 2 项:状态里带下载进度、带模型就绪/目录)+ 网页 4 项。
542
+
543
+ ## 0.8.6 — 2026-10-25
544
+
545
+ **语音模型也自动下:别人不需要知道「模型目录」填什么**
546
+
547
+ - 🐛 上一版要求手填「语音模型目录」—— 不现实:别人装插件怎么会知道去哪找模型、填什么路径。填不对就报「没给模型目录」。
548
+ - ✅ 现在「自动装依赖」一条龙:找 Python → 建独立环境 → 装 `sherpa-onnx` + `numpy` + `imageio-ffmpeg` → 自检 → **下载语音模型(约 230MB,SenseVoice 多语种 int8)** → 解压 → **自动把「转写命令」和「语音模型目录」写进配置**。
549
+ - ✅ 每一步都有 ✓ / ⋯ / ✗ 和日志;模型没下完会明说「再点一次会接着下」(环境不用重装);已经有模型就跳过。
550
+ - ✅ 「语音模型目录」那栏保留,但改成「一般不用管」——只有想换自己模型(whisper 之类)时才需要改。
551
+ - ✅ 新增 `docs/语音识别.md` 说明整条链路和目录结构。
552
+ - 自测:宿主半边 **83 项** + 网页 4 项。
553
+
554
+ ## 0.8.5 — 2026-10-25
555
+
556
+ **「我没发消息,它却自己回了一条」的真凶:水位只存在内存里,重启就归零**
557
+
558
+ - 🐛 以前「看到哪儿了」(每个聊天读到哪条)+「已转过的消息 id」**都只存在内存**。插件一重启(改插件、换版本都要重启),这两样就清零 ⇒ 最近那十几条**历史**消息被当成新消息重投一遍 ⇒ 触发一轮自动回复。她根本没发新消息,所以看着像闹鬼。
559
+ - ✅ 现在落盘到 `qq-bridge-state.json`:重启后**接着上次的水位**继续,不重投。
560
+ - ✅ 另外:**第一次见到某个聊天**(刚加绑定 / 第一次跑)时,水位从**此刻**开始算,不把历史当新的。
561
+ - ✅ 顺带把「已转 id」超 300 个时的 `clear()` 改成只丢最老的 —— 以前 clear 之后下一跳又可能把旧的当新的。
562
+ - 自测:宿主半边 **83 项**(新增 2 项:水位会落盘、没记录过的聊天不补投历史)。
563
+
564
+ ## 0.8.4 — 2026-10-25
565
+
566
+ **语音多了「模型目录」一栏;旧消息不再补投(一打开会话就冒一条回复的那个)**
567
+
568
+ - ✅ 语音转写多一栏 **「语音模型目录」**(你自己 SenseVoice 模型所在目录,要有 `model.int8.onnx` + `tokens.txt`)。「把命令填好」会把它一起写进命令 —— 之前漏了这一步,才会报「没给模型目录」;没填的时候也会直接提醒你。
569
+ - 🐛 **一打开会话就自动冒出一条回复**:会话关着的时候,QQ 那边攒下的消息会一直等(0.7.1 那个「不丢消息」);她一打开会话,这些旧消息就一次性补投进去 ⇒ 触发一轮自动回复。现在加了**补投窗口(默认 10 分钟)**:超过就只记账、不补投。
570
+ - 自测:宿主半边 **81 项**(新增 1 项:超窗口的旧消息不补投)+ 网页 4 项。
571
+
572
+ ## 0.8.3 — 2026-10-25
573
+
574
+ **修「语音转写失败:找不到音频文件」——QQ 给的是网络地址,得先下下来**
575
+
576
+ - 🐛 实测报错:`[语音(转写失败:找不到音频文件:https://multimedia.nt.qq.com.cn/download?appid=…)]`。
577
+ 真因:QQ 语音段里的 `file` 经常是**一个网络地址**(不是本地路径),以前直接把它丢给转写命令,命令当然找不到文件。
578
+ - ✅ 现在分三步落地,才交给命令:
579
+ 1. 先问后端 `get_record`(它能自己解 silk 并直接给 wav)——拿到本地路径或 base64 就落盘;
580
+ 2. 拿不到就把这个地址**下载下来**,放进 `qq-bridge/audio/`(按扩展名存 amr/silk/mp3/wav);
581
+ 3. 不是 wav 的话再用 **ffmpeg** 转成 16k 单声道 wav —— ffmpeg 找不到就用「装依赖」时装的那份(`imageio-ffmpeg` 自带静态 exe),都没有就原样交给命令。
582
+ - ✅ 顺带:跑转写命令时补了 UTF-8 环境(不然 python 往管道打中文会乱码,就是她刚才看到的那串「�Ҳ�����Ƶ�ļ���」)。
583
+ - ✅ 「装依赖」现在会把 `imageio-ffmpeg` 一起装上(多几 MB,换一个必定能用的 ffmpeg)。
584
+ - 自测:宿主半边 **80 项**(新增 2 项:file 是网络地址时先下载再转写、下载的音频真的落在 audio 目录)。
585
+
586
+ ## 0.8.2 — 2026-10-25
587
+
588
+ **门槛去掉「总开关」:只认每一行自己的勾 —— 不会再出现「开了却不生效」**
589
+
590
+ - 🐛 上一版有两个开关管同一件事:一个「回复门槛」总开关 + 绑定表里每行的「门槛」。结果很容易变成「我在绑定里给群打了勾,但总开关没开 → 一点效果都没有」,看着就是坏了。
591
+ - ✅ 现在**只有每行那个勾**:想给哪个群开,就去「绑定」页签给那一行打勾;别的群照常;私聊永远不判。
592
+ - ✅ 老配置自动搬:以前总开关是开的 → 那几个群行自动补上勾,不用重设。
593
+ - ✍️ 「提示与门槛」页签里那段说明也改了:不再讲总开关,直接告诉你去哪儿打勾。
594
+ - 自测:宿主半边 **78 项**(门槛相关的 6 项全部改成按行判)+ 网页 4 项。
595
+
596
+ ## 0.8.1 — 2026-10-25
597
+
598
+ **语音识别的依赖改成「插件页里点一下自己装」:看得见进度、看得见报错**
599
+
600
+ - ✅ 之前只给了「填一条命令」的框 —— 等于把装环境这事丢给用户(或者让助手替他装)。现在插件页里多一块:
601
+ **「自动装依赖」按钮 + 步骤进度 + 安装日志**。
602
+ - ✅ 点一下会在 `~/.dsh/qq-bridge-asr/` 建**独立环境**(不碰系统 Python),按步骤走:找 Python → 建 venv → 装 sherpa-onnx + numpy → 自检 import;每步都显示 ✓ / ✗ / ⋯,失败会写明卡在哪一步。
603
+ - ✅ 装的过程中**每 2 秒看一次进度**,日志实时更新(只看最后几行);装完点「把命令填好」,自动把「语音转写」打开、把命令写进配置。
604
+ - ✅ 请求立刻返回、装依赖在后台跑(装几分钟也不会把设置页卡住;关掉页面再回来日志还在)。
605
+ - 自测:宿主半边 **78 项**(新增 2 项:`/asr-status` 回状态、命令模板里带 asr_cli.py 和 {file})+ 网页 4 项。
606
+
607
+ ## 0.8.0 — 2026-10-25
608
+
609
+ **语音转写:QQ 语音条 → 文字(插件自己不带模型,交给你的命令)**
610
+
611
+ - ✅ 新增「语音转写」(默认关):QQ 里的语音条不再只是一个 `[语音]`,而会变成「(语音)他说的那句话」—— 有人给你发语音,它也能读到内容。
612
+ - ✅ 插件**不塞任何模型**(不然包会变成几百 MB):它只做三件事 —— 拿到音频(优先用 NapCat `get_record` 转成 wav)→ 交给你在设置里填的命令 → 拿回 stdout 那行文字。本机 whisper、SenseVoice、云端 API 都能接。
613
+ - ✅ 命令模板里写 `{file}`,会替换成音频路径;**非 0 退出码 / 超时**都按「转写失败」处理,但**不会吞掉整条消息**(会显示 `[语音(转写失败:原因)]`,同一条消息里的文字照常转进去)。
614
+ - ✅ 仓库里给了现成接法:`tools/asr_cli.py`(本机 SenseVoice + sherpa-onnx,离线免费)。
615
+ - 自测:宿主半边 **76 项**(新增 3 项:开着能变文字、关着还是 [语音]、命令失败不吞消息)。
616
+
617
+ ## 0.7.1 — 2026-10-25
618
+
619
+ **那间会话当时没开着,消息不会丢;措辞也改掉了「主人」**
620
+
621
+ - 🐛 以前:绑的那间会话**当时是死的**(没打开过 / 被关了),这条消息会被当成「已经转过了」记掉 —— 你一会儿把它点活,那条也补不回来了。现在**真转进去了才算数**,没转进去就下一跳(4 秒后)自己再试一次,直到那间屋活过来。
622
+ - ✅ 顺带不会再「换到别的会话」:每行严格只认自己绑的那间,死了就是等着,不会跑偏。
623
+ - ✍️ 文案里「主人私聊就是在跟它说话」改掉了 —— 两个人是平等的,「主人」这种说法不该写死在界面里。
624
+ - 自测:宿主半边 **73 项**(新增 2 项:死着时不丢、点活后自动补上)。
625
+
626
+ ## 0.7.0 — 2026-10-25
627
+
628
+ **出站两个闸:只发 `[QQ]` 块里的内容 / 只回被 QQ 叫醒的那一轮**
629
+
630
+ (来自用户 GAME-66 的建议,issue #2;两条都**默认关**,老用户升级后行为不变。)
631
+
632
+ - ✅ **只发 `[QQ]` 块里的内容**:开着时,它只在正文里写 `[QQ]…[/QQ]` 那段才发到 QQ;块外的解释、思路、文件路径全留在本地 —— 手机不再被整段推理刷屏。
633
+ - ✅ **只回 QQ 叫醒的那一轮**:开着时,**你在 QQ 问一句,它回一句**;你坐在 DSH 界面里自己聊的,不推到手机。判定用的是「这一轮是不是被 QQ 消息叫醒的」,一次性生效。
634
+ - 自测:宿主半边 **71 项**(新增 4 项:块内只发那段 / 没写块就不发 / 非 QQ 唤醒不推 / QQ 唤醒正常回)。
635
+
636
+ ## 0.6.0 — 2026-10-25
637
+
638
+ **绑定改成一列一行:一个群 / 一个私聊 → 一间会话;设置页分四个页签**
639
+
640
+ - ✅ **绑定列表**:每一行是「类型(私聊/群)|号码|对应会话|称呼|门槛」。想绑几个群就加几行,各回各的,不再挤进同一间会话。
641
+ - ✅ **挡住了会串的那种配置**:同一间会话只能绑一条、同一个聊天也只能绑一条 —— 保存时自动挡掉,并告诉你挡了几条(以前两个群挤一间屋,回话就会串)。
642
+ - ✅ **门槛按行走**:只有「群」那行能开门槛;**私聊永远不判**(你自己私聊,本来就是在跟它说话,不用先判断)。
643
+ - ✅ **设置页分四个页签**:总览 / 绑定 / 发消息 / 提示与门槛 —— 原来 8 张卡片堆一页确实太乱。
644
+ - ✅ **老配置自动搬家**:以前的「私聊会话 + 群会话 + 私聊QQ + 群号 + 称呼」自动变成两行,不用手填。
645
+ - 自测:宿主半边 **66 项**(新增 6 项:老配置搬家、两行各自类型、两个群各发各的群号、私聊不挂门槛等)+ 网页 4 项。
646
+
647
+ ## 0.5.0 — 2026-10-25
648
+
649
+ **回复门槛:不是跟我说话,就别接——这一轮直接停**
650
+
651
+ - ✅ 新增开关「**回复门槛**」(默认关)。开着时:QQ 消息照样转进来、照样叫醒它,但转进去的文案末尾会带一句提醒 ——
652
+ 先判断这条是不是在跟它说话;不是就调 `qq_reply_gate{ reply: false }`,**这一轮当场停掉**,什么都不回 QQ。
653
+ - ✅ 停的方式是 `agent.cancel({ kind: 'user' })`(跟她自己按「停止」同一条路),所以这一轮的 `turn/end` 不是 `completed`,
654
+ 我们本来就「只在 completed 时回发」⇒ **一个字都不会漏到 QQ**,不用另写抑制逻辑。
655
+ - ✅ 门槛工具**只在「被 QQ 叫醒后的 10 分钟内」有效**:她自己坐在 DSH 界面里聊天时,模型就算想关闸也关不掉(不会把自己的一轮聊没)。
656
+ - ✅ 工具是**按会话挂**的(只挂在绑了 QQ 的那几间屋),关掉开关就跟着卸下来 —— 不占别的会话的工具列表。
657
+ - ⚠️ 打开这个开关那一刻,被挂上工具的那间会话**下一次请求会全量重算一遍**(多了一个工具定义);关着的时候不会。
658
+ - 自测:宿主半边 **62 项**(新增 6 项:挂工具 / reply=false 会 cancel / 取消理由是 user / 非 QQ 唤醒时拒绝 / 关掉会卸 / 关着调用明确说没开)+ 网页 4 项。
659
+
660
+ ## 0.4.4 — 2026-10-25
661
+
662
+ **支持 OneBot 的 access_token(NapCat / SnowLuma 开了鉴权也能用)**
663
+
664
+ - 🐛 以前所有请求都按「不鉴权」写死 —— 开了 HTTP 鉴权的后端会一律回 **401 unauthorized**,轮询和测试发送全失败。
665
+ - ✅ 设置页「发到哪」多一格 **接口令牌**(password 输入框),填了就在每个请求上带 `Authorization: Bearer <令牌>`;留空照旧,老配置零变化。
666
+ - ✅ 覆盖全部请求:发送私聊 / 群、拉历史、拉合并转发、探活。
667
+
668
+ ## 0.4.3 — 2026-10-25
669
+
670
+ **转发记录排版:里外分开,一条一句**
671
+
672
+ - 🐛 0.4.2 里嵌套转发的内层会被**挤进同一行**(换行被压成空格),看着像一大坨,分不清哪句是谁说的、哪段是里面那层的。
673
+ - ✅ 现在**外层几句话就几行**,里面那层**另起一段、缩进一层**:
674
+
675
+ ```
676
+ [合并转发的聊天记录 · 共 3 条]
677
+ 阿甲:你看这个
678
+ 阿乙:(转发的聊天记录)
679
+ [合并转发的聊天记录 · 共 2 条]
680
+ 阿丙:里层的一句话
681
+ 阿丁:[图片]
682
+ 阿戊:还有这个
683
+ ```
684
+
685
+ - ✅ 一条转发里套了**好几个**转发,也各起一段(不会合成一段)。
686
+ - ✅ 某人的消息只有一段转发、没有别的话时,也会写「某某:(转发的聊天记录)」,看得出是谁转的。
687
+ - ✅ 行数封顶 40 行、字数封顶 2500,太长会写「…(太长,剩下的没展开)」。
688
+ - 自测:宿主半边 **53 项**(新增 4 项:外层一行一句、内层另起一段有缩进、内层的人自己占一行、两个嵌套各一段)。
689
+
690
+ ## 0.4.2 — 2026-10-25
691
+
692
+ **转发里套转发也能展开了;转发里的图跟着「收图」开关走**
693
+
694
+ - ✅ **嵌套转发**:以前里面那层只写一个「[又一层转发]」,现在会再取一次、展开成真实内容(**最多 2 层**,每层最多 12 条,外层总长封顶 2000 字,防止一次吞掉太多上下文)。再往里就写「[里面还套了一层转发(不再展开)]」。
695
+ - ✅ **转发里的图**:跟随设置里现有的 **「收图」** 开关(不另加开关)。开着就把**第一层**转发里的图也收下来(跟顶层消息的图共用「一条最多几张」这个上限),模型能直接看;关着就只标 `[图片]`。
696
+ - ⚠️ **第二层开始只标 `[图片]`、不下载**:一次合并转发里可能有几十张图,全下又慢又容易被 QQ 限流。
697
+ - 自测:宿主半边 **49 项**(新增 5 项:嵌套展开、内层人名、内层图标记、第一层图会收、内层图不重复收),网页半边 4 项。
698
+
699
+ ## 0.4.1 — 2026-10-25
700
+
701
+ **称呼也分两套:私聊一套、群一套**
702
+
703
+ - 🐛 上一版把私聊和群拆成两间屋了,可「称呼」还是一个 —— 群里转进来的话会被叫成你私聊里的那个名字(「小明发来了图片」),群里通常不止一个人,那样是错的。
704
+ - ✅ 设置页拆成两行:**私聊里的称呼**(填你自己,比如「小明」)和 **群里的称呼**(默认「群里的人」,别填某个人的名字)。
705
+ - ✅ 转进 DSH 的文案按方向取:私聊用前者、群里用后者。
706
+ - 自测:宿主半边 44 项(新增 2 项:私聊的图用私聊称呼、群里的图用群称呼且不出现私聊那个名字)。
707
+
708
+ ## 0.4.0 — 2026-10-25
709
+
710
+ **私聊一间屋、群一间屋:不再两边都发**
711
+
712
+ - 🐛 以前只有一个「目标会话」:私聊和群的消息都进同一间,而回话**两个方向各发一条** —— 两个都开的时候,一句本该只给私聊的话会同时出现在群里。
713
+ - ✅ 现在拆成两个:**「私聊会话」**和**「群会话」**(不看群就留空)。QQ 私聊进来的话进私聊那间屋,群里的话进群那间屋;**哪间屋说的话,就只回那一边**,不会串。
714
+ - ✅ 两行选了同一间屋也能用(老行为:两边都发),但设置页会当场提示一句,日志里也写清楚。
715
+ - ✅ 老配置不用改:以前填的「目标会话」自动当成「私聊会话」。
716
+ - 自测:宿主半边 42 项(新增 6 项:两间屋各进各的、回话只回对应那一边、同一间屋两边都发),网页半边 4 项。
717
+
718
+ ## 0.3.7 — 2026-10-25
719
+
720
+ **QQ 里的分享卡片、合并转发的聊天记录,现在也读得懂了**
721
+
722
+ - 🐛 以前拆消息只看 `文字 / 图 / 表情包` 这三种 —— 别人发一张 **B站 分享卡片**、或者**合并转发一段聊天记录**时,段里既没有文字也没有图,整条被当成「空消息」直接丢掉。看着就是「QQ 桥读不了卡片」。
723
+ - ✅ **分享卡片**(`json` / `xml` 段)现在会抽成一句话:标题、简介、原链接;如果卡片里是 B站 视频,还会把 **BV 号**单独标出来,方便交给刷B站那边定点看。
724
+ - ✅ **合并转发的聊天记录**(`forward` 段)会拿 id 去 NapCat 取一次内容,压成最多 12 行「谁:说了什么」,里面的图 / 表情 / 语音记成 `[图片]` `[表情]` `[语音]`。
725
+ - ✅ 顺带把 `@某人`、单独的表情段也记成文字,不再整条丢失。
726
+ - 自测从 28 项加到 36 项(卡片 4 项、合并转发 4 项)。
727
+
728
+ # 改动记录
729
+
730
+ > **版本号规矩**:每改一次 **+0.0.1**;末位满 10 **进位**(`0.1.9` → `0.2.0`)。
731
+ > 版本号只写一处真源:`package.json`;提交信息也以版本号开头。
732
+
733
+ ## 0.3.6 — 2026-10-23
734
+
735
+ **修:换过「目标会话」之后,新会话从第 2 轮起就不往 QQ 发了**
736
+
737
+ - 🐛 去重表原来只记「第几轮」、不记「哪个会话」。换目标会话之后,新会话的轮号一跟旧会话撞上,就被当成「这轮已经发过了」跳过 —— 而且是静默跳过,日志里一个字都没有,很难查。
738
+ - ✅ 现在按「会话 + 轮号」记。换过目标会话不用重启,下一轮就正常。
739
+ - ✅ 「这一轮最后一句」也按会话分开存:两个会话同时说话时,文本不会再串。
740
+
741
+ ## 0.3.5 — 2026-10-23
742
+
743
+ **会话 id 两种写法的比较也顺手兜住了**
744
+
745
+ - 「目标会话」原来在事件过滤、归档判断、设置页高亮三处都是字面比较。配置里存的是不带 `session-` 前缀的写法时,DSH → QQ 会**默默不发**。现在都统一去掉前缀再比。
746
+
747
+ ## 0.3.4
748
+
749
+ **修:回发的动图还是变成静态 webp(这次是真原因)**
750
+
751
+ - 🐛 上一版只按「附件 id」认原图。但伙伴回发时常常是**照抄 DSH 给的那条只读副本路径**(.dsh/attachments/v1/objects/… 里的 webp),按 id 根本对不上,于是又读了副本。
752
+ - ✅ 现在两个键都记:「附件 id」和「那条只读副本路径」。发图时先反查,能换回原图就换 —— GIF 还是 GIF。
753
+ - ✅ 转给它的文字里顺带写上「要发回去就用 <本地路径>」,一句话说完,不解释原理。
754
+ - ⚙️ 「图片根目录」不用填了:直接问 DSH 的工作区登记表,优先用**目标会话所在的工作区**,找不到才退回原来的猜测链,并在日志里列出试过的位置。
755
+
756
+ ## 0.3.3
757
+
758
+ **新增「称呼」;转进去的文案不再默认是「她」**
759
+
760
+ - ⚙️ 设置里多了「称呼」(默认「对方」)。填「小明」,它收到的就是「小明发来了图片」。
761
+ - ✅ 图片相关的几句话(几张图 / 没配字)都用这个称呼了,不再写死「她」。
762
+
763
+ ## 0.3.2
764
+
765
+ **修:回发的动图变成静态 webp**
766
+
767
+ - 🐛 收到的图,DSH 会留一份「归一化副本」(会重新编码)。回发时读的是那份副本,所以 GIF 动图发回去就成了静态 webp。
768
+ - ✅ 收图时记一张「附件 ↔ 我们存的原图」的表(`~/.dsh/qq-bridge-received.json`,只留最近 200 条),回发时**优先用原图**:GIF 还是 GIF。
769
+ - 原图不在了(比如 inbox 被清过)就自动退回 DSH 那份副本,不会因此发不出去。
770
+
771
+ ## 0.3.1
772
+
773
+ **发图:正文里写的 markdown 图片也发得出去(贴纸就是这么发的)**
774
+
775
+ - ✅ 以前只认「附件图片块」,所以它用 `![小肥鱼](_stickers/feiyu-test.webp)` 这种写法发贴纸时,QQ 那边收到的是一串 markdown 文字。
776
+ - ✅ 现在发之前先把正文里的 `![名字](路径)` 抠出来:本地文件读出来当图片发,`http(s)://` 的直接把地址交给 QQ。正文里那串就不再出现了。
777
+ - ⚙️ 新设置「图片根目录」:相对路径按哪儿找。留空就依次试登记过的工作区目录 / 当前目录 / `<DSH_HOME>\workspace` / 用户主目录。
778
+ - 找不到文件时这一张跳过,日志里会说清是哪个路径。
779
+ - 正文里只有一张贴纸、没有字,也能发出去。
780
+
781
+ ## 0.3.0
782
+
783
+ **两个方向都能传图片了**
784
+
785
+ - ✅ QQ → DSH:你发的图片(含表情包)也转给它。以前纯图消息是**直接丢掉**的。
786
+ - ✅ 收进来的图会在 `~/.dsh/qq-bridge-inbox/` 存一份,文字里带上本地路径 —— 纯文本模型看见的是一句占位文字,但至少知道有几张、存在哪。
787
+ - ✅ DSH → QQ:它这一轮里发的图也会发到 QQ(base64 直接塞,不依赖 QQ 那边能不能访问本机文件)。
788
+ - ⚙️ 新设置:收图 / 发图 开关、一条最多收几张(1–10,默认 4)、单张最大多少 MB(默认 5,两个方向共用)。
789
+ - ⚠️ 模型看不看得见图,取决于**那个会话用的模型**:能看图的直接看;纯文本模型只会收到占位文字。
790
+
791
+ ## 0.2.0
792
+
793
+ **修:`followup` 没 await,宿主会整个退出**
794
+
795
+ - `agent.followup()` 返回 Promise。没接住的话它 reject 时 catch 根本看不到,变成未处理的 rejection,**整个 DSH 宿主退出**。现在 await 并兜住错误。
796
+
797
+ ## 0.1.5
798
+
799
+ **修:目标会话清单里混着「已归档」的会话;README 补上 QQ 后端怎么装**
800
+
801
+ - 🐛 归档只是把会话藏起来,`sessions.list()` 里**还在**;原来只调 list(),
802
+ 于是设置页里把已归档的也列出来了(这台机器上有 40 个),选了也没用。
803
+ - ✅ `inject` 加 `workspaceRegistry`,从 `archivedSessionIds` 取归档名单(照朗读桥的写法),清单里直接过滤。
804
+ - ✅ 发送时也检查:盯的会话如果已经归档,这轮不转,日志写明。
805
+ - 📄 README:新增「装哪个 QQ 后端」——**NapCat(已验证)/ SnowLuma(同接口,未实测)**,
806
+ 列出插件实际用到的 6 个 OneBot 11 接口,换后端时照着核对。
807
+ - 📄 修掉 package.json 和设置页里"先只做了 DSH → QQ 这半"的旧说法(两半都做完了)。
808
+
809
+ ## 0.1.4
810
+
811
+ **`QQ → DSH` 这半彻底搬进插件,不再赖 BOT**
812
+
813
+ - 插件每 4 秒轮询 NapCat 的 HTTP 历史接口(`get_friend_msg_history` / `get_group_msg_history`),
814
+ 发现她新发的私聊/群消息 → 直接 `followup` 进目标会话。
815
+ - 只认 `私聊QQ` 这个人发的;自己的回话(self 消息)不会自我繁殖成死循环。
816
+ - 靠 `message_id` + 时间戳去重,同一条不重复转;总开关关 / 没选会话时只推进时间、不处理旧消息。
817
+ - 结论:**这台桌面只要 NapCat + 这个插件,BOT 可以完全不要了。**
818
+
819
+ ## 0.1.3
820
+
821
+ **QQ→DSH 这半的 BOT 半场也接上了**(改在 Estrella 那边,不在本仓库)
822
+
823
+ - Estrella(BOT)加了 `custom.qqBridge` 开关(**默认关**):打开后,私聊/群里**她本人**说的话
824
+ 由 BOT 转发到本插件的 `/receive`,然后 BOT **闭嘴不回**,全交给 DSH 里那位伙伴。
825
+ - 兜底:转成功 → BOT 不回;转失败(DSH 没开 / 连不上)→ BOT **照常回**(桥断 ≠ 鱼哑)。
826
+ - 本仓库只是把这件事写进 README(见「怎么把 BOT 接上」);运行入口仍是 0.1.2 的 `/receive`。
827
+
828
+ ## 0.1.2
829
+
830
+ **加了「QQ → DSH」这半的入口**(`/receive` 路由)
831
+
832
+ - BOT 把她说的话 POST 到 `/dsh-qq-bridge/receive`,这里用 `agent.followup(手搓 user 消息)` 走 DSH 自己的回合。
833
+ - 消息 `source.kind = 'qq'`(不冒充她亲手在 DSH 里打的)。
834
+ - 闸门:没选目标会话 / 总开关关 / 会话没活着 / 空 text 都拒绝,并回一句人话。
835
+ - BOT 那边(Estrella 改动)要在这半之后再接上。
836
+
837
+ ## 0.1.1
838
+
839
+ **加了设置页(网页半边)**
840
+
841
+ - 设置 → 「通到 QQ」:总开关 / 试运行 / 目标会话(**下拉选,不手填**)/ 私聊 QQ 号 / 群号 /
842
+ 转私聊 / 转群 / 「测试一句」/ 保存设置,外加一份最近记录。
843
+ - 页面顶上显示「连着的 QQ 是哪个号、昵称」(走 NapCat `/get_login_info` 探活)。
844
+ - 有改动时,底下照样浮出「⚠ 有改动还没保存 + 保存设置」条(照「主动搭话」那套)。
845
+
846
+ ## 0.1.0
847
+
848
+ **上半场:DSH → QQ(宿主)**
849
+
850
+ - 盯「目标会话」,伙伴**一整轮最后一句**说完,自动发到 QQ 私聊/群。
851
+ - 走 NapCat OneBot HTTP(`127.0.0.1:3000`),**不经过 Estrella(BOT鱼)**。
852
+ - 安全默认:总开关关、试运行开。
853
+ - 多步只发最后一步;`interrupted` / `reason != completed` 不转发;只认目标会话。
854
+ - 附带 `/state` `/save` `/test-send` 三条路由 + 宿主半边冒烟测试。## 0.1.7
855
+
856
+ **文案通用化:不再出现作者自己的称呼;两个 QQ 后端都标为已测**
857
+
858
+ - 说明和设置页里的「大肥鱼 × 对方」全部改成「你自己要的那一间」——别人装了看得懂。
859
+ - 后端表更正:**SnowLuma 和 NapCat 都是测过的**(之前把 NapCat 写成"未实测",是写错了)。
860
+ - 设置页文案里误留的 markdown 星号(`**一整轮**`)去掉,免得原样显示成星号。
861
+