Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ClientBuilder

ClientBuilder is the fluent, type-safe constructor for a Client connection. Obtain one via Client::builder().

Just getting started? Client::quick_connect connects and authenticates in one call with no options to think about. Come back here when you need proxies, custom transports, low memory mode, or anything else the defaults don’t cover.

use ferogram::Client;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let (client, _shutdown) = Client::builder()
        .api_id(12345)
        .api_hash("abc123")
        .session("my.session")
        .catch_up(true)
        .connect()
        .await?;
    Ok(())
}

connect() returns Result<(Client, ShutdownToken), BuilderError>. The BuilderError can be MissingApiId, MissingApiHash, or a network-level Connect(InvocationError).


Common patterns

Most people only need a handful of options. Here are the three most common setups:

#![allow(unused)]
fn main() {
use ferogram::Client;

// 99% of users - just fill in credentials and go
let (client, _) = Client::builder()
    .api_id(API_ID)
    .api_hash(API_HASH)
    .session("bot.session")
    .connect().await?;

// Termux / tiny VPS - running on constrained hardware
let (client, _) = Client::builder()
    .api_id(API_ID)
    .api_hash(API_HASH)
    .session("bot.session")
    .low_memory_mode(true)
    .connect().await?;

// Power user - custom update queue to handle high-traffic bots
use ferogram::update_config::OverflowStrategy;

let (client, _) = Client::builder()
    .api_id(API_ID)
    .api_hash(API_HASH)
    .session("bot.session")
    .update_queue_capacity(512)
    .update_overflow_strategy(OverflowStrategy::DropNewest)
    .connect().await?;
}

The full option reference follows below.


Credentials

sync .api_id(id: i32) → ClientBuilder
Set the Telegram API ID obtained from my.telegram.org. Required - connect() returns BuilderError::MissingApiId if not set.
sync .api_hash(hash: impl Into<String>) → ClientBuilder
Set the Telegram API hash from my.telegram.org. Required - connect() returns BuilderError::MissingApiHash if not set.

Session

Three session backends are available. They are mutually exclusive - the last call wins.

sync .session(path: impl AsRef<Path>) → ClientBuilder
Use a binary file session at path. This is the default backend ("ferogram.session" in the working directory if no session method is called).
.session("mybot.session")
sync .session_string(s: impl Into<String>) → ClientBuilder
Use a portable base64 string session. Pass an empty string to start fresh; the string exported by client.export_session_string() can be injected here directly (e.g. via an environment variable). No file is written to disk.
.session_string(std::env::var("SESSION").unwrap_or_default())
sync .in_memory() → ClientBuilder
Use a non-persistent in-memory session. The session is lost when the process exits. Useful for tests and throwaway scripts.
sync .session_backend(backend: Arc<dyn SessionBackend>) → ClientBuilder
Inject a fully custom SessionBackend implementation. Use this for LibSqlBackend (bundled SQLite, no system dependency) or any custom persistence layer.
#[cfg(feature = "libsql-session")]
use ferogram::LibSqlBackend;
use std::sync::Arc;

.session_backend(Arc::new(LibSqlBackend::new(“my.db”))) Requires the libsql-session feature for LibSqlBackend. See Feature Flags.


Updates

sync .catch_up(enabled: bool) → ClientBuilder
When true, replay missed updates via updates.getDifference immediately after connecting. Useful for bots or userbots that must not miss messages during downtime. Default: false.

Network

sync .dc_addr(addr: impl Into<String>) → ClientBuilder
Override the first DC address. Useful when connecting to a test server.
.dc_addr("149.154.167.40:443")   // production DC 1
.dc_addr("149.154.167.40:80")    // test DC
sync .allow_ipv6(allow: bool) → ClientBuilder
Allow IPv6 DC addresses when resolving the DC table. Default: false.
sync .transport(kind: TransportKind) → ClientBuilder
Choose the MTProto transport framing layer.
VariantDescription
TransportKind::AbridgedSmallest overhead (default)
TransportKind::IntermediateCompatible with more proxies
TransportKind::ObfuscatedDeep-packet-inspection resistant
TransportKind::HttpPlain HTTP wrapping

Note: when using .mtproxy() or .proxy_link(), the transport is set automatically from the secret prefix - do not also call .transport().

sync .probe_transport(enabled: bool) → ClientBuilder
Race Obfuscated, Abridged, and HTTP transports in parallel and connect using whichever completes the DH handshake first. The losing attempts are cancelled immediately. Ideal when you don't know which transport your network or firewall permits. Incompatible with MTProxy. Default: false.
#![allow(unused)]
fn main() {
.probe_transport(true)
}

See Transport Probing & Resilient Connect for the race schedule, timing details, and interaction with MTProxy.

sync .resilient_connect(enabled: bool) → ClientBuilder
If direct TCP fails, retry via DNS-over-HTTPS (Mozilla + Google DoH), then fall back to Firebase / Google special-config endpoints. Useful in restricted networks where Telegram DCs are ISP-blocked. Default: false.
#![allow(unused)]
fn main() {
.resilient_connect(true)
}

See Transport Probing & Resilient Connect for the full fallback chain and when to combine with probe_transport.

