WARNING: This project is in active development (v1.0.0-beta.4c). The API is undergoing constant changes as we work toward full feature parity with python-telegram-bot and Rust-native performance optimizations. Use at your own risk -- breaking changes will occur between releases until v1.0.0 stable.
A complete, asynchronous Telegram Bot API framework for Rust, faithfully ported from python-telegram-bot -- the most popular Python library for the Telegram Bot API.
This project carries forward the architecture, handler system, and developer experience that made python-telegram-bot the go-to choice for thousands of bot developers, and transplants it into Rust's async ecosystem with full type safety, zero-cost abstractions, and fearless concurrency.
Heritage
This library stands on the shoulders of python-telegram-bot. The handler hierarchy, filter composition system, ConversationHandler state machine, job queue design, and persistence architecture are all direct ports of their Python counterparts. If you've built bots with python-telegram-bot, you already know how this library works.
We acknowledge and thank the python-telegram-bot maintainers and community for over a decade of work that made this project possible.
Why Rust?
| Concern | Python | Rust |
|---|---|---|
| Performance | GIL-limited concurrency, interpreted | True parallelism, compiled to native code |
| Memory safety | Runtime GC, potential leaks in long-running bots | Ownership system prevents leaks at compile time |
| Type safety | Optional type hints, runtime errors | Enforced at compile time, no AttributeError at 3 AM |
| Deployment | Requires Python runtime + virtualenv | Single static binary, 6.2 MB stripped |
| Resource usage | 57 MB RSS (measured) | 15 MB idle / 17 MB load (matches teloxide, see benchmarks) |
| Concurrency | asyncio (single-threaded) | tokio (multi-threaded work-stealing) |
For bots that handle high volumes of updates, run on constrained hardware (VPS, Raspberry Pi, containers), or need to be deployed without a runtime, Rust is the right tool.
Architecture
The library is organized as a Cargo workspace with four crates:
rust-telegram-bot/
crates/
telegram-bot-raw/ # Pure API types and methods (like Python's `telegram` module)
telegram-bot-ext/ # Application framework (like Python's `telegram.ext`)
telegram-bot-macros/ # Proc macros (#[derive(BotCommands)])
telegram-bot/ # Facade crate -- re-exports all three for convenience
rust-tg-bot-raw contains every type and method from Bot API 9.6: Message, Update, User, Chat, inline types, payments, passport, games, stickers, and all API methods on the Bot struct. It depends only on serde, reqwest, and tokio.
rust-tg-bot-ext provides the application framework: ApplicationBuilder, typed handler system, composable filters, ConversationHandler, JobQueue, persistence backends (JSON file, SQLite, Redis, PostgreSQL), rate limiting, webhook support with optional TLS, and callback data caching.
rust-tg-bot-macros provides the #[derive(BotCommands)] proc macro for declarative command handler registration.
rust-tg-bot is the facade crate you add to Cargo.toml. It re-exports everything from the other three crates under rust_tg_bot::raw and rust_tg_bot::ext.
Quick Start
1. Create a bot with @BotFather
Open Telegram, message @BotFather, send /newbot, and follow the prompts. Copy the token.
2. Add the dependency
[]
= { = "https://github.com/HexiCoreDev/rust-telegram-bot" }
= "0.3"
3. Write your bot
use ;
async
async
async
4. Run it
TELEGRAM_BOT_TOKEN="123456:ABC-DEF"
Feature Highlights
Composable Filters
Filters use Rust's bitwise operators for composition -- the same mental model as python-telegram-bot, but enforced at compile time:
use ;
// Text messages that are NOT commands
let text_only = TEXT & !COMMAND;
// Use it with a handler
app.add_handler.await;
Over 50 built-in filters are available: TEXT, COMMAND, PHOTO, VIDEO, AUDIO, VOICE, DOCUMENT, LOCATION, CONTACT, ANIMATION, STICKER, POLL, VENUE, GAME, INVOICE, FORWARDED, REPLY, PREMIUM_USER, FORUM, chat type filters, entity filters, regex filters, and more.
Typed Handler Registration
Handlers use strongly-typed constructors and are registered via add_handler:
use ;
// Command handler -- matches /start
app.add_handler.await;
// Message handler -- matches text that is not a command
app.add_handler.await;
// Callback query handler -- matches inline keyboard button presses
app.add_handler.await;
// Generic function handler with custom predicate
app.add_handler.await;
// Catch-all handler (e.g., for logging)
app.add_handler.await;
Builder-based Bot API
Every Bot API method returns a builder with optional parameters as chainable setters. Builders implement IntoFuture, so you can .await directly:
use ;
// Simple -- .await directly
context.bot.send_message.await?;
// With optional parameters
context
.bot
.send_message
.parse_mode
.await?;
// Inline keyboard using typed constructors
use ;
let keyboard = new;
context
.bot
.send_message
.reply_markup
.await?;
// Edit a message
context
.bot
.edit_message_text
.chat_id
.message_id
.await?;
Typed Constants
No more magic strings. All enums are strongly typed:
use ;
// Parse modes
context.bot.send_message
.parse_mode
.await?;
// Entity types
if entity.entity_type == BotCommand
// Chat types
if chat.chat_type != Private
Handler Groups
Handlers are organized into numbered groups. Within a group, the first matching handler wins. Across groups, processing continues unless a handler returns HandlerResult::Stop:
use ;
// Group -1: always runs first (e.g., user tracking)
app.add_handler.await;
// Group 0: command handlers
app.add_handler.await;
app.add_handler.await;
// Group 1: message handlers
app.add_handler.await;
Typed Data Access
The Context provides typed read and write guards for bot-wide, per-user, and per-chat data:
use ;
async
ConversationHandler State Machine
Multi-step conversations with automatic state tracking, timeouts, nested conversations, and persistence:
use ConversationHandler;
use Duration;
let conv = builder
.entry_point
.state
.state
.state
.fallback
.conversation_timeout
.persistent
.name
.build;
Features ported from python-telegram-bot:
- Per-chat/per-user/per-message conversation keys
- Re-entry support
map_to_parentfor nested conversations- Automatic timeout with configurable handlers
- Non-blocking callbacks with state revert on failure
- Persistence integration
Webhook Support
The simplest way to run in webhook mode -- the framework handles the axum server, secret token validation, and update dispatching internally:
use WebhookConfig;
let config = new
.port
.url_path
.secret_token;
app.run_webhook.await?;
See webhook_bot.rs for a complete working example, or custom_webhook_bot.rs for adding custom routes alongside the webhook.
Job Queue Scheduling
Schedule one-shot, repeating, daily, and monthly jobs using tokio timers and a builder pattern:
use ;
use Arc;
use Duration;
let jq = new;
// One-shot: fire after 30 seconds
jq.once
.name
.chat_id
.start
.await;
// Repeating: every 60 seconds
jq.repeating
.name
.start
.await;
// Daily at 09:00 UTC on weekdays
use NaiveTime;
let time = from_hms_opt.unwrap;
jq.daily
.name
.start
.await;
// Monthly on the 1st at midnight
jq.monthly
.name
.start
.await;
Every job returns a Job handle for cancellation, status checking, and enable/disable toggling.
Persistence
Swap between backends without changing application code:
JSON file (human-readable, great for development):
use JsonFilePersistence;
use ApplicationBuilder;
let persistence = new;
let app = new
.token
.persistence
.build;
SQLite (production-ready, WAL mode, atomic writes):
use SqlitePersistence;
use ApplicationBuilder;
let persistence = open.unwrap;
let app = new
.token
.persistence
.build;
Custom backend -- implement the BasePersistence trait:
use BasePersistence;
use HashMap;
Feature Flags
[]
# Default: polling support only
= { = "https://github.com/HexiCoreDev/rust-telegram-bot" }
# Everything
= { = "https://github.com/HexiCoreDev/rust-telegram-bot", = ["full"] }
# Pick what you need
= { = "https://github.com/HexiCoreDev/rust-telegram-bot", = [
"webhooks", # axum-based webhook server
"webhooks-tls", # TLS auto-configuration for webhooks
"job-queue", # Scheduled job execution
"persistence-json", # JSON file persistence
"persistence-sqlite", # SQLite persistence
"persistence-redis", # Redis persistence
"persistence-postgres", # PostgreSQL persistence (JSONB)
"rate-limiter", # API rate limiting
"macros", # #[derive(BotCommands)]
] }
Comparison
| Feature | rust-tg-bot | python-telegram-bot | teloxide |
|---|---|---|---|
| Bot API version | 9.6 | 9.5 | 9.2 |
| Language | Rust | Python | Rust |
| Async runtime | tokio | asyncio | tokio |
| Handler system | 22 typed handlers + FnHandler | 22 handler types | Dispatcher + handler chains |
| Filter composition | &, |, ^, ! operators |
Same operators | Predicate combinators |
| ConversationHandler | Full port (timeouts, nesting, persistence) | Full | dialogue macro |
| Job queue | Built-in (tokio timers) | APScheduler wrapper | External |
| Persistence | JSON file, SQLite, Redis, PostgreSQL, custom trait | Pickle, Dict, custom | Community crates |
| Webhook support | axum | tornado / starlette | axum / warp |
| Type safety | Compile-time | Runtime (optional hints) | Compile-time |
| Memory idle (measured) | 15 MB | 57 MB | 15 MB |
| Memory under load (measured) | 17 MB | 60 MB | 17 MB |
| Binary size (stripped) | 6.2 MB | Requires Python runtime | 6.6 MB |
| Minimum version | Rust 1.75 | Python 3.10 | Rust 1.68 |
| Builder pattern | IntoFuture (directly awaitable) | Keyword args | Method chains |
| Typed constants | ParseMode::Html |
ParseMode.HTML |
String-based |
| Maturity | v1.0.0-beta.4c (new) | Mature (10+ years) | Mature (3+ years) |
Examples
The crates/telegram-bot/examples/ directory contains complete, runnable examples:
| Example | Description | Python equivalent |
|---|---|---|
echo_bot |
Echoes text messages back to the user | echobot.py |
webhook_bot |
Webhook mode with run_webhook() -- simplest production setup |
N/A |
inline_keyboard |
Inline keyboard with callback queries | inlinekeyboard.py |
timer_bot |
Job queue: delayed messages, cancellation | timerbot.py |
conversation_bot |
Multi-step conversation with state machine | conversationbot.py |
raw_api_bot |
Direct Bot API usage without the ext framework | N/A |
context_types_bot |
Typed data access: bot_data, chat_data, user tracking | contexttypesbot.py |
custom_webhook_bot |
Custom axum routes alongside the Telegram webhook | N/A |
bench_bot |
Benchmark bot matching PTB/teloxide feature set | N/A |
Run any example:
TELEGRAM_BOT_TOKEN="your-token"
Webhook examples require the webhooks feature:
TELEGRAM_BOT_TOKEN="your-token" WEBHOOK_URL="https://your.domain" \
Project Status
Current: v1.0.0-beta.4c -- API complete, stabilizing
What is implemented:
- All Bot API 9.6 types and methods (281 types, 168 method builders)
ApplicationBuilderwith typestate pattern- Typed handler system (
CommandHandler,MessageHandler,FnHandler, and more) - 50+ composable filters with
&,|,^,!operators ConversationHandlerwith full state machine, timeouts, nesting, and persistenceJobQueuewith one-shot, repeating, daily, and monthly scheduling (builder pattern)- JSON file, SQLite, Redis, and PostgreSQL persistence backends
- Typed data access guards (
DataReadGuard,DataWriteGuard) - Polling and webhook (axum) update delivery with optional TLS
- Callback data caching
- Rate limiter wired into request pipeline
- Defaults system for parse mode, link preview, etc.
- 90+ type constructors for ergonomic API type creation
- Context shortcuts:
reply_html,reply_photo,reply_document,reply_sticker,reply_location answer_callback_query()andedit_callback_message_text()on Context- Arc dispatch with bounded update channel (capacity 64)
#[non_exhaustive]on all 344+ public types/enums for forward compatibility#[derive(BotCommands)]proc macro for declarative command registration#![warn(missing_docs)]on all crates
Build & Test
- 385+ tests passing, zero clippy warnings
- 25 roundtrip serialization tests, 9 proptest filter tests, 10 persistence stress tests
- GitHub Actions CI: check, test, clippy, format, examples, docs (stable + MSRV 1.75)
- Release pipeline: cross-compile binaries + crates.io publish
- Measured performance: 6.2 MB binary (stripped), 15 MB idle / 17 MB RSS under load (release) -- matches teloxide, beats it on binary size
Forward Compatibility
Unlike python-telegram-bot's api_kwargs (which captures unknown JSON fields into a dict), RTB uses Rust's #[non_exhaustive] attribute on all 344+ public types and enums. This means:
- New fields added by Telegram are silently dropped on deserialization until the library is updated.
- New enum variants can be added without breaking downstream code.
- Downstream crates cannot construct types via struct expressions — they must use constructors or
serde_json::from_value().
This is a deliberate trade-off: #[non_exhaustive] provides compile-time forward compatibility guarantees that api_kwargs cannot, at the cost of not preserving unknown fields on roundtrip. For most bot use cases, unknown fields are irrelevant; if you need to inspect raw JSON, use serde_json::from_str::<serde_json::Value>() directly.
Roadmap
- Publish to crates.io
- Comprehensive
cargo docdocumentation with#[deny(missing_docs)] - Integration tests against real Bot API payloads
- Benchmarks with
criterion(throughput, memory, latency) - Webhook TLS auto-configuration
- Passport decryption utilities
- Payment flow helpers
- Bot API forward-compatibility layer (auto-update from spec)
Documentation
- Guide: rust-tg-bot-docs.vercel.app — mdBook with tutorials, guides, and architecture docs
- API reference: docs.rs/rust-tg-bot — auto-generated from source (available after crates.io publish)
Generate API docs locally:
Contributing
Contributions of all sizes are welcome. Please see CONTRIBUTING.md for guidelines.
License
Licensed under the GNU Lesser General Public License v3.0.
You may copy, distribute, and modify the software provided that modifications are described and licensed for free under LGPL-3.0. Derivative works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3.0, but applications that use the library don't have to be.
Links
- Telegram Bot API Documentation
- python-telegram-bot -- the project that started it all
- Repository
- @BotFather -- create your bot token here