@zhin.js/adapter-icqq 1.1.16 → 1.1.18
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 +72 -0
- package/README.md +44 -3
- package/adapters/icqq/index.js +10 -2
- package/adapters/icqq/index.ts +10 -2
- package/agents/icqq/members.js +16 -0
- package/agents/icqq/members.ts +17 -0
- package/agents/icqq/skills/icqq-group-admin/tools/essence/index.js +1 -1
- package/agents/icqq/skills/icqq-group-admin/tools/essence/index.ts +1 -1
- package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.js +2 -2
- package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.ts +2 -2
- package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.js +1 -0
- package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.ts +1 -0
- package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.js +2 -1
- package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.ts +2 -1
- package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.js +2 -2
- package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.ts +2 -2
- package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.js +4 -4
- package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.ts +4 -4
- package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.js +4 -4
- package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.ts +4 -4
- package/agents/icqq/skills/icqq-group-directory/tools/group_member_info/index.js +16 -6
- package/agents/icqq/skills/icqq-group-directory/tools/group_member_info/index.ts +16 -5
- package/agents/icqq/skills/icqq-messages/tools/forward_messages/index.js +1 -1
- package/agents/icqq/skills/icqq-messages/tools/forward_messages/index.ts +1 -1
- package/agents/icqq/skills/icqq-messages/tools/quote_reply/index.js +1 -1
- package/agents/icqq/skills/icqq-messages/tools/quote_reply/index.ts +1 -1
- package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.js +1 -1
- package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.ts +1 -1
- package/agents/icqq/system.md +3 -3
- package/commands/icqq/login/code/index.js +28 -0
- package/commands/icqq/login/code/index.ts +30 -0
- package/commands/icqq/login/confirm/index.js +20 -0
- package/commands/icqq/login/confirm/index.ts +21 -0
- package/commands/icqq/login/definition.js +65 -0
- package/commands/icqq/login/definition.ts +72 -0
- package/commands/icqq/login/qr/index.js +53 -0
- package/commands/icqq/login/qr/index.ts +55 -0
- package/commands/icqq/login/retry/index.js +21 -0
- package/commands/icqq/login/retry/index.ts +23 -0
- package/commands/icqq/login/status/index.js +31 -0
- package/commands/icqq/login/status/index.ts +34 -0
- package/lib/console-spec.d.ts +17 -0
- package/lib/console-spec.js +11 -0
- package/lib/console.d.ts +8 -0
- package/lib/console.js +16 -0
- package/lib/endpoint.d.ts +9 -2
- package/lib/endpoint.js +200 -45
- package/lib/group-moderation.js +7 -2
- package/lib/group-scene.d.ts +11 -0
- package/lib/group-scene.js +71 -0
- package/lib/icqq-inbound.d.ts +2 -0
- package/lib/icqq-inbound.js +3 -0
- package/lib/icqq-inbox.d.ts +5 -0
- package/lib/icqq-inbox.js +14 -0
- package/lib/icqq-login-control.d.ts +18 -0
- package/lib/icqq-login-control.js +8 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +12 -0
- package/lib/protocol.js +28 -11
- package/package.json +17 -9
- package/pages/index/index.js +15 -0
- package/pages/index/index.ts +16 -0
- package/plugin.js +6 -1
- package/schema.json +174 -0
- package/src/console-spec.ts +17 -0
- package/src/console.ts +21 -0
- package/src/endpoint.ts +199 -49
- package/src/group-moderation.ts +7 -2
- package/src/group-scene.ts +76 -0
- package/src/icqq-inbound.ts +5 -0
- package/src/icqq-inbox.ts +15 -0
- package/src/icqq-login-control.ts +30 -0
- package/src/index.ts +2 -0
- package/src/protocol.ts +38 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,77 @@
|
|
|
1
1
|
# @zhin.js/adapter-icqq
|
|
2
2
|
|
|
3
|
+
## 1.1.18
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6345a38: feat(adapters): add per-adapter Console pages
|
|
8
|
+
|
|
9
|
+
Every platform adapter now ships a `pages/index` Console page backed by a shared
|
|
10
|
+
`createAdapterConsolePage` component and `createAdapterConsoleRpc` server helper:
|
|
11
|
+
|
|
12
|
+
- See running endpoints, test connectivity, and manage
|
|
13
|
+
`plugins.<adapter>.endpoints` (add/remove writes `.env` references) from the
|
|
14
|
+
Console instead of chat commands.
|
|
15
|
+
- Surface LoginAssist pending tasks (QR / slider / SMS / device) so ICQQ login no
|
|
16
|
+
longer requires the terminal.
|
|
17
|
+
- QQ moves its QR binding flow (`qq endpoint add`) into Console actions with
|
|
18
|
+
public/private selection.
|
|
19
|
+
- Pages are permission-gated by `<adapter>:read`; writes need `<adapter>:write`.
|
|
20
|
+
|
|
21
|
+
- ff3efe7: feat(adapters): notify masters when ICQQ or QQ goes offline and support ICQQ re-login
|
|
22
|
+
|
|
23
|
+
- Add an explicit `notify` target to ICQQ and QQ endpoint config: when the
|
|
24
|
+
transport goes offline or login fails, send a short notice through another
|
|
25
|
+
adapter instance's Endpoint via `OutboundHost`.
|
|
26
|
+
- ICQQ automatically retries login after `system.offline.kickoff` when
|
|
27
|
+
`autoReconnect` is enabled, and exposes pending QR/slider/device challenges.
|
|
28
|
+
- Add `icqq login status|qr|confirm|code|retry` recovery commands. They can be
|
|
29
|
+
invoked from the QQ conversation that received the notice; `qr` returns the
|
|
30
|
+
login QR code as an image when the notifying platform supports it.
|
|
31
|
+
- `notify.id` also counts as the master identity for the related endpoint
|
|
32
|
+
management commands, so recovery can happen from the notified conversation.
|
|
33
|
+
- Document the new config and commands in the ICQQ/QQ READMEs and generated
|
|
34
|
+
configuration reference.
|
|
35
|
+
|
|
36
|
+
- c58c589: Group deferred tools by their canonical plugin or MCP server instead of installation paths. Omit already represented delivered text and ledger facts only from the current model projection, preserving full history and restoring facts when their covering reply leaves the active context. Allow tools to declare a synchronous, read-only availability predicate before turn disclosure, with execution-time checks remaining authoritative.
|
|
37
|
+
|
|
38
|
+
Preserve every confirmed platform message identity across Agent replies and ICQQ split sends. Give each bubble its own stable single-message alias while retaining the reply's ordered association; preserve confirmed prefixes on partial delivery without retrying or inventing unknown message IDs.
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [6345a38]
|
|
41
|
+
- Updated dependencies [ff3efe7]
|
|
42
|
+
- Updated dependencies [c58c589]
|
|
43
|
+
- @zhin.js/adapter@1.1.25
|
|
44
|
+
- @zhin.js/client@1.1.21
|
|
45
|
+
- @zhin.js/core@1.1.53
|
|
46
|
+
- @zhin.js/im-contract@1.1.9
|
|
47
|
+
- @zhin.js/tool@1.1.12
|
|
48
|
+
|
|
49
|
+
## 1.1.17
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- e23cabc: ADR 0041 slice B: the turn context now carries real scene facts. `EndpointManagement` gains `inspectGroupScene` (name, owner, admins, member count, global mute) and `inspectGroupMemberProfile` (role, card, title, mute expiry; one lookup), implemented by the ICQQ adapter from uncached group/member reads. The CLI resolves them per turn (bot role always fresh; group and sender facts reuse a 60 s, 256-entry in-memory cache scoped to the generation; failures omit fields) and the inbound actor now carries `groupNickname` / `groupTitle` / `mutedUntil` plus the platform role. The Session / Bot state blocks are replaced by one projected scene line `[scene_id:…;scene_type:…;scene_name:…;owner_id:…;admins:…;member_count:…;global_mute:…;assistant_role:…;]` (values escaped and clipped to 64 chars). No new on-disk state.
|
|
54
|
+
- 4d728c0: ADR 0043 §4 + §5 (H4) and the ADR 0041 actor-only amendment. Participant headers are no longer stored: user messages keep only `actor`, assistant messages keep `assistantActor` (plus the delivered `messageId`), and the header is rendered at the LLM boundary; legacy rows that still carry `name` are read as before. Headers now use session message aliases `id:m<n>` (an ordinal over the stored active path, so already-sent prefixes never change), `time:HH:mm` shown only when a minute has passed since the last shown time (the first message and the first message of a day also carry `date`), and `reply_to:m<n>` when the message replies to one inside the visible window. A reply to a message outside the window gets one `[quote id:…;user_id:…;time:…;] <first 120 chars>` line (media as placeholders, a forward expanded one level) so the model no longer needs `inspect_conversation_reference` for ordinary quotes; the quote is resolved by the ingress through the reference port (3 s timeout) and stored as structured `UserMessage.quote`. Tool arguments named `message_id` / `message_ids` / `*_message_id` / `reference` and `reply` segments accept the alias as well as the platform id; `inspect_conversation_reference` also accepts the quoted message id.
|
|
55
|
+
- 51f1328: 修复 icqq 群成员相关 Agent 工具:`group_member_info` 返回空对象(模型据此误判成员不在群里)、`group_invite` 永远判定"已在群里"、`set_mute` / `set_title` / `set_anonymous` 读旧缓存误判 Bot 不是管理员。统一改为实时查询群成员。
|
|
56
|
+
- d0e1fac: Keep stable prompt instructions ahead of turn-specific capabilities, project unchanged context without placeholder text, and retain scoped state snapshots with readable updates and compaction recovery. Preserve native conversation/tool chronology and separate participant identity from runtime guidance.
|
|
57
|
+
|
|
58
|
+
Resolve the ICQQ bot account and current-group role before capability discovery, and support explicitly declared current-conversation bot-role requirements without changing existing requester permissions or approvals. Keep role lookup failures unknown and preserve execution-time platform checks.
|
|
59
|
+
|
|
60
|
+
- 3771f7a: 修复同一群先后两人入群时后一次入群被当重复事件丢掉:side event 去重键(core 与 icqq)加入成员与操作人。
|
|
61
|
+
- 4f96ad5: Fix member-join welcome display: icqq now sends the mention's display name as the `at` element text, the joining member's nickname from the notice is threaded into `target.name`, and icqq group messages populate `conversation.name` from `group_name` so `{group}` renders the group name instead of the group number.
|
|
62
|
+
- Updated dependencies [f67883e]
|
|
63
|
+
- Updated dependencies [e23cabc]
|
|
64
|
+
- Updated dependencies [4d728c0]
|
|
65
|
+
- Updated dependencies [eb8feeb]
|
|
66
|
+
- Updated dependencies [24a2b71]
|
|
67
|
+
- Updated dependencies [d0e1fac]
|
|
68
|
+
- Updated dependencies [3771f7a]
|
|
69
|
+
- Updated dependencies [4f96ad5]
|
|
70
|
+
- @zhin.js/core@1.1.52
|
|
71
|
+
- @zhin.js/adapter@1.1.24
|
|
72
|
+
- @zhin.js/tool@1.1.11
|
|
73
|
+
- @zhin.js/im-contract@1.1.8
|
|
74
|
+
|
|
3
75
|
## 1.1.16
|
|
4
76
|
|
|
5
77
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -173,6 +173,47 @@ ICQQ Agent 可按需加载 `icqq-conversation` 查询当前群/频道和 Bot
|
|
|
173
173
|
|
|
174
174
|
`icqq-group-admin` 新增撤回他人消息与退群:前者保留群管发起权限、实时平台角色检查,无需逐次审批;退群仅主人可发起、每次审批,Bot 为群主时拒绝执行。引用/转发走统一发送链路,不支持跨会话转发。当前 guild-channel 接入仅提供身份查询,不能使用这些群/私聊操作。详见 [权限与能力说明](./PERMITS.md)。
|
|
175
175
|
|
|
176
|
+
## Console 页面
|
|
177
|
+
|
|
178
|
+
启动后打开 Console 的「ICQQ」页(地址 `/icqq`),可以:
|
|
179
|
+
|
|
180
|
+
- 查看运行中的连接、点「测试连接」做连通性诊断;
|
|
181
|
+
- 增删 `plugins.icqq.endpoints`(凭据写入 `.env`,配置里只保留 `${ENV}` 引用);
|
|
182
|
+
- 处理登录待办(扫码 / 滑块 / 设备确认等,来自 LoginAssist);
|
|
183
|
+
|
|
184
|
+
ICQQ 首次登录的二维码、滑块、设备确认会出现在「登录待办」里,不需要再回到终端确认。
|
|
185
|
+
|
|
186
|
+
读取需要 `icqq:read`,写操作需要 `icqq:write`;管理员 full token 不受限。页面里的「添加 endpoint」等价于原来的 `icqq endpoint add`。
|
|
187
|
+
|
|
188
|
+
## 掉线通知与重新登录
|
|
189
|
+
|
|
190
|
+
配置 `notify` 后,ICQQ 被踢下线、登录失败或启动失败时,会通过另一个适配器实例给 master 发通知。例如用 QQ 官方机器人实例通知:
|
|
191
|
+
|
|
192
|
+
```yaml
|
|
193
|
+
qq:
|
|
194
|
+
# ... 已配置好的 QQ 官方机器人
|
|
195
|
+
icqq:
|
|
196
|
+
master: 123456
|
|
197
|
+
autoReconnect: true # 默认 true:被踢下线后自动重新登录
|
|
198
|
+
notify:
|
|
199
|
+
adapter: qq
|
|
200
|
+
endpoint: bot-1
|
|
201
|
+
kind: private
|
|
202
|
+
id: "${QQ_MASTER_OPENID}" # master 在 QQ 官方机器人里的 openid
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`autoReconnect: true` 时,被踢下线会自动重试登录;二维码/滑块/设备确认就绪后,通知会提示下一步。收到通知后可以直接在 QQ 里完成:
|
|
206
|
+
|
|
207
|
+
| 命令 | 作用 |
|
|
208
|
+
|------|------|
|
|
209
|
+
| `icqq login qr` | 把 ICQQ 登录二维码发到当前会话(QQ 支持图片时) |
|
|
210
|
+
| `icqq login confirm` | 扫码后确认继续登录 |
|
|
211
|
+
| `icqq login code <内容>` | 提交滑块 ticket、短信验证码或设备确认内容 |
|
|
212
|
+
| `icqq login status` | 查看在线状态与登录待办 |
|
|
213
|
+
| `icqq login retry` | 强制重新触发登录 |
|
|
214
|
+
|
|
215
|
+
命令只接受 master(顶层或 `endpoints[i].master`)或 `notify.id` 对应账号;两者都没配置时请用 Console 的 ICQQ 页面。没有配置 `notify` 时,掉线只写日志并在 Console 显示登录待办。
|
|
216
|
+
|
|
176
217
|
## 故障排查
|
|
177
218
|
|
|
178
219
|
| 现象 | 排查 |
|
|
@@ -190,14 +231,14 @@ MIT
|
|
|
190
231
|
|
|
191
232
|
发送成功回执只接受平台真实消息 ID。网络超时、断线、缺少消息 ID 的响应会报告 `deliveryUnknown`,框架不会自动重发;手动重发可能重复发送。
|
|
192
233
|
|
|
193
|
-
ICQQ 原生分享和文件 API 可能已经成功,但只返回空值或文件 ID;这类结果不能当作可引用、可撤回的消息 ID
|
|
234
|
+
ICQQ 原生分享和文件 API 可能已经成功,但只返回空值或文件 ID;这类结果不能当作可引用、可撤回的消息 ID。适配器不会根据时间和卡片类型从历史猜测本次分享的 ID;同一时刻的其他卡片也可能匹配。没有直接消息 ID 的部分保留为未知,平台操作仍只执行一次。文件撤回应使用平台专用文件 API。
|
|
194
235
|
|
|
195
236
|
### 独立元素按顺序拆分发送
|
|
196
237
|
|
|
197
238
|
ICQQ 要求分享、文件、语音、视频、JSON 卡片和合并转发单独成条。一条消息里同时有文字和这些元素时(例如 Agent 回复“找到了:”加一张音乐分享卡片),适配器按原顺序拆成多条依次发送,引用只附在第一条普通消息上。
|
|
198
239
|
|
|
199
|
-
-
|
|
200
|
-
- 第一条就失败时原样报告失败;后面的部分失败时报告 `partial_delivery
|
|
240
|
+
- 回执按发送顺序保留每部分的真实 ID 和原始段位置;没有 ID 的卡片或文件保留未知槽位,不把完整回复正文复制给每个 ID。所有部分都没有 ID(如单独一张分享)时,仍报告投递结果未知。
|
|
241
|
+
- 第一条就失败时原样报告失败;后面的部分失败时报告 `partial_delivery`(结果未知),保留已完成部分的回执。前面的调用可能已被平台接收,重发可能重复。
|
|
201
242
|
|
|
202
243
|
## 语言
|
|
203
244
|
|
package/adapters/icqq/index.js
CHANGED
|
@@ -3,10 +3,12 @@ import { projectLocaleToken } from 'zhin.js/i18n';
|
|
|
3
3
|
/**
|
|
4
4
|
* Convention entry: discover `adapters/icqq/index.ts` → defineAdapter.
|
|
5
5
|
*/
|
|
6
|
-
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
+
import { createAdapterOutageNotifier, defineAdapter } from 'zhin.js/adapter';
|
|
7
7
|
import { loginAssistToken } from '@zhin.js/core/runtime';
|
|
8
|
+
import { outboundHostToken } from 'zhin.js';
|
|
8
9
|
import { IcqqEndpoint } from "../../lib/endpoint.js";
|
|
9
10
|
import { icqqRuntimeStateToken } from "../../lib/icqq-runtime-state.js";
|
|
11
|
+
import { icqqLoginControlToken } from "../../lib/icqq-login-control.js";
|
|
10
12
|
import { resolveIcqqConfig, } from "../../lib/protocol.js";
|
|
11
13
|
export { IcqqEndpoint } from "../../lib/endpoint.js";
|
|
12
14
|
export default defineAdapter({
|
|
@@ -44,11 +46,17 @@ export default defineAdapter({
|
|
|
44
46
|
id: config.id,
|
|
45
47
|
mode: 'direct',
|
|
46
48
|
});
|
|
47
|
-
|
|
49
|
+
const outbound = context.has?.(outboundHostToken) ? context.use(outboundHostToken) : undefined;
|
|
50
|
+
const endpoint = new IcqqEndpoint({
|
|
48
51
|
id: context.id,
|
|
49
52
|
loginAssist: context.use(loginAssistToken),
|
|
50
53
|
locale: context.use(projectLocaleToken),
|
|
51
54
|
config,
|
|
55
|
+
notifier: createAdapterOutageNotifier(outbound, config.notify),
|
|
52
56
|
});
|
|
57
|
+
if (context.has?.(icqqLoginControlToken)) {
|
|
58
|
+
context.use(icqqLoginControlToken).set(config.id, endpoint.loginControl);
|
|
59
|
+
}
|
|
60
|
+
return endpoint;
|
|
53
61
|
},
|
|
54
62
|
});
|
package/adapters/icqq/index.ts
CHANGED
|
@@ -2,10 +2,12 @@ import { projectLocaleToken } from 'zhin.js/i18n';
|
|
|
2
2
|
/**
|
|
3
3
|
* Convention entry: discover `adapters/icqq/index.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
|
-
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
+
import { createAdapterOutageNotifier, defineAdapter } from 'zhin.js/adapter';
|
|
6
6
|
import { loginAssistToken } from '@zhin.js/core/runtime';
|
|
7
|
+
import { outboundHostToken } from 'zhin.js';
|
|
7
8
|
import { IcqqEndpoint } from '../../src/endpoint.js';
|
|
8
9
|
import { icqqRuntimeStateToken } from '../../src/icqq-runtime-state.js';
|
|
10
|
+
import { icqqLoginControlToken } from '../../src/icqq-login-control.js';
|
|
9
11
|
import {
|
|
10
12
|
resolveIcqqConfig,
|
|
11
13
|
type IcqqEndpointConfig,
|
|
@@ -55,11 +57,17 @@ export default defineAdapter<IcqqEndpointConfig>({
|
|
|
55
57
|
id: config.id,
|
|
56
58
|
mode: 'direct',
|
|
57
59
|
});
|
|
58
|
-
|
|
60
|
+
const outbound = context.has?.(outboundHostToken) ? context.use(outboundHostToken) : undefined;
|
|
61
|
+
const endpoint = new IcqqEndpoint({
|
|
59
62
|
id: context.id,
|
|
60
63
|
loginAssist: context.use(loginAssistToken),
|
|
61
64
|
locale: context.use(projectLocaleToken),
|
|
62
65
|
config,
|
|
66
|
+
notifier: createAdapterOutageNotifier(outbound, config.notify),
|
|
63
67
|
});
|
|
68
|
+
if (context.has?.(icqqLoginControlToken)) {
|
|
69
|
+
context.use(icqqLoginControlToken).set(config.id, endpoint.loginControl);
|
|
70
|
+
}
|
|
71
|
+
return endpoint;
|
|
64
72
|
},
|
|
65
73
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
/**
|
|
3
|
+
* 实时查询群成员(跳过缓存);不在群里或查询失败返回 undefined。
|
|
4
|
+
* 不要用 `pickMember`:它总会返回实体对象(判空永远为真),`is_admin` 等也读旧缓存——Bot 刚被设为管理员时会误判。
|
|
5
|
+
*/
|
|
6
|
+
export async function freshMember(client, groupId, userId) {
|
|
7
|
+
try {
|
|
8
|
+
return await client.getGroupMemberInfo(groupId, userId, true);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function isGroupManager(info) {
|
|
15
|
+
return info?.role === 'owner' || info?.role === 'admin';
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Client, MemberInfo } from '@icqqjs/icqq';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 实时查询群成员(跳过缓存);不在群里或查询失败返回 undefined。
|
|
5
|
+
* 不要用 `pickMember`:它总会返回实体对象(判空永远为真),`is_admin` 等也读旧缓存——Bot 刚被设为管理员时会误判。
|
|
6
|
+
*/
|
|
7
|
+
export async function freshMember(client: Client, groupId: number, userId: number): Promise<MemberInfo | undefined> {
|
|
8
|
+
try {
|
|
9
|
+
return await client.getGroupMemberInfo(groupId, userId, true);
|
|
10
|
+
} catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isGroupManager(info: MemberInfo | undefined): boolean {
|
|
16
|
+
return info?.role === 'owner' || info?.role === 'admin';
|
|
17
|
+
}
|
|
@@ -5,7 +5,7 @@ import { z } from 'zod';
|
|
|
5
5
|
export default defineAgentTool({
|
|
6
6
|
description: '设置或移除 QQ 群精华消息',
|
|
7
7
|
inputSchema: z.object({
|
|
8
|
-
message_id: z.string().describe('消息 ID'),
|
|
8
|
+
message_id: z.string().describe('消息 ID,或消息头里的 m<n> 别名'),
|
|
9
9
|
action: z.enum(['add', 'remove']).describe('add=设为精华, remove=移除精华'),
|
|
10
10
|
}),
|
|
11
11
|
adapter: 'icqq',
|
|
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
export default defineAgentTool<{ message_id: string; action: 'add' | 'remove' }>({
|
|
5
5
|
description: '设置或移除 QQ 群精华消息',
|
|
6
6
|
inputSchema: z.object({
|
|
7
|
-
message_id: z.string().describe('消息 ID'),
|
|
7
|
+
message_id: z.string().describe('消息 ID,或消息头里的 m<n> 别名'),
|
|
8
8
|
action: z.enum(['add', 'remove']).describe('add=设为精华, remove=移除精华'),
|
|
9
9
|
}),
|
|
10
10
|
adapter: 'icqq',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { messages as t } from "../../../../messages.js";
|
|
3
|
+
import { freshMember } from "../../../../members.js";
|
|
3
4
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
export default defineAgentTool({
|
|
@@ -16,8 +17,7 @@ export default defineAgentTool({
|
|
|
16
17
|
const groupInfo = client.pickGroup(group_id);
|
|
17
18
|
if (!groupInfo)
|
|
18
19
|
return { success: false, message: t(context.locale, 'groupMissing') };
|
|
19
|
-
|
|
20
|
-
if (memberInfo)
|
|
20
|
+
if (await freshMember(client, group_id, user_id))
|
|
21
21
|
return { success: false, message: t(context.locale, 'memberExists') };
|
|
22
22
|
await client.inviteFriend(group_id, user_id);
|
|
23
23
|
return { success: true, message: t(context.locale, 'inviteAttempted', { p0: user_id, p1: group_id }) };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { messages as t } from "../../../../messages.js";
|
|
2
|
+
import { freshMember } from "../../../../members.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool<{ group_id: number; user_id: number }>({
|
|
@@ -14,8 +15,7 @@ export default defineAgentTool<{ group_id: number; user_id: number }>({
|
|
|
14
15
|
const client = context.$client;
|
|
15
16
|
const groupInfo= client.pickGroup(group_id);
|
|
16
17
|
if(!groupInfo) return { success: false, message: t(context.locale, 'groupMissing') };
|
|
17
|
-
|
|
18
|
-
if(memberInfo) return { success: false, message: t(context.locale, 'memberExists') };
|
|
18
|
+
if (await freshMember(client, group_id, user_id)) return { success: false, message: t(context.locale, 'memberExists') };
|
|
19
19
|
await client.inviteFriend(group_id, user_id);
|
|
20
20
|
return { success: true, message: t(context.locale, 'inviteAttempted', { p0: user_id, p1: group_id }) };
|
|
21
21
|
},
|
|
@@ -7,6 +7,7 @@ export default defineAgentTool({
|
|
|
7
7
|
description: '让机器人退出当前 QQ 群;仅主人可发起且每次人工审批,机器人是群主时拒绝执行以免解散群',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
|
+
currentConversationBotRoles: ['admin', 'member'],
|
|
10
11
|
permissions: ['role(master)'],
|
|
11
12
|
requiresApproval: 'human',
|
|
12
13
|
risk: 'dangerous',
|
|
@@ -7,6 +7,7 @@ export default defineAgentTool({
|
|
|
7
7
|
description: '让机器人退出当前 QQ 群;仅主人可发起且每次人工审批,机器人是群主时拒绝执行以免解散群',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
|
+
currentConversationBotRoles: ['admin', 'member'],
|
|
10
11
|
permissions: ['role(master)'],
|
|
11
12
|
requiresApproval: 'human',
|
|
12
13
|
risk: 'dangerous',
|
|
@@ -7,10 +7,11 @@ export default defineAgentTool({
|
|
|
7
7
|
description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及机器人具备实时平台权限,无需逐次审批',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
|
+
currentConversationBotRoles: ['owner', 'admin'],
|
|
10
11
|
permissions: ['role(master,admin,trusted,owner)'],
|
|
11
12
|
requiresApproval: 'never',
|
|
12
13
|
inputSchema: z.object({
|
|
13
|
-
message_id: z.string().min(1).describe('当前群中要撤回的真实消息 ID'),
|
|
14
|
+
message_id: z.string().min(1).describe('当前群中要撤回的真实消息 ID,或消息头里的 m<n> 别名'),
|
|
14
15
|
reason: z.string().min(1).describe('撤回的具体原因,自动处理必须依据已确认的群规'),
|
|
15
16
|
}),
|
|
16
17
|
async execute({ message_id }, context) {
|
|
@@ -7,10 +7,11 @@ export default defineAgentTool({
|
|
|
7
7
|
description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及机器人具备实时平台权限,无需逐次审批',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
|
+
currentConversationBotRoles: ['owner', 'admin'],
|
|
10
11
|
permissions: ['role(master,admin,trusted,owner)'],
|
|
11
12
|
requiresApproval: 'never',
|
|
12
13
|
inputSchema: z.object({
|
|
13
|
-
message_id: z.string().min(1).describe('当前群中要撤回的真实消息 ID'),
|
|
14
|
+
message_id: z.string().min(1).describe('当前群中要撤回的真实消息 ID,或消息头里的 m<n> 别名'),
|
|
14
15
|
reason: z.string().min(1).describe('撤回的具体原因,自动处理必须依据已确认的群规'),
|
|
15
16
|
}),
|
|
16
17
|
async execute({ message_id }, context) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { messages as t } from "../../../../messages.js";
|
|
3
|
+
import { freshMember, isGroupManager } from "../../../../members.js";
|
|
3
4
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
export default defineAgentTool({
|
|
@@ -15,8 +16,7 @@ export default defineAgentTool({
|
|
|
15
16
|
async execute({ group_id, enable }, context) {
|
|
16
17
|
const client = context.$client;
|
|
17
18
|
const on = enable ?? true;
|
|
18
|
-
|
|
19
|
-
if (!selfInfo.is_admin && !selfInfo.is_owner)
|
|
19
|
+
if (!isGroupManager(await freshMember(client, group_id, client.uin)))
|
|
20
20
|
return { success: false, message: t(context.locale, 'anonymousForbidden') };
|
|
21
21
|
await client.setGroupAnonymous(group_id, on);
|
|
22
22
|
return { success: true, message: on ? t(context.locale, 'anonymousEnabled') : t(context.locale, 'anonymousDisabled') };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { messages as t } from "../../../../messages.js";
|
|
2
|
+
import { freshMember, isGroupManager } from "../../../../members.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool<{ group_id: number; enable?: boolean }>({
|
|
@@ -14,8 +15,7 @@ export default defineAgentTool<{ group_id: number; enable?: boolean }>({
|
|
|
14
15
|
async execute({ group_id, enable }, context) {
|
|
15
16
|
const client = context.$client;
|
|
16
17
|
const on = enable ?? true;
|
|
17
|
-
|
|
18
|
-
if(!selfInfo.is_admin && !selfInfo.is_owner) return { success: false, message: t(context.locale, 'anonymousForbidden') };
|
|
18
|
+
if (!isGroupManager(await freshMember(client, group_id, client.uin))) return { success: false, message: t(context.locale, 'anonymousForbidden') };
|
|
19
19
|
await client.setGroupAnonymous(group_id, on);
|
|
20
20
|
return { success: true, message: on ? t(context.locale, 'anonymousEnabled') : t(context.locale, 'anonymousDisabled') };
|
|
21
21
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { messages as t } from "../../../../messages.js";
|
|
3
|
+
import { freshMember, isGroupManager } from "../../../../members.js";
|
|
3
4
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
export default defineAgentTool({
|
|
@@ -14,11 +15,10 @@ export default defineAgentTool({
|
|
|
14
15
|
requiresApproval: 'always',
|
|
15
16
|
async execute({ group_id, user_id, duration }, context) {
|
|
16
17
|
const client = context.$client;
|
|
17
|
-
const
|
|
18
|
-
if (!
|
|
18
|
+
const [self, member] = await Promise.all([freshMember(client, group_id, client.uin), freshMember(client, group_id, user_id)]);
|
|
19
|
+
if (!isGroupManager(self))
|
|
19
20
|
return { success: false, message: t(context.locale, 'muteForbidden') };
|
|
20
|
-
|
|
21
|
-
if (!memberInfo)
|
|
21
|
+
if (!member)
|
|
22
22
|
return { success: false, message: t(context.locale, 'memberMissing') };
|
|
23
23
|
await client.setGroupBan(group_id, user_id, duration === 0 ? 0 : duration);
|
|
24
24
|
return { success: true, message: duration === 0 ? t(context.locale, 'unmuted', { user: user_id }) : t(context.locale, 'muted', { user: user_id, seconds: duration ?? 0 }) };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { messages as t } from "../../../../messages.js";
|
|
2
|
+
import { freshMember, isGroupManager } from "../../../../members.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool<{ group_id: number; user_id: number; duration?: number }>({
|
|
@@ -13,10 +14,9 @@ export default defineAgentTool<{ group_id: number; user_id: number; duration?: n
|
|
|
13
14
|
requiresApproval: 'always',
|
|
14
15
|
async execute({ group_id, user_id, duration }, context) {
|
|
15
16
|
const client = context.$client;
|
|
16
|
-
const
|
|
17
|
-
if(!
|
|
18
|
-
|
|
19
|
-
if(!memberInfo) return { success: false, message: t(context.locale, 'memberMissing') };
|
|
17
|
+
const [self, member] = await Promise.all([freshMember(client, group_id, client.uin), freshMember(client, group_id, user_id)]);
|
|
18
|
+
if (!isGroupManager(self)) return { success: false, message: t(context.locale, 'muteForbidden') };
|
|
19
|
+
if (!member) return { success: false, message: t(context.locale, 'memberMissing') };
|
|
20
20
|
await client.setGroupBan(group_id, user_id, duration === 0 ? 0 : duration);
|
|
21
21
|
return { success: true, message: duration === 0 ? t(context.locale, 'unmuted', { user: user_id }) : t(context.locale, 'muted', { user: user_id, seconds: duration ?? 0 }) };
|
|
22
22
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { messages as t } from "../../../../messages.js";
|
|
3
|
+
import { freshMember, isGroupManager } from "../../../../members.js";
|
|
3
4
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
export default defineAgentTool({
|
|
@@ -15,11 +16,10 @@ export default defineAgentTool({
|
|
|
15
16
|
requiresApproval: 'always',
|
|
16
17
|
async execute({ group_id, user_id, title, duration }, context) {
|
|
17
18
|
const client = context.$client;
|
|
18
|
-
const
|
|
19
|
-
if (!
|
|
19
|
+
const [self, member] = await Promise.all([freshMember(client, group_id, client.uin), freshMember(client, group_id, user_id)]);
|
|
20
|
+
if (!isGroupManager(self))
|
|
20
21
|
return { success: false, message: t(context.locale, 'titleForbidden') };
|
|
21
|
-
|
|
22
|
-
if (!memberInfo)
|
|
22
|
+
if (!member)
|
|
23
23
|
return { success: false, message: t(context.locale, 'memberMissing') };
|
|
24
24
|
await client.setGroupSpecialTitle(group_id, user_id, title, duration ?? -1);
|
|
25
25
|
return { success: true, message: t(context.locale, 'titleChanged', { p0: user_id, p1: title }) };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { messages as t } from "../../../../messages.js";
|
|
2
|
+
import { freshMember, isGroupManager } from "../../../../members.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool<{ group_id: number; user_id: number; title: string; duration?: number }>({
|
|
@@ -14,10 +15,9 @@ export default defineAgentTool<{ group_id: number; user_id: number; title: strin
|
|
|
14
15
|
requiresApproval: 'always',
|
|
15
16
|
async execute({ group_id, user_id, title, duration }, context) {
|
|
16
17
|
const client = context.$client;
|
|
17
|
-
const
|
|
18
|
-
if(!
|
|
19
|
-
|
|
20
|
-
if(!memberInfo) return { success: false, message: t(context.locale, 'memberMissing') };
|
|
18
|
+
const [self, member] = await Promise.all([freshMember(client, group_id, client.uin), freshMember(client, group_id, user_id)]);
|
|
19
|
+
if (!isGroupManager(self)) return { success: false, message: t(context.locale, 'titleForbidden') };
|
|
20
|
+
if (!member) return { success: false, message: t(context.locale, 'memberMissing') };
|
|
21
21
|
await client.setGroupSpecialTitle(group_id, user_id, title, duration ?? -1);
|
|
22
22
|
return { success: true, message: t(context.locale, 'titleChanged', { p0: user_id, p1: title }) };
|
|
23
23
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { messages as t } from "../../../../messages.js";
|
|
3
|
+
import { freshMember } from "../../../../members.js";
|
|
3
4
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
export default defineAgentTool({
|
|
@@ -13,15 +14,24 @@ export default defineAgentTool({
|
|
|
13
14
|
permissions: ['role(master,admin,trusted,owner)'],
|
|
14
15
|
requiresApproval: 'never',
|
|
15
16
|
async execute({ group_id, user_id }, context) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const memberInfo = group.pickMember(user_id);
|
|
20
|
-
if (!memberInfo)
|
|
17
|
+
// pickMember 返回的是实体对象,序列化后只剩 `{}`:模型会据此以为成员不在群里。按 group_kick 的口径实时查询。
|
|
18
|
+
const info = await freshMember(context.$client, group_id, user_id);
|
|
19
|
+
if (!info)
|
|
21
20
|
return { success: false, message: t(context.locale, 'memberMissing') };
|
|
22
21
|
return {
|
|
23
22
|
success: true,
|
|
24
|
-
member:
|
|
23
|
+
member: {
|
|
24
|
+
group_id: info.group_id,
|
|
25
|
+
user_id: info.user_id,
|
|
26
|
+
nickname: info.nickname,
|
|
27
|
+
card: info.card,
|
|
28
|
+
role: info.role,
|
|
29
|
+
title: info.title,
|
|
30
|
+
level: info.level,
|
|
31
|
+
join_time: info.join_time,
|
|
32
|
+
last_sent_time: info.last_sent_time,
|
|
33
|
+
shutup_time: info.shutup_time,
|
|
34
|
+
},
|
|
25
35
|
};
|
|
26
36
|
},
|
|
27
37
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { messages as t } from "../../../../messages.js";
|
|
2
|
+
import { freshMember } from "../../../../members.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
interface GroupMemberInfoInput {
|
|
@@ -17,13 +18,23 @@ export default defineAgentTool<GroupMemberInfoInput>({
|
|
|
17
18
|
permissions: ['role(master,admin,trusted,owner)'],
|
|
18
19
|
requiresApproval: 'never',
|
|
19
20
|
async execute({ group_id, user_id }, context) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!memberInfo) return { success: false, message: t(context.locale, 'memberMissing') };
|
|
21
|
+
// pickMember 返回的是实体对象,序列化后只剩 `{}`:模型会据此以为成员不在群里。按 group_kick 的口径实时查询。
|
|
22
|
+
const info = await freshMember(context.$client, group_id, user_id);
|
|
23
|
+
if (!info) return { success: false, message: t(context.locale, 'memberMissing') };
|
|
24
24
|
return {
|
|
25
25
|
success: true,
|
|
26
|
-
member:
|
|
26
|
+
member: {
|
|
27
|
+
group_id: info.group_id,
|
|
28
|
+
user_id: info.user_id,
|
|
29
|
+
nickname: info.nickname,
|
|
30
|
+
card: info.card,
|
|
31
|
+
role: info.role,
|
|
32
|
+
title: info.title,
|
|
33
|
+
level: info.level,
|
|
34
|
+
join_time: info.join_time,
|
|
35
|
+
last_sent_time: info.last_sent_time,
|
|
36
|
+
shutup_time: info.shutup_time,
|
|
37
|
+
},
|
|
27
38
|
};
|
|
28
39
|
},
|
|
29
40
|
});
|
|
@@ -8,7 +8,7 @@ export default defineAgentTool({
|
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['private', 'group'],
|
|
10
10
|
requiresApproval: 'never',
|
|
11
|
-
inputSchema: z.object({ message_ids: z.array(z.string().min(1)).min(1).describe('当前会话的真实消息 ID
|
|
11
|
+
inputSchema: z.object({ message_ids: z.array(z.string().min(1)).min(1).describe('当前会话的真实消息 ID(或消息头里的 m<n> 别名),按转发顺序排列') }),
|
|
12
12
|
async execute({ message_ids }, context) {
|
|
13
13
|
const conversation = currentConversation(context);
|
|
14
14
|
if (conversation.kind === 'channel')
|
|
@@ -8,7 +8,7 @@ export default defineAgentTool({
|
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['private', 'group'],
|
|
10
10
|
requiresApproval: 'never',
|
|
11
|
-
inputSchema: z.object({ message_ids: z.array(z.string().min(1)).min(1).describe('当前会话的真实消息 ID
|
|
11
|
+
inputSchema: z.object({ message_ids: z.array(z.string().min(1)).min(1).describe('当前会话的真实消息 ID(或消息头里的 m<n> 别名),按转发顺序排列') }),
|
|
12
12
|
async execute({ message_ids }, context) {
|
|
13
13
|
const conversation = currentConversation(context);
|
|
14
14
|
if (conversation.kind === 'channel') throw new Error(t(context.locale, 'channelOperationUnsupported'));
|
|
@@ -8,7 +8,7 @@ export default defineAgentTool({
|
|
|
8
8
|
scopes: ['private', 'group'],
|
|
9
9
|
requiresApproval: 'never',
|
|
10
10
|
inputSchema: z.object({
|
|
11
|
-
message_id: z.string().min(1).describe('当前会话中真实的原消息 ID'),
|
|
11
|
+
message_id: z.string().min(1).describe('当前会话中真实的原消息 ID,或消息头里的 m<n> 别名'),
|
|
12
12
|
text: z.string().min(1).describe('本次回复的文字'),
|
|
13
13
|
}),
|
|
14
14
|
async execute({ message_id, text }, context) {
|
|
@@ -8,7 +8,7 @@ export default defineAgentTool({
|
|
|
8
8
|
scopes: ['private', 'group'],
|
|
9
9
|
requiresApproval: 'never',
|
|
10
10
|
inputSchema: z.object({
|
|
11
|
-
message_id: z.string().min(1).describe('当前会话中真实的原消息 ID'),
|
|
11
|
+
message_id: z.string().min(1).describe('当前会话中真实的原消息 ID,或消息头里的 m<n> 别名'),
|
|
12
12
|
text: z.string().min(1).describe('本次回复的文字'),
|
|
13
13
|
}),
|
|
14
14
|
async execute({ message_id, text }, context) {
|
|
@@ -8,7 +8,7 @@ export default defineAgentTool({
|
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['private', 'group'],
|
|
10
10
|
requiresApproval: 'never',
|
|
11
|
-
inputSchema: z.object({ message_id: z.string().min(1).describe('机器人在当前会话中发送的真实消息 ID') }),
|
|
11
|
+
inputSchema: z.object({ message_id: z.string().min(1).describe('机器人在当前会话中发送的真实消息 ID,或消息头里的 m<n> 别名') }),
|
|
12
12
|
async execute({ message_id }, context) {
|
|
13
13
|
const message = await resolveMessage(context, message_id);
|
|
14
14
|
if (message.sender.user_id !== context.$client.uin)
|
|
@@ -8,7 +8,7 @@ export default defineAgentTool({
|
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['private', 'group'],
|
|
10
10
|
requiresApproval: 'never',
|
|
11
|
-
inputSchema: z.object({ message_id: z.string().min(1).describe('机器人在当前会话中发送的真实消息 ID') }),
|
|
11
|
+
inputSchema: z.object({ message_id: z.string().min(1).describe('机器人在当前会话中发送的真实消息 ID,或消息头里的 m<n> 别名') }),
|
|
12
12
|
async execute({ message_id }, context) {
|
|
13
13
|
const message = await resolveMessage(context, message_id);
|
|
14
14
|
if (message.sender.user_id !== context.$client.uin) return { success: false, message: t(context.locale, 'ownMessageRequired') };
|