wx-bot-sdk 0.1.3

Standalone Weixin Bot SDK in Rust
Documentation

wx-bot-sdk

独立的微信 Bot Rust SDK,从 @tencent-weixin/openclaw-weixin 提取而来。

该 SDK 提供:

  • 微信 Bot 扫码登录
  • 单账号 / 多账号消息监听
  • 文本、图片、视频、文件发送
  • 媒体消息下载到本地临时文件
  • 账号凭据、getUpdates 同步游标、context token 本地持久化

当前项目仍处于早期版本,接口可能继续调整。

安装

在你的 Rust 项目中添加依赖:

[dependencies]
wx-bot-sdk = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }

本仓库内运行示例:

cargo run --example echo -- <WEIXIN_BOT_TOKEN>

账号与状态目录

SDK 默认把运行状态保存在当前目录的 .weixin-bot/ 下:

.weixin-bot/
  accounts.json
  accounts/
    <account-id>.json
    <account-id>.sync.json
    <account-id>.context-tokens.json

账号 ID 会按 OpenClaw 兼容格式归一化保存。例如:

e93e6ce56d3c@im.bot

会保存为:

e93e6ce56d3c-im-bot.json
e93e6ce56d3c-im-bot.sync.json
e93e6ce56d3c-im-bot.context-tokens.json

可通过环境变量修改状态目录:

WEIXIN_BOT_STATE_DIR=/path/to/state cargo run --example qr_login

PowerShell:

$env:WEIXIN_BOT_STATE_DIR=".weixin-bot-dev"
cargo run --example qr_login

扫码登录

运行扫码登录示例:

cargo run --example qr_login

PowerShell:

cargo run --example qr_login

终端会显示二维码。扫码并确认后会输出:

Token: ...
Account ID: ...
Base URL: ...
User ID: ...

登录成功后,账号 token、base URL、user ID 会保存到 .weixin-bot/accounts/。后续可用 WEIXIN_ACCOUNT_ID 复用本地登录凭据。

为什么第二次扫码会直接进入聊天界面?

扫码登录请求会携带本地最近保存的 bot token 列表。服务端识别到该微信账号已经绑定过当前 Bot 时,手机端可能会直接跳转到聊天界面,而不是再次提示绑定。这与原始 openclaw-weixin 行为一致。

如需重新测试首次绑定流程,可以使用新的状态目录:

$env:WEIXIN_BOT_STATE_DIR=".weixin-bot-fresh"
cargo run --example qr_login

Echo Bot

examples/echo.rs 展示了最基础的消息监听与自动回复。

使用 token 启动

WEIXIN_BOT_TOKEN="your-token" cargo run --example echo

或:

cargo run --example echo -- your-token

PowerShell:

$env:WEIXIN_BOT_TOKEN="your-token"
cargo run --example echo

判断消息类型

WeixinMsgContext 中包含:

