@zhin.js/adapter-icqq 1.1.20 → 1.1.22

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/PERMITS.md +1 -0
  3. package/README.md +11 -1
  4. package/adapters/icqq/index.js +96 -25
  5. package/adapters/icqq/index.ts +97 -27
  6. package/agents/icqq/messages.js +20 -0
  7. package/agents/icqq/messages.ts +20 -0
  8. package/agents/icqq/skills/icqq-conversation/SKILL.md +2 -2
  9. package/agents/icqq/skills/icqq-conversation/tools/conversation_info/index.js +1 -1
  10. package/agents/icqq/skills/icqq-conversation/tools/conversation_info/index.ts +1 -1
  11. package/agents/icqq/skills/icqq-group-admin/SKILL.md +8 -3
  12. package/agents/icqq/skills/icqq-group-admin/tools/announce/index.js +2 -0
  13. package/agents/icqq/skills/icqq-group-admin/tools/announce/index.ts +2 -0
  14. package/agents/icqq/skills/icqq-group-admin/tools/essence/index.js +2 -0
  15. package/agents/icqq/skills/icqq-group-admin/tools/essence/index.ts +2 -0
  16. package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.js +2 -0
  17. package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.ts +2 -0
  18. package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.js +1 -1
  19. package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.ts +1 -1
  20. package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.js +3 -1
  21. package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.ts +3 -1
  22. package/agents/icqq/skills/icqq-group-admin/tools/set_group_admin/index.js +43 -0
  23. package/agents/icqq/skills/icqq-group-admin/tools/set_group_admin/index.ts +36 -0
  24. package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.js +2 -0
  25. package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.ts +2 -0
  26. package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.js +2 -0
  27. package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.ts +2 -0
  28. package/agents/icqq/skills/icqq-messages/SKILL.md +1 -1
  29. package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.js +1 -1
  30. package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.ts +1 -1
  31. package/lib/endpoint.js +6 -2
  32. package/lib/group-actions.d.ts +10 -0
  33. package/lib/group-actions.js +94 -0
  34. package/lib/protocol.d.ts +2 -1
  35. package/lib/self-profile.d.ts +6 -0
  36. package/lib/self-profile.js +56 -0
  37. package/lib/sensitive-commands.d.ts +8 -0
  38. package/lib/sensitive-commands.js +16 -0
  39. package/package.json +26 -20
  40. package/plugin.js +8 -1
  41. package/prompt-sections/platform/index.js +1 -1
  42. package/prompt-sections/platform/index.ts +1 -1
  43. package/schema.json +106 -1
  44. package/src/endpoint.ts +6 -2
  45. package/src/group-actions.ts +105 -0
  46. package/src/protocol.ts +2 -1
  47. package/src/self-profile.ts +55 -0
  48. package/src/sensitive-commands.ts +19 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # @zhin.js/adapter-icqq
2
2
 
