wechatbot 0.1.0

WeChat iLink Bot SDK for Rust
Documentation

wechatbot — Rust SDK

WeChat iLink Bot SDK for Rust — async, type-safe, zero-copy where possible.

Install

[dependencies]
wechatbot = "0.1"
tokio = { version = "1", features = ["full"] }

Requires Rust 2021 edition. Built on tokio + reqwest.

Quick Start

use wechatbot::{WeChatBot, BotOptions};

#[tokio::main]
async fn main() {
    let bot = WeChatBot::new(BotOptions::default());
    let creds = bot.login(false).await.unwrap();
    println!("Logged in: {}", creds.account_id);

    bot.on_message(Box::new(|msg| {
        println!("{}: {}", msg.user_id, msg.text);
    })).await;

    bot.run().await.unwrap();
}

Architecture

src/
├── lib.rs           ← Public re-exports
├── types.rs         ← All protocol & public types (serde)
├── error.rs         ← Error hierarchy (thiserror)
├── protocol.rs      ← Raw iLink API calls (reqwest)
├── crypto.rs        ← AES-128-ECB encrypt/decrypt + key encoding
└── bot.rs           ← WeChatBot client (login, run, reply, send)

API Reference

Creating a Bot

use wechatbot::{WeChatBot, BotOptions};

let bot = WeChatBot::new(BotOptions {
    base_url: None,     // default: ilinkai.weixin.qq.com
    cred_path: None,    // default: ~/.wechatbot/credentials.json
    on_qr_url: Some(Box::new(|url| {
        println!("Scan: {}", url);
    })),
    on_error: Some(Box::new(|err| {
        eprintln!("Error: {}", err);
    })),
});

Authentication

// Login (skips QR if credentials exist)
let creds = bot.login(false).await?;

// Force re-login
let creds = bot.login(true).await?;

// Credentials struct
println!("Token: {}", creds.token);
println!("Base URL: {}", creds.base_url);
println!("Account: {}", creds.account_id);
println!("User: {}", creds.user_id);

Message Handling

bot.on_message(Box::new(|msg| {
    match msg.content_type {
        ContentType::Text => println!("Text: {}", msg.text),
        ContentType::Image => {
            for img in &msg.images {
                println!("Image URL: {:?}", img.url);
            }
        }
        ContentType::Voice => {
            for voice in &msg.voices {
                println!("Voice: {:?} ({}ms)", voice.text, voice.duration_ms.unwrap_or(0));
            }
        }
        ContentType::File => {
            for file in &msg.files {
                println!("File: {:?}", file.file_name);
            }
        }
        ContentType::Video => println!("Video received"),
    }

    if let Some(ref quoted) = msg.quoted {
        println!("Quoted: {:?}", quoted.title);
    }
})).await;

Sending Messages

// Reply to incoming message
bot.reply(&msg, "Echo: hello").await?;

// Send to user (needs prior context_token)
bot.send(user_id, "Hello").await?;

// Typing indicator
bot.send_typing(user_id).await?;

Lifecycle

// Start polling (blocks)
bot.run().await?;

// Stop
bot.stop().await;

Error Handling

use wechatbot::WeChatBotError;

match result {
    Err(WeChatBotError::Api { message, errcode, .. }) => {
        if errcode == -14 {
            // session expired — handled automatically
        }
    }
    Err(WeChatBotError::NoContext(user_id)) => {
        // no context_token for this user yet
    }
    Err(WeChatBotError::Transport(e)) => {
        // network error
    }
    _ => {}
}

AES-128-ECB Crypto

use wechatbot::{generate_aes_key, encrypt_aes_ecb, decrypt_aes_ecb, decode_aes_key};

// Generate key
let key = generate_aes_key();

// Encrypt/decrypt
let ciphertext = encrypt_aes_ecb(b"Hello", &key);
let plaintext = decrypt_aes_ecb(&ciphertext, &key)?;

// Decode protocol key (handles all 3 formats)
let key = decode_aes_key("ABEiM0RVZneImaq7zN3u/w==")?;
let key = decode_aes_key("00112233445566778899aabbccddeeff")?;

Types

All protocol types derive Serialize + Deserialize + Clone + Debug:

// Wire-level (protocol)
WireMessage, WireMessageItem, CDNMedia, TextItem, ImageItem, ...

// Parsed (user-friendly)
IncomingMessage, ImageContent, VoiceContent, FileContent, VideoContent

// Auth
Credentials

// Enums
MessageType, MessageState, MessageItemType, ContentType, MediaType

Testing

cd rust
cargo test

License

MIT