Readme in different languages: EN ยท RU
maxoxide
๐ฆ An async Rust library for building bots on the Max messenger platform, inspired by teloxide.
Features
- โ Coverage of the published Max Bot REST API
- โ Long polling (dev & test) and Webhook via axum (production)
- โ
Strongly-typed events (
Update,Message,Callback, โฆ) - โ
Dispatcherwith fluent handler registration and filters - โ
Inline keyboards (all documented button types:
callback,link,message,request_contact,request_geo_location) - โ File uploads โ multipart, correct token flow for video/audio
- โ Markdown / HTML message formatting
- โ
Webhook secret verification (
X-Max-Bot-Api-Secret) - โ Tokio async throughout
Quick start
[]
= "1.0.0"
= { = "1", = ["full"] }
# For webhook support (production):
# maxoxide = { version = "1.0.0", features = ["webhook"] }
use ;
use Update;
async
MAX_BOT_TOKEN=your_token
API methods
| Method | Description |
|---|---|
bot.get_me() |
Bot info |
bot.send_text_to_chat(chat_id, text) |
Send plain text to a dialog/group/channel by chat_id |
bot.send_text_to_user(user_id, text) |
Send plain text to a user by global MAX user_id |
bot.send_markdown_to_chat(chat_id, text) |
Send Markdown to a dialog/group/channel by chat_id |
bot.send_markdown_to_user(user_id, text) |
Send Markdown to a user by global MAX user_id |
bot.send_message_to_chat(chat_id, body) |
Send message with attachments / keyboard by chat_id |
bot.send_message_to_user(user_id, body) |
Send message with attachments / keyboard by global MAX user_id |
bot.edit_message(mid, body) |
Edit a message |
bot.delete_message(mid) |
Delete a message |
bot.answer_callback(body) |
Answer an inline button press |
bot.get_chat(chat_id) |
Chat info |
bot.get_chats(โฆ) |
List all group chats |
bot.edit_chat(chat_id, body) |
Edit chat title / description |
bot.leave_chat(chat_id) |
Leave a chat |
bot.get_members(โฆ) |
List members |
bot.add_members(โฆ) |
Add members |
bot.remove_member(โฆ) |
Remove a member |
bot.get_admins(chat_id) |
List admins |
bot.pin_message(โฆ) |
Pin a message |
bot.unpin_message(โฆ) |
Unpin |
bot.send_action(chat_id, "typing_on") |
Typing-indicator request; API call works, but client visibility is not confirmed in live MAX tests |
bot.subscribe(body) |
Register a webhook |
bot.get_upload_url(type) |
Get upload URL |
bot.upload_file(type, path, name, mime) |
Full two-step file upload |
bot.upload_bytes(type, bytes, name, mime) |
Same, from bytes |
bot.set_my_commands(commands) |
Experimental: public MAX API currently returns 404 for /me/commands |
User ID vs Chat ID
These two IDs are different and should not be used interchangeably:
user_idis the global MAX ID of a user.chat_idis the ID of a concrete dialog, group, or channel.- In a private chat,
message.sender.user_ididentifies the user, whilemessage.chat_id()identifies that specific dialog with the bot. - Use
send_text_to_chat(chat_id, ...)/send_message_to_chat(chat_id, ...)when you already know the dialog or group. - Use
send_text_to_user(user_id, ...)/send_message_to_user(user_id, ...)when you only know the user's global MAX ID.
Known MAX platform gaps
As of March 25, 2026, the crate can send these requests, but live behavior on the MAX side is still inconsistent:
Button::RequestContactis documented by MAX, but live tests received a contact attachment with emptycontact_idandvcf_phone. Sending the button works; receiving the user's phone number is not confirmed on the MAX side.Button::RequestGeoLocationis documented by MAX, and the mobile client shows a sent location card, but live polling tests did not observe a matching update on the bot side. End-to-end delivery is not confirmed on the MAX side.bot.send_action(chat_id, "typing_on")returns success from the API, but live MAX tests did not confirm a visible typing indicator in the client.bot.set_my_commandsis kept as an experimental helper, but the public MAX REST docs do not list a write endpoint for bot commands, and livePOST /me/commandsrequests return404 Path /me/commands is not recognized.
Dispatcher filters
dp.on_command; // specific command
dp.on_message; // any new message
dp.on_edited_message; // edited message
dp.on_callback; // any callback button
dp.on_callback_payload; // specific payload
dp.on_bot_started; // user starts bot
dp.on_filter; // custom predicate
dp.on; // every update
First matching handler wins. Register more specific filters before general ones.
Inline keyboard
use ;
let keyboard = KeyboardPayload ;
let body = text.with_keyboard;
bot.send_message_to_chat.await?;
File upload
Max uses a two-step upload flow. upload_file / upload_bytes handle it automatically:
use ;
let token = bot
.upload_file
.await?;
let body = NewMessageBody ;
bot.send_message_to_chat.await?;
// or:
// bot.send_message_to_user(user_id, body).await?;
Note:
type=photowas removed from the Max API. Always useUploadType::Image.
Webhook server (features = ["webhook"])
use WebhookServer;
use SubscribeBody;
bot.subscribe.await?;
new
.secret
.path
.serve
.await;
Max requires HTTPS on port 443 and does not support self-signed certificates.
Project layout
maxoxide/
โโโ Cargo.toml
โโโ src/
โ โโโ lib.rs โ public API & re-exports
โ โโโ bot.rs โ Bot + all HTTP methods
โ โโโ uploader.rs โ two-step file upload helpers
โ โโโ dispatcher.rs โ Dispatcher, Filter, Context
โ โโโ errors.rs โ MaxError
โ โโโ webhook.rs โ axum webhook server (feature = "webhook")
โ โโโ tests.rs โ unit tests
โ โโโ types/
โ โโโ mod.rs โ all types (User, Chat, Message, Update, โฆ)
โโโ examples/
โโโ echo_bot.rs
โโโ keyboard_bot.rs
โโโ live_api_test.rs
โโโ webhook_bot.rs (feature = "webhook")
Running tests
Live API test
For real-data verification there is a separate interactive harness:
At startup it asks in the terminal for:
- bot token
- bot URL for the tester
- optional webhook URL and secret
- optional local file path for
upload_file - HTTP timeout, polling timeout and delay between requests
The harness then walks the tester through Max-client actions and records PASS / FAIL / SKIP for real API calls. It uses small delays between requests, drains the long-poll backlog before the run, and asks for explicit confirmation before destructive or non-reversible steps such as:
set_my_commandsdelete_chatleave_chat- visible group title edits