3
+ ## 1.1.22
4
+
5
+ ### Patch Changes
6
+
7
+ - b249e5e: ADR 0044 C4:事件映射迁出 core。新增 `@zhin.js/adapter-onebot-events`,承载 OneBot v11 / NapCat / icqq 的原始通知与请求 → 标准事件映射(`receiveOneBotLikeSideEvent`、`mapNoticeParts`、`mapRequestParts`、`resolveSideEventDedupeKey` 及 OneBot 映射表,icqq 差异用覆盖表表达);KOOK、Slack 的映射迁入各自适配器;`@zhin.js/core` 只保留标准事件名、类型与 `buildNotice` / `buildRequest` / `buildSystem` / `senderFromId`,不再含任何平台名。core 不再导出上述映射函数与 `ONEBOT_/KOOK_/SLACK_NOTICE_PARTS_MAP`、`SideEventPlatform`、`OneBotLike*` 类型,适配器改从共享包导入。
8
+ - 41ff1a5: ADR 0044 C5: every official adapter now declares a capability `profile` (its `segments` moved into `profile.default.messaging.segments`). icqq declares the complete profile (per conversation kind: scene, all 14 actions with real limits, events, feedback, delivery) and implements the seven new management ports (`pokeUser`, `unmuteGroupMember`, `muteAllGroupMembers`, `setGroupCard`, `setGroupTitle`, `setGroupEssence`, `inviteGroupMember`) with fresh bot-role reads. qq official declares its passive reply windows; undeclared capabilities stay unsupported.
9
+ - cbd3f14: ADR 0044 integration and C6 guard. `delivery.spool` now reproduces the pre-0044 media rule (icqq `false`, every other official adapter `true`). icqq narrows `actions.recall.others` unless the Bot is admin or owner, and declares recall for groups. Endpoint kind capabilities merge the Endpoint's `operations` (Discord reactions keep working; Discord declares reaction feedback). Adapters in the `@zhin.js/` scope are trusted: their custom prompt text is not labelled `external_content`. Core no longer keeps a telegram/discord interactive-policy table (adapters declare `segments.interactive`). New `pnpm check:no-platform-branches` blocks platform-name branches in runtime code.
10
+ - 299863a: ADR 0044 C3: runtime consumers read capabilities instead of platform names. Scene facts (group info, member profile) are queried where the profile declares `scene.groupInfo` / `scene.memberProfile`; activity feedback (reaction, typing, status message, refresh interval, quote-only delivery) follows `feedback`, `actions.reaction` and `delivery.proactive` and the per-platform table is gone; large inline media is spooled only where `delivery.spool` is declared; the sandbox ingress and its console shell follow a new additive `runtime.sandbox` capability. `defineAgentTool` and Skill frontmatter accept `requiresCapabilities` (for example `actions.mute`), evaluated against the per-turn `TurnCapabilities`, so moderation tools disappear when the Bot is not an administrator and return when it is; the icqq/qq keyword boost in tool selection is removed and the icqq group-admin tools are tagged. The `icqq` command-line special case in the exec policy is now a generic `registerSensitiveExecCommand` rule registered by the icqq plugin. `EndpointCapabilities` gains a serializable per-kind `kinds` view. Adapters without a profile keep their previous behaviour (their derived profile also reads the management ports they implement).
11
+ - 4fe49b5: 支持配置按需生效:分离保存与有效版本,独立变更先应用,账号资料更新保留连接,模型按回合使用确定版本;Console 返回结构化应用结果。监听改为仅显式 --watch 启用,开发模板显式选择源码和配置监听。
12
+
13
+ 自变更消费配置应用回执,避免已处理配置被二次重载;持久化本次写入修订和应用结果,未知结果或修订冲突时停止自动续作并保留现场,恢复不会覆盖未经本次变更确认的配置。
14
+
15
+ - 2766d09: New `set_group_admin` Agent tool in `icqq-group-admin`: set (`enabled: true`) or remove (`enabled: false`) a QQ group administrator. It is injected only when the bot is the current group's owner (not when it is merely an admin), requires `role(master,admin,trusted,owner)` plus per-call human approval, and re-checks live roles before calling `setGroupAdmin` (bot still owner; promote only regular members; demote only current admins). An unconfirmed platform result is reported as such and never retried automatically.
16
+ - Updated dependencies [b249e5e]
17
+ - Updated dependencies [57661f6]
18
+ - Updated dependencies [cbd3f14]
19
+ - Updated dependencies [299863a]
20
+ - Updated dependencies [aaa93d7]
21
+ - Updated dependencies [4fe49b5]
22
+ - Updated dependencies [cce07db]
23
+ - Updated dependencies [b652db3]
24
+ - Updated dependencies [0315ba0]
25
+ - Updated dependencies [00be764]
26
+ - @zhin.js/adapter-onebot-events@0.0.1
27
+ - @zhin.js/core@1.1.56
28
+ - @zhin.js/adapter@1.1.28
29
+ - @zhin.js/im-contract@1.1.11
30
+ - @zhin.js/tool@1.1.15
31
+ - @zhin.js/skill@1.1.13
32
+ - @zhin.js/feature-kit@1.1.12
33
+ - @zhin.js/client@1.1.24
34
+ - @zhin.js/page@1.1.14
35
+ - @zhin.js/agent-feature@1.1.13
36
+ - @zhin.js/permission@1.1.12
37
+
38
+ ## 1.1.21
39
+
40
+ ### Patch Changes
41
+
42
+ - af8f803: Use the current Endpoint's platform profile for Agent account identity, with per-field configured fallbacks and scoped, identity-approved profile tools. Keep SOUL first-person and independent of account names. Profile ports preserve the source Client, cancellation, truthful update outcomes and private image authority.
43
+
44
+ Adapter Feature API 2.1 and Tool Feature API 1.1 describe the additive profile requirements; new Core, official adapters and generated plugins declare the corresponding floors. Existing compatible API-2.0/API-1.0 plugins remain accepted by the newer providers. npm releases remain patch.
45
+
46
+ Private IM assets now bind to the full canonical conversation and principal. Existing IM asset records that contain only the earlier lossy hash remain on disk until their original expiry, but cannot be safely rebound; recapture them. No automatic file deletion, platform profile mutation or workspace SOUL rewrite occurs during upgrade.
47
+
48
+ - Updated dependencies [b88e988]
49
+ - Updated dependencies [f78eb84]
50
+ - Updated dependencies [c15cef9]
51
+ - Updated dependencies [b0c02a2]
52
+ - Updated dependencies [af8f803]
53
+ - Updated dependencies [26fda97]
54
+ - Updated dependencies [976886c]
55
+ - Updated dependencies [032dc96]
56
+ - @zhin.js/adapter@1.1.27
57
+ - @zhin.js/client@1.1.23
58
+ - @zhin.js/im-contract@1.1.10
59
+ - @zhin.js/core@1.1.55
60
+ - @zhin.js/tool@1.1.14
61
+ - @zhin.js/page@1.1.13
62
+ - @zhin.js/agent-feature@1.1.12
63
+ - @zhin.js/feature-kit@1.1.11
64
+ - @zhin.js/permission@1.1.11
65
+ - @zhin.js/skill@1.1.12
66
+
3
67
  ## 1.1.20
4
68
 
5
69
  ### Patch Changes
package/PERMITS.md CHANGED
@@ -12,6 +12,7 @@
12
12
  | `icqq-group-admin` / `recall_member_message` | master/admin/trusted/owner | 无逐次审批;Bot 管理员可撤回普通成员,Bot 群主可撤回管理员或普通成员,执行前实时检查 |
13
13
  | `icqq-group-admin` / `group_leave` | 仅 master | 当前群,每次人工审批;Bot 为群主时拒绝,以免 SDK 解散群 |
14
14
  | `icqq-group-admin` / `group_kick` | master/admin/trusted/owner | 每次人工审批;执行前检查 Bot 与目标成员角色 |
15
+ | `icqq-group-admin` / `set_group_admin` | master/admin/trusted/owner | 仅 Bot 为当前群群主时注入;每次人工审批;执行前实时检查 Bot 仍为群主、设管理员目标为普通成员、取消目标为现任管理员 |
15
16
 
