微信(WeChat)
将 Hermes 连接到 微信(腾讯的个人即时通讯平台)。该适配器使用腾讯的 iLink Bot API 来支持个人微信账号 —— 这与企业微信(WeCom)不同。消息通过长轮询方式传输,因此无需公网端点或 Webhook。
此适配器仅适用于 个人微信账号(微信)。如需企业/公司级微信服务,请参见 企业微信适配器。
前置条件
- 一个个人微信账号
- Python 包:
aiohttp和cryptography - 可选包:
qrcode(用于设置过程中在终端中渲染二维码)
安装所需依赖:
pip install aiohttp cryptography
# Optional: for terminal QR code display
pip install qrcode
设置步骤
1. 运行设置向导
连接微信账号最简便的方式是通过交互式设置向导:
hermes gateway setup
当提示时选择 Weixin。向导将执行以下操作:
- 向 iLink Bot API 请求生成二维码
- 在终端中显示二维码(或提供访问链接)
- 等待您使用微信手机 App 扫描二维码
- 提示您在手机上确认登录
- 自动保存账户凭证至
~/.hermes/weixin/accounts/
确认后,您会看到类似如下信息:
微信连接成功,account_id=your-account-id
向导会自动保存 account_id、token 和 base_url,无需手动配置。
2. 配置环境变量
首次扫码登录后,至少需在 ~/.hermes/.env 中设置账号 ID:
WEIXIN_ACCOUNT_ID=your-account-id
# Optional: override the token (normally auto-saved from QR login)
# WEIXIN_TOKEN=your-bot-token
# Optional: restrict access
WEIXIN_DM_POLICY=open
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2
# Optional: restore legacy multiline splitting behavior
# WEIXIN_SPLIT_MULTILINE_MESSAGES=true
# Optional: home channel for cron/notifications
WEIXIN_HOME_CHANNEL=chat_id
WEIXIN_HOME_CHANNEL_NAME=Home
3. 启动网关
hermes gateway
适配器将恢复已保存的凭证,连接 iLink API,并开始长轮询接收消息。
功能特性
- 长轮询传输 —— 无需公网地址、Webhook 或 WebSocket
- 二维码登录 —— 通过
hermes gateway setup实现扫码即连 - 私聊与群聊支持 —— 可配置访问策略
- 媒体文件支持 —— 图片、视频、文件和语音消息
- AES-128-ECB 加密 CDN —— 所有媒体传输自动加密/解密
- 上下文令牌持久化 —— 磁盘存储,支持重启后对话连续性
- Markdown 格式化 —— 标题、表格和代码块会重新格式化以适应微信阅读体验
- 智能消息分块 —— 消息长度在限制内时保持为单个气泡;仅超大内容在逻辑边界处拆分
- 输入状态提示 —— 代理处理时,微信客户端显示“正在输入…”状态
- SSRF 防护 —— 外部媒体 URL 在下载前经过验证
- 消息去重 —— 5 分钟滑动窗口防止重复处理
- 自动重试带退避机制 —— 可从临时 API 错误中恢复
配置选项
在 config.yaml 文件中的 platforms.weixin.extra 下设置以下参数:
| 键名 | 默认值 | 描述 |
|---|---|---|
account_id | — | iLink Bot 账号 ID(必填) |
token | — | iLink Bot Token(必填,扫码登录后自动保存) |
base_url | `https://ilinkai.weixin.qq.com`` | iLink API 基础地址 |
cdn_base_url | `https://novac2c.cdn.weixin.qq.com/c2c`` | 媒体传输所用 CDN 基础地址 |
dm_policy | open | 私聊访问策略:open、allowlist、disabled、pairing |
group_policy | disabled | 群组访问策略:open、allowlist、disabled |
allow_from | [] | 允许私聊的用户 ID 列表(当 dm_policy=allowlist 时生效) |
group_allow_from | [] | 允许响应的群组 ID 列表(当 group_policy=allowlist 时生效) |
split_multiline_messages | false | 当 true 时,多行回复将被拆分为多个聊天消息(旧版行为);当 false 时,除非超过长度限制,否则保持为一条消息 |
访问策略
私聊策略(DM Policy)
控制谁可以向机器人发送私聊消息:
| 值 | 行为 |
|---|---|
open | 任何人都可发送私聊(默认) |
allowlist | 仅允许 allow_from 列表中的用户发送 |
disabled | 忽略所有私聊消息 |
pairing | 配对模式(仅用于初始设置) |
WEIXIN_DM_POLICY=allowlist
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2
群组策略(Group Policy)
控制机器人在哪些群组中响应:
| 值 | 行为 |
|---|---|
open | 机器人在所有群组中响应 |
allowlist | 仅在 group_allow_from 列表中的群组中响应 |
disabled | 忽略所有群组消息(默认) |
WEIXIN_GROUP_POLICY=allowlist
WEIXIN_GROUP_ALLOWED_USERS=group_id_1,group_id_2
个人微信账号的默认群组策略为 disabled(不同于企业微信默认为 open)。这是有意为之的设计,因为个人微信账号可能加入大量群组。
媒体支持
入站(接收)
适配器接收来自用户的媒体附件,从微信 CDN 下载,解密后本地缓存以供代理处理:
| 类型 | 处理方式 |
|---|---|
| 图片 | 下载、AES 解密,缓存为 JPEG 格式 |
| 视频 | 下载、AES 解密,缓存为 MP4 格式 |
| 文件 | 下载、AES 解密,缓存并保留原始文件名 |
| 语音 | 若存在文字转录,则提取为文本;否则下载 SILK 格式音频并缓存 |
引用消息:被引用(回复)的消息中的媒体也会被提取,使代理能获取用户回复的上下文。
AES-128-ECB 加密 CDN
微信媒体文件通过加密 CDN 传输。适配器透明处理该过程:
- 入站:加密媒体通过
encrypted_query_param地址从 CDN 下载,使用消息负载中提供的每文件密钥进行 AES-128-ECB 解密。 - 出站:文件在本地使用随机 AES-128-ECB 密钥加密,上传至 CDN,加密引用信息包含在发出的消息中。
- AES 密钥为 16 字节(128 位)。密钥可能以原始 base64 或十六进制编码形式到达 —— 适配器自动处理两种格式。
- 此功能需要
cryptographyPython 包。
无需额外配置 —— 加密与解密自动完成。
出站(发送)
| 方法 | 发送内容 |
|---|---|
send | 带 Markdown 格式的文本消息 |
send_image / send_image_file | 原生图片消息(通过 CDN 上传) |
send_document | 文件附件(通过 CDN 上传) |
send_video | 视频消息(通过 CDN 上传) |
所有出站媒体均走加密 CDN 上传流程:
- 生成随机 AES-128 密钥
- 使用 AES-128-ECB + PKCS#7 填充加密文件
- 向 iLink API 请求上传地址(
getuploadurl) - 将密文上传至 CDN
- 发送包含加密媒体引用的消息
上下文令牌持久化
iLink Bot API 要求每个目标通信方在每次出站消息中回传一个 context_token。适配器维护一个基于磁盘的上下文令牌存储:
- 每个账号+联系人组合的令牌保存至
~/.hermes/weixin/accounts/<account_id>.context-tokens.json - 启动时自动恢复之前保存的令牌
- 每条入站消息更新对应发送者的令牌
- 出站消息自动包含最新的上下文令牌
确保即使网关重启,也能维持对话连续性。
Markdown 格式化
微信个人聊天不原生支持完整 Markdown。适配器会对内容进行重构以提升可读性:
- 标题(
# Title)→ 转换为【Title】(一级标题)或**Title**(二级及以上) - 表格 → 重构成带标签的键值列表(例如
- Column: Value) - 代码块 → 保持原样(微信对代码块渲染表现良好)
- 过多空白行 → 合并为双换行
消息分块处理消息在符合平台限制时以单条聊天消息形式发送。仅当消息过大时才会进行拆分:
- 最大消息长度:4000 个字符
- 小于该限制的消息即使包含多个段落或换行符,也保持完整
- 超长消息会在逻辑边界处拆分(如段落、空行、代码块标记)
- 代码块标记尽可能保持完整(除非代码块本身超过限制,否则不会在中间拆分)
- 单个超长内容块将回退至基础适配器的截断逻辑
- 多个数据块之间设置 0.3 秒延迟,防止微信因频繁发送触发限流
输入状态提示
适配器会在微信客户端显示输入状态:
- 当消息到达时,适配器通过
getconfigAPI 获取一个typing_ticket - 每个用户的输入票证缓存时间为 10 分钟
send_typing发送“开始输入”信号;stop_typing发送“停止输入”信号- 网关在代理处理消息期间自动触发输入状态提示
长轮询连接
适配器使用 HTTP 长轮询(非 WebSocket)接收消息:
工作原理
- 连接:验证凭证并启动轮询循环
- 轮询:调用
getupdates,设置 35 秒超时;服务器会保持请求打开,直到有消息到达或超时 - 分发:收到的入站消息通过
asyncio.create_task并发分发 - 同步缓冲:持久化的同步游标(
get_updates_buf)保存在磁盘中,确保重启后能从正确位置恢复
重试行为
遇到 API 错误时,适配器采用简单重试策略:
| 条件 | 行为 |
|---|---|
| 临时错误(第1–2次) | 2秒后重试 |
| 重复错误(第3次及以上) | 退避30秒,然后重置计数器 |
会话过期(errcode=-14) | 暂停10分钟(可能需要重新登录) |
| 超时 | 立即重新轮询(正常长轮询行为) |
去重机制
入站消息通过消息 ID 进行去重,窗口为 5 分钟。可防止网络波动或轮询响应重叠导致的重复处理。
Token 锁机制
同一 Token 只允许一个 Weixin 网关实例使用。适配器启动时获取作用域锁,关闭时释放。若已有其他网关正在使用相同 Token,启动将失败,并返回明确错误信息。
所有环境变量
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
WEIXIN_ACCOUNT_ID | ✅ | — | iLink Bot 账号 ID(来自扫码登录) |
WEIXIN_TOKEN | ✅ | — | iLink Bot Token(扫码登录后自动保存) |
WEIXIN_BASE_URL | — | `https://ilinkai.weixin.qq.com`` | iLink API 基础地址 |
WEIXIN_CDN_BASE_URL | — | `https://novac2c.cdn.weixin.qq.com/c2c`` | 媒体传输所用 CDN 基础地址 |
WEIXIN_DM_POLICY | — | open | 私信访问策略:open, allowlist, disabled, pairing |
WEIXIN_GROUP_POLICY | — | disabled | 群组访问策略:open, allowlist, disabled |
WEIXIN_ALLOWED_USERS | — | (空) | 逗号分隔的私信白名单用户 ID 列表 |
WEIXIN_GROUP_ALLOWED_USERS | — | (空) | 逗号分隔的群组白名单 ID 列表 |
WEIXIN_HOME_CHANNEL | — | — | 定时任务/通知输出的聊天 ID |
WEIXIN_HOME_CHANNEL_NAME | — | Home | 主频道显示名称 |
WEIXIN_ALLOW_ALL_USERS | — | — | 网关级标志,允许所有用户(由设置向导使用) |
故障排查
| 问题 | 解决方法 |
|---|---|
Weixin startup failed: aiohttp and cryptography are required | 安装以下两个组件:pip install aiohttp cryptography |
Weixin startup failed: WEIXIN_TOKEN is required | 运行 hermes gateway setup 完成扫码登录,或手动设置 WEIXIN_TOKEN |
Weixin startup failed: WEIXIN_ACCOUNT_ID is required | 在你的 .env 中设置 WEIXIN_ACCOUNT_ID,或运行 hermes gateway setup |
Another local Hermes gateway is already using this Weixin token | 先停止另一个网关实例——每个 Token 只允许一个轮询器 |
会话已过期(errcode=-14) | 登录会话已失效。请重新运行 hermes gateway setup 扫描新二维码 |
| 设置过程中二维码过期 | 二维码最多自动刷新 3 次。若持续过期,请检查网络连接 |
| 机器人不响应私信 | 检查 WEIXIN_DM_POLICY — 若设为 allowlist,则发送者必须在 WEIXIN_ALLOWED_USERS 列表中 |
| 机器人忽略群组消息 | 群组策略默认为 disabled。请设置 WEIXIN_GROUP_POLICY=open 或 allowlist |
| 媒体下载/上传失败 | 确保已安装 cryptography。检查对 novac2c.cdn.weixin.qq.com 的网络访问权限 |
Blocked unsafe URL (SSRF protection) | 出站媒体 URL 指向私有/内部地址。仅允许公开 URL |
| 语音消息显示为文字 | 若微信提供转写文本,适配器将使用该文本。此为预期行为 |
| 消息出现重复 | 适配器按消息 ID 去重。若仍见重复,请检查是否运行了多个网关实例 |
iLink POST ... HTTP 4xx/5xx | iLink 服务返回 API 错误。请检查 Token 有效性及网络连通性 |
| 终端二维码无法渲染 | 安装 qrcode:pip install qrcode。也可直接打开上方打印的网址 |