字段 说明
ctx.message_type 消息类型:text / image / video / voice / file / unknown
ctx.body 文本内容;语音消息可能包含识别文本
ctx.from 发送者 user id
ctx.account_id 当前 Bot 账号 ID
ctx.media_path 媒体消息下载后的本地路径
ctx.media_type 媒体 MIME 类型,例如 audio/silkimage/*
ctx.context_token 会话 context token,SDK 发送回复时会自动使用

注意:语音消息可能带有识别文本,但 ctx.message_type 仍会是 voice,不要仅通过 ctx.body 是否为空判断消息类型。

示例处理逻辑:

on_message: handler(|ctx| async move {
    match ctx.message_type.as_str() {
        "text" => Ok(Some(format!("你说了: {}", ctx.body))),
        "voice" => Ok(Some(format!("收到语音,识别文本: {}", ctx.body))),
        "image" => Ok(Some("收到图片".into())),
        "video" => Ok(Some("收到视频".into())),
        "file" => Ok(Some("收到文件".into())),
        _ => Ok(Some("收到消息".into())),
    }
})

发送消息

发送文本

use wx_bot_sdk::{WeixinBot, WeixinBotOptions};

#[tokio::main]
async fn main() -> wx_bot_sdk::Result<()> {
    let bot = WeixinBot::new(WeixinBotOptions {
        token: std::env::var("WEIXIN_BOT_TOKEN")?,
        base_url: None,
        cdn_base_url: None,
        state_dir: None,
        account_id: None,
        user_id: None,
    });

    bot.send_text("user@im.bot", "hello").await?;
    Ok(())
}

发送文件 / deck.pptx

仓库内提供了 examples/send_deck.rs,默认发送根目录的 deck.pptx

cargo run --example send_deck -- <to_user_id>

指定文件路径:

cargo run --example send_deck -- <to_user_id> /path/to/deck.pptx

PowerShell 示例:

cargo run --example send_deck -- 6263701457de@im.bot

send_deck 会按以下顺序创建 Bot:

  1. 如果设置了 WEIXIN_ACCOUNT_ID,使用本地已保存账号;
  2. 如果设置了 WEIXIN_BOT_TOKEN,使用 token;
  3. 否则进入扫码登录。

复用已扫码账号:

$env:WEIXIN_ACCOUNT_ID="e93e6ce56d3c@im.bot"
cargo run --example send_deck -- 6263701457de@im.bot

使用 token:

$env:WEIXIN_BOT_TOKEN="your-token"
cargo run --example send_deck -- 6263701457de@im.bot

多账号 Echo

examples/multi_echo.rs 支持同时启动多个账号:

WEIXIN_BOT_TOKENS="token1,token2" cargo run --example multi_echo

或:

cargo run --example multi_echo -- token1 token2

收到消息时会打印对应 ctx.account_id

常用环境变量

环境变量 说明
WEIXIN_BOT_TOKEN Bot token,适用于单账号示例
WEIXIN_BOT_TOKENS 多账号 token 列表,用逗号分隔
WEIXIN_ACCOUNT_ID 使用扫码登录后保存的本地账号
WEIXIN_TO_USER send_deck 的默认收件人
WEIXIN_API_BASE_URL 自定义 API base URL,默认 https://ilinkai.weixin.qq.com
WEIXIN_BOT_STATE_DIR 自定义状态目录,默认 .weixin-bot

主要 API

WeixinBot

创建方式:

let bot = WeixinBot::new(WeixinBotOptions { /* ... */ });
let bot = WeixinBot::from_account("e93e6ce56d3c@im.bot")?;
let bot = WeixinBot::login_interactive(None).await?;

常用方法:

方法 说明
WeixinBot::new(opts) 使用 token 创建 Bot
WeixinBot::from_account(account_id) 从本地保存账号创建 Bot
WeixinBot::login_interactive(api_base_url) 终端扫码登录
bot.start(StartOptions) 启动消息监听
bot.stop() 停止监听
bot.send_text(to, text) 发送文本
bot.send_image(to, path, caption) 发送图片
bot.send_video(to, path, caption) 发送视频
bot.send_file(to, path, caption) 发送文件附件
bot.send_media_url(to, url, caption) 下载远程媒体并发送
bot.account_id() 当前 Bot 账号 ID
bot.user_id() 扫码用户 ID,可能为空
bot.token() 当前 token

handler

handler 用于把 async 闭包转换为消息处理器:

use wx_bot_sdk::{StartOptions, bot::handler};

bot.start(StartOptions {
    long_poll_timeout_ms: None,
    on_message: handler(|ctx| async move {
        println!("from={} type={} body={}", ctx.from, ctx.message_type, ctx.body);
        Ok(Some("收到".to_string()))
    }),
}).await?;

返回值说明:

  • Ok(Some(text)):自动回复文本
  • Ok(None):不回复
  • Err(err):处理失败,错误会向上传递

媒体文件说明

收到图片、视频、语音、文件时,SDK 会尝试下载并解密到系统临时目录:

if let Some(path) = ctx.media_path.as_deref() {
    println!("media saved to {path}");
}

发送本地媒体时,SDK 会根据扩展名判断类型:

  • image/* → 图片消息
  • video/* → 视频消息
  • 其他 → 文件附件

例如 .pptx 会作为文件附件发送。

开发与验证

格式化:

cargo fmt

检查所有示例:

cargo check --examples

运行测试:

cargo test

License

MIT