16
17
  已配置的自动群审核独立按获准规则执行撤回;上述工具不新增审核触发条件、敏感类别、处罚或目标群。Bot 对自己的消息始终不需要群管权限;平台拒绝或未确认时不能宣称撤回成功,也不自动重试。
17
18
 
package/README.md CHANGED
@@ -171,7 +171,7 @@ IM turn 的场景,应通过 Host 提供的显式账号作用域操作,不能
171
171
 
172
172
  ICQQ Agent 可按需加载 `icqq-conversation` 查询当前群/频道和 Bot 自身角色。`icqq-messages` 提供当前会话的引用回复、真实消息合并转发和撤回自己发言;自己的消息撤回不要求群管身份或逐次审批,时限由平台决定。
173
173
 
174
- `icqq-group-admin` 新增撤回他人消息与退群:前者保留群管发起权限、实时平台角色检查,无需逐次审批;退群仅主人可发起、每次审批,Bot 为群主时拒绝执行。引用/转发走统一发送链路,不支持跨会话转发。当前 guild-channel 接入仅提供身份查询,不能使用这些群/私聊操作。详见 [权限与能力说明](./PERMITS.md)。
174
+ `icqq-group-admin` 新增撤回他人消息与退群:前者保留群管发起权限、实时平台角色检查,无需逐次审批;退群仅主人可发起、每次审批,Bot 为群主时拒绝执行。引用/转发走统一发送链路,不支持跨会话转发。`set_group_admin` 设置/取消管理员仅在 Bot 为当前群群主时注入,每次人工审批并实时复核角色。当前 guild-channel 接入仅提供身份查询,不能使用这些群/私聊操作。详见 [权限与能力说明](./PERMITS.md)。
175
175
 
176
176
  ## Console 页面
177
177
 
@@ -249,3 +249,13 @@ ICQQ 要求分享、文件、语音、视频、JSON 卡片和合并转发单独
249
249
  Endpoint 管理和工具反馈跟随当前操作语言;登录没有当前聊天发送者,LoginAssist 提示采用项目语言。提示文案变化不改变扫码、短信、滑块验证过程,群管理权限、审批与平台结果检查保持原规则。
250
250
 
251
251
  Endpoint 添加用法中的调用头由当前环境的实际命令前缀与插件命名空间提供;未知时只提示所需参数,不生成猜测命令。
252
+
253
+ ## 当前账号资料
254
+
255
+ `endpointSpec.profile` 接到原生 ICQQ Client 的资料卡与设置方法;Agent 通过统一的 `get_self_profile` / `set_self_profile` 工具访问当前账号,修改沿用身份类审批。平台资料优先于配置兜底,群名片仍是独立的场景资料。
256
+
257
+ - 可读:昵称、性别、年龄、个性签名、头像 URL。只投影这些公开资料,不把 SDK 返回的邮箱、QID 等一起交给模型。
258
+ - 可写:昵称、性别、签名、个人说明、头像。SDK 返回 `false` 时明确拒绝,不报告成功。头像输入经工具链解析为已授权图片,结果不回传图片 base64。
259
+ - 年龄不反推生日;当前安装的 ICQQ 1.12.3 `setBirthday` 月日编码有缺陷,因此不开放生日写入。兴趣无对应已验证接口,只能使用配置兜底。
260
+
261
+ 配置的 `nickname`、`profile` 可放在适配器默认值或某个 `endpoints[]` 项中;Endpoint 逐字段覆盖默认值,只有平台未提供的字段才使用它们。配置兜底不会写回 QQ。
@@ -1,38 +1,109 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { IcqqAdapter } from "../../lib/adapter.js";
3
2
  /**
4
3
  * Convention entry: discover `adapters/icqq/index.ts` → defineAdapter.
5
4
  */
6
- import { defineAdapter } from 'zhin.js/adapter';
7
- import {} from "../../lib/protocol.js";
5
+ import { defineAdapter, resolvePlatformRole } from 'zhin.js/adapter';
6
+ import { IcqqAdapter } from "../../lib/adapter.js";
8
7
  export { IcqqEndpoint } from "../../lib/endpoint.js";
