@zxh126/pi-qqbot 2.4.1 → 2.6.2
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.en.md +611 -0
- package/{CHANGELOG.md → CHANGELOG.zh-CN.md} +81 -0
- package/LICENSE +21 -21
- package/README.md +22 -9
- package/approver.ts +2 -2
- package/billing.ts +579 -579
- package/constants.ts +2 -0
- package/docs/README.zh-CN.md +19 -9
- package/docs/accounts.md +88 -88
- package/index.ts +131 -20
- package/lock.ts +237 -237
- package/logger.ts +58 -58
- package/package.json +4 -3
- package/progress.ts +176 -176
- package/qq/api.ts +17 -0
- package/qq/auth.ts +35 -35
- package/qq/gateway.ts +20 -3
- package/qq/keyboard.ts +141 -0
- package/queue.ts +120 -120
- package/redact.ts +136 -136
- package/selfupdate.ts +212 -212
- package/slash.ts +67 -5
- package/stream-buffer.ts +22 -22
- package/util/chunk.ts +107 -42
|
@@ -3,6 +3,87 @@
|
|
|
3
3
|
本文件记录 pi-qqbot 的版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
4
4
|
版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
|
|
5
5
|
|
|
6
|
+
> 英文版 changelog:[CHANGELOG.en.md](CHANGELOG.en.md)
|
|
7
|
+
|
|
8
|
+
## [2.6.2] - 2026-11-01
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`/qq …` 绕过命令门禁**:`/qq` 本身就是扩展命令,所以直接发 `/qq reload`、`/qq quit`、`/qq new`
|
|
13
|
+
会落进 `routeSlash` 的 passthrough 分支,既不查 `commands.allowReload` / `allowQuit` / `allowUpdate` /
|
|
14
|
+
`allowSession`,也不查忙碌状态(`needsIdle`)——默认配置(`allowQuit: false`)下 QQ 侧发 `/qq quit`
|
|
15
|
+
就能把主机 pi 关掉。现在 `/qq` 在 passthrough **之前**分流:`bridgeGate()` 把子命令翻回内置命令名
|
|
16
|
+
(多词 `list tree` / `list fork` / `list resume` 先整体匹配),复用同一套 `allowed()` / `needsIdle` 判定。
|
|
17
|
+
- **派发回执吞掉参数**:`/qq reload` 过去回「已派发 /qq 给 pi。」——`parseSlash` 只取首段当命令名,
|
|
18
|
+
看起来像 `reload` 没被受理。现在回执与日志都显示完整命令(超 80 字截断加 `…`)。
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- 自毁类桥接子命令(`reload` / `new` / `quit` / `clone`)改为**先回执再派发**,避免回执和
|
|
23
|
+
`ctx.reload()` / `newSession()` 抢时序。
|
|
24
|
+
- `/qq sendfile …`、`/qq usage …`、`/qq pick …` 以及未知子命令不再额外发一条通用回执(子命令自己会回),
|
|
25
|
+
减少一次无意义的消息。
|
|
26
|
+
- 顺手清掉 `slash.ts` 里一处历史补丁残留的空行 `+`(`+reply(formatPicker(picker))`,语义上无害但脏)。
|
|
27
|
+
|
|
28
|
+
### Compatibility
|
|
29
|
+
|
|
30
|
+
- 发 `/reload`、`/new`、`/quit`、`/compact` 等原命令的行为与门禁不变;只是 `/qq <sub>` 现在受同样约束。
|
|
31
|
+
- 无需改配置:原本 `allowReload: true` 的部署照旧可用;`allowQuit: false`(默认)下 `/qq quit` 变成回执拒绝。
|
|
32
|
+
|
|
33
|
+
### Tests
|
|
34
|
+
|
|
35
|
+
- 新增 `test/qq-bridge-gate.test.mjs`(21 条断言):子命令映射、三种门禁拒绝、忙碌拒绝、
|
|
36
|
+
先回执后派发顺序、多词 sub、回执截断、`/reload` 未回归。
|
|
37
|
+
- 全套 274 条断言 / 22 个测试文件通过;`tsc --noEmit --strict` 对改动文件 `slash.ts` 无报错。
|
|
38
|
+
|
|
39
|
+
## [2.6.1] - 2026-11-01
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- **changelog 分语种**:中文版更名 `CHANGELOG.zh-CN.md`,新增英文版 `CHANGELOG.en.md`(含全部历史版本);
|
|
44
|
+
README.md(英文)引用 `CHANGELOG.en.md`,docs/README.zh-CN.md 引用 `CHANGELOG.zh-CN.md`。
|
|
45
|
+
|
|
46
|
+
## [2.6.0] - 2026-11-01
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **审批与单选列表支持内联按钮**:审批卡片附带「✅ 允许一次 / ❌ 拒绝 / 🚀 以后都同意」按钮
|
|
51
|
+
(强制审批工具只有前两个);/tree、/fork、/resume 与模型选择列表附带选项按钮(2 列、最多 5 行,
|
|
52
|
+
label 超 10 字符截断,正文仍保留完整列表)。按钮只在 markdown 回复(`markdownReply=true`)时附带。
|
|
53
|
+
- **`INTERACTION_CREATE` 事件链路**:网关新增 `1<<26` 意图订阅与互动事件分发;`QQApi.acknowledgeInteraction`
|
|
54
|
+
在 5 秒窗口内 `PUT /interactions/{id}` 回应(命中 ACK 0,过期/越权/越界 ACK 3 重复操作)。
|
|
55
|
+
- **`qq/keyboard.ts`**:键盘构建与 `button_data` 编解码(顶层 `t` 字段路由 approval / pick)。
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- `QQApi.sendC2C` 的 `SendOptions.keyboard` 只随 markdown 消息发送;markdown 失败回退纯文本时自动丢弃键盘。
|
|
60
|
+
- `replyTo` / `bridgeReply` 增加可选 `keyboard` 透传;选择按钮点击复用「回复编号」的 `/qq pick` 派发路径,
|
|
61
|
+
审批按钮点击复用 `Approver.confirm` / `allowAll`,文本回复(同意/拒绝 N、编号)全部保留为兜底。
|
|
62
|
+
|
|
63
|
+
### Compatibility
|
|
64
|
+
|
|
65
|
+
- `markdownReply=false` 时行为与之前完全一致(键盘不附带、纯文本回复)。
|
|
66
|
+
- 自定义按钮需 QQ 开放平台「内邀开通」;未开通时按钮不渲染,编号/文本回复不受影响。
|
|
67
|
+
|
|
68
|
+
## [2.5.0] - 2026-11-01
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- **`markdownReply` 配置生效**:置 `true` 后回复统一走 QQ markdown 消息(`msg_type=2`),GFM 表格、
|
|
73
|
+
代码块等由 QQ 客户端渲染。此前该配置只被解析、从未被消费。发送失败(如无 markdown 权限)由
|
|
74
|
+
`qq/api.ts` 自动回退纯文本,行为安全。
|
|
75
|
+
- **`util/chunk.ts` 切分器防拆表格**:`splitText` 把 GFM 表格整块保留(表格行先入缓冲、遇到非表格行
|
|
76
|
+
再整块提交),代码块边界不作切分点;长回复不再出现被拦腰截断的破表。
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- 回复格式由 `state.cfg.markdownReply` 决定:`replyTo` 的 `format` 默认值改为 `auto`,最终答案、
|
|
81
|
+
错误通知、审批提示与各回执统一生效;仍可显式传 `"text"` 强制纯文本。
|
|
82
|
+
|
|
83
|
+
### Compatibility
|
|
84
|
+
|
|
85
|
+
- `markdownReply` 默认仍为 `false`,未开启时行为与之前完全一致。
|
|
86
|
+
|
|
6
87
|
## [2.4.1] - 2026-11-01
|
|
7
88
|
|
|
8
89
|
### Added
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 pi-qqbot contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pi-qqbot contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@ Bridge the **[Pi](https://pi.dev) coding agent** into QQ private (C2C) chats: se
|
|
|
12
12
|
|---|---|
|
|
13
13
|
| **Chat-driven agent** | QQ private messages are fed into the current pi session; the assistant's answer is sent back to QQ (long replies are split automatically). |
|
|
14
14
|
| **Message queue** | While a task is running, further QQ messages are **queued** instead of rejected; each one starts automatically as soon as the current run settles. `/cancel` is the exception — it acts immediately and clears the queue. |
|
|
15
|
-
| **Remote approval** | Before pi runs `bash` / `powershell` / `write` / `edit` — or any tool annotated `destructiveHint` — the bot asks in QQ first. |
|
|
15
|
+
| **Remote approval** | Before pi runs `bash` / `powershell` / `write` / `edit` — or any tool annotated `destructiveHint` — the bot asks in QQ first. With `markdownReply: true` the card carries inline buttons; text replies work either way. |
|
|
16
16
|
| **Remote-context notice** | The first QQ message of a session is really handed to pi (loading the extension or chatting locally in the TUI adds nothing), then the bot tells it the conversation comes from a remote QQ user and that the machine may be locked: desktop screenshots / screen OCR / UI automation / headed browsers are off by default, images must be rendered off-screen and sent with `qq_send_file`. Re-injected after context compaction. |
|
|
17
|
-
| **Session-scoped "always allow"** |
|
|
17
|
+
| **Session-scoped "always allow"** | Tap 🚀 以后都同意 on the approval card (or reply 「以后都同意」) and every approval request in the **current session** is auto-approved, including the one already waiting. |
|
|
18
18
|
| **Single-instance lock** | Only one pi process hosts the QQ gateway; the others stand by and take over automatically when the host exits. |
|
|
19
19
|
| **Cancel a running task** | Send `/cancel` (or 「取消」/「停止」) and the current run is aborted immediately — the QQ reply, the pending tool calls, the half-written answer and the pending queue are all cleaned up. |
|
|
20
20
|
| **Slash-command routing** | QQ messages starting with `/` are routed: pi extension commands, prompt templates and `/skill:name` are dispatched to pi; a set of pi built-ins (`/reload`, `/new`, `/compact`, `/session`, `/name`, `/model`, `/thinking`, `/quit`) is taken over; host-only commands are answered instead of being fed to the model. |
|
|
@@ -90,7 +90,7 @@ Then create `~/.pi/agent/pi-qqbot.json`:
|
|
|
90
90
|
| `appId` / `appSecret` | required | Credentials of your QQ Open Platform bot |
|
|
91
91
|
| `sandbox` | `false` | Set `true` while the bot is not yet published (sandbox environment) |
|
|
92
92
|
| `allowlist` | `[]` | openid allowlist; empty means no restriction |
|
|
93
|
-
| `markdownReply` | `false` | QQ
|
|
93
|
+
| `markdownReply` | `false` | Reply with QQ markdown messages (msg_type=2) so GFM tables render; approval and selection lists then carry inline buttons (custom keyboards need platform enablement). Needs the bot's markdown permission; falls back to plain text on send failure |
|
|
94
94
|
| `streaming` | `false` | Reserved, streaming is not enabled in v1 |
|
|
95
95
|
| `approve.tools` | `["bash","powershell","write","edit"]` | Tools that require approval; tools annotated `destructiveHint` are always gated too |
|
|
96
96
|
| `approve.remember` | `true` | Remember the decision per tool inside the session (no more prompts for that tool) |
|
|
@@ -255,10 +255,10 @@ Any message that starts with `/` is routed before it can become a chat prompt:
|
|
|
255
255
|
| `/name [name]` | get or set the session name | `commands.allowSession` |
|
|
256
256
|
| `/model [provider/model]` | list available models or switch | `commands.allowSession` |
|
|
257
257
|
| `/thinking [level]` | get or set the thinking level | `commands.allowSession` |
|
|
258
|
-
| `/tree` | list branch points (numbered), answer with a number to jump there | `commands.allowSession` |
|
|
259
|
-
| `/fork` | list earlier user messages (numbered), answer with a number to fork from it | `commands.allowSession` |
|
|
258
|
+
| `/tree` | list branch points (numbered), answer with a number or tap the button to jump there | `commands.allowSession` |
|
|
259
|
+
| `/fork` | list earlier user messages (numbered), answer with a number or tap the button to fork from it | `commands.allowSession` |
|
|
260
260
|
| `/clone` | duplicate the current session at its current leaf | `commands.allowSession` |
|
|
261
|
-
| `/resume` | list the project's other sessions (numbered), answer with a number to switch | `commands.allowSession` |
|
|
261
|
+
| `/resume` | list the project's other sessions (numbered), answer with a number or tap the button to switch | `commands.allowSession` |
|
|
262
262
|
| `/quit` | shuts the host pi down | `commands.allowQuit` (default **false**) |
|
|
263
263
|
| `/update` | updates pi-qqbot from npm (`pi update`) and reloads itself — see [Self-update](#self-update-from-qq) | `commands.allowUpdate` (default **false**) |
|
|
264
264
|
|
|
@@ -297,11 +297,20 @@ Guards:
|
|
|
297
297
|
- Prompt templates and `/skill:name` start an agent run, so while a task is in progress they are
|
|
298
298
|
**queued** like ordinary messages (see [Queueing while busy](#queueing-while-busy)). Extension commands
|
|
299
299
|
that manage their own LLM interaction still run immediately.
|
|
300
|
-
- Selection commands (`/tree`, `/fork`, `/resume`) answer with a numbered list of at most 10 entries, scoped to
|
|
300
|
+
- Selection commands (`/tree`, `/fork`, `/resume`) answer with a numbered list of at most 10 entries (with tap-to-select buttons when `markdownReply` is on), scoped to
|
|
301
301
|
the user who asked and valid for 10 minutes. The next bare number from that user executes the choice; anything
|
|
302
302
|
else (or 「取消」) abandons it. A number without a pending list is ordinary chat.
|
|
303
303
|
- `/reload` is off by default: it restarts the bot, and if the new code fails to load the bot stays
|
|
304
304
|
down until someone fixes it on the host. Turn it on with `commands.allowReload: true`.
|
|
305
|
+
- Sending the bridge command yourself (`/qq reload`, `/qq quit`, `/qq new`, `/qq list fork`, …) hits the
|
|
306
|
+
**same** gates: the sub-command is mapped back to its built-in name first, so `commands.allowReload` /
|
|
307
|
+
`allowQuit` / `allowUpdate` / `allowSession` and the busy check all apply. Routing it as a plain
|
|
308
|
+
extension command would have bypassed every gate — with `allowQuit: false`, `/qq quit` could shut the
|
|
309
|
+
host pi down. Destructive sub-commands (`reload`, `new`, `quit`, `clone`) answer *before* they are
|
|
310
|
+
dispatched, because they replace the runtime they would answer through.
|
|
311
|
+
- Dispatch receipts echo the **whole** command (`已派发 /deploy prod 给 pi。`) instead of only the first
|
|
312
|
+
word, truncated at 80 characters with `…`. Previously `/qq reload` was answered with
|
|
313
|
+
`已派发 /qq 给 pi。`, which looked like the argument had been dropped.
|
|
305
314
|
- Why the indirection: pi only dispatches **extension** commands from a message
|
|
306
315
|
(`prompt()` → `_tryExecuteExtensionCommand`), and `reload()` / `newSession()` are command-context
|
|
307
316
|
APIs. So this extension registers `/qq` and forwards `/reload` → `/qq reload`. Built-in names are
|
|
@@ -481,7 +490,11 @@ This is pi's own update path, not a re-implementation: `npm view @zxh126/pi-qqbo
|
|
|
481
490
|
要本会话内全部放行,回复「以后都同意」(可用「取消自动同意」关闭)。
|
|
482
491
|
```
|
|
483
492
|
|
|
484
|
-
|
|
493
|
+
With `markdownReply: true` the card carries ✅ 允许一次 / ❌ 拒绝 / 🚀 以后都同意 buttons — tapping one
|
|
494
|
+
is the same as replying. Strict tools (`qq_send_file`) show only the first two buttons. Text replies keep
|
|
495
|
+
working either way.
|
|
496
|
+
|
|
497
|
+
Reply vocabulary (approval keywords are Chinese, with English aliases; the card buttons are the faster path when they are shown):
|
|
485
498
|
|
|
486
499
|
| Intent | Reply |
|
|
487
500
|
|---|---|
|
|
@@ -521,7 +534,7 @@ Debug logs are written to `~/.pi/agent/pi-qqbot-<name>.log` (one file per bot, s
|
|
|
521
534
|
|
|
522
535
|
## Changelog
|
|
523
536
|
|
|
524
|
-
See [CHANGELOG.md](./CHANGELOG.md) — every release with what changed and why. The progress-report
|
|
537
|
+
See [CHANGELOG.en.md](./CHANGELOG.en.md) — every release with what changed and why. (中文:[CHANGELOG.zh-CN.md](./CHANGELOG.zh-CN.md)) The progress-report
|
|
525
538
|
pause/resume (`/progress off` / `on`) landed in **1.5.0**; multi-bot auto-claiming in **2.0.0**;
|
|
526
539
|
receiving QQ images/files (saved to temp files and handed to pi as `图片路径 <path>`) in **2.1.0**;
|
|
527
540
|
sending local files to QQ (`qq_send_file` / `/sendfile`, chunked rich-media upload) in **2.2.0**.
|
package/approver.ts
CHANGED
|
@@ -99,8 +99,8 @@ export class Approver {
|
|
|
99
99
|
render(seq: number, opts: { strict?: boolean } = {}): string {
|
|
100
100
|
const item = this.pending.get(seq);
|
|
101
101
|
const hint = opts.strict
|
|
102
|
-
?
|
|
103
|
-
:
|
|
102
|
+
? `点下方按钮,或回「同意 ${seq}」/「拒绝 ${seq}」,需单独确认。`
|
|
103
|
+
: `点下方按钮,或回「同意 ${seq}」/「拒绝 ${seq}」;想全部放行回「以后都同意」。`;
|
|
104
104
|
return [`[审批 #${seq}] ${item?.summary ?? ""}`, hint].join("\n");
|
|
105
105
|
}
|
|
106
106
|
|