@zhin.js/adapter-qq 1.1.17 → 1.1.19
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 +82 -0
- package/README.md +75 -21
- package/adapters/qq/index.js +4 -33
- package/adapters/qq/index.ts +3 -41
- package/agents/qq/boundaries.md +2 -0
- package/agents/qq/conversation.js +92 -0
- package/agents/qq/conversation.ts +99 -0
- package/agents/qq/messages.js +60 -0
- package/agents/qq/messages.ts +60 -0
- package/agents/qq/skills/qq/SKILL.md +8 -2
- package/agents/qq/skills/qq/tools/add_role/index.js +2 -1
- package/agents/qq/skills/qq/tools/add_role/index.ts +2 -1
- package/agents/qq/skills/qq/tools/channel_info/index.js +1 -1
- package/agents/qq/skills/qq/tools/channel_info/index.ts +1 -1
- package/agents/qq/skills/qq/tools/create_role/index.js +1 -1
- package/agents/qq/skills/qq/tools/create_role/index.ts +1 -1
- package/agents/qq/skills/qq/tools/guild_kick_member/index.js +30 -0
- package/agents/qq/skills/qq/tools/guild_kick_member/index.ts +29 -0
- package/agents/qq/skills/qq/tools/guild_mute_member/index.js +31 -0
- package/agents/qq/skills/qq/tools/guild_mute_member/index.ts +30 -0
- package/agents/qq/skills/qq/tools/list_channels/index.js +1 -1
- package/agents/qq/skills/qq/tools/list_channels/index.ts +1 -1
- package/agents/qq/skills/qq/tools/list_roles/index.js +1 -1
- package/agents/qq/skills/qq/tools/list_roles/index.ts +1 -1
- package/agents/qq/skills/qq/tools/member_detail/index.js +1 -1
- package/agents/qq/skills/qq/tools/member_detail/index.ts +1 -1
- package/agents/qq/skills/qq/tools/remove_role/index.js +2 -1
- package/agents/qq/skills/qq/tools/remove_role/index.ts +2 -1
- package/agents/qq/skills/qq-conversation/SKILL.md +24 -0
- package/agents/qq/skills/qq-conversation/tools/conversation_info/index.js +78 -0
- package/agents/qq/skills/qq-conversation/tools/conversation_info/index.ts +76 -0
- package/agents/qq/skills/qq-directory/tools/list_guilds/index.js +1 -1
- package/agents/qq/skills/qq-directory/tools/list_guilds/index.ts +1 -1
- package/agents/qq/skills/qq-group-admin/SKILL.md +34 -0
- package/agents/qq/skills/qq-group-admin/tools/group_kick/index.js +39 -0
- package/agents/qq/skills/qq-group-admin/tools/group_kick/index.ts +38 -0
- package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.js +33 -0
- package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.ts +32 -0
- package/agents/qq/skills/qq-group-admin/tools/recall_member_message/index.js +25 -0
- package/agents/qq/skills/qq-group-admin/tools/recall_member_message/index.ts +25 -0
- package/agents/qq/skills/qq-group-admin/tools/set_mute/index.js +37 -0
- package/agents/qq/skills/qq-group-admin/tools/set_mute/index.ts +36 -0
- package/agents/qq/skills/qq-group-directory/SKILL.md +24 -0
- package/agents/qq/skills/qq-group-directory/tools/group_info/index.js +16 -0
- package/agents/qq/skills/qq-group-directory/tools/group_info/index.ts +16 -0
- package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.js +19 -0
- package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.ts +19 -0
- package/agents/qq/skills/qq-group-directory/tools/group_members/index.js +19 -0
- package/agents/qq/skills/qq-group-directory/tools/group_members/index.ts +19 -0
- package/agents/qq/skills/qq-group-state/SKILL.md +21 -0
- package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.js +19 -0
- package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.ts +19 -0
- package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.js +19 -0
- package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.ts +19 -0
- package/agents/qq/skills/qq-group-state/tools/list_muted/index.js +21 -0
- package/agents/qq/skills/qq-group-state/tools/list_muted/index.ts +21 -0
- package/agents/qq/skills/qq-messages/SKILL.md +27 -0
- package/agents/qq/skills/qq-messages/tools/quote_reply/index.js +21 -0
- package/agents/qq/skills/qq-messages/tools/quote_reply/index.ts +21 -0
- package/agents/qq/skills/qq-messages/tools/recall_own_message/index.js +18 -0
- package/agents/qq/skills/qq-messages/tools/recall_own_message/index.ts +17 -0
- package/commands/qq/endpoint/definition.js +73 -55
- package/commands/qq/endpoint/definition.ts +75 -86
- package/lib/adapter.d.ts +13 -0
- package/lib/adapter.js +27 -0
- package/lib/client.d.ts +7 -9
- package/lib/client.js +1 -2
- package/lib/console.d.ts +3 -0
- package/lib/console.js +93 -0
- package/lib/endpoint.d.ts +17 -27
- package/lib/endpoint.js +325 -146
- package/lib/group-admin.d.ts +41 -0
- package/lib/group-admin.js +114 -0
- package/lib/i18n.js +4 -4
- package/lib/image-size.d.ts +6 -1
- package/lib/image-size.js +2 -3
- package/lib/index.d.ts +5 -4
- package/lib/index.js +3 -3
- package/lib/interaction.js +1 -1
- package/lib/outbound.d.ts +4 -1
- package/lib/outbound.js +3 -2
- package/lib/protocol.d.ts +7 -0
- package/lib/protocol.js +4 -0
- package/lib/qq-console-spec.d.ts +17 -0
- package/lib/qq-console-spec.js +21 -0
- package/lib/qq-console.d.ts +10 -0
- package/lib/qq-console.js +126 -0
- package/lib/qq-endpoint-write.d.ts +18 -0
- package/lib/qq-endpoint-write.js +7 -0
- package/lib/qq-runtime-state.d.ts +29 -9
- package/lib/qq-runtime-state.js +5 -5
- package/lib/qq-send.d.ts +7 -14
- package/lib/qq-send.js +20 -6
- package/lib/webhook.d.ts +3 -6
- package/lib/webhook.js +6 -49
- package/lib/ws.d.ts +10 -33
- package/lib/ws.js +21 -70
- package/middlewares/bot-kind-reply/index.js +1 -2
- package/middlewares/bot-kind-reply/index.ts +0 -2
- package/package.json +26 -18
- package/pages/index/index.js +15 -0
- package/pages/index/index.ts +16 -0
- package/plugin.js +9 -6
- package/schema.json +174 -0
- package/src/adapter.ts +23 -0
- package/src/client.ts +4 -12
- package/src/console.ts +103 -0
- package/src/endpoint.ts +328 -141
- package/src/group-admin.ts +155 -0
- package/src/i18n.ts +4 -4
- package/src/image-size.ts +7 -3
- package/src/index.ts +5 -5
- package/src/interaction.ts +1 -1
- package/src/outbound.ts +3 -2
- package/src/protocol.ts +10 -0
- package/src/qq-console-spec.ts +29 -0
- package/src/qq-console.ts +130 -0
- package/src/qq-endpoint-write.ts +24 -0
- package/src/qq-runtime-state.ts +31 -15
- package/src/qq-send.ts +28 -12
- package/src/webhook.ts +7 -57
- package/src/ws.ts +27 -105
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,87 @@
|
|
|
1
1
|
# @zhin.js/adapter-qq
|
|
2
2
|
|
|
3
|
+
## 1.1.19
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2c3610a: Move account lifecycle ownership into the generic Adapter base and let native Endpoints retain their SDK Client inheritance. Infer platform-specific operation Sessions from a single adapter registry while preserving command parameter, option, configuration and factory inference.
|
|
8
|
+
|
|
9
|
+
Bind message replies, management ports, login challenges and Agent cancellation to the captured account incarnation. Persist runtime account intent with encrypted, revision-checked Host storage; keep account management independent of Root process restarts. Require explicit operator confirmation before importing existing accounts, and give replacement accounts a new identity without inheriting previous Sessions.
|
|
10
|
+
|
|
11
|
+
Expose account save, start, stop, update and replacement through the existing adapter Console, with exact account ownership, truthful pending/unknown outcomes and no startup configuration or environment-file rewrites.
|
|
12
|
+
|
|
13
|
+
Complete the target migration across every adapter, the public authoring API, generators, examples and Console. This intentionally removes the old create factory, inheritable Endpoint/ClientEndpoint classes, Client descriptors and file-rewrite account management; plugin authors must use createAdapter, native Client inheritance and the single AdapterRegistry. Satori and OneBot12 require explicit platform/userId selection, and WeCom requires a separate agentId.
|
|
14
|
+
|
|
15
|
+
Verify opaque credential changes before the same configuration snapshot is committed. Drain admitted native callbacks across close/rollback, cancel target work on stop, and keep raw transport parsing, platform ACK timing and SDK native methods under their existing owners. Account metadata conceals credentials carried in URLs.
|
|
16
|
+
|
|
17
|
+
- a9f4e48: PATCH COMPATIBILITY CUTOVER: this batch intentionally changes plugin APIs. The maintainer explicitly chose patch releases with declared version boundaries. Upgrade the complete tested dependency set; patch numbering does not promise compatibility with the removed interfaces.
|
|
18
|
+
|
|
19
|
+
The first planned floors are zhin.js 1.1.19, Core 1.1.54, Adapter 1.1.26, FeatureKit 1.1.10, Plugin Runtime 1.1.18, Runtime/Isolate 1.1.16 and Agent 1.1.45. Keep old-interface plugins with the earlier locked stack until they are migrated. Adapter's independent Feature API changes from 1.0.0 to 2.0.0; official consumers declare ^2.0.0. Existing Feature API admission rejects explicitly incompatible ranges; omitted API metadata keeps its existing behavior and does not certify compatibility. Runtime engine and patch-only release gates remain unchanged.
|
|
20
|
+
|
|
21
|
+
Use the native-Client Adapter target contract, canonical FeatureKit AdapterRegistry augmentation and named Endpoint control/management requests. Route message controls from canonical message/conversation refs; retain exact source authority and propagate caller/account/explicit-Root-stop cancellation. Replace removed Agent activity feedback exports with source-bound feedback. HMR pause/resume belongs to concrete plugin instances; plugins choose accepted-work policy without changing Controller commit/rollback authority. Cooperative worker/process control cancellation does not kill an isolate or reopen retired business admission.
|
|
22
|
+
|
|
23
|
+
Provide authenticated historical-data preview, exact selected binding, transactional application, authoritative inspection and explicit resumption, including active games. Preserve before-images and journals; never replay an unconfirmed effect, infer an account from an alias, or reuse historical permission grants. Owner V2 approvals remain inactive legacy data, while new writes use V3. Rollback requires matching software, lockfile and verified data; installing old code alone cannot read a new V3 approval file. Conflicting after-images are never overwritten.
|
|
24
|
+
|
|
25
|
+
See docs/plans/adapter-operation-version-boundaries.md and the Chinese/English upgrade guide for the complete version inventory, custom-plugin changes, acceptance and recovery sequence. These code and metadata changes do not authorize merging, publication, deployment, login, production migration or removal of installed workspace patches.
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [2c3610a]
|
|
28
|
+
- Updated dependencies [a9f4e48]
|
|
29
|
+
- Updated dependencies [f8505b0]
|
|
30
|
+
- Updated dependencies [2c3610a]
|
|
31
|
+
- @zhin.js/adapter@1.1.26
|
|
32
|
+
- @zhin.js/feature-kit@1.1.10
|
|
33
|
+
- @zhin.js/core@1.1.54
|
|
34
|
+
- @zhin.js/host-http@1.1.12
|
|
35
|
+
- @zhin.js/client@1.1.22
|
|
36
|
+
- @zhin.js/page@1.1.11
|
|
37
|
+
- @zhin.js/agent-feature@1.1.11
|
|
38
|
+
- @zhin.js/skill@1.1.11
|
|
39
|
+
- @zhin.js/tool@1.1.13
|
|
40
|
+
- @zhin.js/permission@1.1.10
|
|
41
|
+
|
|
42
|
+
## 1.1.18
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- 6345a38: feat(adapters): add per-adapter Console pages
|
|
47
|
+
|
|
48
|
+
Every platform adapter now ships a `pages/index` Console page backed by a shared
|
|
49
|
+
`createAdapterConsolePage` component and `createAdapterConsoleRpc` server helper:
|
|
50
|
+
|
|
51
|
+
- See running endpoints, test connectivity, and manage
|
|
52
|
+
`plugins.<adapter>.endpoints` (add/remove writes `.env` references) from the
|
|
53
|
+
Console instead of chat commands.
|
|
54
|
+
- Surface LoginAssist pending tasks (QR / slider / SMS / device) so ICQQ login no
|
|
55
|
+
longer requires the terminal.
|
|
56
|
+
- QQ moves its QR binding flow (`qq endpoint add`) into Console actions with
|
|
57
|
+
public/private selection.
|
|
58
|
+
- Pages are permission-gated by `<adapter>:read`; writes need `<adapter>:write`.
|
|
59
|
+
|
|
60
|
+
- ff3efe7: feat(adapters): notify masters when ICQQ or QQ goes offline and support ICQQ re-login
|
|
61
|
+
|
|
62
|
+
- Add an explicit `notify` target to ICQQ and QQ endpoint config: when the
|
|
63
|
+
transport goes offline or login fails, send a short notice through another
|
|
64
|
+
adapter instance's Endpoint via `OutboundHost`.
|
|
65
|
+
- ICQQ automatically retries login after `system.offline.kickoff` when
|
|
66
|
+
`autoReconnect` is enabled, and exposes pending QR/slider/device challenges.
|
|
67
|
+
- Add `icqq login status|qr|confirm|code|retry` recovery commands. They can be
|
|
68
|
+
invoked from the QQ conversation that received the notice; `qr` returns the
|
|
69
|
+
login QR code as an image when the notifying platform supports it.
|
|
70
|
+
- `notify.id` also counts as the master identity for the related endpoint
|
|
71
|
+
management commands, so recovery can happen from the notified conversation.
|
|
72
|
+
- Document the new config and commands in the ICQQ/QQ READMEs and generated
|
|
73
|
+
configuration reference.
|
|
74
|
+
|
|
75
|
+
- 26888e4: QQ 官方适配器按官方 v2 接口向 ICQQ 能力对齐:新增 `qq-conversation` / `qq-messages` / `qq-group-directory` / `qq-group-admin` / `qq-group-state` 技能(会话信息、引用回复、撤回自己 / 成员消息、群信息与成员、移出、禁言、入群审批、禁言列表、入群申请、黑名单)及频道成员禁言 / 移出;Endpoint 管理端口补齐群成员列表、踢人、禁言与自动群审核;撤回支持入站原生消息 ID,频道消息支持表情回应。好友 / 群列表、合并转发、戳一戳、点赞、公告、精华、头衔、退群等官方无接口的能力明确标注为不支持。
|
|
76
|
+
- Updated dependencies [6345a38]
|
|
77
|
+
- Updated dependencies [ff3efe7]
|
|
78
|
+
- Updated dependencies [c58c589]
|
|
79
|
+
- @zhin.js/adapter@1.1.25
|
|
80
|
+
- @zhin.js/client@1.1.21
|
|
81
|
+
- @zhin.js/core@1.1.53
|
|
82
|
+
- @zhin.js/im-contract@1.1.9
|
|
83
|
+
- @zhin.js/tool@1.1.12
|
|
84
|
+
|
|
3
85
|
## 1.1.17
|
|
4
86
|
|
|
5
87
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -11,7 +11,10 @@ Zhin.js QQ 官方机器人适配器(Plugin Runtime),默认通过 **WebSock
|
|
|
11
11
|
- Webhook / middleware 模式已实现(经 `httpHostToken` 注册 POST 路由)
|
|
12
12
|
- AI `@` 触发标注:群消息(GROUP_AT_MESSAGE_CREATE 仅 @ 时下发)与频道 `mentions[].bot` 会在入站 metadata 标 `mentioned: true`(新 Plugin Runtime 纯文本 content 经 metadata 传递 @)
|
|
13
13
|
- 群事件:机器人进群/退群、群成员加入/退出、入群申请(支持 `$approve()` / `$reject()`)
|
|
14
|
-
-
|
|
14
|
+
- 群管理(官方 v2 群接口):成员列表 / 详情、移出(可拉黑)、禁言(≤30 天)、入群申请审批、黑名单查询;Endpoint 管理端口与自动群审核(`inspectGroupMember` / `moderateGroupMember`)可直接使用
|
|
15
|
+
- 撤回:自己的消息,以及机器人为群管理员时普通成员 2 分钟内的消息(入站原生消息 ID 也可撤回)
|
|
16
|
+
- 频道子频道消息表情回应(`reaction` 操作,Unicode emoji 或 `type:id`)
|
|
17
|
+
- 完整 SDK 业务 API:当前消息操作的 `session.endpoint` 就是 `qq-official-bot` 实例,可直接使用群管理、禁言、自动审批策略、自定义菜单和指令面板等原生 Service
|
|
15
18
|
|
|
16
19
|
## 安装
|
|
17
20
|
|
|
@@ -22,12 +25,13 @@ pnpm add @zhin.js/adapter-qq
|
|
|
22
25
|
## Plugin Runtime
|
|
23
26
|
|
|
24
27
|
- `@zhin.js/adapter` — 约定式 `adapters/qq/index.ts`(`defineAdapter`)
|
|
25
|
-
- `@zhin.js/core` — `
|
|
28
|
+
- `@zhin.js/core` — `endpoint[endpointSpec].events` 入站、`outboundMessageToken` 出站
|
|
26
29
|
- `zhin.js` — `plugin.ts`(`definePlugin`)
|
|
27
30
|
- 配置经插件 `schema.json` 落到顶层 `<instanceKey>`
|
|
28
31
|
- **无需** `@zhin.js/host-http` / `@zhin.js/host-router`(WebSocket 路径)
|
|
29
32
|
|
|
30
|
-
入站:`
|
|
33
|
+
入站:`endpoint[endpointSpec].events.emit('message.receive', { conversation, message, content, sender, metadata })`(ConversationRef 结构化寻址)
|
|
34
|
+
|
|
31
35
|
出站:`send({ conversation, payload })` → `sendPrivateMessage` / `sendGroupMessage` / `sendGuildMessage`
|
|
32
36
|
|
|
33
37
|
## 前置条件
|
|
@@ -67,7 +71,7 @@ WebSocket Identify 的 `intents` **必须与开放平台已开通的权限一致
|
|
|
67
71
|
| `private`(私域) | `GUILD_MESSAGES`(频道全量) | 同上 |
|
|
68
72
|
|
|
69
73
|
公域机器人订阅 `GUILD_MESSAGES` 会 Identify 失败断连。显式配置 `intents` 时优先于 `botKind`。
|
|
70
|
-
`create-zhin` / `zhin setup` 会询问公/私域并写入;`qq endpoint add` 扫码成功后在同一会话追问 `public`/`private`(或
|
|
74
|
+
`create-zhin` / `zhin setup` 会询问公/私域并写入;`qq endpoint add` 扫码成功后在同一会话追问 `public`/`private`(或 `公域`/`私域`),确认后通过所属 Adapter 的单次 CAS 加密保存为未启动账号,需手动启动。
|
|
71
75
|
|
|
72
76
|
多账号:一个插件实例挂多个 endpoint(`endpoints` 数组逐项覆盖顶层字段,`id` 必填):
|
|
73
77
|
|
|
@@ -110,10 +114,10 @@ qq:
|
|
|
110
114
|
|
|
111
115
|
| 命令 | 说明 |
|
|
112
116
|
|------|------|
|
|
113
|
-
| `qq endpoint add [name]` | 手机 QQ 扫码绑定 → **追问公域/私域** →
|
|
117
|
+
| `qq endpoint add [name]` | 手机 QQ 扫码绑定 → **追问公域/私域** → 一次 CAS 保存为未启动账号(无需重启) |
|
|
114
118
|
| `qq endpoint cancel` | 取消进行中的扫码绑定或待确认的公域/私域选择 |
|
|
115
119
|
| `qq endpoint list` | 列出运行中与配置中的 endpoints |
|
|
116
|
-
| `qq endpoint remove <name>` |
|
|
120
|
+
| `qq endpoint remove <name>` | 删除账号配置,取消目标在途并关闭,保留历史 |
|
|
117
121
|
|
|
118
122
|
add/cancel/remove 受 `master` 限制:实例配置声明了 `master`(顶层或 `endpoints[i].master`)时仅
|
|
119
123
|
master 可执行;未配置则放行(首个扫码绑定者会写入该 endpoint 的 `master`)。二维码当前以链接文本下发
|
|
@@ -132,30 +136,48 @@ master 可执行;未配置则放行(首个扫码绑定者会写入该 endpoi
|
|
|
132
136
|
|
|
133
137
|
## AI 工具(Skill)
|
|
134
138
|
|
|
135
|
-
|
|
|
136
|
-
|
|
137
|
-
|
|
|
138
|
-
|
|
|
139
|
-
|
|
|
139
|
+
| 技能 | 工具 | 场景 |
|
|
140
|
+
|------|------|------|
|
|
141
|
+
| `qq-conversation` | `conversation_info` | 当前会话、机器人实时群角色(`bot_state`)、可用操作与官方限制 |
|
|
142
|
+
| `qq-messages` | `quote_reply`、`recall_own_message` | 私聊 / 群 / 频道 |
|
|
143
|
+
| `qq-group-directory` | `group_info`、`group_members`、`group_member_info` | 当前群 |
|
|
144
|
+
| `qq-group-admin` | `group_kick`、`set_mute`、`recall_member_message`、`handle_join_request` | 当前群(撤回也支持频道) |
|
|
145
|
+
| `qq-group-state` | `list_muted`、`list_join_requests`、`group_blacklist` | 当前群 |
|
|
146
|
+
| `qq-directory` | `list_guilds` | 任意 |
|
|
147
|
+
| `qq` | `list_channels`、`channel_info`、`list_roles`、`create_role`、`add_role`、`remove_role`、`member_detail`、`guild_mute_member`、`guild_kick_member` | 频道 |
|
|
148
|
+
|
|
149
|
+
群工具只作用于当前会话的群(`group_openid`),成员用 `member_openid` 标识;写操作前实时核验机器人角色。
|
|
150
|
+
`group_kick` / `handle_join_request` / `guild_kick_member` 每次人工审批,禁言类每次审批。Permit 词汇见 `PERMITS.md`。
|
|
151
|
+
|
|
152
|
+
### 与 ICQQ 的对齐情况
|
|
153
|
+
|
|
154
|
+
| ICQQ 能力 | QQ 官方 | 说明 |
|
|
155
|
+
|-----------|---------|------|
|
|
156
|
+
| `group_member_info`、`conversation_info`、`quote_reply`、`recall_own_message`、`recall_member_message`、`group_kick`、`set_mute`、`list_muted` | ✅ 对齐 | 官方 v2 群接口;成员 / 移出类为白名单能力(11253) |
|
|
157
|
+
| 入群申请审批、Console 成员列表 / 踢人 / 禁言、自动群审核 | ✅ 对齐 | `EndpointManagement` 端口 |
|
|
158
|
+
| 表情回应 | ⚠️ 部分 | 仅频道子频道消息;官方无群 / 单聊表情回应 |
|
|
159
|
+
| `friend_list`、`group_list`、`user_info` | ❌ 官方不支持 | 无机器人好友 / 群列表、任意用户资料接口 |
|
|
160
|
+
| `forward_messages` | ❌ 官方不支持 | 无合并转发发送接口 |
|
|
161
|
+
| `poke`、`send_user_like`、`group_sign` | ❌ 官方不支持 | |
|
|
162
|
+
| `announce`、`essence`、`set_title`、`set_anonymous`、`group_files`、`group_invite`、`group_leave` | ❌ 官方不支持 | 群公告 / 精华 / 头衔 / 匿名 / 群文件 / 邀请 / 退群均无接口 |
|
|
163
|
+
| 设管理员、删好友(Console) | ❌ 官方不支持 | |
|
|
140
164
|
|
|
141
165
|
## 平台 Client 与 QQ API
|
|
142
166
|
|
|
143
|
-
`
|
|
144
|
-
`client.api` 暴露完整的 `qq-official-bot` 1.3 业务 Service 与实体入口;连接启停仍由 Endpoint 生命周期统一管理:
|
|
167
|
+
在已有消息操作中,`session.endpoint` 是完整的 `qq-official-bot` 1.3 原生 Bot 实例,直接暴露业务 Service 与实体入口;账号连接启停仍通过所属 Adapter 管理:
|
|
145
168
|
|
|
146
169
|
```ts
|
|
147
|
-
|
|
170
|
+
// session 来自声明 adapter: 'qq' 的当前消息操作。
|
|
171
|
+
const client = session.endpoint;
|
|
148
172
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const requests = await client.api.groupService.getJoinRequests(groupOpenid);
|
|
153
|
-
await client.api.groupService.setMemberMute(groupOpenid, [
|
|
173
|
+
const group = await client.groupService.getInfo(groupOpenid);
|
|
174
|
+
const requests = await client.groupService.getJoinRequests(groupOpenid);
|
|
175
|
+
await client.groupService.setMemberMute(groupOpenid, [
|
|
154
176
|
{ op: 'add', member_openid: memberOpenid, mute_expire_at: expiresAt },
|
|
155
177
|
]);
|
|
156
178
|
|
|
157
|
-
const menu = await client.
|
|
158
|
-
const panels = await client.
|
|
179
|
+
const menu = await client.menuPanelService.getCustomMenu();
|
|
180
|
+
const panels = await client.menuPanelService.getCommandPanels({ scope: 'group' });
|
|
159
181
|
```
|
|
160
182
|
|
|
161
183
|
群管理接口中有部分是白名单能力,入群审批和禁言还要求机器人具备群管理员权限;
|
|
@@ -196,6 +218,38 @@ QQ 开放平台按有没有带上用户消息的 `msg_id`(或事件的 `event_
|
|
|
196
218
|
公域机器人在群里只能收到 @ 它的消息,所以群聊旁听(`ai.trigger.ambient`)只对拥有全量
|
|
197
219
|
消息权限的私域机器人有意义。
|
|
198
220
|
|
|
221
|
+
## Console 页面
|
|
222
|
+
|
|
223
|
+
启动后打开 Console 的「QQ」页(地址 `/qq`),可以:
|
|
224
|
+
|
|
225
|
+
- 查看运行中的连接、点「测试连接」做连通性诊断;
|
|
226
|
+
- 在账号库查看、修改、启停、替换或删除 QQ 账号;新增走扫码绑定,保存后需手动启动;
|
|
227
|
+
- 「平台操作」里扫码绑定新机器人:扫码 → 选择公域 / 私域 → 通过同一账号 CAS 保存为未启动状态;原来的 `qq endpoint add` 仍然保留。
|
|
228
|
+
|
|
229
|
+
读取需要 `qq:read`,写操作需要 `qq:write`;管理员 full token 不受限。页面里的「扫码绑定新机器人」等价于原来的 `qq endpoint add`。
|
|
230
|
+
|
|
231
|
+
首次修改启动配置里的既有账号前,管理员必须核对清单并确认一次迁移。原 canonical 身份与历史保留,完整账号配置加密进入 Host 数据库;此后账号管理只写数据库,不修改启动文件或 `.env`,不重启进程。没有持久化账号端口的环境可使用静态配置连接,动态写操作明确不可用。
|
|
232
|
+
|
|
233
|
+
启停意图在进程重启后恢复。停止或删除会取消并关闭目标账号的在途操作,其他账号继续运行;删除保留历史。已启动账号的连接配置更新先关闭旧 incarnation,再仅重连该账号;平台身份变化按替换处理,分配新内部身份、保留旧历史,新账号不继承旧 Session、个人设置或启动意图。提交结果未知时先刷新核实,不自动重复 CAS 或平台操作。
|
|
234
|
+
|
|
235
|
+
## 掉线通知
|
|
236
|
+
|
|
237
|
+
QQ 官方机器人掉线 / 启动失败时,可以配置 `notify` 让另一个适配器实例通知 master:
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
icqq:
|
|
241
|
+
# ... 已配置好的 ICQQ 实例
|
|
242
|
+
qq:
|
|
243
|
+
master: "${QQ_MASTER_OPENID}"
|
|
244
|
+
notify:
|
|
245
|
+
adapter: icqq
|
|
246
|
+
endpoint: "123456"
|
|
247
|
+
kind: private
|
|
248
|
+
id: "123456" # master 的 QQ 号
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
通知里会提示恢复方式:在 QQ 回复 `qq endpoint add` 重新扫码绑定(或打开 Console 的 QQ 页面)。`notify.id` 同时作为 endpoint 管理命令的 master 身份,方便从通知会话直接完成绑定。未配置 `notify` 时只在日志和 Console 的 QQ 页面显示连接状态。
|
|
252
|
+
|
|
199
253
|
## 故障排查
|
|
200
254
|
|
|
201
255
|
| 现象 | 排查 |
|
package/adapters/qq/index.js
CHANGED
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/qq/index.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { resolveQqConfig, } from "../../lib/protocol.js";
|
|
9
|
-
import { qqRuntimeStateToken } from "../../lib/qq-runtime-state.js";
|
|
6
|
+
import { QqAdapter } from "../../lib/adapter.js";
|
|
7
|
+
import {} from "../../lib/protocol.js";
|
|
10
8
|
export { QqHttpEndpoint, QqWebsocketEndpoint, } from "../../lib/endpoint.js";
|
|
11
9
|
export default defineAdapter({
|
|
12
10
|
capabilities: ['inbound', 'outbound'],
|
|
13
|
-
operations: ['recall'],
|
|
11
|
+
operations: ['recall', 'reaction'],
|
|
14
12
|
// 媒体 url 直发,base64/path 由 SDK formatMediaData 物化走 /files 上传;
|
|
15
13
|
// markdown/keyboard 原生按钮承载交互段。
|
|
16
14
|
segments: {
|
|
@@ -19,32 +17,5 @@ export default defineAdapter({
|
|
|
19
17
|
outboundMedia: ['url', 'upload'],
|
|
20
18
|
interactive: 'native',
|
|
21
19
|
},
|
|
22
|
-
|
|
23
|
-
const config = resolveQqConfig(context.config);
|
|
24
|
-
// 注册到插件运行时状态(qq endpoint list 的"运行中"数据源)
|
|
25
|
-
const state = context.use(qqRuntimeStateToken);
|
|
26
|
-
state.endpoints.set(config.id, {
|
|
27
|
-
id: config.id,
|
|
28
|
-
mode: config.mode,
|
|
29
|
-
});
|
|
30
|
-
// 正向判 websocket:TS 对 `mode === 'webhook' || mode === 'middleware'` 的
|
|
31
|
-
// 否定分支不做联合消减(多字面量判别式),反向写会让 config 窄化失败。
|
|
32
|
-
const endpoint = config.mode === 'websocket'
|
|
33
|
-
? new QqWebsocketEndpoint({
|
|
34
|
-
id: context.id,
|
|
35
|
-
config,
|
|
36
|
-
})
|
|
37
|
-
: new QqHttpEndpoint({
|
|
38
|
-
id: context.id,
|
|
39
|
-
http: context.use(httpHostToken),
|
|
40
|
-
config,
|
|
41
|
-
});
|
|
42
|
-
// 运行状态表只增不减:stop 时同步摘除,避免 stop 后 qq endpoint list 仍显示运行中。
|
|
43
|
-
const stopEndpoint = endpoint.stop.bind(endpoint);
|
|
44
|
-
endpoint.stop = async () => {
|
|
45
|
-
state.endpoints.delete(config.id);
|
|
46
|
-
await stopEndpoint();
|
|
47
|
-
};
|
|
48
|
-
return endpoint;
|
|
49
|
-
},
|
|
20
|
+
createAdapter: context => new QqAdapter(context),
|
|
50
21
|
});
|
package/adapters/qq/index.ts
CHANGED
|
@@ -2,36 +2,25 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/qq/index.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import {
|
|
6
|
-
import { QqHttpEndpoint, QqWebsocketEndpoint } from '../../src/endpoint.js';
|
|
5
|
+
import { QqAdapter } from '../../src/adapter.js';
|
|
7
6
|
import {
|
|
8
|
-
resolveQqConfig,
|
|
9
7
|
type QqEndpointConfig,
|
|
10
8
|
} from '../../src/protocol.js';
|
|
11
|
-
import { qqRuntimeStateToken } from '../../src/qq-runtime-state.js';
|
|
12
9
|
|
|
13
10
|
export {
|
|
14
11
|
QqHttpEndpoint,
|
|
15
12
|
QqWebsocketEndpoint,
|
|
16
13
|
} from '../../src/endpoint.js';
|
|
17
14
|
export type {
|
|
18
|
-
CreateQqBot,
|
|
19
|
-
CreateQqHttpBot,
|
|
20
15
|
QqBotTransport,
|
|
21
16
|
QqEndpointOptions,
|
|
22
17
|
QqHttpBotTransport,
|
|
23
18
|
QqHttpEndpointOptions,
|
|
24
19
|
} from '../../src/endpoint.js';
|
|
25
20
|
|
|
26
|
-
declare module '@zhin.js/core' {
|
|
27
|
-
interface AdapterEndpoints {
|
|
28
|
-
qq: import('../../src/endpoint.js').QqWebsocketEndpoint;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
21
|
export default defineAdapter<QqEndpointConfig>({
|
|
33
22
|
capabilities: ['inbound', 'outbound'],
|
|
34
|
-
operations: ['recall'],
|
|
23
|
+
operations: ['recall', 'reaction'],
|
|
35
24
|
// 媒体 url 直发,base64/path 由 SDK formatMediaData 物化走 /files 上传;
|
|
36
25
|
// markdown/keyboard 原生按钮承载交互段。
|
|
37
26
|
segments: {
|
|
@@ -40,32 +29,5 @@ export default defineAdapter<QqEndpointConfig>({
|
|
|
40
29
|
outboundMedia: ['url', 'upload'],
|
|
41
30
|
interactive: 'native',
|
|
42
31
|
},
|
|
43
|
-
|
|
44
|
-
const config = resolveQqConfig(context.config);
|
|
45
|
-
// 注册到插件运行时状态(qq endpoint list 的"运行中"数据源)
|
|
46
|
-
const state = context.use(qqRuntimeStateToken);
|
|
47
|
-
state.endpoints.set(config.id, {
|
|
48
|
-
id: config.id,
|
|
49
|
-
mode: config.mode,
|
|
50
|
-
});
|
|
51
|
-
// 正向判 websocket:TS 对 `mode === 'webhook' || mode === 'middleware'` 的
|
|
52
|
-
// 否定分支不做联合消减(多字面量判别式),反向写会让 config 窄化失败。
|
|
53
|
-
const endpoint = config.mode === 'websocket'
|
|
54
|
-
? new QqWebsocketEndpoint({
|
|
55
|
-
id: context.id,
|
|
56
|
-
config,
|
|
57
|
-
})
|
|
58
|
-
: new QqHttpEndpoint({
|
|
59
|
-
id: context.id,
|
|
60
|
-
http: context.use(httpHostToken),
|
|
61
|
-
config,
|
|
62
|
-
});
|
|
63
|
-
// 运行状态表只增不减:stop 时同步摘除,避免 stop 后 qq endpoint list 仍显示运行中。
|
|
64
|
-
const stopEndpoint = endpoint.stop.bind(endpoint);
|
|
65
|
-
endpoint.stop = async () => {
|
|
66
|
-
state.endpoints.delete(config.id);
|
|
67
|
-
await stopEndpoint();
|
|
68
|
-
};
|
|
69
|
-
return endpoint;
|
|
70
|
-
},
|
|
32
|
+
createAdapter: context => new QqAdapter(context),
|
|
71
33
|
});
|
package/agents/qq/boundaries.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
# Boundaries
|
|
2
2
|
|
|
3
3
|
Operate only on the active qq endpoint and authenticated conversation context. Respect every Tool permission and approval policy. Do not infer identifiers, roles, or authorization from display names.
|
|
4
|
+
|
|
5
|
+
QQ official bots only expose the documented Bot API. Group members are `member_openid` values scoped to this bot and group. There is no group list, friend list, user profile, poke, like, merged forward, leave group, announcement, essence, title, anonymous, group file or sign-in API; say so instead of simulating them. Many group management APIs are invite-only (error 11253).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { outboundMessageToken } from '@zhin.js/core/runtime';
|
|
3
|
+
import { parseCompoundMessageId, qqOutboundKind } from "../../lib/protocol.js";
|
|
4
|
+
// Shared with EndpointManagement so Console, auto-moderation and Agent tools enforce the same official limits.
|
|
5
|
+
export { kickQqGroupMember, muteQqGroupMember, QQ_GROUP_MUTE_MAX_SECONDS } from "../../lib/group-admin.js";
|
|
6
|
+
import { messages as t } from './messages.js';
|
|
7
|
+
/** The trusted turn owns the endpoint and address; model input cannot select another one. */
|
|
8
|
+
export function currentConversation(context) {
|
|
9
|
+
const { origin } = context;
|
|
10
|
+
if (origin.kind !== 'im' || origin.platform !== 'qq' || !origin.conversation) {
|
|
11
|
+
throw new Error(t(context.locale, 'conversationRequired'));
|
|
12
|
+
}
|
|
13
|
+
context.signal.throwIfAborted();
|
|
14
|
+
return origin.conversation;
|
|
15
|
+
}
|
|
16
|
+
/** Current QQ group_openid. */
|
|
17
|
+
export function currentGroup(context) {
|
|
18
|
+
const conversation = currentConversation(context);
|
|
19
|
+
if (conversation.kind !== 'group')
|
|
20
|
+
throw new Error(t(context.locale, 'groupRequired'));
|
|
21
|
+
return conversation.id;
|
|
22
|
+
}
|
|
23
|
+
/** Current guild child channel and its guild. */
|
|
24
|
+
export function currentGuildChannel(context) {
|
|
25
|
+
const conversation = currentConversation(context);
|
|
26
|
+
const guildId = conversation.parent?.kind === 'channel' ? conversation.parent.id : '';
|
|
27
|
+
if (conversation.kind !== 'channel' || !guildId)
|
|
28
|
+
throw new Error(t(context.locale, 'channelRequired'));
|
|
29
|
+
return { guildId, channelId: conversation.id };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a message ID inside the current conversation. Outbound receipts are `kind-target:msgId`
|
|
33
|
+
* and must match the current conversation; anything else is a native inbound ID of this conversation.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveMessageTarget(context, id) {
|
|
36
|
+
const conversation = currentConversation(context);
|
|
37
|
+
const kind = qqOutboundKind(conversation);
|
|
38
|
+
const value = id.trim();
|
|
39
|
+
if (!value)
|
|
40
|
+
throw new Error(t(context.locale, 'messageOutsideConversation'));
|
|
41
|
+
let compound;
|
|
42
|
+
try {
|
|
43
|
+
compound = parseCompoundMessageId(value);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
compound = undefined;
|
|
47
|
+
}
|
|
48
|
+
if (compound) {
|
|
49
|
+
if (compound.kind !== kind || compound.channelId !== conversation.id || !compound.qqMsgId) {
|
|
50
|
+
throw new Error(t(context.locale, 'messageOutsideConversation'));
|
|
51
|
+
}
|
|
52
|
+
return { kind, targetId: conversation.id, messageId: compound.qqMsgId, ownOutbound: true };
|
|
53
|
+
}
|
|
54
|
+
return { kind, targetId: conversation.id, messageId: value, ownOutbound: false };
|
|
55
|
+
}
|
|
56
|
+
export async function recallTarget(context, target) {
|
|
57
|
+
context.signal.throwIfAborted();
|
|
58
|
+
const client = context.$client;
|
|
59
|
+
const recall = {
|
|
60
|
+
private: client.recallPrivateMessage, group: client.recallGroupMessage,
|
|
61
|
+
channel: client.recallGuildMessage, direct: client.recallDirectMessage,
|
|
62
|
+
}[target.kind];
|
|
63
|
+
if (!recall)
|
|
64
|
+
return { success: false, message: t(context.locale, 'recallUnconfirmed') };
|
|
65
|
+
try {
|
|
66
|
+
const result = await recall.call(client, target.targetId, target.messageId);
|
|
67
|
+
const success = result !== false;
|
|
68
|
+
return { success, message: t(context.locale, success ? 'messageRecalled' : 'recallUnconfirmed') };
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
message: t(context.locale, 'recallUnconfirmed'),
|
|
74
|
+
error: error instanceof Error ? error.message : String(error),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function sendToConversation(context, content) {
|
|
79
|
+
const conversation = currentConversation(context);
|
|
80
|
+
const receipt = await context.use(outboundMessageToken).send({
|
|
81
|
+
requester: context.owner.id,
|
|
82
|
+
conversation,
|
|
83
|
+
content,
|
|
84
|
+
});
|
|
85
|
+
return { success: receipt.status === 'sent', receipt };
|
|
86
|
+
}
|
|
87
|
+
/** Live check: the bot must be owner/admin of the current group. */
|
|
88
|
+
export async function requireGroupManager(context, groupId) {
|
|
89
|
+
const state = await context.$client.groupService.getBotState(groupId);
|
|
90
|
+
context.signal.throwIfAborted();
|
|
91
|
+
return state.member_role === 'owner' || state.member_role === 'admin' ? state : undefined;
|
|
92
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { outboundMessageToken, type SendContent } from '@zhin.js/core/runtime';
|
|
2
|
+
import type { ToolExecutionContext } from '@zhin.js/tool';
|
|
3
|
+
import { parseCompoundMessageId, qqOutboundKind, type QqChannelKind } from '../../src/protocol.js';
|
|
4
|
+
// Shared with EndpointManagement so Console, auto-moderation and Agent tools enforce the same official limits.
|
|
5
|
+
export { kickQqGroupMember, muteQqGroupMember, QQ_GROUP_MUTE_MAX_SECONDS } from '../../src/group-admin.js';
|
|
6
|
+
import { messages as t } from './messages.js';
|
|
7
|
+
|
|
8
|
+
export type QqToolContext = ToolExecutionContext<unknown, 'qq'>;
|
|
9
|
+
|
|
10
|
+
/** The trusted turn owns the endpoint and address; model input cannot select another one. */
|
|
11
|
+
export function currentConversation(context: QqToolContext) {
|
|
12
|
+
const { origin } = context;
|
|
13
|
+
if (origin.kind !== 'im' || origin.platform !== 'qq' || !origin.conversation) {
|
|
14
|
+
throw new Error(t(context.locale, 'conversationRequired'));
|
|
15
|
+
}
|
|
16
|
+
context.signal.throwIfAborted();
|
|
17
|
+
return origin.conversation;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Current QQ group_openid. */
|
|
21
|
+
export function currentGroup(context: QqToolContext): string {
|
|
22
|
+
const conversation = currentConversation(context);
|
|
23
|
+
if (conversation.kind !== 'group') throw new Error(t(context.locale, 'groupRequired'));
|
|
24
|
+
return conversation.id;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Current guild child channel and its guild. */
|
|
28
|
+
export function currentGuildChannel(context: QqToolContext): { guildId: string; channelId: string } {
|
|
29
|
+
const conversation = currentConversation(context);
|
|
30
|
+
const guildId = conversation.parent?.kind === 'channel' ? conversation.parent.id : '';
|
|
31
|
+
if (conversation.kind !== 'channel' || !guildId) throw new Error(t(context.locale, 'channelRequired'));
|
|
32
|
+
return { guildId, channelId: conversation.id };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface QqMessageTarget {
|
|
36
|
+
readonly kind: QqChannelKind;
|
|
37
|
+
readonly targetId: string;
|
|
38
|
+
readonly messageId: string;
|
|
39
|
+
/** `send()` receipt IDs are compound; only those are known to be the bot's own messages. */
|
|
40
|
+
readonly ownOutbound: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a message ID inside the current conversation. Outbound receipts are `kind-target:msgId`
|
|
45
|
+
* and must match the current conversation; anything else is a native inbound ID of this conversation.
|
|
46
|
+
*/
|
|
47
|
+
export function resolveMessageTarget(context: QqToolContext, id: string): QqMessageTarget {
|
|
48
|
+
const conversation = currentConversation(context);
|
|
49
|
+
const kind = qqOutboundKind(conversation);
|
|
50
|
+
const value = id.trim();
|
|
51
|
+
if (!value) throw new Error(t(context.locale, 'messageOutsideConversation'));
|
|
52
|
+
let compound: ReturnType<typeof parseCompoundMessageId> | undefined;
|
|
53
|
+
try { compound = parseCompoundMessageId(value); } catch { compound = undefined; }
|
|
54
|
+
if (compound) {
|
|
55
|
+
if (compound.kind !== kind || compound.channelId !== conversation.id || !compound.qqMsgId) {
|
|
56
|
+
throw new Error(t(context.locale, 'messageOutsideConversation'));
|
|
57
|
+
}
|
|
58
|
+
return { kind, targetId: conversation.id, messageId: compound.qqMsgId, ownOutbound: true };
|
|
59
|
+
}
|
|
60
|
+
return { kind, targetId: conversation.id, messageId: value, ownOutbound: false };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function recallTarget(context: QqToolContext, target: QqMessageTarget) {
|
|
64
|
+
context.signal.throwIfAborted();
|
|
65
|
+
const client = context.$client;
|
|
66
|
+
const recall = {
|
|
67
|
+
private: client.recallPrivateMessage, group: client.recallGroupMessage,
|
|
68
|
+
channel: client.recallGuildMessage, direct: client.recallDirectMessage,
|
|
69
|
+
}[target.kind];
|
|
70
|
+
if (!recall) return { success: false, message: t(context.locale, 'recallUnconfirmed') };
|
|
71
|
+
try {
|
|
72
|
+
const result = await recall.call(client, target.targetId, target.messageId);
|
|
73
|
+
const success = result !== false;
|
|
74
|
+
return { success, message: t(context.locale, success ? 'messageRecalled' : 'recallUnconfirmed') };
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
message: t(context.locale, 'recallUnconfirmed'),
|
|
79
|
+
error: error instanceof Error ? error.message : String(error),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function sendToConversation(context: QqToolContext, content: SendContent) {
|
|
85
|
+
const conversation = currentConversation(context);
|
|
86
|
+
const receipt = await context.use(outboundMessageToken).send({
|
|
87
|
+
requester: context.owner.id,
|
|
88
|
+
conversation,
|
|
89
|
+
content,
|
|
90
|
+
});
|
|
91
|
+
return { success: receipt.status === 'sent', receipt };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Live check: the bot must be owner/admin of the current group. */
|
|
95
|
+
export async function requireGroupManager(context: QqToolContext, groupId: string) {
|
|
96
|
+
const state = await context.$client.groupService.getBotState(groupId);
|
|
97
|
+
context.signal.throwIfAborted();
|
|
98
|
+
return state.member_role === 'owner' || state.member_role === 'admin' ? state : undefined;
|
|
99
|
+
}
|
package/agents/qq/messages.js
CHANGED
|
@@ -20,5 +20,65 @@ export const messages = createMessages({
|
|
|
20
20
|
"roleRemoved": {
|
|
21
21
|
"zh-CN": "已移除成员的角色",
|
|
22
22
|
"en": "Role removed from the member"
|
|
23
|
+
},
|
|
24
|
+
"conversationRequired": {
|
|
25
|
+
"zh-CN": "此操作需要当前 QQ 官方机器人会话",
|
|
26
|
+
"en": "This operation requires the current QQ official bot conversation"
|
|
27
|
+
},
|
|
28
|
+
"groupRequired": {
|
|
29
|
+
"zh-CN": "此操作仅支持当前 QQ 群",
|
|
30
|
+
"en": "This operation requires the current QQ group"
|
|
31
|
+
},
|
|
32
|
+
"channelRequired": {
|
|
33
|
+
"zh-CN": "此操作仅支持当前 QQ 频道子频道",
|
|
34
|
+
"en": "This operation requires the current QQ guild channel"
|
|
35
|
+
},
|
|
36
|
+
"messageOutsideConversation": {
|
|
37
|
+
"zh-CN": "该消息 ID 不属于当前会话,请勿猜测消息 ID",
|
|
38
|
+
"en": "The message ID does not belong to this conversation; do not guess message IDs"
|
|
39
|
+
},
|
|
40
|
+
"ownMessageRequired": {
|
|
41
|
+
"zh-CN": "该工具只能撤回机器人自己发送的消息(发送回执里的消息 ID)",
|
|
42
|
+
"en": "This tool can only recall messages the bot sent (the ID from its send receipt)"
|
|
43
|
+
},
|
|
44
|
+
"botNotManager": {
|
|
45
|
+
"zh-CN": "机器人当前不是本群群主或管理员,平台不允许此操作",
|
|
46
|
+
"en": "The bot is not an owner or admin of this group, so the platform does not allow this"
|
|
47
|
+
},
|
|
48
|
+
"targetNotMember": {
|
|
49
|
+
"zh-CN": "目标必须是本群的普通成员(官方不允许处置群主、管理员或机器人)",
|
|
50
|
+
"en": "The target must be an ordinary member of this group (owners, admins and bots cannot be targeted)"
|
|
51
|
+
},
|
|
52
|
+
"messageRecalled": {
|
|
53
|
+
"zh-CN": "平台已确认撤回消息",
|
|
54
|
+
"en": "The platform confirmed the message was recalled"
|
|
55
|
+
},
|
|
56
|
+
"recallUnconfirmed": {
|
|
57
|
+
"zh-CN": "平台未确认撤回成功(官方只允许撤回 2 分钟内的消息),请勿自动重试",
|
|
58
|
+
"en": "Recall was not confirmed (QQ only allows recalling messages younger than 2 minutes); do not retry automatically"
|
|
59
|
+
},
|
|
60
|
+
"memberKicked": {
|
|
61
|
+
"zh-CN": "已将成员 {p0} 移出本群",
|
|
62
|
+
"en": "Removed member {p0} from this group"
|
|
63
|
+
},
|
|
64
|
+
"blacklistFailed": {
|
|
65
|
+
"zh-CN": "成员已移出,但加入黑名单失败",
|
|
66
|
+
"en": "The member was removed, but adding them to the blacklist failed"
|
|
67
|
+
},
|
|
68
|
+
"muted": {
|
|
69
|
+
"zh-CN": "已禁言 {user} {seconds} 秒",
|
|
70
|
+
"en": "Muted {user} for {seconds} seconds"
|
|
71
|
+
},
|
|
72
|
+
"unmuted": {
|
|
73
|
+
"zh-CN": "已解除 {user} 的禁言",
|
|
74
|
+
"en": "Unmuted {user}"
|
|
75
|
+
},
|
|
76
|
+
"joinApproved": {
|
|
77
|
+
"zh-CN": "已通过 {user} 的入群申请",
|
|
78
|
+
"en": "Approved the join request from {user}"
|
|
79
|
+
},
|
|
80
|
+
"joinDeclined": {
|
|
81
|
+
"zh-CN": "已拒绝 {user} 的入群申请",
|
|
82
|
+
"en": "Declined the join request from {user}"
|
|
23
83
|
}
|
|
24
84
|
});
|