8
+ /** Group reactions take QQ system face ids. Private chats have none. */
9
+ const ICQQ_REACTIONS = [
10
+ { value: '76', label: '赞' }, { value: '66', label: '爱心' }, { value: '124', label: 'OK' },
11
+ { value: '99', label: '鼓掌' }, { value: '144', label: '喝彩' }, { value: '320', label: '庆祝' },
12
+ { value: '319', label: '比心' }, { value: '299', label: '牛啊' }, { value: '297', label: '拜谢' },
13
+ { value: '182', label: '笑哭' }, { value: '179', label: 'doge' }, { value: '264', label: '捂脸' },
14
+ { value: '271', label: '吃瓜' }, { value: '269', label: '暗中观察' }, { value: '268', label: '问号脸' },
15
+ { value: '270', label: 'emm' }, { value: '106', label: '委屈' }, { value: '285', label: '摸鱼' },
16
+ { value: '104', label: '哈欠' }, { value: '267', label: '头秃' }, { value: '326', label: '生气' },
17
+ ];
18
+ /** QQ group mute: 1 minute to 30 days (0 lifts it, which is the `unmute` action). */
19
+ const ICQQ_MUTE_SECONDS = { min: 60, max: 2592000 };
9
20
  export default defineAdapter({
10
21
  capabilities: ['inbound', 'outbound'],
11
- operations: ['recall', 'reaction'],
12
- segments: {
13
- // Canonical types formatOutboundBody encodes; markdown / keyboard degrade to text first.
14
- supported: ['text', 'mention', 'image', 'audio', 'video', 'file', 'reply', 'forward', 'face', 'dice', 'rps', 'share'],
15
- outboundMedia: ['base64', 'url', 'path'],
16
- interactive: 'text',
17
- // QQ group reactions take system face ids; private chats have no reactions.
18
- reaction: {
19
- scopes: ['group'],
20
- values: [
21
- { value: '76', label: '赞' }, { value: '66', label: '爱心' }, { value: '124', label: 'OK' },
22
- { value: '99', label: '鼓掌' }, { value: '144', label: '喝彩' }, { value: '320', label: '庆祝' },
23
- { value: '319', label: '比心' }, { value: '299', label: '牛啊' }, { value: '297', label: '拜谢' },
24
- { value: '182', label: '笑哭' }, { value: '179', label: 'doge' }, { value: '264', label: '捂脸' },
25
- { value: '271', label: '吃瓜' }, { value: '269', label: '暗中观察' }, { value: '268', label: '问号脸' },
26
- { value: '270', label: 'emm' }, { value: '106', label: '委屈' }, { value: '285', label: '摸鱼' },
27
- { value: '104', label: '哈欠' }, { value: '267', label: '头秃' }, { value: '326', label: '生气' },
28
- ],
22
+ profile: {
23
+ default: {
24
+ messaging: {
25
+ segments: {
26
+ // Canonical types formatOutboundBody encodes; markdown / keyboard degrade to text first.
27
+ supported: ['text', 'mention', 'image', 'audio', 'video', 'file', 'reply', 'forward', 'face', 'dice', 'rps', 'share'],
28
+ outboundMedia: ['base64', 'url', 'path'],
29
+ interactive: 'text',
30
+ reaction: { scopes: ['group'], values: ICQQ_REACTIONS },
31
+ model: {
32
+ share: {
33
+ fields: ['description', 'image', 'audio', 'platform'],
34
+ hint: 'renders as a QQ link card. For a song, set audio to the playable link, image to the cover, description to the artist and platform to its music service (qq, netease, kugou, kuwo or migu), copied from tool results; leave a field "" when you have no real value.',
35
+ },
36
+ },
37
+ },
38
+ mention: { user: true },
39
+ quote: true,
40
+ recall: { timeLimitSeconds: 120 },
41
+ forward: {},
42
+ },
43
+ scene: { roles: { owner: 'owner', admin: 'admin', member: 'member' } },
44
+ actions: {
45
+ recall: { scopes: ['private', 'group'], params: { timeLimitSeconds: 120 } },
46
+ quoteReply: { scopes: ['private', 'group'] },
47
+ forward: { scopes: ['private', 'group'] },
48
+ poke: { scopes: ['private', 'group'] },
49
+ },
50
+ delivery: { send: true, proactive: true, spool: false },
51
+ },
52
+ private: {
53
+ // A private chat has no reactions, so feedback is a short status message.
54
+ feedback: { mode: 'status-message', text: '⏳' },
55
+ events: { emit: ['message.recalled', 'poke'] },
29
56
  },
30
- model: {
31
- share: {
32
- fields: ['description', 'image', 'audio', 'platform'],
33
- hint: 'renders as a QQ link card. For a song, set audio to the playable link, image to the cover, description to the artist and platform to its music service (qq, netease, kugou, kuwo or migu), copied from tool results; leave a field "" when you have no real value.',
57
+ group: {
58
+ messaging: { reaction: { emojis: ICQQ_REACTIONS.map((item) => item.value) } },
59
+ scene: {
60
+ groupInfo: true,
61
+ memberProfile: true,
62
+ card: true,
63
+ title: true,
64
+ mute: { durationSeconds: ICQQ_MUTE_SECONDS, all: true },
65
+ },
66
+ actions: {
67
+ // Own messages need no role; recalling others' needs an admin (narrowed in resolveCapabilities).
68
+ recall: { scopes: ['private', 'group'], params: { timeLimitSeconds: 120, others: true } },
69
+ reaction: { scopes: ['group'], params: { emojis: ICQQ_REACTIONS.map((item) => item.value), own: true, others: true, cancellable: true } },
70
+ mute: { scopes: ['group'], requiresRole: 'admin', params: { durationSeconds: ICQQ_MUTE_SECONDS } },
71
+ unmute: { scopes: ['group'], requiresRole: 'admin' },
72
+ muteAll: { scopes: ['group'], requiresRole: 'admin' },
73
+ kick: { scopes: ['group'], requiresRole: 'admin' },
74
+ setCard: { scopes: ['group'], requiresRole: 'admin' },
75
+ setTitle: { scopes: ['group'], requiresRole: 'owner' },
76
+ setAdmin: { scopes: ['group'], requiresRole: 'owner' },
77
+ essence: { scopes: ['group'], requiresRole: 'admin' },
78
+ announce: { scopes: ['group'], requiresRole: 'admin' },
79
+ invite: { scopes: ['group'] },
80
+ },
81
+ events: {
82
+ emit: ['member.joined', 'member.left', 'member.role_changed', 'member.muted', 'message.recalled', 'poke'],
83
+ },
84
+ feedback: { mode: 'reaction', emoji: '⏳' },
85
+ prompt: {
86
+ text: '被禁言或不是管理员时不要尝试管理类操作;头衔和设/撤管理员只有群主能做;邀请只能邀请你的好友。',
34
87
  },
35
88
  },
89
+ channel: {
90
+ // Guild channels: text and media in and out; no recall, poke, forward, reaction or management actions.
91
+ actions: {
92
+ recall: false,
93
+ poke: false,
94
+ forward: false,
95
+ },
96
+ feedback: { mode: 'none' },
97
+ },
98
+ },
99
+ resolveCapabilities({ capabilities, facts }) {
100
+ if (!capabilities.actions?.recall)
101
+ return undefined;
102
+ const role = resolvePlatformRole(capabilities.scene, facts.bot?.platformRole);
103
+ // Unknown role fails closed, like every other role-gated action.
104
+ if (role === 'owner' || role === 'admin')
105
+ return undefined;
106
+ return { actions: { recall: { params: { others: false } } } };
36
107
  },
37
108
  createAdapter: context => new IcqqAdapter(context),
38
109
  });
@@ -1,42 +1,112 @@
1
- import { IcqqAdapter } from '../../src/adapter.js';
2
1
  /**
3
2
  * Convention entry: discover `adapters/icqq/index.ts` → defineAdapter.
4
3
  */
5
- import { defineAdapter } from 'zhin.js/adapter';
6
- import {
7
- type IcqqEndpointConfig,
8
- } from '../../src/protocol.js';
4
+ import { defineAdapter, resolvePlatformRole } from 'zhin.js/adapter';
5
+ import { IcqqAdapter } from '../../src/adapter.js';
6
+ import type { IcqqEndpointConfig } from '../../src/protocol.js';
9
7
 
10
8
  export { IcqqEndpoint } from '../../src/endpoint.js';
11
9
  export type { IcqqEndpointOptions } from '../../src/endpoint.js';
12
10
 
11
+ /** Group reactions take QQ system face ids. Private chats have none. */
12
+ const ICQQ_REACTIONS = [
13
+ { value: '76', label: '赞' }, { value: '66', label: '爱心' }, { value: '124', label: 'OK' },
14
+ { value: '99', label: '鼓掌' }, { value: '144', label: '喝彩' }, { value: '320', label: '庆祝' },
15
+ { value: '319', label: '比心' }, { value: '299', label: '牛啊' }, { value: '297', label: '拜谢' },
16
+ { value: '182', label: '笑哭' }, { value: '179', label: 'doge' }, { value: '264', label: '捂脸' },
17
+ { value: '271', label: '吃瓜' }, { value: '269', label: '暗中观察' }, { value: '268', label: '问号脸' },
18
+ { value: '270', label: 'emm' }, { value: '106', label: '委屈' }, { value: '285', label: '摸鱼' },
19
+ { value: '104', label: '哈欠' }, { value: '267', label: '头秃' }, { value: '326', label: '生气' },
20
+ ];
21
+
22
+ /** QQ group mute: 1 minute to 30 days (0 lifts it, which is the `unmute` action). */
23
+ const ICQQ_MUTE_SECONDS = { min: 60, max: 2592000 } as const;
24
+
13
25
  export default defineAdapter<IcqqEndpointConfig>({
14
26
  capabilities: ['inbound', 'outbound'],
15
- operations: ['recall', 'reaction'],
16
- segments: {
17
- // Canonical types formatOutboundBody encodes; markdown / keyboard degrade to text first.
18
- supported: ['text', 'mention', 'image', 'audio', 'video', 'file', 'reply', 'forward', 'face', 'dice', 'rps', 'share'],
19
- outboundMedia: ['base64', 'url', 'path'],
20
- interactive: 'text',
21
- // QQ group reactions take system face ids; private chats have no reactions.
22
- reaction: {
23
- scopes: ['group'],
24
- values: [
25
- { value: '76', label: '赞' }, { value: '66', label: '爱心' }, { value: '124', label: 'OK' },
26
- { value: '99', label: '鼓掌' }, { value: '144', label: '喝彩' }, { value: '320', label: '庆祝' },
27
- { value: '319', label: '比心' }, { value: '299', label: '牛啊' }, { value: '297', label: '拜谢' },
28
- { value: '182', label: '笑哭' }, { value: '179', label: 'doge' }, { value: '264', label: '捂脸' },
29
- { value: '271', label: '吃瓜' }, { value: '269', label: '暗中观察' }, { value: '268', label: '问号脸' },
30
- { value: '270', label: 'emm' }, { value: '106', label: '委屈' }, { value: '285', label: '摸鱼' },
31
- { value: '104', label: '哈欠' }, { value: '267', label: '头秃' }, { value: '326', label: '生气' },
32
- ],
27
+ profile: {
28
+ default: {
29
+ messaging: {
30
+ segments: {
31
+ // Canonical types formatOutboundBody encodes; markdown / keyboard degrade to text first.
32
+ supported: ['text', 'mention', 'image', 'audio', 'video', 'file', 'reply', 'forward', 'face', 'dice', 'rps', 'share'],
33
+ outboundMedia: ['base64', 'url', 'path'],
34
+ interactive: 'text',
35
+ reaction: { scopes: ['group'], values: ICQQ_REACTIONS },
36
+ model: {
37
+ share: {
38
+ fields: ['description', 'image', 'audio', 'platform'],
39
+ hint: 'renders as a QQ link card. For a song, set audio to the playable link, image to the cover, description to the artist and platform to its music service (qq, netease, kugou, kuwo or migu), copied from tool results; leave a field "" when you have no real value.',
40
+ },
41
+ },
42
+ },
43
+ mention: { user: true },
44
+ quote: true,
45
+ recall: { timeLimitSeconds: 120 },
46
+ forward: {},
47
+ },
48
+ scene: { roles: { owner: 'owner', admin: 'admin', member: 'member' } },
49
+ actions: {
50
+ recall: { scopes: ['private', 'group'], params: { timeLimitSeconds: 120 } },
51
+ quoteReply: { scopes: ['private', 'group'] },
52
+ forward: { scopes: ['private', 'group'] },
53
+ poke: { scopes: ['private', 'group'] },
54
+ },
55
+ delivery: { send: true, proactive: true, spool: false },
56
+ },
57
+ private: {
58
+ // A private chat has no reactions, so feedback is a short status message.
59
+ feedback: { mode: 'status-message', text: '⏳' },
60
+ events: { emit: ['message.recalled', 'poke'] },
33
61
  },
34
- model: {
35
- share: {
36
- fields: ['description', 'image', 'audio', 'platform'],
37
- hint: 'renders as a QQ link card. For a song, set audio to the playable link, image to the cover, description to the artist and platform to its music service (qq, netease, kugou, kuwo or migu), copied from tool results; leave a field "" when you have no real value.',
62
+ group: {
63
+ messaging: { reaction: { emojis: ICQQ_REACTIONS.map((item) => item.value) } },
64
+ scene: {
65
+ groupInfo: true,
66
+ memberProfile: true,
67
+ card: true,
68
+ title: true,
69
+ mute: { durationSeconds: ICQQ_MUTE_SECONDS, all: true },
70
+ },
71
+ actions: {
72
+ // Own messages need no role; recalling others' needs an admin (narrowed in resolveCapabilities).
73
+ recall: { scopes: ['private', 'group'], params: { timeLimitSeconds: 120, others: true } },
74
+ reaction: { scopes: ['group'], params: { emojis: ICQQ_REACTIONS.map((item) => item.value), own: true, others: true, cancellable: true } },
75
+ mute: { scopes: ['group'], requiresRole: 'admin', params: { durationSeconds: ICQQ_MUTE_SECONDS } },
76
+ unmute: { scopes: ['group'], requiresRole: 'admin' },
77
+ muteAll: { scopes: ['group'], requiresRole: 'admin' },
78
+ kick: { scopes: ['group'], requiresRole: 'admin' },
79
+ setCard: { scopes: ['group'], requiresRole: 'admin' },
80
+ setTitle: { scopes: ['group'], requiresRole: 'owner' },
81
+ setAdmin: { scopes: ['group'], requiresRole: 'owner' },
82
+ essence: { scopes: ['group'], requiresRole: 'admin' },
83
+ announce: { scopes: ['group'], requiresRole: 'admin' },
84
+ invite: { scopes: ['group'] },
85
+ },
86
+ events: {
87
+ emit: ['member.joined', 'member.left', 'member.role_changed', 'member.muted', 'message.recalled', 'poke'],
88
+ },
89
+ feedback: { mode: 'reaction', emoji: '⏳' },
90
+ prompt: {
91
+ text: '被禁言或不是管理员时不要尝试管理类操作;头衔和设/撤管理员只有群主能做;邀请只能邀请你的好友。',
38
92
  },
39
93
  },
94
+ channel: {
95
+ // Guild channels: text and media in and out; no recall, poke, forward, reaction or management actions.
96
+ actions: {
97
+ recall: false,
98
+ poke: false,
99
+ forward: false,
100
+ },
101
+ feedback: { mode: 'none' },
102
+ },
103
+ },
104
+ resolveCapabilities({ capabilities, facts }) {
105
+ if (!capabilities.actions?.recall) return undefined;
106
+ const role = resolvePlatformRole(capabilities.scene, facts.bot?.platformRole);
107
+ // Unknown role fails closed, like every other role-gated action.
108
+ if (role === 'owner' || role === 'admin') return undefined;
109
+ return { actions: { recall: { params: { others: false } } } };
40
110
  },
41
111
  createAdapter: context => new IcqqAdapter(context),
42
112
  });
@@ -85,6 +85,26 @@ export const messages = createMessages({
85
85
  "zh-CN": "当前权限或成员状态不允许踢出",
86
86
  "en": "Current permissions or member status do not allow removal"
87
87
  },
88
+ "adminSet": {
89
+ "zh-CN": "已将 {p0} 设为管理员",
90
+ "en": "Member {p0} is now an administrator"
91
+ },
92
+ "adminUnset": {
93
+ "zh-CN": "已取消 {p0} 的管理员",
94
+ "en": "Administrator role removed from {p0}"
95
+ },
96
+ "adminUnconfirmed": {
97
+ "zh-CN": "平台未确认管理员变更成功,请勿自动重试",
98
+ "en": "The platform did not confirm the administrator change; do not retry automatically"
99
+ },
100
+ "adminForbidden": {
101
+ "zh-CN": "机器人不是群主,无法设置或取消管理员",
102
+ "en": "The bot must be the group owner to set or remove administrators"
103
+ },
104
+ "adminTargetInvalid": {
105
+ "zh-CN": "目标成员身份不允许本次变更:设为管理员需是普通成员,取消管理员需是现任管理员",
106
+ "en": "The member's role does not allow this change: promotion requires a regular member, removal requires a current administrator"
107
+ },
88
108
  "anonymousDisabled": {
89
109
  "zh-CN": "已关闭匿名聊天",
90
110
  "en": "Anonymous chat disabled"
@@ -85,6 +85,26 @@ export const messages = createMessages({
85
85
  "zh-CN": "当前权限或成员状态不允许踢出",
86
86
  "en": "Current permissions or member status do not allow removal"
87
87
  },
88
+ "adminSet": {
89
+ "zh-CN": "已将 {p0} 设为管理员",
90
+ "en": "Member {p0} is now an administrator"
91
+ },
92
+ "adminUnset": {
93
+ "zh-CN": "已取消 {p0} 的管理员",
94
+ "en": "Administrator role removed from {p0}"
95
+ },
96
+ "adminUnconfirmed": {
97
+ "zh-CN": "平台未确认管理员变更成功,请勿自动重试",
98
+ "en": "The platform did not confirm the administrator change; do not retry automatically"
99
+ },
100
+ "adminForbidden": {
101
+ "zh-CN": "机器人不是群主,无法设置或取消管理员",
102
+ "en": "The bot must be the group owner to set or remove administrators"
103
+ },
104
+ "adminTargetInvalid": {
105
+ "zh-CN": "目标成员身份不允许本次变更:设为管理员需是普通成员,取消管理员需是现任管理员",
106
+ "en": "The member's role does not allow this change: promotion requires a regular member, removal requires a current administrator"
107
+ },
88
108
  "anonymousDisabled": {
89
109
  "zh-CN": "已关闭匿名聊天",
90
110
  "en": "Anonymous chat disabled"
@@ -15,9 +15,9 @@ tools:
15
15
  ---
16
16
  # icqq-conversation
17
17
 
18
- Use `conversation_info` before claiming the bot's role or available platform permissions. It uses the current endpoint account, not the sender's role. Group roles are refreshed from the platform on demand; failure is not evidence that the bot is an ordinary member.
18
+ Use `conversation_info` before claiming your role or available platform permissions. It uses the current endpoint account, not the sender's role. Group roles are refreshed from the platform on demand; failure is not evidence that you are an ordinary member.
19
19
 
20
- Caller roles authorize a request; bot platform roles determine whether QQ can execute it. They are independent. A successful inspection does not replace the live permission check before a management action.
20
+ Caller roles authorize a request; your platform roles determine whether QQ can execute it. They are independent. A successful inspection does not replace the live permission check before a management action.
21
21
 
22
22
  Guild member lookup can be incomplete. Missing self membership means `unknown`. A `channel_admin` record does not identify which child channels it governs, so it cannot authorize an operation in the current channel.
23
23
 
@@ -3,7 +3,7 @@ import { defineAgentTool } from '@zhin.js/tool';
3
3
  import { z } from 'zod';
4
4
  import { currentConversation } from '../../../../conversation.js';
5
5
  export default defineAgentTool({
6
- description: '读取当前 ICQQ 会话及机器人自己的群/频道身份;区分调用者权限、机器人平台权限和不支持的操作',
6
+ description: '读取当前 ICQQ 会话及你自己的群/频道身份;区分调用者权限、你的平台权限和不支持的操作',
7
7
  adapter: 'icqq',
8
8
  requiresApproval: 'never',
9
9
  inputSchema: z.object({}),
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { currentConversation } from '../../../../conversation.js';
4
4
 
5
5
  export default defineAgentTool({
6
- description: '读取当前 ICQQ 会话及机器人自己的群/频道身份;区分调用者权限、机器人平台权限和不支持的操作',
6
+ description: '读取当前 ICQQ 会话及你自己的群/频道身份;区分调用者权限、你的平台权限和不支持的操作',
7
7
  adapter: 'icqq',
8
8
  requiresApproval: 'never',
9
9
  inputSchema: z.object({}),
@@ -11,10 +11,13 @@ keywords:
11
11
  - 公告
12
12
  - 精华
13
13
  - 退群
14
+ - 设管理员
15
+ - 取消管理员
14
16
  - 撤回他人消息
15
17
  tools:
16
18
  - group_invite
17
19
  - group_kick
20
+ - set_group_admin
18
21
  - set_mute
19
22
  - set_title
20
23
  - set_anonymous
@@ -35,8 +38,10 @@ scopes:
35
38
 
36
39
  已配置的自动审核流程可对入群 10 分钟内的明确广告直接踢出;该例外由审核流程核验,不授权本技能或普通对话绕过人工审批。
37
40
 
38
- `group_leave` 仅主人(master)可发起,每次人工审批,只能退出当前群。Bot 为群主时拒绝执行,因为 SDK 的退群会解散群。
41
+ `set_group_admin` 仅在你是当前群群主时注入(你只是管理员时不可见),沿用 master/admin/trusted/owner 发起权限,每次人工审批并展示目标群、成员、设/撤方向和理由。执行前实时复核:你必须仍是群主;设为管理员只针对普通成员,取消只针对现任管理员;平台未确认时不宣称成功、不自动重试。
39
42
 
40
- `recall_member_message` 沿用 master/admin/trusted/owner 发起权限,无需逐次审批;目标固定在当前群中的真实消息。执行前实时查询 Bot 与目标成员角色:管理员只能撤回普通成员,群主可撤回管理员或普通成员。平台时限与执行结果仍然有效,不自动重试未确认的结果。
43
+ `group_leave` 仅主人(master)可发起,每次人工审批,只能退出当前群。你为群主时拒绝执行,因为 SDK 的退群会解散群。
41
44
 
42
- 撤回 Bot 自己的消息不要求群管角色:使用 `icqq-messages` 的 `recall_own_message`。已配置自动群审核的撤回继续走原有审核路径,不因本工具新增审批,也不扩大审核类别或目标群。
45
+ `recall_member_message` 沿用 master/admin/trusted/owner 发起权限,无需逐次审批;目标固定在当前群中的真实消息。执行前实时查询你与目标成员的角色:管理员只能撤回普通成员,群主可撤回管理员或普通成员。平台时限与执行结果仍然有效,不自动重试未确认的结果。
46
+
47
+ 撤回你自己的消息不要求群管角色:使用 `icqq-messages` 的 `recall_own_message`。已配置自动群审核的撤回继续走原有审核路径,不因本工具新增审批,也不扩大审核类别或目标群。
@@ -9,6 +9,8 @@ export default defineAgentTool({
9
9
  content: z.string().describe('公告内容'),
10
10
  }),
11
11
  adapter: 'icqq',
12
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
13
+ requiresCapabilities: ['actions.announce'],
12
14
  requiresApproval: 'once',
13
15
  scopes: ['group'],
14
16
  permissions: ['role(master,admin,trusted,owner)'],
@@ -8,6 +8,8 @@ export default defineAgentTool<{ group_id: number; content: string }>({
8
8
  content: z.string().describe('公告内容'),
9
9
  }),
10
10
  adapter: 'icqq',
11
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
12
+ requiresCapabilities: ['actions.announce'],
11
13
  requiresApproval: 'once',
12
14
  scopes:['group'],
13
15
  permissions: ['role(master,admin,trusted,owner)'],
@@ -9,6 +9,8 @@ export default defineAgentTool({
9
9
  action: z.enum(['add', 'remove']).describe('add=设为精华, remove=移除精华'),
10
10
  }),
11
11
  adapter: 'icqq',
12
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
13
+ requiresCapabilities: ['actions.essence'],
12
14
  requiresApproval: 'once',
13
15
  scopes: ['group'],
14
16
  permissions: ['role(master,admin,trusted,owner)'],
@@ -8,6 +8,8 @@ export default defineAgentTool<{ message_id: string; action: 'add' | 'remove' }>
8
8
  action: z.enum(['add', 'remove']).describe('add=设为精华, remove=移除精华'),
9
9
  }),
10
10
  adapter: 'icqq',
11
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
12
+ requiresCapabilities: ['actions.essence'],
11
13
  requiresApproval: 'once',
12
14
  scopes:['group'],
13
15
  permissions: ['role(master,admin,trusted,owner)'],
@@ -10,6 +10,8 @@ export default defineAgentTool({
10
10
  reason: z.string().min(1).describe('本次踢人的具体原因,随目标群和成员一起供人工确认'),
11
11
  }),
12
12
  adapter: 'icqq',
13
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
14
+ requiresCapabilities: ['actions.kick'],
13
15
  scopes: ['group'],
14
16
  permissions: ['role(master,admin,trusted,owner)'],
15
17
  requiresApproval: 'human',
@@ -9,6 +9,8 @@ export default defineAgentTool<{ group_id: number; user_id: number; reason: stri
9
9
  reason: z.string().min(1).describe('本次踢人的具体原因,随目标群和成员一起供人工确认'),
10
10
  }),
11
11
  adapter: 'icqq',
12
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
13
+ requiresCapabilities: ['actions.kick'],
12
14
  scopes:['group'],
13
15
  permissions: ['role(master,admin,trusted,owner)'],
14
16
  requiresApproval: 'human',
@@ -4,7 +4,7 @@ import { z } from 'zod';
4
4
  import { currentGroup } from '../../../../conversation.js';
5
5
  import { messages as t } from '../../../../messages.js';
6
6
  export default defineAgentTool({
7
- description: '让机器人退出当前 QQ 群;仅主人可发起且每次人工审批,机器人是群主时拒绝执行以免解散群',
7
+ description: '退出你当前所在的 QQ 群;仅主人可发起且每次人工审批,你是群主时拒绝执行以免解散群',
8
8
  adapter: 'icqq',
9
9
  scopes: ['group'],
10
10
  currentConversationBotRoles: ['admin', 'member'],
@@ -4,7 +4,7 @@ import { currentGroup } from '../../../../conversation.js';
4
4
  import { messages as t } from '../../../../messages.js';
5
5
 
6
6
  export default defineAgentTool({
7
- description: '让机器人退出当前 QQ 群;仅主人可发起且每次人工审批,机器人是群主时拒绝执行以免解散群',
7
+ description: '退出你当前所在的 QQ 群;仅主人可发起且每次人工审批,你是群主时拒绝执行以免解散群',
8
8
  adapter: 'icqq',
9
9
  scopes: ['group'],
10
10
  currentConversationBotRoles: ['admin', 'member'],
@@ -4,8 +4,10 @@ import { z } from 'zod';
4
4
  import { currentGroup, recallMessage, resolveMessage } from '../../../../conversation.js';
5
5
  import { messages as t } from '../../../../messages.js';
6
6
  export default defineAgentTool({
7
- description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及机器人具备实时平台权限,无需逐次审批',
7
+ description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及你具备实时平台权限,无需逐次审批',
8
8
  adapter: 'icqq',
9
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
10
+ requiresCapabilities: ['actions.recall.others'],
9
11
  scopes: ['group'],
10
12
  currentConversationBotRoles: ['owner', 'admin'],
11
13
  permissions: ['role(master,admin,trusted,owner)'],
@@ -4,8 +4,10 @@ import { currentGroup, recallMessage, resolveMessage } from '../../../../convers
4
4
  import { messages as t } from '../../../../messages.js';
5
5
 
6
6
  export default defineAgentTool({
7
- description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及机器人具备实时平台权限,无需逐次审批',
7
+ description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及你具备实时平台权限,无需逐次审批',
8
8
  adapter: 'icqq',
9
+ // ADR 0044: offered only while the Bot can actually do this here (e.g. it is a group admin).
10
+ requiresCapabilities: ['actions.recall.others'],
9
11
  scopes: ['group'],
10
12
  currentConversationBotRoles: ['owner', 'admin'],
11
13
  permissions: ['role(master,admin,trusted,owner)'],