WeCom (Enterprise WeChat)
通过 WeCom(企业微信)将 Hermes 接入腾讯的企业通信平台。该适配器使用 WeCom 的 AI Bot WebSocket 网关实现实时双向通信,无需公网端点或 webhook。
前提条件
- 一个 WeCom 企业组织账号
- 在 WeCom 管理后台中创建好的 AI Bot
- 该 bot 凭据页中的 Bot ID 与 Secret
- Python 包:
aiohttp和httpx
设置
第 1 步:创建 AI Bot
推荐:扫码创建(一条命令)
hermes gateway setup
选择 WeCom,然后用 WeCom 手机端扫码。Hermes 会自动创建一个权限配置正确的 bot 应用,并保存凭据。
向导会:
- 在终端显示二维码
- 等你用 WeCom 手机端扫码
- 自动获取 Bot ID 与 Secret
- 引导你配置访问控制
备选:手动设置
如果扫码创建不可用,向导会回退到手动输入:
- 登录 WeCom Admin Console
- 进入 Applications → Create Application → AI Bot
- 配置 bot 名称与描述
- 从凭据页复制 Bot ID 与 Secret
- 运行
hermes gateway setup,选择 WeCom 并按提示输入凭据
请妥善保管 Bot Secret。任何持有它的人都可以冒充你的 bot。
第 2 步:配置 Hermes
Option A: Interactive Setup (Recommended)
hermes gateway setup
向导会引导你配置:
- bot 凭据(扫码或手动输入)
- 访问控制(allowlist、pairing 或开放访问)
- 用于通知的 home channel
Option B: Manual Configuration
把以下内容加入 ~/.hermes/.env:
WECOM_BOT_ID=your-bot-id
WECOM_SECRET=your-secret
# Optional: restrict access
WECOM_ALLOWED_USERS=user_id_1,user_id_2
# Optional: home channel for cron/notifications
WECOM_HOME_CHANNEL=chat_id
第 3 步:启动网关
hermes gateway
功能
- WebSocket transport:持久连接,无需公网端点
- DM 和群消息:支持可配置访问策略
- 按群发送者 allowlist:细粒度控制每个群里谁能与 bot 交互
- 媒体支持:图片、文件、语音、视频上传下载
- AES 加密媒体:自动解密入站附件
- 引用上下文:保留 reply threading 关系
- Markdown rendering:支持富文本回复
- Reply-mode streaming:把回复关联回原始入站消息上下文
- Auto-reconnect:连接断开后自动退避重连
配置选项
在 config.yaml 的 platforms.wecom.extra 下配置:
| Key | Default | Description |
|---|---|---|
bot_id | — | WeCom AI Bot ID(必需) |
secret | — | WeCom AI Bot Secret(必需) |
websocket_url | wss://openws.work.weixin.qq.com | WebSocket 网关 URL |
dm_policy | open | 私聊访问策略:open、allowlist、disabled、pairing |
group_policy | open | 群访问策略:open、allowlist、disabled |
allow_from | [] | 私聊 allowlist |
group_allow_from | [] | 群 allowlist |
groups | {} | 每个群的细粒度配置 |
访问策略
DM Policy
| Value | Behavior |
|---|---|
open | 任何人都可以私聊 bot(默认) |
allowlist | 只有 allow_from 中的用户可以私聊 |
disabled | 忽略所有私聊 |
pairing | 配对模式 |
WECOM_DM_POLICY=allowlist
Group Policy
| Value | Behavior |
|---|---|
open | 在所有群中响应(默认) |
allowlist | 只在 group_allow_from 列出的群中响应 |
disabled | 忽略所有群消息 |
WECOM_GROUP_POLICY=allowlist
Per-Group Sender Allowlists
你还可以在 config.yaml 中按群进一步限制发送者:
platforms:
wecom:
enabled: true
extra:
bot_id: "your-bot-id"
secret: "your-secret"
group_policy: "allowlist"
group_allow_from:
- "group_id_1"
- "group_id_2"
groups:
group_id_1:
allow_from:
- "user_alice"
- "user_bob"
group_id_2:
allow_from:
- "user_charlie"
"*":
allow_from:
- "user_admin"
工作方式:
group_policy与group_allow_from先决定某个群本身是否允许接入- 如果该群通过顶层策略,再由
groups.<group_id>.allow_from进一步限制群内哪些发送者能和 bot 交互 - 通配符
"*"可作为未显式列出的群的默认规则 - allowlist 条目支持
*通配符,且匹配不区分大小写 - 条目也可写成
wecom:user:或wecom:group:前缀形式,适配器会自动剥离前缀
如果某个群没有配置 allow_from,则该群内所有用户都可使用 bot(前提是该群本身已经通过顶层策略校验)。
媒体支持
入站(接收)
适配器会接收并缓存以下媒体附件供 agent 处理:
| Type | How it's handled |
|---|---|
| Images | 下载并缓存,支持 URL 和 base64 两种形式 |
| Files | 下载并缓存,保留原始文件名 |
| Voice | 如果存在语音转写文本,会优先提取文本 |
| Mixed messages | WeCom 的混合消息(文本 + 图片)会被拆解并提取全部组件 |
Quoted messages: 被引用消息中的媒体也会被抽取,方便 agent 理解用户正在回复什么。
AES-Encrypted Media Decryption
部分入站媒体会使用 AES-256-CBC 加密。适配器会自动处理:
- 当媒体项包含
aeskey字段时,适配器会先下载密文,再用 AES-256-CBC + PKCS#7 padding 解密 - AES key 为
aeskey字段的 base64 解码值,长度必须为 32 字节 - IV 取自 key 的前 16 字节
- 依赖
cryptographyPython 包(pip install cryptography)
出站(发送)
| Method | What it sends | Size limit |
|---|---|---|
send | Markdown 文本消息 | 4000 chars |
send_image / send_image_file | 原生图片消息 | 10 MB |
send_document | 文件附件 | 20 MB |
send_voice | 原生语音消息(仅支持 AMR) | 2 MB |
send_video | 视频消息 | 10 MB |
分块上传: 文件会通过三段式协议(init → chunks → finish)按 512 KB 分块上传,适配器会自动处理。
自动降级: 当媒体超出对应原生类型限制,但未超过 20 MB 文件绝对上限时,会自动降级为普通文件发送:
- 图片 > 10 MB → 作为文件发送
- 视频 > 10 MB → 作为文件发送
- 语音 > 2 MB → 作为文件发送
- 非 AMR 音频 → 作为文件发送(WeCom 原生语音只支持 AMR)
超过 20 MB 的文件会被拒绝,并在聊天中返回说明信息。
Reply-Mode Stream Responses
当 bot 通过 WeCom callback 收到消息后,适配器会记录入站请求 ID。如果在请求上下文仍然有效时发送回复,适配器会优先使用 WeCom reply-mode(aibot_respond_msg)并启用 streaming,把回复直接关联到原消息上。
如果入站请求上下文已过期或不可用,适配器会回退到主动发送模式 aibot_send_msg。
reply-mode 也支持媒体:上传后的媒体可以作为对原消息的回复发送回去。
连接与重连
适配器会维持到 wss://openws.work.weixin.qq.com 的持久 WebSocket 连接。
Connection Lifecycle
- Connect:建立 WebSocket 连接,并发送带有
bot_id和secret的aibot_subscribe鉴权帧 - Heartbeat:每 30 秒发送应用层 ping,保持连接活跃
- Listen:持续读取入站帧并分发消息回调
Reconnection Behavior
断线后,适配器会按指数退避重连:
| Attempt | Delay |
|---|---|
| 第 1 次重试 | 2 秒 |
| 第 2 次重试 | 5 秒 |
| 第 3 次重试 | 10 秒 |
| 第 4 次重试 | 30 秒 |
| 第 5 次及以后 | 60 秒 |
每次成功重连后,退避计数都会清零。断线时,所有待决请求 future 都会失败,避免调用方无限挂起。
Deduplication
入站消息会按 message ID 做 5 分钟窗口去重,缓存上限为 1000 条,防止在重连或网络抖动场景下重复处理。
All Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
WECOM_BOT_ID | ✅ | — | WeCom AI Bot ID |
WECOM_SECRET | ✅ | — | WeCom AI Bot Secret |
WECOM_ALLOWED_USERS | — | (empty) | 网关级 allowlist 用户 ID |
WECOM_HOME_CHANNEL | — | — | cron / 通知输出 chat ID |
WECOM_WEBSOCKET_URL | — | wss://openws.work.weixin.qq.com | WebSocket 网关 URL |
WECOM_DM_POLICY | — | open | 私聊访问策略 |
WECOM_GROUP_POLICY | — | open | 群访问策略 |
Troubleshooting
| Problem | Fix |
|---|---|
WECOM_BOT_ID and WECOM_SECRET are required | 设置环境变量或使用设置向导 |
WeCom startup failed: aiohttp not installed | 安装 aiohttp:pip install aiohttp |
WeCom startup failed: httpx not installed | 安装 httpx:pip install httpx |
invalid secret (errcode=40013) | 检查 secret 是否与后台配置一致 |
Timed out waiting for subscribe acknowledgement | 检查到 openws.work.weixin.qq.com 的网络连通性 |
| 群里不响应 | 检查 group_policy 和 group_allow_from |
| 某些用户在群里被忽略 | 检查群级 allow_from 列表 |
| 媒体解密失败 | 安装 cryptography |
cryptography is required for WeCom media decryption | 该入站媒体为 AES 加密,请安装 cryptography |
| 语音消息被当作文件发送 | WeCom 原生语音只支持 AMR,其他格式会自动降级 |
File too large | WeCom 对所有文件上传都限制为 20 MB |
| 图片被当作文件发送 | 图片超过 10 MB 时会自动降级为文件附件 |
Timeout sending message to WeCom | WebSocket 可能已断开,请检查重连日志 |
WeCom websocket closed during authentication | 可能是网络问题或凭据错误,请检查 bot_id 与 secret |