@sliverp/qqbot 1.3.0

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 (78) hide show
  1. package/README.md +231 -0
  2. package/clawdbot.plugin.json +16 -0
  3. package/dist/index.d.ts +17 -0
  4. package/dist/index.js +22 -0
  5. package/dist/src/api.d.ts +194 -0
  6. package/dist/src/api.js +555 -0
  7. package/dist/src/channel.d.ts +3 -0
  8. package/dist/src/channel.js +146 -0
  9. package/dist/src/config.d.ts +25 -0
  10. package/dist/src/config.js +148 -0
  11. package/dist/src/gateway.d.ts +17 -0
  12. package/dist/src/gateway.js +722 -0
  13. package/dist/src/image-server.d.ts +62 -0
  14. package/dist/src/image-server.js +401 -0
  15. package/dist/src/known-users.d.ts +100 -0
  16. package/dist/src/known-users.js +264 -0
  17. package/dist/src/onboarding.d.ts +10 -0
  18. package/dist/src/onboarding.js +190 -0
  19. package/dist/src/outbound.d.ts +149 -0
  20. package/dist/src/outbound.js +476 -0
  21. package/dist/src/proactive.d.ts +170 -0
  22. package/dist/src/proactive.js +398 -0
  23. package/dist/src/runtime.d.ts +3 -0
  24. package/dist/src/runtime.js +10 -0
  25. package/dist/src/session-store.d.ts +49 -0
  26. package/dist/src/session-store.js +242 -0
  27. package/dist/src/types.d.ts +116 -0
  28. package/dist/src/types.js +1 -0
  29. package/dist/src/utils/image-size.d.ts +51 -0
  30. package/dist/src/utils/image-size.js +234 -0
  31. package/dist/src/utils/payload.d.ts +112 -0
  32. package/dist/src/utils/payload.js +186 -0
  33. package/index.ts +27 -0
  34. package/moltbot.plugin.json +16 -0
  35. package/node_modules/ws/LICENSE +20 -0
  36. package/node_modules/ws/README.md +548 -0
  37. package/node_modules/ws/browser.js +8 -0
  38. package/node_modules/ws/index.js +13 -0
  39. package/node_modules/ws/lib/buffer-util.js +131 -0
  40. package/node_modules/ws/lib/constants.js +19 -0
  41. package/node_modules/ws/lib/event-target.js +292 -0
  42. package/node_modules/ws/lib/extension.js +203 -0
  43. package/node_modules/ws/lib/limiter.js +55 -0
  44. package/node_modules/ws/lib/permessage-deflate.js +528 -0
  45. package/node_modules/ws/lib/receiver.js +706 -0
  46. package/node_modules/ws/lib/sender.js +602 -0
  47. package/node_modules/ws/lib/stream.js +161 -0
  48. package/node_modules/ws/lib/subprotocol.js +62 -0
  49. package/node_modules/ws/lib/validation.js +152 -0
  50. package/node_modules/ws/lib/websocket-server.js +554 -0
  51. package/node_modules/ws/lib/websocket.js +1393 -0
  52. package/node_modules/ws/package.json +69 -0
  53. package/node_modules/ws/wrapper.mjs +8 -0
  54. package/openclaw.plugin.json +16 -0
  55. package/package.json +38 -0
  56. package/qqbot-1.3.0.tgz +0 -0
  57. package/scripts/proactive-api-server.ts +346 -0
  58. package/scripts/send-proactive.ts +273 -0
  59. package/scripts/upgrade.sh +106 -0
  60. package/skills/qqbot-cron/SKILL.md +490 -0
  61. package/skills/qqbot-media/SKILL.md +138 -0
  62. package/src/api.ts +752 -0
  63. package/src/channel.ts +303 -0
  64. package/src/config.ts +172 -0
  65. package/src/gateway.ts +1588 -0
  66. package/src/image-server.ts +474 -0
  67. package/src/known-users.ts +358 -0
  68. package/src/onboarding.ts +254 -0
  69. package/src/openclaw-plugin-sdk.d.ts +483 -0
  70. package/src/outbound.ts +571 -0
  71. package/src/proactive.ts +528 -0
  72. package/src/runtime.ts +14 -0
  73. package/src/session-store.ts +292 -0
  74. package/src/types.ts +123 -0
  75. package/src/utils/image-size.ts +266 -0
  76. package/src/utils/payload.ts +265 -0
  77. package/tsconfig.json +16 -0
  78. package/upgrade-and-run.sh +89 -0
