blazegram
Declarative Telegram bot framework for Rust.
One screen at a time. Zero garbage in chat. Direct MTProto over persistent TCP.
Blazes skips the HTTP Bot API entirely. Instead of polling an HTTP server that polls Telegram, it holds a single persistent TCP socket to Telegram's datacenter via grammers MTProto. The result: lower latency, 2 GB file uploads, no middleman.
| HTTP Bot API | blazegram (MTProto) | |
|---|---|---|
| Transport | HTTPS poll / webhook | Persistent TCP to DC |
| Overhead per call | ~50 ms (HTTP + JSON) | ~5 ms (binary TL) |
| File limit | 50 MB multipart | 2 GB binary |
| Connection | new TCP per cycle | one socket, kept alive |
| External dep | Bot API server | none |
On top of that, blazegram introduces the Screen — a declarative description of what the user
should see right now. When you call navigate(), a Virtual Chat Differ computes the minimal
set of Telegram API calls (edit, delete, send) to get from the current state to the new one.
You never manage message IDs.
30-second example
use *;
async
BOT_TOKEN=123:ABC
First launch authenticates via MTProto and creates a .session file.
Subsequent starts reconnect in under 100 ms.
The differ
Every navigate() call runs through the differ before touching the network:
callback (button press) → edit messages in place (1 API call)
user sent text / command → delete old, send new at end (2–3 calls)
content identical → nothing (0 calls)
[Settings] pressed:
Before After
┌──────────────────┐ ┌──────────────────┐
│ Main Menu │ → │ Settings │
│ [Settings] [Help]│ │ [Lang] [Back] │
└──────────────────┘ └──────────────────┘
differ: EditText(msg_id=42) ← one API call, not two
If the user typed something between screens, the old message is scrolled out of view — editing it would be invisible. The differ detects this and switches to delete + send instead. Active progressive streams are auto-cancelled before diffing, so no concurrent edits can race.
Screens
# use *;
text.build;
text
.keyboard
.build;
builder
.photo
.caption
.keyboard
.done
.build;
// multi-message
builder
.text.done
.photo.caption.done
.build;
Navigation stack
ctx.push.await?;
// ... user clicks [Back]
ctx.pop.await?;
Stack depth capped at 20. Oldest entry dropped silently.
Forms
builder
.text_step
.validator
.done
.integer_step
.min.max.done
.choice_step
.done
.confirm_step
.on_complete
.build
Validation errors auto-delete and show a 3 s toast. Cancel/back buttons built-in.
Progressive updates
Stream edits to one message. Auto-throttled to stay under Telegram rate limits.
If navigate() is called before finalize(), the stream is cancelled automatically.
let h = ctx.progressive.await?;
h.update.await;
h.update.await;
h.finalize.await?;
State
ctx.set;
let n: i32 = ctx.get.unwrap_or;
let p: Profile = ctx.state;
ctx.set_state;
| Backend | Setup |
|---|---|
| In-memory (default) | nothing |
| Memory + snapshot | .snapshot("state.bin") |
| SQLite | .sqlite_store("bot.db") |
| Redis | .redis_store("redis://...") + feature redis |
Reply mode
For conversational bots that don't need chat cleanup:
ctx.reply.await?; // sends
ctx.reply.await?; // edits
ctx.reply.await?; // edits
User messages are not deleted. Previous replies are not deleted. Next handler call starts a fresh message.
Frozen / permanent messages
// frozen: survives navigate(), differ won't touch it
let sent = ctx.send_text.await?;
ctx.freeze_message;
// permanent: never tracked, never deleted
ctx.send_permanent.await?;
Inline mode
.inline
i18n
locales/en.json { "hi": "Hello, { $name }!" }
locales/de.json { "hi": "Hallo, { $name }!" }
let text = ctx.t_with;
Language auto-detected from user.language_code. Falls back to default.
Template engine
let html = render;
Supports {{ var }}, {% if cond %}, {% for x in list %}, {% else %}.
Middleware
builder
.middleware
.middleware
.middleware
// custom:
.middleware
Testing
use MockBotApi;
use TestApp;
async
No network. MockBotApi records every API call for assertions.
All features
| Screen system | Declarative text / photo / video / document / multi-message screens |
| Virtual Chat Differ | Minimal edit / delete / send operations per transition |
| Inline keyboards | Buttons, grids, URLs, web apps, switch-inline, callback params |
| Reply keyboards | Bottom keyboard with resize, one-time, placeholder |
| Navigation stack | push() / pop(), max depth 20 |
| Forms | Text, integer, choice, photo steps; validation; cancel/back/confirm |
| Pagination | Auto-paged lists with prev/next buttons |
| Progressive updates | Throttled streaming edits; auto-cancel on navigate |
| Reply mode | Conversational send-then-edit, no cleanup |
| Frozen messages | Survive navigate() transitions |
| Permanent messages | Bypass differ completely |
| Inline mode | Query results with builder API, pagination, chosen result handler |
| i18n | JSON bundles, { $var } interpolation, auto language detection |
| Templates | {{ var }}, {% if %}, {% for %} in any message text |
| Middleware | Logging, analytics, throttle built-in; custom via trait |
| Metrics | Update/error counters, latency histograms, .summary() |
| State | Memory, snapshot, SQLite, Redis backends |
| File cache | file_id caching to skip re-uploads |
| Rate limiter | Token bucket + automatic FLOOD_WAIT retry |
| Entity fallback | Auto plain-text retry when HTML entities fail |
| Broadcast | Concurrent multi-chat send with rate limiting |
| Payments | Invoices, pre-checkout, Stars API |
| Forum topics | Create, edit, close, reopen |
| BotApi trait | 73 async methods, fully mockable |
| Testing | MockBotApi + TestApp, zero network |
Architecture
Your handlers .command() / .callback() / .on_input()
│
▼
Ctx navigate() / push() / pop() / reply()
│
▼
Differ old tracked msgs + new Screen → minimal ops
│
▼
Executor retry on FLOOD_WAIT, fallback on entity errors
│
▼
BotApi 73 async methods (trait, mockable)
│
▼
grammers MTProto → Telegram DC (persistent TCP)
Per-chat mutex serializer guarantees sequential update processing. No race conditions across concurrent users.
Install
[]
= "0.3"
= { = "1", = ["full"] }
Optional:
= { = "0.3", = ["redis"] } # Redis state backend
= "0.3" # structured logging
MSRV
1.75
License
MIT