wechatbot
March 24, 2026 · View on GitHub
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?;
Media Operations
// Reply with media content
bot.reply_media(&msg, SendContent::Image(png_bytes)).await?;
bot.reply_media(&msg, SendContent::File { data, file_name: "report.pdf".into() }).await?;
bot.reply_media(&msg, SendContent::Video(mp4_bytes)).await?;
// Download media from incoming message (priority: image > file > video > voice)
if let Some(media) = bot.download(&msg).await? {
println!("Type: {}, Size: {} bytes", media.media_type, media.data.len());
if let Some(name) = &media.file_name {
println!("Filename: {}", name);
}
}
// Download a raw CDN reference directly
let raw = bot.download_raw(&msg.images[0].media.as_ref().unwrap(), None).await?;
// Upload to CDN without sending a message
let result = bot.upload(&file_bytes, user_id, 3).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