layer-client 0.1.2

Production-grade async Telegram client โ€” updates, bots, flood-wait, dialogs, messages
Documentation

๐Ÿค layer-client

High-level async Telegram client for Rust.

Crates.io License: MIT OR Apache-2.0 Rust TL Layer

The friendly face of the layer stack โ€” connect, authenticate, send messages, and stream updates with a clean async API.


๐Ÿ“ฆ Installation

[dependencies]
layer-client = "0.1.2"
tokio = { version = "1", features = ["full"] }

โœจ What It Does

layer-client wraps the raw MTProto machinery into a clean, ergonomic async API. You don't need to know anything about TL schemas, DH handshakes, or message framing โ€” just connect and go.

  • ๐Ÿ” User auth โ€” phone code + optional 2FA (SRP)
  • ๐Ÿค– Bot auth โ€” bot token login
  • ๐Ÿ’ฌ Messaging โ€” send, delete, fetch message history
  • ๐Ÿ“ก Update stream โ€” typed async events (NewMessage, CallbackQuery, etc.)
  • ๐Ÿ” FLOOD_WAIT retries โ€” automatic with configurable policy
  • ๐ŸŒ DC migration โ€” handled transparently
  • ๐Ÿ’พ Session persistence โ€” survive restarts without re-login
  • ๐Ÿ”ง Raw API access โ€” client.invoke(req) for any TL function

๐Ÿš€ Quick Start โ€” User Account

use layer_client::{Client, Config, SignInError};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Client::connect(Config {
        session_path: "my.session".into(),
        api_id:       12345,          // https://my.telegram.org
        api_hash:     "abc123".into(),
        ..Default::default()
    }).await?;

    if !client.is_authorized().await? {
        let token = client.request_login_code("+1234567890").await?;

        // read code from user input
        let code = "12345";

        match client.sign_in(&token, code).await {
            Ok(name) => println!("โœ… Signed in as {name}"),
            Err(SignInError::PasswordRequired(t)) => {
                let hint = t.hint().unwrap_or("no hint");
                println!("2FA required โ€” hint: {hint}");
                client.check_password(t, "my_password").await?;
            }
            Err(e) => return Err(e.into()),
        }
        client.save_session().await?;
    }

    client.send_message("me", "Hello from layer! ๐Ÿ‘‹").await?;
    Ok(())
}

๐Ÿค– Quick Start โ€” Bot

use layer_client::{Client, Config, update::Update};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::connect(Config {
        session_path: "bot.session".into(),
        api_id:       12345,
        api_hash:     "abc123".into(),
        ..Default::default()
    }).await?;

    client.bot_sign_in("1234567890:ABCdef...").await?;
    client.save_session().await?;

    let mut updates = client.stream_updates();

    while let Some(update) = updates.next().await {
        match update {
            Update::NewMessage(msg) if !msg.outgoing() => {
                println!("๐Ÿ“จ {}", msg.text().unwrap_or("(no text)"));

                if let Some(peer) = msg.peer_id() {
                    client.send_message_to_peer(
                        peer.clone(),
                        &format!("Echo: {}", msg.text().unwrap_or("")),
                    ).await?;
                }
            }
            Update::CallbackQuery(cb) => {
                client.answer_callback_query(cb.query_id, Some("Got it!"), false).await?;
            }
            Update::InlineQuery(iq) => {
                println!("๐Ÿ” Inline: {}", iq.query());
            }
            _ => {}
        }
    }

    Ok(())
}

๐Ÿ“š API Reference

Connection

// Connect and load/create session
let client = Client::connect(config).await?;

// Check if already logged in
let authed = client.is_authorized().await?;

// Save session to disk
client.save_session().await?;

// Sign out
client.sign_out().await?;

Authentication

// Phone login
let token = client.request_login_code("+1234567890").await?;
client.sign_in(&token, "12345").await?;

// 2FA
client.check_password(password_token, "my_password").await?;

// Bot login
client.bot_sign_in("token:here").await?;

Messaging

// By username, "me", or numeric ID
client.send_message("@username", "Hello!").await?;
client.send_message("me", "Saved!").await?;
client.send_to_self("Quick note").await?;

// By resolved peer
client.send_message_to_peer(peer, "text").await?;

// Delete messages
client.delete_messages(vec![123, 456], true).await?;

// Fetch history
let msgs = client.get_messages(peer, 50, 0).await?;

Updates

let mut stream = client.stream_updates();

while let Some(update) = stream.next().await {
    match update {
        Update::NewMessage(msg)      => { /* new message */ }
        Update::MessageEdited(msg)   => { /* edit */       }
        Update::MessageDeleted(del)  => { /* deletion */   }
        Update::CallbackQuery(cb)    => { /* button press */}
        Update::InlineQuery(iq)      => { /* @bot query */ }
        Update::Raw(raw)             => { /* anything else */}
        _ => {}
    }
}

IncomingMessage

msg.id()          // โ†’ i32
msg.text()        // โ†’ Option<&str>
msg.peer_id()     // โ†’ Option<&Peer>
msg.sender_id()   // โ†’ Option<&Peer>
msg.outgoing()    // โ†’ bool
msg.reply(&mut client, "text").await?;

Bots

// Answer callback query
client.answer_callback_query(cb.query_id, Some("Done!"), false).await?;

// Answer with alert popup
client.answer_callback_query(cb.query_id, Some("Alert!"), true).await?;

// Answer inline query
client.answer_inline_query(iq.query_id, results, 300, false, None).await?;

Peer Resolution

// Supported formats
client.resolve_peer("me").await?;
client.resolve_peer("@username").await?;
client.resolve_peer("123456789").await?;

Raw API

// Invoke any TL function directly
let result = client.invoke(&layer_tl_types::functions::updates::GetState {}).await?;

โš™๏ธ Configuration

Config {
    session_path: "my.session".into(),    // where to store auth key
    api_id:       12345,                   // from https://my.telegram.org
    api_hash:     "abc123".into(),         // from https://my.telegram.org
    dc_addr:      None,                    // override initial DC (default: DC2)
    retry_policy: Arc::new(AutoSleep::default()),  // flood-wait policy
}

Retry Policies

// Auto-sleep on FLOOD_WAIT (default)
retry_policy: Arc::new(AutoSleep::default())

// Never retry โ€” propagate all errors immediately
retry_policy: Arc::new(NoRetries)

// Custom policy
struct MyPolicy;
impl RetryPolicy for MyPolicy {
    fn should_retry(&self, ctx: &RetryContext) -> ControlFlow<(), Duration> {
        // your logic
    }
}

๐Ÿ”— Part of the layer stack

layer-client        โ† you are here
โ””โ”€โ”€ layer-mtproto   (session, DH, framing)
    โ””โ”€โ”€ layer-tl-types  (generated API types)
        โ””โ”€โ”€ layer-crypto    (AES, RSA, SHA)

๐Ÿ“„ License

Licensed under either of, at your option:


๐Ÿ‘ค Author

Ankit Chaubey github.com/ankit-chaubey ยท ankitchaubey.in ยท ankitchaubey.dev@gmail.com

๐Ÿ“ฆ github.com/ankit-chaubey/layer