# 微信 [English](./README.md) OpenClaw 的微信渠道插件,支持通过扫码完成登录授权。 ## 前提条件 已安装 [OpenClaw](https://docs.openclaw.ai/install)(需要 `openclaw` CLI 可用)。 ## 一键安装 ```bash npx -y @tencent-weixin/openclaw-weixin-cli install ``` ## 手动安装 如果一键安装不适用,可以按以下步骤手动操作: ### 1. 安装插件 ```bash openclaw plugins install "@tencent-weixin/openclaw-weixin" ``` ### 2. 启用插件 ```bash openclaw config set plugins.entries.openclaw-weixin.enabled true ``` ### 3. 扫码登录 ```bash openclaw channels login --channel openclaw-weixin ``` 终端会显示一个二维码,用手机扫码并在手机上确认授权。确认后,登录凭证会自动保存到本地,无需额外操作。 ### 4. 重启 gateway ```bash openclaw gateway restart ``` ## 添加更多微信账号 ```bash openclaw channels login --channel openclaw-weixin ``` 每次扫码登录都会创建一个新的账号条目,支持多个微信号同时在线。 ## 多账号上下文隔离 默认情况下,所有渠道的 AI 会话共享同一个上下文。如果希望每个微信账号的对话上下文相互隔离: ```bash openclaw config set agents.mode per-channel-per-peer ``` 这样每个「微信账号 + 发消息用户」组合都会拥有独立的 AI 记忆,账号之间不会串台。 ## 后端 API 协议 本插件通过 HTTP JSON API 与后端网关通信。二次开发者若需对接自有后端,需实现以下接口。 所有接口均为 `POST`,请求和响应均为 JSON。通用请求头: | Header | 说明 | |--------|------| | `Content-Type` | `application/json` | | `AuthorizationType` | 固定值 `ilink_bot_token` | | `Authorization` | `Bearer `(登录后获取) | | `X-WECHAT-UIN` | 随机 uint32 的 base64 编码 | ### 接口列表 | 接口 | 路径 | 说明 | |------|------|------| | getUpdates | `getupdates` | 长轮询获取新消息 | | sendMessage | `sendmessage` | 发送消息(文本/图片/视频/文件) | | getUploadUrl | `getuploadurl` | 获取 CDN 上传预签名 URL | | getConfig | `getconfig` | 获取账号配置(typing ticket 等) | | sendTyping | `sendtyping` | 发送/取消输入状态指示 | ### getUpdates 长轮询接口。服务端在有新消息或超时后返回。 **请求体:** ```json { "get_updates_buf": "" } ``` | 字段 | 类型 | 说明 | |------|------|------| | `get_updates_buf` | `string` | 上次响应返回的同步游标,首次请求传空字符串 | **响应体:** ```json { "ret": 0, "msgs": [...], "get_updates_buf": "<新游标>", "longpolling_timeout_ms": 35000 } ``` | 字段 | 类型 | 说明 | |------|------|------| | `ret` | `number` | 返回码,`0` = 成功 | | `errcode` | `number?` | 错误码(如 `-14` = 会话超时) | | `errmsg` | `string?` | 错误描述 | | `msgs` | `WeixinMessage[]` | 消息列表(结构见下方) | | `get_updates_buf` | `string` | 新的同步游标,下次请求时回传 | | `longpolling_timeout_ms` | `number?` | 服务端建议的下次长轮询超时(ms) | ### sendMessage 发送一条消息给用户。 **请求体:** ```json { "msg": { "to_user_id": "<目标用户 ID>", "context_token": "<会话上下文令牌>", "item_list": [ { "type": 1, "text_item": { "text": "你好" } } ] } } ``` ### getUploadUrl 获取 CDN 上传预签名参数。上传文件前需先调用此接口获取 `upload_param` 和 `thumb_upload_param`。 **请求体:** ```json { "filekey": "<文件标识>", "media_type": 1, "to_user_id": "<目标用户 ID>", "rawsize": 12345, "rawfilemd5": "<明文 MD5>", "filesize": 12352, "thumb_rawsize": 1024, "thumb_rawfilemd5": "<缩略图明文 MD5>", "thumb_filesize": 1040 } ``` | 字段 | 类型 | 说明 | |------|------|------| | `media_type` | `number` | `1` = IMAGE, `2` = VIDEO, `3` = FILE | | `rawsize` | `number` | 原文件明文大小 | | `rawfilemd5` | `string` | 原文件明文 MD5 | | `filesize` | `number` | AES-128-ECB 加密后的密文大小 | | `thumb_rawsize` | `number?` | 缩略图明文大小(IMAGE/VIDEO 时必填) | | `thumb_rawfilemd5` | `string?` | 缩略图明文 MD5(IMAGE/VIDEO 时必填) | | `thumb_filesize` | `number?` | 缩略图密文大小(IMAGE/VIDEO 时必填) | **响应体:** ```json { "upload_param": "<原图上传加密参数>", "thumb_upload_param": "<缩略图上传加密参数>" } ``` ### getConfig 获取账号配置,包括 typing ticket。 **请求体:** ```json { "ilink_user_id": "<用户 ID>", "context_token": "<可选,会话上下文令牌>" } ``` **响应体:** ```json { "ret": 0, "typing_ticket": "" } ``` ### sendTyping 发送或取消输入状态指示。 **请求体:** ```json { "ilink_user_id": "<用户 ID>", "typing_ticket": "<从 getConfig 获取>", "status": 1 } ``` | 字段 | 类型 | 说明 | |------|------|------| | `status` | `number` | `1` = 正在输入,`2` = 取消输入 | ### 消息结构 #### WeixinMessage | 字段 | 类型 | 说明 | |------|------|------| | `seq` | `number?` | 消息序列号 | | `message_id` | `number?` | 消息唯一 ID | | `from_user_id` | `string?` | 发送者 ID | | `to_user_id` | `string?` | 接收者 ID | | `create_time_ms` | `number?` | 创建时间戳(ms) | | `session_id` | `string?` | 会话 ID | | `message_type` | `number?` | `1` = USER, `2` = BOT | | `message_state` | `number?` | `0` = NEW, `1` = GENERATING, `2` = FINISH | | `item_list` | `MessageItem[]?` | 消息内容列表 | | `context_token` | `string?` | 会话上下文令牌,回复时需回传 | #### MessageItem | 字段 | 类型 | 说明 | |------|------|------| | `type` | `number` | `1` TEXT, `2` IMAGE, `3` VOICE, `4` FILE, `5` VIDEO | | `text_item` | `{ text: string }?` | 文本内容 | | `image_item` | `ImageItem?` | 图片(含 CDN 引用和 AES 密钥) | | `voice_item` | `VoiceItem?` | 语音(SILK 编码) | | `file_item` | `FileItem?` | 文件附件 | | `video_item` | `VideoItem?` | 视频 | | `ref_msg` | `RefMessage?` | 引用消息 | #### CDN 媒体引用 (CDNMedia) 所有媒体类型(图片/语音/文件/视频)通过 CDN 传输,使用 AES-128-ECB 加密: | 字段 | 类型 | 说明 | |------|------|------| | `encrypt_query_param` | `string?` | CDN 下载/上传的加密参数 | | `aes_key` | `string?` | base64 编码的 AES-128 密钥 | ### CDN 上传流程 1. 计算文件明文大小、MD5,以及 AES-128-ECB 加密后的密文大小 2. 如需缩略图(图片/视频),同样计算缩略图的明文和密文参数 3. 调用 `getUploadUrl` 获取 `upload_param`(和 `thumb_upload_param`) 4. 使用 AES-128-ECB 加密文件内容,PUT 上传到 CDN URL 5. 缩略图同理加密并上传 6. 使用返回的 `encrypt_query_param` 构造 `CDNMedia` 引用,放入 `MessageItem` 发送 > 完整的类型定义见 [`src/api/types.ts`](src/api/types.ts),API 调用实现见 [`src/api/api.ts`](src/api/api.ts)。