sync .pfs(enabled: bool) → ClientBuilder
Enable Perfect Forward Secrecy. When set, a temporary DH key bind is performed immediately after the permanent auth key is established. Traffic runs under a short-lived session key derived from that bind; the permanent key is never used to encrypt traffic directly. If the bind RPC fails for any reason, the pool falls back to the standard session without interrupting the connection.

Adds one extra DH round-trip per connection. Off by default. Enable only if your threat model requires it.

#![allow(unused)]
fn main() {
let (client, _shutdown) = Client::builder()
    .api_id(12345)
    .api_hash("your_hash")
    .session("bot.session")
    .pfs(true)
    .connect()
    .await?;
}

Proxy

sync .socks5(addr: impl Into<String>) → ClientBuilder
Route all connections through a SOCKS5 proxy. Pass a "host:port" string:
.socks5("127.0.0.1:1080")
sync .mtproxy(proxy: MtProxyConfig) → ClientBuilder
Route all connections through an MTProxy. The transport is automatically selected from the proxy secret prefix; do not also call .transport(). Build with ferogram::parse_proxy_link(url) or construct manually.
sync .proxy_link(url: &str) → ClientBuilder
Set an MTProxy from a https://t.me/proxy?... or tg://proxy?... link. An empty string is a no-op. Transport is selected from the secret prefix automatically.
.proxy_link("https://t.me/proxy?server=1.2.3.4&port=443&secret=abc123")
See Proxies & Transports for full details.

Identity (InitConnection)

These strings are sent to Telegram in the InitConnection call and appear in the active sessions list on my.telegram.org.

sync .device_model(model: impl Into<String>) → ClientBuilder
Device model shown in sessions. Default: "Linux".
Example: .device_model("Pixel 9 Pro")
sync .system_version(version: impl Into<String>) → ClientBuilder
OS / system version shown in sessions. Default: "1.0".
Example: .system_version("Android 15")
sync .app_version(version: impl Into<String>) → ClientBuilder
App version shown in sessions. Default: the crate version from CARGO_PKG_VERSION.
sync .lang_code(code: impl Into<String>) → ClientBuilder
BCP-47 language code sent in InitConnection. Default: "en".
sync .system_lang_code(code: impl Into<String>) → ClientBuilder
System language code. Default: "en".
sync .lang_pack(pack: impl Into<String>) → ClientBuilder
Language pack name. Default: "" (empty). Leave unset unless building a client that mirrors an official Telegram app.

Retry & Restart

sync .retry_policy(policy: Arc<dyn RetryPolicy>) → ClientBuilder
Override the flood-wait / rate-limit retry strategy. The default is AutoSleep: automatically sleep for FLOOD_WAIT durations. See Retry & Flood Wait.
sync .restart_policy(policy: Arc<dyn ConnectionRestartPolicy>) → ClientBuilder
Override the reconnect behaviour after a connection drop. The default is NeverRestart: the event loop exits on disconnect and the shutdown signal fires. Use FixedInterval for automatic reconnection, or implement the trait for custom backoff logic.
#![allow(unused)]
fn main() {
use std::sync::Arc;
use std::time::Duration;
use ferogram::FixedInterval;

// Reconnect 5 seconds after any drop
.restart_policy(Arc::new(FixedInterval {
    interval: Duration::from_secs(5),
}))
}

See Connection Restart Policy for all built-in types, custom implementation, and scheduled periodic restarts.


Experimental Features

sync .experimental_features(features: ExperimentalFeatures) → ClientBuilder
Opt in to experimental behaviours that deviate from strict Telegram spec. All flags default to false. Always use ..Default::default() to stay forward-compatible with new flags.
#![allow(unused)]
fn main() {
use ferogram::{Client, ExperimentalFeatures};

Client::builder()
    .api_id(12345)
    .api_hash("abc")
    .experimental_features(ExperimentalFeatures {
        allow_zero_hash: true,  // bots only: allow hash=0 on cache miss
        ..Default::default()
    })
    .connect()
    .await?;
}

See Experimental Features for all flags, safety constraints, and when to use each one.


Terminal Methods

sync .build() → Result<Config, BuilderError>
Build the Config struct without establishing a network connection. Useful if you want to pass the Config to Client::connect(config) manually, or to inspect the built configuration.
async .connect() → Result<(Client, ShutdownToken), BuilderError>
Build the Config and connect in one step. Returns Err(BuilderError::MissingApiId) or Err(BuilderError::MissingApiHash) before attempting any network I/O if required fields are absent.

BuilderError

VariantMeaning
BuilderError::MissingApiId.api_id() was not called (or set to 0)
BuilderError::MissingApiHash.api_hash() was not called (or left empty)
BuilderError::Connect(InvocationError)Network / MTProto connection failed
#![allow(unused)]
fn main() {
match client_result {
    Err(BuilderError::MissingApiId) => eprintln!("Set API_ID"),
    Err(BuilderError::MissingApiHash) => eprintln!("Set API_HASH"),
    Err(BuilderError::Connect(e)) => eprintln!("Network error: {e}"),
    Ok((client, _)) => { /* use client */ }
}
}