# weixin-agent-api **Repository Path**: naza/weixin-agent-api ## Basic Information - **Project Name**: weixin-agent-api - **Description**: 微信对接智能体API(基于微信clawbot对接openclaw整理) - **Primary Language**: NodeJS - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-23 - **Last Updated**: 2026-03-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 微信 Agent API 接入手册 (1.0.2) ## 1. 概述 本文档描述了微信 Agent API 的完整接入方案,支持任何编程语言实现。该 API 通过 HTTP/JSON 协议提供微信消息的收发能力。根据tencent-weixin-openclaw-weixin-1.0.2整理 ## 2. 基础信息 ### 2.1 API 基础 URL ``` https://ilinkai.weixin.qq.com ``` 所有 API 请求都基于此基础 URL。 ### 2.2 认证方式 使用 Bearer Token 认证,在请求头中设置: ``` Authorization: Bearer AuthorizationType: ilink_bot_token ``` ### 2.3 通用请求头 ```json { "Content-Type": "application/json", "Authorization": "Bearer ", "AuthorizationType": "ilink_bot_token", "X-WECHAT-UIN": "" } ``` ## 3. 登录流程(扫码授权) ### 3.1 获取登录二维码 **请求:** ``` GET /ilink/bot/get_bot_qrcode?bot_type=3 ``` **响应:** ```json { "qrcode": "qr123456", "qrcode_img_content": "https://ilinkai.weixin.qq.com/qr/code/xxx" } ``` ### 3.2 轮询登录状态 **请求:** ``` GET /ilink/bot/get_qrcode_status?qrcode=qr123456 ``` **请求头:** ``` Content-Type: application/json iLink-App-ClientVersion: 1 ``` **超时设置:** - 建议设置 35 秒超时 **轮询间隔:** - 建议每 1 秒轮询一次 **二维码有效期:** - 二维码有效期为 8 分钟(480 秒) **响应状态:** - `wait`: 等待扫码 - `scaned`: 已扫码,等待确认 - `confirmed`: 登录成功 - `expired`: 二维码过期 **成功响应:** ```json { "status": "confirmed", "bot_token": "your_bot_token", "ilink_bot_id": "bot@im.wechat", "baseurl": "https://ilinkai.weixin.qq.com", "ilink_user_id": "user@im.wechat" } ``` **轮询逻辑建议:** - 每 1 秒轮询一次状态 - 每 10 秒输出一次等待状态(可选) - 超时后返回 `wait` 状态,继续轮询 - 遇到错误时每 30 秒输出一次错误信息,继续轮询 - 最大轮询次数为 480 次(8 分钟) ## 4. 消息接收(长轮询) ### 4.1 获取消息更新 **请求:** ``` POST /ilink/bot/getupdates Content-Type: application/json Authorization: Bearer AuthorizationType: ilink_bot_token X-WECHAT-UIN: { "get_updates_buf": "", "base_info": { "channel_version": "1.0.0" } } ``` **请求头说明:** - `Content-Type`: 必须设置为 `application/json` - `Authorization`: Bearer Token 格式 - `AuthorizationType`: 必须设置为 `ilink_bot_token` - `X-WECHAT-UIN`: 随机 32 位无符号整数的 Base64 编码(推荐) **超时设置:** - 建议设置 35 秒超时 - 超时是正常现象,应继续轮询 **Buffer 管理:** - 首次请求使用空字符串 `""` - 后续请求使用上一次响应中的 `get_updates_buf` - Buffer 用于增量获取消息,避免重复 **响应:** ```json { "ret": 0, "msgs": [ { "message_id": 123456, "from_user_id": "user@im.wechat", "to_user_id": "bot@im.wechat", "create_time_ms": 1709912345678, "message_type": 1, "message_state": 2, "item_list": [ { "type": 1, "text_item": { "text": "Hello World" } } ], "context_token": "ctx_token_123" } ], "get_updates_buf": "", "longpolling_timeout_ms": 35000 } ``` ### 4.2 消息类型 | 类型值 | 消息类型 | 描述 | |--------|----------|------| | 1 | TEXT | 文本消息 | | 2 | IMAGE | 图片消息 | | 3 | VOICE | 语音消息 | | 4 | FILE | 文件消息 | | 5 | VIDEO | 视频消息 | ## 5. 消息发送 ### 5.1 发送文本消息 **请求:** ``` POST /ilink/bot/sendmessage Content-Type: application/json Authorization: Bearer AuthorizationType: ilink_bot_token X-WECHAT-UIN: { "msg": { "from_user_id": "", "to_user_id": "user@im.wechat", "client_id": "client_123", "message_type": 2, "message_state": 2, "item_list": [ { "type": 1, "text_item": { "text": "Hello from bot" } } ], "context_token": "ctx_token_123" }, "base_info": { "channel_version": "1.0.0" } } ``` **字段说明:** - `from_user_id`: 留空字符串 `""` - `to_user_id`: 目标用户 ID - `client_id`: 客户端唯一标识,建议使用时间戳 - `message_type`: 消息类型,2 表示 Bot 消息 - `message_state`: 消息状态,2 表示完成状态 - `item_list`: 消息内容列表 - `context_token`: 上下文令牌,用于保持对话连续性(可选) ### 5.2 发送媒体消息 **步骤1:获取上传 URL** ``` POST /ilink/bot/getuploadurl Content-Type: application/json Authorization: Bearer AuthorizationType: ilink_bot_token X-WECHAT-UIN: { "filekey": "file123", "media_type": 1, "to_user_id": "user@im.wechat", "rawsize": 1024, "rawfilemd5": "abcdef123456", "filesize": 1040, "thumb_rawsize": 512, "thumb_rawfilemd5": "abc123", "thumb_filesize": 528, "base_info": { "channel_version": "1.0.0" } } ``` **请求参数说明:** | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `filekey` | `string` | 是 | 文件唯一标识 | | `media_type` | `number` | 是 | 媒体类型:1=IMAGE, 2=VIDEO, 3=FILE | | `to_user_id` | `string` | 是 | 目标用户 ID | | `rawsize` | `number` | 是 | 原文件明文大小(字节) | | `rawfilemd5` | `string` | 是 | 原文件明文 MD5(32位十六进制) | | `filesize` | `number` | 是 | AES-128-ECB 加密后的密文大小(字节) | | `thumb_rawsize` | `number` | 否 | 缩略图明文大小(IMAGE/VIDEO 时必填) | | `thumb_rawfilemd5` | `string` | 否 | 缩略图明文 MD5(IMAGE/VIDEO 时必填) | | `thumb_filesize` | `number` | 否 | 缩略图密文大小(IMAGE/VIDEO 时必填) | | `no_need_thumb` | `boolean` | 否 | 不需要缩略图上传 URL,默认 false | | `aeskey` | `string` | 否 | AES-128 密钥(hex 格式) | **响应体:** ```json { "upload_param": "<原图上传加密参数>", "thumb_upload_param": "<缩略图上传加密参数>" } ``` **步骤2:上传文件到 CDN** 使用 AES-128-ECB 加密文件内容,然后 PUT 上传到 CDN URL: ```javascript async function uploadToCDN(uploadParam, fileBuffer, aesKeyHex) { const crypto = require('crypto'); const aesKey = Buffer.from(aesKeyHex, 'hex'); const cipher = crypto.createCipheriv('aes-128-ecb', aesKey, null); let encrypted = cipher.update(fileBuffer); encrypted = Buffer.concat([encrypted, cipher.final()]); const response = await fetch(uploadParam, { method: 'PUT', body: encrypted }); return response.ok; } ``` **步骤3:发送媒体消息** ``` POST /ilink/bot/sendmessage Content-Type: application/json Authorization: Bearer AuthorizationType: ilink_bot_token X-WECHAT-UIN: { "msg": { "from_user_id": "", "to_user_id": "user@im.wechat", "client_id": "client_123", "message_type": 2, "message_state": 2, "item_list": [ { "type": 2, "image_item": { "media": { "encrypt_query_param": "", "aes_key": "base64_encoded_aes_key", "encrypt_type": 1 }, "mid_size": 1040 } } ], "context_token": "ctx_token_123" }, "base_info": { "channel_version": "1.0.0" } } ``` **CDN 媒体引用说明:** | 字段 | 类型 | 说明 | |------|------|------| | `encrypt_query_param` | `string` | CDN 下载/上传的加密参数(从 getUploadUrl 获取) | | `aes_key` | `string` | base64 编码的 AES-128 密钥 | | `encrypt_type` | `number` | 加密类型,固定为 1 | **完整上传流程:** 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` 发送 ## 6. 配置管理 ### 6.0 使用场景 **功能说明:** 配置管理 API 主要用于实现微信客户端的"正在输入"状态提示功能,提升用户体验。当机器人需要处理耗时操作时,可以向用户显示"正在输入"的状态,避免用户误以为机器人没有响应。 **适用场景:** - **复杂任务处理**:当机器人需要执行耗时操作(如调用外部 API、数据库查询、复杂计算等) - **多轮对话**:需要思考或生成详细回复时 - **需要人工干预**:当机器人需要等待人工客服接管时 - **媒体处理**:处理图片、视频等需要时间的操作 **实现流程:** 1. 收到用户消息后,调用 `getconfig` 获取 `typing_ticket` 2. 调用 `sendtyping` 发送 `status: 1`(正在输入) 3. 处理消息并生成回复 4. 发送回复消息 5. 可选:调用 `sendtyping` 发送 `status: 2`(取消输入) ### 6.1 获取配置 **请求:** ``` POST /ilink/bot/getconfig Content-Type: application/json Authorization: Bearer AuthorizationType: ilink_bot_token X-WECHAT-UIN: { "ilink_user_id": "user@im.wechat", "context_token": "ctx_token_123", "base_info": { "channel_version": "1.0.0" } } ``` **字段说明:** - `ilink_user_id`: 用户 ID - `context_token`: 上下文令牌(可选) **响应:** ```json { "ret": 0, "typing_ticket": "base64_encoded_ticket" } ``` ### 6.2 发送输入状态 **请求:** ``` POST /ilink/bot/sendtyping Content-Type: application/json Authorization: Bearer AuthorizationType: ilink_bot_token X-WECHAT-UIN: { "ilink_user_id": "user@im.wechat", "typing_ticket": "base64_encoded_ticket", "status": 1, "base_info": { "channel_version": "1.0.0" } } ``` **请求参数说明:** | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `ilink_user_id` | `string` | 是 | 目标用户 ID | | `typing_ticket` | `string` | 是 | 从 getConfig 获取的 typing ticket(base64 编码) | | `status` | `number` | 是 | 输入状态:1=正在输入,2=取消输入 | **使用示例:** ```javascript async function sendTypingStatus(botToken, userId, typingTicket, isTyping) { const response = await fetch(`${BASE_URL}/ilink/bot/sendtyping`, { method: 'POST', headers: buildHeaders(botToken), body: JSON.stringify({ ilink_user_id: userId, typing_ticket: typingTicket, status: isTyping ? 1 : 2, base_info: { channel_version: '1.0.0' } }) }); return response.ok; } // 完整使用示例 async function processMessageWithTyping(botToken, message) { const userId = message.from_user_id; // 1. 获取 typing ticket const configResponse = await fetch(`${BASE_URL}/ilink/bot/getconfig`, { method: 'POST', headers: buildHeaders(botToken), body: JSON.stringify({ ilink_user_id: userId, context_token: message.context_token, base_info: { channel_version: '1.0.0' } }) }); const configData = await configResponse.json(); const typingTicket = configData.typing_ticket; // 2. 发送"正在输入"状态 await sendTypingStatus(botToken, userId, typingTicket, true); // 3. 处理消息(模拟耗时操作) await new Promise(resolve => setTimeout(resolve, 2000)); // 4. 生成回复 const reply = `您发送的消息是:${getMessageText(message)}`; // 5. 发送回复 await sendMessage(botToken, userId, reply, message.context_token); // 6. 可选:取消输入状态 await sendTypingStatus(botToken, userId, typingTicket, false); } function getMessageText(message) { const textItem = message.item_list?.find(item => item.type === 1); return textItem?.text_item?.text || ''; } ``` ## 7. 错误处理 ### 7.1 常见错误码 | 错误码 | 描述 | 处理建议 | |--------|------|----------| | -14 | 会话超时 | 重新登录获取新的 token | | -1 | 服务器错误 | 稍后重试 | | 401 | 未授权 | 检查 token 是否正确 | | 400 | 请求参数错误 | 检查请求参数格式 | ### 7.2 长轮询超时 长轮询请求通常设置 35 秒超时,超时后返回空消息列表是正常现象,应继续轮询。 ## 8. 最佳实践 1. **长轮询管理**:使用指数退避策略处理超时和错误 2. **消息去重**:使用 message_id 避免重复处理消息 3. **上下文管理**:保存 context_token 用于回复消息 4. **错误重试**:对网络错误和临时服务器错误进行重试 5. **连接保持**:定期发送请求保持会话活跃 ## 9. 安全注意事项 1. **Token 保护**:不要在客户端代码中硬编码 token 2. **请求验证**:验证所有输入参数 3. **加密传输**:始终使用 HTTPS 4. **权限控制**:限制 API 访问权限 ## 10. 版本兼容性 - 建议在 base_info 中指定 channel_version - 关注 API 变更和弃用通知 - 保持客户端与服务器版本兼容 ## 附录 A: 完整 API 端点 | 端点 | 方法 | 描述 | 认证 | |------|------|------|------| | /ilink/bot/get_bot_qrcode | GET | 获取登录二维码 | 否 | | /ilink/bot/get_qrcode_status | GET | 轮询登录状态 | 否 | | /ilink/bot/getupdates | POST | 获取消息更新 | 是 | | /ilink/bot/sendmessage | POST | 发送消息 | 是 | | /ilink/bot/getconfig | POST | 获取配置 | 是 | | /ilink/bot/sendtyping | POST | 发送输入状态 | 是 | | /ilink/bot/getuploadurl | POST | 获取上传 URL | 是 | ## 附录 B: 完整端到端示例 ### B.1 登录流程图 ```mermaid flowchart TD A[开始登录] --> B[获取二维码] B --> C[显示二维码] C --> D[轮询登录状态] D --> E{状态判断} E -->|wait| F[等待1秒] F --> D E -->|scaned| G[已扫码
等待确认] G --> D E -->|confirmed| H[获取凭证] H --> I[保存凭证] I --> J[登录成功] E -->|expired| K[二维码过期] K --> L[重新开始] D -->|超时或错误| M[记录错误] M --> N[继续轮询] N --> D D -->|达到最大次数| O[登录超时] ``` ### B.2 登录时序图 ```mermaid sequenceDiagram participant Client as 客户端 participant API as 微信API participant User as 用户 Client->>API: GET /ilink/bot/get_bot_qrcode API-->>Client: 返回二维码 Client->>User: 显示二维码 loop 轮询登录状态 Client->>API: GET /ilink/bot/get_qrcode_status API-->>Client: status: wait Client->>Client: 等待1秒 end User->>User: 扫描二维码 Client->>API: GET /ilink/bot/get_qrcode_status API-->>Client: status: scaned User->>User: 确认登录 Client->>API: GET /ilink/bot/get_qrcode_status API-->>Client: status: confirmed
bot_token, ilink_bot_id Client->>Client: 保存凭证 ``` ### B.3 完整登录代码 ```javascript const BASE_URL = "https://ilinkai.weixin.qq.com"; async function completeLoginFlow() { // 步骤 1: 获取二维码 const qrResponse = await fetch(`${BASE_URL}/ilink/bot/get_bot_qrcode?bot_type=3`, { headers: { 'Content-Type': 'application/json' } }); const qrData = await qrResponse.json(); console.log(`二维码链接: ${qrData.qrcode_img_content}`); // 步骤 2: 轮询登录状态 let status = 'wait'; let botToken = ''; let pollCount = 0; const maxPolls = 480; // 8 分钟 let scannedPrinted = false; while (pollCount < maxPolls) { pollCount++; try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 35000); const statusResponse = await fetch( `${BASE_URL}/ilink/bot/get_qrcode_status?qrcode=${qrData.qrcode}`, { headers: { 'Content-Type': 'application/json', 'iLink-App-ClientVersion': '1' }, signal: controller.signal } ); clearTimeout(timeoutId); const statusData = await statusResponse.json(); status = statusData.status; if (status === 'confirmed') { botToken = statusData.bot_token; console.log(`登录成功! Bot ID: ${statusData.ilink_bot_id}`); break; } if (status === 'scaned' && !scannedPrinted) { console.log('已扫码,等待确认...'); scannedPrinted = true; } if (pollCount % 10 === 0) { console.log(`等待扫码... (${pollCount}s)`); } } catch (error) { if (error.name !== 'AbortError') { console.error(`轮询失败 (${pollCount}s):`, error.message); } if (pollCount % 30 === 0) { console.log('继续轮询...'); } } await new Promise(resolve => setTimeout(resolve, 1000)); } return botToken; } ``` ### B.4 消息收发时序图 ```mermaid sequenceDiagram participant Bot as Bot participant API as 微信API participant User as 用户 Bot->>API: POST /ilink/bot/getupdates
buffer: "" API-->>Bot: msgs: [消息1, 消息2]
buffer: "abc123" Bot->>Bot: 处理消息1 Bot->>Bot: 处理消息2 Bot->>API: POST /ilink/bot/sendmessage
回复消息1 API-->>Bot: 发送成功 Bot->>API: POST /ilink/bot/sendmessage
回复消息2 API-->>Bot: 发送成功 Bot->>API: POST /ilink/bot/getupdates
buffer: "abc123" API-->>Bot: msgs: []
buffer: "abc456" Bot->>Bot: 等待1秒 Bot->>API: POST /ilink/bot/getupdates
buffer: "abc456" API-->>Bot: msgs: [消息3]
buffer: "abc789" User->>User: 收到回复 ``` ### B.5 完整消息收发代码 ```javascript async function messageLoop(botToken) { let buffer = ''; while (true) { // 获取消息 const updates = await getUpdates(botToken, buffer); buffer = updates.get_updates_buf || ''; if (updates.msgs && updates.msgs.length > 0) { for (const msg of updates.msgs) { // 处理文本消息 const textItem = msg.item_list?.find(item => item.type === 1); if (textItem?.text_item?.text) { const userText = textItem.text_item.text; console.log(`收到消息: ${userText}`); // 回复消息 const reply = `你说了: ${userText}`; await sendMessage(botToken, msg.from_user_id, reply, msg.context_token); } } } } } ``` ## 附录 C: 轮询和超时设置 ### C.1 登录轮询 **轮询间隔:** - 建议每 1 秒轮询一次 **超时设置:** - 单次请求超时:35 秒 - 超时后返回 `wait` 状态,继续轮询 **最大轮询次数:** - 480 次(8 分钟) **状态输出建议:** - 每 10 秒输出一次等待状态 - 遇到错误时每 30 秒输出一次错误信息 **错误处理:** - 捕获 AbortError(超时),继续轮询 - 其他错误记录日志,继续轮询 ### C.2 消息长轮询 **轮询间隔:** - 建议每 1 秒轮询一次 **超时设置:** - 单次请求超时:35 秒 - 超时是正常现象,应继续轮询 **Buffer 管理:** - 首次请求使用空字符串 `""` - 后续请求使用上一次响应中的 `get_updates_buf` ### C.3 请求头构建 **必须包含的请求头:** ```javascript { "Content-Type": "application/json", "Authorization": "Bearer ", "AuthorizationType": "ilink_bot_token", "X-WECHAT-UIN": "" } ``` **X-WECHAT-UIN 生成方法:** ```javascript const crypto = require('crypto'); const uint32 = crypto.randomBytes(4).readUInt32BE(0); const uin = Buffer.from(String(uint32), "utf-8").toString("base64"); ``` ### C.4 客户端 ID 生成 **建议使用时间戳:** ```javascript const clientId = `client_${Date.now()}`; ``` 这样可以确保每次请求都有唯一的客户端标识。 ## 附录 D: 消息类型详细说明 ### 消息结构 #### WeixinMessage(消息对象) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `seq` | `number?` | 否 | 消息序列号 | | `message_id` | `number?` | 否 | 消息唯一 ID | | `from_user_id` | `string?` | 否 | 发送者 ID | | `to_user_id` | `string?` | 否 | 接收者 ID | | `create_time_ms` | `number?` | 否 | 创建时间戳(毫秒) | | `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?` | 否 | 引用消息 | #### CDNMedia(CDN 媒体引用) 所有媒体类型(图片/语音/文件/视频)通过 CDN 传输,使用 AES-128-ECB 加密: | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `encrypt_query_param` | `string?` | 否 | CDN 下载/上传的加密参数 | | `aes_key` | `string?` | 否 | base64 编码的 AES-128 密钥 | ### D.1 文本消息 (type: 1) ```json { "type": 1, "text_item": { "text": "消息内容" } } ``` ### D.2 图片消息 (type: 2) ```json { "type": 2, "image_item": { "media": { "encrypt_query_param": "", "aes_key": "base64_encoded_aes_key", "encrypt_type": 1 }, "thumb_media": { "encrypt_query_param": "", "aes_key": "base64_encoded_aes_key", "encrypt_type": 1 }, "mid_size": 1040, "thumb_size": 512, "thumb_width": 200, "thumb_height": 150, "width": 800, "height": 600 } } ``` **字段说明:** - `media`: 原图 CDN 引用 - `thumb_media`: 缩略图 CDN 引用 - `mid_size`: 中图大小 - `thumb_size`: 缩略图大小 - `thumb_width`: 缩略图宽度 - `thumb_height`: 缩略图高度 - `width`: 原图宽度 - `height`: 原图高度 ### D.3 语音消息 (type: 3) ```json { "type": 3, "voice_item": { "media": { "encrypt_query_param": "", "aes_key": "base64_encoded_aes_key", "encrypt_type": 1 }, "mid_size": 2048, "encode_type": 6, "bits_per_sample": 16, "sample_rate": 16000, "playtime": 15000, "text": "语音转文字内容" } } ``` **字段说明:** - `mid_size`: 语音大小 - `encode_type`: 语音编码类型(1=pcm, 2=adpcm, 3=feature, 4=speex, 5=amr, 6=silk, 7=mp3, 8=ogg-speex) - `bits_per_sample`: 每个样本的位数 - `sample_rate`: 采样率 - `playtime`: 语音长度(毫秒) - `text`: 语音转文字内容 ### D.4 文件消息 (type: 4) ```json { "type": 4, "file_item": { "media": { "encrypt_query_param": "", "aes_key": "base64_encoded_aes_key", "encrypt_type": 1 }, "mid_size": 4096, "file_name": "document.pdf", "md5": "abcdef123456", "len": "1024000" } } ``` **字段说明:** - `mid_size`: 文件大小 - `file_name`: 文件名 - `md5`: 文件 MD5 - `len`: 文件长度 ### D.5 视频消息 (type: 5) ```json { "type": 5, "video_item": { "media": { "encrypt_query_param": "", "aes_key": "base64_encoded_aes_key", "encrypt_type": 1 }, "thumb_media": { "encrypt_query_param": "", "aes_key": "base64_encoded_aes_key", "encrypt_type": 1 }, "mid_size": 8192, "play_length": 30000, "video_md5": "abcdef123456", "thumb_size": 2048, "thumb_width": 200, "thumb_height": 150, "width": 1280, "height": 720 } } ``` **字段说明:** - `mid_size`: 视频大小 - `play_length`: 视频长度(毫秒) - `video_md5`: 视频 MD5 - `thumb_media`: 缩略图 CDN 引用 - `thumb_size`: 缩略图大小 - `thumb_width`: 缩略图宽度 - `thumb_height`: 缩略图高度 - `width`: 视频宽度 - `height`: 视频高度 ## 附录 E: 常见问题 (FAQ) ### E.1 登录相关 **Q: 二维码多久过期?** A: 二维码有效期为 8 分钟(480 秒),过期后需要重新获取。 **Q: 登录凭证多久过期?** A: 登录凭证通常长期有效,但建议定期重新登录以确保安全。 **Q: 可以同时登录多个 Bot 吗?** A: 可以,每个 Bot 有独立的 bot_token 和 ilink_bot_id。 ### E.2 消息相关 **Q: 长轮询超时怎么办?** A: 超时是正常现象,继续轮询即可。建议设置 35 秒超时。 **Q: 如何避免重复处理消息?** A: 使用 message_id 进行去重,保存已处理的消息 ID。 **Q: context_token 有什么用?** A: context_token 用于标识对话上下文,回复消息时带上可以保持对话连续性。 ### E.3 错误处理 **Q: 收到 -14 错误码怎么办?** A: -14 表示会话超时,需要重新登录获取新的 token。 **Q: 如何处理网络错误?** A: 实现指数退避策略,重试间隔逐渐增加(1s, 2s, 4s, 8s...)。 **Q: 401 错误表示什么?** A: 401 表示未授权,检查 token 是否正确或是否过期。 ### E.4 性能优化 **Q: 如何提高消息处理效率?** A: - 使用消息队列异步处理 - 批量处理消息 - 实现连接池复用 **Q: 如何减少 API 调用次数?** A: - 合理设置长轮询间隔 - 缓存配置信息 - 批量发送消息 ### E.5 安全相关 **Q: 如何保护 token 安全?** A: - 使用环境变量存储 token - 不要在代码中硬编码 token - 定期轮换 token - 使用 HTTPS 传输 **Q: 如何防止 API 滥用?** A: - 实现速率限制 - 监控异常请求 - 使用 IP 白名单