@marshulll/openclaw-wecom 0.1.20 → 0.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.
- package/README.en.md +16 -1
- package/README.md +16 -1
- package/README.zh.md +16 -1
- package/docs/INSTALL.md +19 -0
- package/docs/wecom.config.full.example.json +1 -1
- package/package.json +1 -1
- package/wecom/src/media-vision.ts +44 -35
- package/wecom/src/wecom-app.ts +8 -0
package/README.en.md
CHANGED
|
@@ -10,9 +10,10 @@ OpenClaw WeCom plugin supporting **Bot API mode** and **Internal App mode** with
|
|
|
10
10
|
- Dual mode: Bot API (JSON callback + stream) / App (XML callback + ACK + proactive send)
|
|
11
11
|
- Multi-account: `channels.wecom.accounts`
|
|
12
12
|
- Message types: text / image / voice / video / file (send & receive)
|
|
13
|
-
- Commands: `/help`, `/status`, `/clear`
|
|
13
|
+
- Commands: `/help`, `/status`, `/clear`, `/sendfile`
|
|
14
14
|
- Stability: signature verification, AES decrypt, token cache, rate limit & retries
|
|
15
15
|
- Group chat: uses `appchat/send` when `chatId` is present
|
|
16
|
+
- Advanced: folder zip sending, send queue, operation logs, media auto recognition
|
|
16
17
|
|
|
17
18
|
## Install
|
|
18
19
|
### npm
|
|
@@ -88,6 +89,20 @@ Install guide: `docs/INSTALL.md`
|
|
|
88
89
|
- Bot mode media bridge: if reply payload includes `mediaUrl + mediaType`,
|
|
89
90
|
and App credentials are present, media will be uploaded and sent
|
|
90
91
|
|
|
92
|
+
## Extra commands
|
|
93
|
+
- `/sendfile`: send files from server (multiple absolute paths)
|
|
94
|
+
- Directories are zipped automatically
|
|
95
|
+
- Example: `/sendfile /tmp/openclaw-wecom /home/shu/Desktop/report.pdf`
|
|
96
|
+
|
|
97
|
+
## Media auto recognition (optional)
|
|
98
|
+
- **Voice send/receive does NOT require API**; only auto transcription needs an OpenAI-compatible API
|
|
99
|
+
- **Video recognition requires ffmpeg** (install on server, then set `media.auto.video.enabled = true`)
|
|
100
|
+
- Small text files can be previewed automatically
|
|
101
|
+
|
|
102
|
+
## Send queue & operation logs (optional)
|
|
103
|
+
- `sendQueue.intervalMs`: delay between /sendfile items to avoid rate limit
|
|
104
|
+
- `operations.logPath`: JSONL log for file sending and push actions
|
|
105
|
+
|
|
91
106
|
## Troubleshooting
|
|
92
107
|
- Callback verification failed: check Token / AESKey / URL
|
|
93
108
|
- No reply: ensure plugin enabled and gateway restarted
|
package/README.md
CHANGED
|
@@ -10,9 +10,10 @@ OpenClaw WeCom 插件,支持 **智能机器人 API 模式** 与 **自建应用
|
|
|
10
10
|
- 双模式:Bot API(JSON 回调 + stream)/ App(XML 回调 + ACK + 主动发送)
|
|
11
11
|
- 多账户:`channels.wecom.accounts`
|
|
12
12
|
- 消息类型:文本 / 图片 / 语音 / 视频 / 文件(收发均支持)
|
|
13
|
-
- 机器人命令:`/help`、`/status`、`/clear`
|
|
13
|
+
- 机器人命令:`/help`、`/status`、`/clear`、`/sendfile`
|
|
14
14
|
- 稳定性:签名校验、AES 解密、token 缓存、限流与重试
|
|
15
15
|
- 群聊:自动识别 `chatId` 并使用 `appchat/send`
|
|
16
|
+
- 进阶:文件夹打包发送、发送队列、操作日志、多媒体自动识别
|
|
16
17
|
|
|
17
18
|
## 安装
|
|
18
19
|
### npm 安装
|
|
@@ -90,6 +91,20 @@ openclaw gateway restart
|
|
|
90
91
|
- Bot 模式媒体桥接:当 reply payload 含 `mediaUrl + mediaType` 时,
|
|
91
92
|
若已配置 App 凭据,会自动上传并发送媒体
|
|
92
93
|
|
|
94
|
+
## 命令补充
|
|
95
|
+
- `/sendfile`:发送服务器文件(支持多个绝对路径)
|
|
96
|
+
- 支持目录:自动打包为 zip 后发送
|
|
97
|
+
- 示例:`/sendfile /tmp/openclaw-wecom /home/shu/Desktop/report.pdf`
|
|
98
|
+
|
|
99
|
+
## 多媒体自动识别(可选)
|
|
100
|
+
- **语音收发不需要 API**,只有开启“语音自动转写”才需要 OpenAI 兼容接口
|
|
101
|
+
- **视频识别需要 ffmpeg**(服务器已安装后,将 `media.auto.video.enabled` 设为 `true`)
|
|
102
|
+
- 文本文件可自动预览(小文件直接读入)
|
|
103
|
+
|
|
104
|
+
## 发送队列与操作日志(可选)
|
|
105
|
+
- `sendQueue.intervalMs`:/sendfile 多文件发送间隔(防止限流)
|
|
106
|
+
- `operations.logPath`:记录发送文件/主动推送(JSONL)
|
|
107
|
+
|
|
93
108
|
## 常见问题
|
|
94
109
|
- 回调验证失败:检查 Token / AESKey / URL 是否一致
|
|
95
110
|
- 没有回复:确认已启用插件并重启 gateway
|
package/README.zh.md
CHANGED
|
@@ -10,9 +10,10 @@ OpenClaw WeCom 插件,支持 **智能机器人 API 模式** 与 **自建应用
|
|
|
10
10
|
- 双模式:Bot API(JSON 回调 + stream)/ App(XML 回调 + ACK + 主动发送)
|
|
11
11
|
- 多账户:`channels.wecom.accounts`
|
|
12
12
|
- 消息类型:文本 / 图片 / 语音 / 视频 / 文件(收发均支持)
|
|
13
|
-
- 机器人命令:`/help`、`/status`、`/clear`
|
|
13
|
+
- 机器人命令:`/help`、`/status`、`/clear`、`/sendfile`
|
|
14
14
|
- 稳定性:签名校验、AES 解密、token 缓存、限流与重试
|
|
15
15
|
- 群聊:自动识别 `chatId` 并使用 `appchat/send`
|
|
16
|
+
- 进阶:文件夹打包发送、发送队列、操作日志、多媒体自动识别
|
|
16
17
|
|
|
17
18
|
## 安装
|
|
18
19
|
### npm 安装
|
|
@@ -90,6 +91,20 @@ openclaw gateway restart
|
|
|
90
91
|
- Bot 模式媒体桥接:当 reply payload 含 `mediaUrl + mediaType` 时,
|
|
91
92
|
若已配置 App 凭据,会自动上传并发送媒体
|
|
92
93
|
|
|
94
|
+
## 命令补充
|
|
95
|
+
- `/sendfile`:发送服务器文件(支持多个绝对路径)
|
|
96
|
+
- 支持目录:自动打包为 zip 后发送
|
|
97
|
+
- 示例:`/sendfile /tmp/openclaw-wecom /home/shu/Desktop/report.pdf`
|
|
98
|
+
|
|
99
|
+
## 多媒体自动识别(可选)
|
|
100
|
+
- **语音收发不需要 API**,只有开启“语音自动转写”才需要 OpenAI 兼容接口
|
|
101
|
+
- **视频识别需要 ffmpeg**(服务器已安装后,将 `media.auto.video.enabled` 设为 `true`)
|
|
102
|
+
- 文本文件可自动预览(小文件直接读入)
|
|
103
|
+
|
|
104
|
+
## 发送队列与操作日志(可选)
|
|
105
|
+
- `sendQueue.intervalMs`:/sendfile 多文件发送间隔(防止限流)
|
|
106
|
+
- `operations.logPath`:记录发送文件/主动推送(JSONL)
|
|
107
|
+
|
|
93
108
|
## 常见问题
|
|
94
109
|
- 回调验证失败:检查 Token / AESKey / URL 是否一致
|
|
95
110
|
- 没有回复:确认已启用插件并重启 gateway
|
package/docs/INSTALL.md
CHANGED
|
@@ -127,6 +127,25 @@ openclaw gateway restart
|
|
|
127
127
|
- Bot 模式 `receiveId`:建议填写 **Bot ID(aibotid)**,用于回调加解密校验;不填也可通过,但会降低校验严格性。
|
|
128
128
|
- App 模式回调解密使用 **CorpID**(即 `corpId`),与 Bot 模式的 `receiveId` 无关。
|
|
129
129
|
|
|
130
|
+
## 高级能力(可选)
|
|
131
|
+
### /sendfile(文件与文件夹)
|
|
132
|
+
- `/sendfile` 仅支持 **服务器绝对路径**
|
|
133
|
+
- 目录会自动打包为 zip 再发送
|
|
134
|
+
|
|
135
|
+
示例:
|
|
136
|
+
```
|
|
137
|
+
/sendfile /tmp/openclaw-wecom /home/shu/Desktop/report.pdf
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 多媒体自动识别
|
|
141
|
+
- **语音收发不需要 API**;只有开启“语音自动转写”才需要 OpenAI 兼容接口
|
|
142
|
+
- **视频识别需要 ffmpeg**(服务器安装后将 `media.auto.video.enabled=true`)
|
|
143
|
+
- 文本文件可自动预览(小文件直接读入)
|
|
144
|
+
|
|
145
|
+
### 发送队列与操作日志
|
|
146
|
+
- `sendQueue.intervalMs`:/sendfile 多文件发送间隔
|
|
147
|
+
- `operations.logPath`:JSONL 日志,记录发送文件与主动推送
|
|
148
|
+
|
|
130
149
|
## Webhook 验证
|
|
131
150
|
- Bot 模式与 App 模式都要求公网 HTTPS。
|
|
132
151
|
- 在企业微信后台配置回调 URL。
|
package/package.json
CHANGED
|
@@ -56,43 +56,52 @@ export async function describeImageWithVision(params: {
|
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
const
|
|
60
|
-
const
|
|
59
|
+
const imageBase64 = buffer.toString("base64");
|
|
60
|
+
const payload = {
|
|
61
|
+
model: config.model,
|
|
62
|
+
messages: [
|
|
63
|
+
{
|
|
64
|
+
role: "user",
|
|
65
|
+
content: [
|
|
66
|
+
{ type: "text", text: config.prompt },
|
|
67
|
+
{ type: "image_url", image_url: { url: `data:${mimeType};base64,${imageBase64}` } },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
max_tokens: config.maxTokens ?? 400,
|
|
72
|
+
};
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{ type: "image_url", image_url: { url: `data:${mimeType};base64,${imageBase64}` } },
|
|
72
|
-
],
|
|
74
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
const timeout = setTimeout(() => controller.abort(), config.timeoutMs ?? 15000);
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: {
|
|
81
|
+
"Content-Type": "application/json",
|
|
82
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
73
83
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const res = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
79
|
-
method: "POST",
|
|
80
|
-
headers: {
|
|
81
|
-
"Content-Type": "application/json",
|
|
82
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
83
|
-
},
|
|
84
|
-
body: JSON.stringify(payload),
|
|
85
|
-
signal: controller.signal,
|
|
86
|
-
});
|
|
84
|
+
body: JSON.stringify(payload),
|
|
85
|
+
signal: controller.signal,
|
|
86
|
+
});
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const data = await res.json() as any;
|
|
92
|
+
const content = data?.choices?.[0]?.message?.content;
|
|
93
|
+
if (typeof content !== "string") {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const trimmed = content.trim();
|
|
97
|
+
if (!trimmed) continue;
|
|
98
|
+
return trimmed;
|
|
99
|
+
} catch {
|
|
100
|
+
// retry once
|
|
101
|
+
} finally {
|
|
102
|
+
clearTimeout(timeout);
|
|
103
|
+
}
|
|
97
104
|
}
|
|
105
|
+
|
|
106
|
+
return null;
|
|
98
107
|
}
|
package/wecom/src/wecom-app.ts
CHANGED
|
@@ -695,6 +695,14 @@ async function processAppMessage(params: {
|
|
|
695
695
|
createdAt: Date.now(),
|
|
696
696
|
size: buffer.length,
|
|
697
697
|
});
|
|
698
|
+
if (visionConfig && !summary) {
|
|
699
|
+
await appendOperationLog(target, {
|
|
700
|
+
action: "vision-image-failed",
|
|
701
|
+
accountId: target.account.accountId,
|
|
702
|
+
path: tempImagePath,
|
|
703
|
+
size: buffer.length,
|
|
704
|
+
});
|
|
705
|
+
}
|
|
698
706
|
logVerbose(target, `app image saved (${buffer.length} bytes): ${tempImagePath}`);
|
|
699
707
|
if (summary) {
|
|
700
708
|
messageText = `[用户发送了一张图片]\n\n[图片识别结果]\n${summary}\n\n请根据识别结果回复用户。`;
|