<div align="center">
# ferogram
Async Rust client for the Telegram MTProto API.
[](https://crates.io/crates/ferogram)
[](https://docs.rs/ferogram)
[](../LICENSE-MIT)
[](https://core.telegram.org/schema)
[](https://t.me/Ferogram)
[](https://t.me/FerogramChat)
Built by **[Ankit Chaubey](https://github.com/ankit-chaubey)**
</div>
This is the main client crate. It talks to Telegram directly over MTProto, no Bot API proxy in between, and handles auth for both bots and user accounts from the same client builder. You get a dispatcher with composable filters, FSM for multi-step conversations, CDN downloads, middleware, and a raw `invoke()` escape hatch for anything not wrapped yet.
If you're starting fresh, this is the only crate you need. Everything else in the workspace exists to support it and can be pulled in separately if you need a specific layer on its own. Want the Bot API instead? Take a look at [ferobot](https://github.com/ankit-chaubey/ferobot).
> [!NOTE]
> ferogram is still in active development. It covers major use cases and runs in production, but the API may still shift.
## Installation
```toml
[dependencies]
ferogram = "0.6.4"
tokio = { version = "1", features = ["full"] }
```
Get `api_id` and `api_hash` from [my.telegram.org](https://my.telegram.org).
Optional feature flags:
```toml
ferogram = { version = "0.6.4", features = [
"sqlite-session", # SqliteBackend via rusqlite
"libsql-session", # LibSqlBackend via libsql-client (Turso)
"html", # parse_html / generate_html (built-in parser)
"html5ever", # parse_html via spec-compliant html5ever
"derive", # #[derive(FsmState)]
"serde", # serde support on session types
] }
```
## Quick start: bot
```rust
use ferogram::{Client, update::Update};
const API_ID: i32 = 0;
const API_HASH: &str = "";
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (client, _) = Client::quick_connect("bot.session", API_ID, API_HASH).await?;
let mut stream = client.stream_updates();
while let Some(upd) = stream.next().await {
if let Update::NewMessage(msg) = upd {
if !msg.outgoing() {
msg.reply(msg.text().unwrap_or_default()).await.ok();
}
}
}
Ok(())
}
```
## Quick start: user account
```rust
use ferogram::Client;
const API_ID: i32 = 0;
const API_HASH: &str = "";
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (client, _) = Client::quick_connect("my.session", API_ID, API_HASH).await?;
client.send_message("me", "Hello from ferogram!").await?;
Ok(())
}
```
## Examples
19 runnable examples covering everything from sending a message to your Saved Messages to a full FSM order bot.
See **[examples/README.md](examples/README.md)** for the full list with descriptions and notes on when to use `quick_connect` vs `Client::builder()`.
## Connecting
`quick_connect` is the fast path. For anything more specific, use the builder:
```rust
use ferogram::Client;
let (client, _shutdown) = Client::builder()
.api_id(12345)
.api_hash("your_api_hash")
.session("my.session")
.catch_up(true)
.connect()
.await?;
```
`.catch_up(true)` replays missed updates after a reconnect, and `.retry_policy(...)` / `.restart_policy(...)` let you customize retry and reconnect behavior. Session storage, transport, and proxy options each have their own section below. Full docs at [docs.rs/ferogram](https://docs.rs/ferogram).
## Dispatcher and filters
```rust
use ferogram::filters::{Dispatcher, command, private, text_contains, group, media};
let mut dp = Dispatcher::new();
dp.on_message(command("start"), |msg| async move {
msg.reply("Hello!").await.ok();
});
});
dp.on_message(group() & media(), |msg| async move {
// handle media in groups
});
while let Some(upd) = stream.next().await {
dp.dispatch(upd).await;
}
```
Filters compose with `&`, `|`, `!`. Built-ins cover `command`, `private`, `group`, `channel`, `text`, `text_contains`, `media`, `photo`, `document`, `forwarded`, `reply`, `from_user`, `album`, `custom`, and more. Callback queries and inline queries route through the same dispatcher via `on_callback_query` / `on_inline_query` / `on_inline_send`.
## Middleware
```rust
let result = next.run(upd).await;
tracing::info!("handler done");
result
});
```
Runs in registration order. Call `next.run(upd)` to pass control forward, or return early to stop the chain.
## FSM
```rust
use ferogram::{FsmState, fsm::MemoryStorage};
use std::sync::Arc;
#[derive(FsmState, Clone, Debug, PartialEq)]
enum Form { Name, Age }
dp.with_state_storage(Arc::new(MemoryStorage::new()));
state.transition(Form::Age).await.ok();
msg.reply("How old are you?").await.ok();
});
```
`MemoryStorage` is built in. To persist state across restarts, implement `StateStorage` for Redis, a database, or anything else. State keys scope per-user, per-chat, or per-user-in-chat via `StateKeyStrategy`. See [`ferogram-fsm`](../ferogram-fsm/) for details.
## Session backends
```rust
Client::builder().session("bot.session") // binary file (default)
Client::builder().in_memory() // no persistence
Client::builder().session_string(env::var("SESSION")?) // base64 string
Client::builder().session_backend(Arc::new(SqliteBackend::open("s.db")?)) // sqlite
Client::builder().session_backend(Arc::new(LibSqlBackend::remote(url, token).await?)) // turso
```
The base64 string backend is useful for serverless or containers where writing to disk isn't an option. To bring your own, implement `SessionBackend` from [`ferogram-session`](../ferogram-session/).
## Transport and proxy
```rust
use ferogram::TransportKind;
Client::builder().transport(TransportKind::Abridged) // default
Client::builder().transport(TransportKind::Obfuscated) // DPI bypass, plain MTProxy secrets
Client::builder().transport(TransportKind::FakeTls) // TLS camouflage, 0xee secrets
// MTProxy from a t.me link
Client::builder().proxy_link("https://t.me/proxy?server=HOST&port=PORT&secret=SECRET")
// SOCKS5
Client::builder().socks5("127.0.0.1:1080")
// Race transports, use first to connect
Client::builder().probe_transport(true)
// Fall back through DoH + Telegram special-config if TCP is blocked
Client::builder().resilient_connect(true)
```
See [`ferogram-connect`](../ferogram-connect/) for the framing layer underneath.
## Raw API
When the high-level API isn't enough, `client.invoke()` takes any TL function directly (current layer exposed as `tl::LAYER`). It's the escape hatch, not the normal path, but it's always there:
```rust
use ferogram::tl;
let req = tl::functions::bots::SetBotCommands {
scope: tl::enums::BotCommandScope::Default(tl::types::BotCommandScopeDefault {}),
lang_code: "en".into(),
commands: vec![tl::enums::BotCommand::BotCommand(tl::types::BotCommand {
command: "start".into(),
description: "Start the bot".into(),
})],
};
client.invoke(&req).await?;
client.invoke_on_dc(2, &req).await?; // target a specific DC
```
See [`ferogram-tl-types`](../ferogram-tl-types/) for all generated types and functions.
## Error handling
```rust
use ferogram::{InvocationError, RpcError};
match client.send_message("@peer", "Hi").await {
Ok(()) => {}
Err(InvocationError::Rpc(RpcError { code, message, .. })) => {
eprintln!("Telegram error {code}: {message}");
}
Err(InvocationError::Io(e)) => eprintln!("I/O: {e}"),
Err(e) => eprintln!("{e}"),
}
```
`FLOOD_WAIT` is handled automatically. To disable it:
```rust
use ferogram::retry::NoRetries;
Client::builder().retry_policy(Arc::new(NoRetries))
```
## Shutdown
```rust
let (client, shutdown) = Client::builder()...connect().await?;
shutdown.cancel(); // graceful
client.disconnect(); // immediate
```
## What's covered
See **[FEATURES.md](../FEATURES.md)** for the full list with method signatures. If something is missing, open a feature request or drop by [t.me/FerogramChat](https://t.me/FerogramChat). the raw API is always there in the meantime.
**Secret chats** (end-to-end encrypted) are fully implemented but not published to crates.io yet. The plan is to release once there is enough community demand for it.
## Voice and video calls
Group audio, video, and P2P calling are now fully implemented. To get started, check out the [tgcalls](https://crates.io/crates/tgcalls) crate and its examples in the [tgcalls](https://github.com/ankit-chaubey/tgcalls) repository. It provides seamless integration between ferogram and the official [ntgcalls](https://crates.io/crates/ntgcalls) Rust bindings for building Telegram voice and video calling applications.
## Crates
Most people only need this crate. But each crate in the workspace is independently publishable if you need just one layer.
| [`ferogram`](.) | High-level client. Auth, messaging, media, dispatcher, FSM, middleware. |
| [`ferogram-session`](../ferogram-session/) | Session types and pluggable storage backends (file, memory, SQLite, LibSQL, base64). |
| [`ferogram-fsm`](../ferogram-fsm/) | FSM state storage and context. `StateStorage` trait, `MemoryStorage`, `StateContext`. |
| [`ferogram-parsers`](../ferogram-parsers/) | Telegram Markdown and HTML entity parsers. |
| [`ferogram-derive`](../ferogram-derive/) | `#[derive(FsmState)]` proc macro. |
| [`ferogram-mtsender`](../ferogram-mtsender/) | DC connection pool and retry policy. `AutoSleep`, `NoRetries`, `CircuitBreaker`. |
| [`ferogram-connect`](../ferogram-connect/) | Raw TCP, MTProto framing, obfuscation, SOCKS5, MTProxy, gzip. |
| [`ferogram-mtproto`](../ferogram-mtproto/) | MTProto 2.0 session, DH key exchange, message framing, PFS key binding. |
| [`ferogram-crypto`](../ferogram-crypto/) | AES-IGE, RSA, SHA, Diffie-Hellman, PQ factorization, auth key derivation. |
| [`ferogram-tl-types`](../ferogram-tl-types/) | Auto-generated TL types, functions, and enums (tracks `tl::LAYER`). |
| [`ferogram-tl-gen`](../ferogram-tl-gen/) | Build-time code generator from TL AST to Rust source. |
| [`ferogram-tl-parser`](../ferogram-tl-parser/) | Parses `.tl` schema text into a Definition AST. |
The rough dependency chain (build-critical path only):
```
ferogram
└ ferogram-mtsender
└ ferogram-connect
├ ferogram-mtproto
│ ├ ferogram-tl-types
│ │ └ (build) ferogram-tl-gen
│ │ └ (build) ferogram-tl-parser
│ └ ferogram-crypto
└ ferogram-crypto
```
## Testing
```bash
cargo test --workspace
cargo test --workspace --all-features
```
## Community
- **Channel** (releases, announcements): [t.me/Ferogram](https://t.me/Ferogram)
- **Chat** (questions, discussion): [t.me/FerogramChat](https://t.me/FerogramChat)
- **API docs**: [docs.rs/ferogram](https://docs.rs/ferogram)
- **GitHub**: [github.com/ankit-chaubey/ferogram](https://github.com/ankit-chaubey/ferogram)
## License
MIT OR Apache-2.0. See [LICENSE-MIT](../LICENSE-MIT) and [LICENSE-APACHE](../LICENSE-APACHE).
Usage must comply with [Telegram's API Terms of Service](https://core.telegram.org/api/terms).