@cxyhhhhh/openclaw-qqbot 2.0.0-dev.202607101321 → 2.0.0-dev.202607101541

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.
@@ -4,7 +4,7 @@
4
4
  "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
5
5
  "channels": ["qqbot"],
6
6
  "extensions": ["./preload.cjs"],
7
- "skills": ["skills/qqbot-channel", "skills/qqbot-remind", "skills/qqbot-upgrade"],
7
+ "skills": ["skills/qqbot-channel", "skills/qqbot-group", "skills/qqbot-remind", "skills/qqbot-upgrade"],
8
8
  "contracts": {
9
9
  "tools": ["qqbot_platform_api", "qqbot_remind"]
10
10
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cxyhhhhh/openclaw-qqbot",
3
- "version": "2.0.0-dev.202607101321",
3
+ "version": "2.0.0-dev.202607101541",
4
4
  "description": "QQ Bot channel plugin for OpenClaw",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@tencent-connect/qqbot-connector": "1.2.0",
45
- "@tencent-connect/qqbot-nodejs": "npm:@cxyhhhhh/qqbot-nodejs@1.0.2-dev.202607101142"
45
+ "@tencent-connect/qqbot-nodejs": "npm:@cxyhhhhh/qqbot-nodejs@1.0.2-dev.202607101530"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^20.19.43",
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: qqbot-group
3
+ description: QQ 群管理技能。查询群基础信息、群成员信息、机器人群内状态。使用 qqbot_platform_api 工具调用接口,自动鉴权。当用户需要查询群信息、查看群成员、检查机器人群内状态时使用此技能。
4
+ metadata: {"openclaw":{"emoji":"👥","requires":{"config":["channels.qqbot"]}}}
5
+ ---
6
+
7
+ # QQ 群 API 请求指导
8
+
9
+ 通过 `qqbot_platform_api` 工具调用群相关接口,鉴权由工具自动处理。
10
+
11
+ 详细字段说明见 `references/api_reference.md`。
12
+
13
+ ---
14
+
15
+ ## ⭐ 接口速查
16
+
17
+ | 操作 | 方法 | 路径 |
18
+ |------|------|------|
19
+ | 获取机器人群内状态 | `GET` | `/v2/groups/{group_id}/bot_state` |
20
+ | 获取群成员信息 | `GET` | `/v2/groups/{group_id}/members/{member_id}` |
21
+ | 获取群基础信息 | `GET` | `/v2/groups/{group_id}/info` |
22
+
23
+ ---
24
+
25
+ ## 💡 调用示例
26
+
27
+ ### 获取机器人群内状态
28
+
29
+ ```json
30
+ {
31
+ "method": "GET",
32
+ "path": "/v2/groups/G_123456789/bot_state"
33
+ }
34
+ ```
35
+
36
+ 返回字段:`joined_at`(入群时间)、`allow_proactive_msg`(是否允许主动推送)、`recv_msg_setting`(`all`/`only_mention`/`mention_and_context`)、`member_role`(`member`/`owner`/`admin`)。
37
+
38
+ ### 获取群成员信息
39
+
40
+ ```json
41
+ {
42
+ "method": "GET",
43
+ "path": "/v2/groups/G_123456789/members/o_xxxxxxxx"
44
+ }
45
+ ```
46
+
47
+ 返回字段:`username`(昵称)、`member_role`、`joined_at`、`bot` 等。昵称仅在单个查询时返回。
48
+
49
+ ### 获取群基础信息
50
+
51
+ ```json
52
+ {
53
+ "method": "GET",
54
+ "path": "/v2/groups/G_123456789/info"
55
+ }
56
+ ```
57
+
58
+ 返回字段:`group_name`、`group_finger_memo`(简介)、`group_class_text`(分类)、`group_tags`(标签)、`group_member_num`(成员数)。
59
+
60
+ ---
61
+
62
+ ## 💬 @群成员
63
+
64
+ 发送消息时使用 `<@member_id>` 语法 @指定群成员:
65
+
66
+ - 格式:`<@member_id>`,例如 `<@o_xxxxxxxx>`
67
+ - 文本消息中直接拼接即可:`你好 <@o_xxxxxxxx>,欢迎入群!`
68
+
69
+ ---
70
+
71
+ ## ⚠️ 注意事项
72
+
73
+ 1. 路径占位符 `{group_id}`、`{member_id}` 需替换为实际值
74
+ 2. 返回时间格式为 RFC3339(如 `2026-06-23T22:03:09+08:00`)
75
+ 3. 群 openid 与频道 guild_id 是不同的概念,不要混淆
@@ -0,0 +1,94 @@
1
+ # QQ 开放平台群接口参考
2
+
3
+ > 鉴权和请求头由 `qqbot_platform_api` 工具自动处理,无需手动管理。
4
+
5
+ ---
6
+
7
+ ## 接口列表
8
+
9
+ ### 1. 获取机器人群内状态
10
+
11
+ - 方法:`GET`
12
+ - 路径:`/v2/groups/{group_id}/bot_state`
13
+
14
+ 响应参数:
15
+
16
+ | 字段 | 类型 | 说明 |
17
+ |------|------|------|
18
+ | joined_at | string | 入群时间(RFC3339 格式) |
19
+ | allow_proactive_msg | bool | 是否允许主动消息推送 |
20
+ | recv_msg_setting | string | 接收消息设置:`all`、`only_mention`、`mention_and_context` |
21
+ | member_role | string | 群成员角色:`member`、`owner`、`admin` |
22
+
23
+ 响应示例:
24
+
25
+ ```json
26
+ {
27
+ "join_timestamp": "2026-06-23T22:03:09+08:00",
28
+ "allow_proactive_msg": true,
29
+ "recv_msg_setting": "all",
30
+ "member_role": "admin"
31
+ }
32
+ ```
33
+
34
+ ---
35
+
36
+ ### 2. 获取群成员信息
37
+
38
+ - 方法:`GET`
39
+ - 路径:`/v2/groups/{group_id}/members/{member_id}`
40
+
41
+ 响应参数:
42
+
43
+ | 字段 | 类型 | 说明 |
44
+ |------|------|------|
45
+ | union_openid | string | 用户在应用/开放平台下的统一标识 |
46
+ | user_openid | string | 用户 openid |
47
+ | username | string | 用户昵称(仅单个查询时返回) |
48
+ | member_role | string | 群成员角色:`member`、`owner`、`admin` |
49
+ | joined_at | string | 入群时间(RFC3339 格式) |
50
+ | bot | bool | 是否为机器人账号 |
51
+
52
+ 响应示例:
53
+
54
+ ```json
55
+ {
56
+ "union_openid": "u_xxxxxxxx",
57
+ "user_openid": "o_xxxxxxxx",
58
+ "username": "张三",
59
+ "member_role": "member",
60
+ "joined_at": "2026-06-23T22:03:09+08:00",
61
+ "bot": false
62
+ }
63
+ ```
64
+
65
+ ---
66
+
67
+ ### 3. 获取群基础信息
68
+
69
+ - 方法:`GET`
70
+ - 路径:`/v2/groups/{group_id}/info`
71
+
72
+ 响应参数:
73
+
74
+ | 字段 | 类型 | 说明 |
75
+ |------|------|------|
76
+ | group_openid | string | 群 openid |
77
+ | group_name | string | 群名称 |
78
+ | group_finger_memo | string | 群简介/群公告 |
79
+ | group_class_text | string | 群分类文案 |
80
+ | group_tags | array[] | 群标签列表 |
81
+ | group_member_num | int | 群成员数量 |
82
+
83
+ 响应示例:
84
+
85
+ ```json
86
+ {
87
+ "group_openid": "G_123456789",
88
+ "group_name": "技术交流群",
89
+ "group_finger_memo": "专注于后端技术开发交流",
90
+ "group_class_text": "技术-后端开发",
91
+ "group_tags": ["技术交流", "后端开发", "QQ机器人"],
92
+ "group_member_num": 1250
93
+ }
94
+ ```
@@ -298,8 +298,8 @@ function buildAgentBody(input: {
298
298
  }): string {
299
299
  const { userContent, base, isGroup, wasMentioned, history } = input;
300
300
 
301
- // 斜杠命令直通:去除一切装饰
302
- if (userContent.startsWith('/')) {
301
+ // 斜杠命令直通:去除一切装饰及首尾空白
302
+ if (userContent.trim().startsWith('/')) {
303
303
  return userContent;
304
304
  }
305
305
 
@@ -27,6 +27,7 @@ import { getPersistedRefIndexStore } from '../features/ref-index-store.js';
27
27
  import { createPolicyInjector } from '../middleware/policy-injector.js';
28
28
  import { getHistoryStore, historyGroupKey } from '../features/history-store.js';
29
29
  import { dynamicAccessControl } from '../middleware/access-control.js';
30
+ import { stripMentionText } from '../utils/mention.js';
30
31
 
31
32
  export interface MiddlewareSetupOptions {
32
33
  /** 获取 runtime */
@@ -66,14 +67,23 @@ export function setupMiddlewares(bot: QQBot, account: ResolvedQQBotAccount, opts
66
67
 
67
68
  // 6. 群聊 @bot 门控(从 ctx.state.policy.group 读取动态配置)
68
69
  bot.use(mentionGate());
69
-
70
70
  // 7. 内容清洗(去 @marker、表情标签、多余空白)
71
- bot.use(contentSanitizer({ parseFaceTags: true }));
71
+ // SDK appId 匹配 @标记,但 QQ openid 不等于 appId,追加 stripMentionText 正确剥离
72
+ bot.use(contentSanitizer({
73
+ parseFaceTags: true,
74
+ transform: (content, ctx) => stripMentionText(content, (ctx.message as any).mentions),
75
+ }));
72
76
 
73
77
  // 8. 三层限流(sender / group / global)
74
78
  bot.use(rateLimiter());
75
79
 
76
- // 9. 并发串行+合并(在副作用中间件之前)
80
+ // 9. 斜杠命令(在并发锁之前,命令匹配后直接 reply + stop,不排队)
81
+ // 依赖:ctx.state.policy(policyInjector, #3)、ctx.message.*(原始消息)
82
+ // 注意:/stop 是框架级命令,不在 qqbot 命令列表中,仍由 urgentPredicate 处理
83
+ const slash = slashCommand({ commands: buildCommandList(account, { getRuntime: opts.getRuntime }) });
84
+ bot.use(slash.middleware);
85
+
86
+ // 10. 并发串行+合并(在副作用中间件之前)
77
87
  // - 同 peer 串行处理,避免平台 session conflict
78
88
  // - 处理中消息暂存 buffer;完成后合并为一条,继续走完剩余中间件链
79
89
  // (typingIndicator/quoteRef/attachmentProcessor/... 直到 bot.on("message"))
@@ -107,18 +117,18 @@ export function setupMiddlewares(bot: QQBot, account: ResolvedQQBotAccount, opts
107
117
  },
108
118
  }));
109
119
 
110
- // 10. C2C 输入状态指示器
120
+ // 11. C2C 输入状态指示器
111
121
  bot.use(typingIndicator());
112
122
 
113
- // 11. 引用消息解析(默认优先 msg_elements 获取文件名等丰富信息)
123
+ // 12. 引用消息解析(默认优先 msg_elements 获取文件名等丰富信息)
114
124
  bot.use(quoteRef({
115
125
  store: getPersistedRefIndexStore(account.accountId),
116
126
  }));
117
127
 
118
- // 12. 附件处理(语音 STT 转录 + 图片/文件下载)
128
+ // 13. 附件处理(语音 STT 转录 + 图片/文件下载)
119
129
  bot.use(attachmentProcessor({ getRuntime: opts.getRuntime }));
120
130
 
121
- // 13. 上下文组装(构建框架规约的 body)
131
+ // 14. 上下文组装(构建框架规约的 body)
122
132
  bot.use(envelopeFormatter({
123
133
  format: (ctx) => {
124
134
  const assembled = assembleBody(ctx, ctx.message as never, account, opts.getRuntime);
@@ -126,8 +136,4 @@ export function setupMiddlewares(bot: QQBot, account: ResolvedQQBotAccount, opts
126
136
  return assembled.agentBody;
127
137
  },
128
138
  }));
129
-
130
- // 14. 斜杠命令(命令被匹配后直接回复,不再向下传递)
131
- const slash = slashCommand({ commands: buildCommandList(account, { getRuntime: opts.getRuntime }) });
132
- bot.use(slash.middleware);
133
139
  }