@@ -0,0 +1,265 @@
1
+ /**
2
+ * QQBot 结构化消息载荷工具
3
+ *
4
+ * 用于处理 AI 输出的结构化消息载荷,包括:
5
+ * - 定时提醒载荷 (cron_reminder)
6
+ * - 媒体消息载荷 (media)
7
+ */
8
+
9
+ // ============================================
10
+ // 类型定义
11
+ // ============================================
12
+
13
+ /**
14
+ * 定时提醒载荷
15
+ */
16
+ export interface CronReminderPayload {
17
+ type: 'cron_reminder';
18
+ /** 提醒内容 */
19
+ content: string;
20
+ /** 目标类型:c2c (私聊) 或 group (群聊) */
21
+ targetType: 'c2c' | 'group';
22
+ /** 目标地址:user_openid 或 group_openid */
23
+ targetAddress: string;
24
+ /** 原始消息 ID(可选) */
25
+ originalMessageId?: string;
26
+ }
27
+
28
+ /**
29
+ * 媒体消息载荷
30
+ */
31
+ export interface MediaPayload {
32
+ type: 'media';
33
+ /** 媒体类型:image, audio, video */
34
+ mediaType: 'image' | 'audio' | 'video';
35
+ /** 来源类型:url 或 file */
36
+ source: 'url' | 'file';
37
+ /** 媒体路径或 URL */
38
+ path: string;
39
+ /** 媒体描述(可选) */
40
+ caption?: string;
41
+ }
42
+
43
+ /**
44
+ * QQBot 载荷联合类型
45
+ */
46
+ export type QQBotPayload = CronReminderPayload | MediaPayload;
47
+
48
+ /**
49
+ * 解析结果
50
+ */
51
+ export interface ParseResult {
52
+ /** 是否为结构化载荷 */
53
+ isPayload: boolean;
54
+ /** 解析后的载荷对象(如果是结构化载荷) */
55
+ payload?: QQBotPayload;
56
+ /** 原始文本(如果不是结构化载荷) */
57
+ text?: string;
58
+ /** 解析错误信息(如果解析失败) */
59
+ error?: string;
60
+ }
61
+
62
+ // ============================================
63
+ // 常量定义
64
+ // ============================================
65
+
66
+ /** AI 输出的结构化载荷前缀 */
67
+ const PAYLOAD_PREFIX = 'QQBOT_PAYLOAD:';
68
+
69
+ /** Cron 消息存储的前缀 */
70
+ const CRON_PREFIX = 'QQBOT_CRON:';
71
+
72
+ // ============================================
73
+ // 解析函数
74
+ // ============================================
75
+
76
+ /**
77
+ * 解析 AI 输出的结构化载荷
78
+ *
79
+ * 检测消息是否以 QQBOT_PAYLOAD: 前缀开头,如果是则提取并解析 JSON
80
+ *
81
+ * @param text AI 输出的原始文本
82
+ * @returns 解析结果
83
+ *
84
+ * @example
85
+ * const result = parseQQBotPayload('QQBOT_PAYLOAD:\n{"type": "media", "mediaType": "image", ...}');
86
+ * if (result.isPayload && result.payload) {
87
+ * // 处理结构化载荷
88
+ * }
89
+ */
90
+ export function parseQQBotPayload(text: string): ParseResult {
91
+ const trimmedText = text.trim();
92
+
93
+ // 检查是否以 QQBOT_PAYLOAD: 开头
94
+ if (!trimmedText.startsWith(PAYLOAD_PREFIX)) {
95
+ return {
96
+ isPayload: false,
97
+ text: text
98
+ };
99
+ }
100
+
101
+ // 提取 JSON 内容(去掉前缀)
102
+ const jsonContent = trimmedText.slice(PAYLOAD_PREFIX.length).trim();
103
+
104
+ if (!jsonContent) {
105
+ return {
106
+ isPayload: true,
107
+ error: '载荷内容为空'
108
+ };
109
+ }
110
+
111
+ try {
112
+ const payload = JSON.parse(jsonContent) as QQBotPayload;
113
+
114
+ // 验证必要字段
115
+ if (!payload.type) {
116
+ return {
117
+ isPayload: true,
118
+ error: '载荷缺少 type 字段'
119
+ };
120
+ }
121
+
122
+ // 根据 type 进行额外验证
123
+ if (payload.type === 'cron_reminder') {
124
+ if (!payload.content || !payload.targetType || !payload.targetAddress) {
125
+ return {
126
+ isPayload: true,
127
+ error: 'cron_reminder 载荷缺少必要字段 (content, targetType, targetAddress)'
128
+ };
129
+ }
130
+ } else if (payload.type === 'media') {
131
+ if (!payload.mediaType || !payload.source || !payload.path) {
132
+ return {
133
+ isPayload: true,
134
+ error: 'media 载荷缺少必要字段 (mediaType, source, path)'
135
+ };
136
+ }
137
+ }
138
+
139
+ return {
140
+ isPayload: true,
141
+ payload
142
+ };
143
+ } catch (e) {
144
+ return {
145
+ isPayload: true,
146
+ error: `JSON 解析失败: ${e instanceof Error ? e.message : String(e)}`
147
+ };
148
+ }
149
+ }
150
+
151
+ // ============================================
152
+ // Cron 编码/解码函数
153
+ // ============================================
154
+
155
+ /**
156
+ * 将定时提醒载荷编码为 Cron 消息格式
157
+ *
158
+ * 将 JSON 编码为 Base64,并添加 QQBOT_CRON: 前缀
159
+ *
160
+ * @param payload 定时提醒载荷
161
+ * @returns 编码后的消息字符串,格式为 QQBOT_CRON:{base64}
162
+ *
163
+ * @example
164
+ * const message = encodePayloadForCron({
165
+ * type: 'cron_reminder',
166
+ * content: '喝水时间到!',
167
+ * targetType: 'c2c',
168
+ * targetAddress: 'user_openid_xxx'
169
+ * });
170
+ * // 返回: QQBOT_CRON:eyJ0eXBlIjoiY3Jvbl9yZW1pbmRlciIs...
171
+ */
172
+ export function encodePayloadForCron(payload: CronReminderPayload): string {
173
+ const jsonString = JSON.stringify(payload);
174
+ const base64 = Buffer.from(jsonString, 'utf-8').toString('base64');
175
+ return `${CRON_PREFIX}${base64}`;
176
+ }
177
+
178
+ /**
179
+ * 解码 Cron 消息中的载荷
180
+ *
181
+ * 检测 QQBOT_CRON: 前缀,解码 Base64 并解析 JSON
182
+ *
183
+ * @param message Cron 触发时收到的消息
184
+ * @returns 解码结果,包含是否为 Cron 载荷、解析后的载荷对象或错误信息
185
+ *
186
+ * @example
187
+ * const result = decodeCronPayload('QQBOT_CRON:eyJ0eXBlIjoiY3Jvbl9yZW1pbmRlciIs...');
188
+ * if (result.isCronPayload && result.payload) {
189
+ * // 处理定时提醒
190
+ * }
191
+ */
192
+ export function decodeCronPayload(message: string): {
193
+ isCronPayload: boolean;
194
+ payload?: CronReminderPayload;
195
+ error?: string;
196
+ } {
197
+ const trimmedMessage = message.trim();
198
+
199
+ // 检查是否以 QQBOT_CRON: 开头
200
+ if (!trimmedMessage.startsWith(CRON_PREFIX)) {
201
+ return {
202
+ isCronPayload: false
203
+ };
204
+ }
205
+
206
+ // 提取 Base64 内容
207
+ const base64Content = trimmedMessage.slice(CRON_PREFIX.length);
208
+
209
+ if (!base64Content) {
210
+ return {
211
+ isCronPayload: true,
212
+ error: 'Cron 载荷内容为空'
213
+ };
214
+ }
215
+
216
+ try {
217
+ // Base64 解码
218
+ const jsonString = Buffer.from(base64Content, 'base64').toString('utf-8');
219
+ const payload = JSON.parse(jsonString) as CronReminderPayload;
220
+
221
+ // 验证类型
222
+ if (payload.type !== 'cron_reminder') {
223
+ return {
224
+ isCronPayload: true,
225
+ error: `期望 type 为 cron_reminder,实际为 ${payload.type}`
226
+ };
227
+ }
228
+
229
+ // 验证必要字段
230
+ if (!payload.content || !payload.targetType || !payload.targetAddress) {
231
+ return {
232
+ isCronPayload: true,
233
+ error: 'Cron 载荷缺少必要字段'
234
+ };
235
+ }
236
+
237
+ return {
238
+ isCronPayload: true,
239
+ payload
240
+ };
241
+ } catch (e) {
242
+ return {
243
+ isCronPayload: true,
244
+ error: `Cron 载荷解码失败: ${e instanceof Error ? e.message : String(e)}`
245
+ };
246
+ }
247
+ }
248
+
249
+ // ============================================
250
+ // 辅助函数
251
+ // ============================================
252
+
253
+ /**
254
+ * 判断载荷是否为定时提醒类型
255
+ */
256
+ export function isCronReminderPayload(payload: QQBotPayload): payload is CronReminderPayload {
257
+ return payload.type === 'cron_reminder';
258
+ }
259
+
260
+ /**
261
+ * 判断载荷是否为媒体消息类型
262
+ */
263
+ export function isMediaPayload(payload: QQBotPayload): payload is MediaPayload {
264
+ return payload.type === 'media';
265
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "rootDir": ".",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "resolveJsonModule": true
13
+ },
14
+ "include": ["index.ts", "src/**/*.ts"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }
@@ -0,0 +1,89 @@
1
+ #!/bin/bash
2
+
3
+ # QQBot 一键更新并启动脚本
4
+
5
+ set -e
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+ cd "$SCRIPT_DIR"
9
+
10
+ # 解析命令行参数
11
+ APPID=""
12
+ SECRET=""
13
+
14
+ while [[ $# -gt 0 ]]; do
15
+ case $1 in
16
+ --appid)
17
+ APPID="$2"
18
+ shift 2
19
+ ;;
20
+ --secret)
21
+ SECRET="$2"
22
+ shift 2
23
+ ;;
24
+ -h|--help)
25
+ echo "用法: $0 [选项]"
26
+ echo ""
27
+ echo "选项:"
28
+ echo " --appid <appid> QQ机器人 AppID"
29
+ echo " --secret <secret> QQ机器人 Secret"
30
+ echo " -h, --help 显示帮助信息"
31
+ echo ""
32
+ echo "也可以通过环境变量设置:"
33
+ echo " QQBOT_APPID QQ机器人 AppID"
34
+ echo " QQBOT_SECRET QQ机器人 Secret"
35
+ exit 0
36
+ ;;
37
+ *)
38
+ echo "未知选项: $1"
39
+ echo "使用 --help 查看帮助信息"
40
+ exit 1
41
+ ;;
42
+ esac
43
+ done
44
+
45
+ # 使用命令行参数或环境变量
46
+ APPID="${APPID:-$QQBOT_APPID}"
47
+ SECRET="${SECRET:-$QQBOT_SECRET}"
48
+
49
+ echo "========================================="
50
+ echo " QQBot 一键更新启动脚本"
51
+ echo "========================================="
52
+
53
+ # 1. 移除老版本
54
+ echo ""
55
+ echo "[1/4] 移除老版本..."
56
+ if [ -f "./scripts/upgrade.sh" ]; then
57
+ bash ./scripts/upgrade.sh
58
+ else
59
+ echo "警告: upgrade.sh 不存在,跳过移除步骤"
60
+ fi
61
+
62
+ # 2. 安装当前版本
63
+ echo ""
64
+ echo "[2/4] 安装当前版本..."
65
+ openclaw plugins install .
66
+
67
+ # 3. 配置机器人通道
68
+ echo ""
69
+ echo "[3/4] 配置机器人通道..."
70
+
71
+ # 构建 token(如果提供了 appid 和 secret)
72
+ if [ -n "$APPID" ] && [ -n "$SECRET" ]; then
73
+ QQBOT_TOKEN="${APPID}:${SECRET}"
74
+ echo "使用提供的 AppID 和 Secret 配置..."
75
+ else
76
+ # 默认 token,可通过环境变量 QQBOT_TOKEN 覆盖
77
+ QQBOT_TOKEN="${QQBOT_TOKEN:-appid:secret}"
78
+ echo "使用默认或环境变量中的 Token..."
79
+ fi
80
+
81
+ openclaw channels add --channel qqbot --token "$QQBOT_TOKEN"
82
+ # 启用 markdown 支持
83
+ openclaw config set channels.qqbot.markdownSupport true
84
+
85
+ # 4. 启动 openclaw
86
+ echo ""
87
+ echo "[4/4] 启动 openclaw..."
88
+ echo "========================================="
89
+ openclaw gateway --verbose