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

Peer Types

ferogram provides typed wrappers over the raw tl::enums::User and tl::enums::Chat types, and a flexible peer input system that every Client method uses automatically.


Auto-resolution

Every Client method that targets a chat, user, or channel accepts any of the following directly. You never need to pre-resolve anything.

#![allow(unused)]
fn main() {
// @username or bare username
client.send_message("@durov", "hi").await?;
client.send_message("durov", "hi").await?;

// "me" or "self": the logged-in account
client.send_message("me", "Note to self").await?;

// E.164 phone number
client.send_message("+12025551234", "hi").await?;

// t.me URL
client.send_message("https://t.me/telegram", "hi").await?;

// Invite link (must already be a member, otherwise call join_link first)
client.send_message("https://t.me/+AbCdEfGhIjKl", "hi").await?;

// Positive i64: user ID
client.send_message(12345678_i64, "hi").await?;

// Negative i64: Bot-API channel ID (-100... prefix)
client.get_message_history(-1001234567890_i64, 50, 0).await?;

// Small negative i64: basic group
client.mark_read(-123456_i64).await?;

// Raw TL peer: zero cost, no network call
use ferogram::tl;
let peer = tl::enums::Peer::User(tl::types::PeerUser { user_id: 123 });
client.send_message(peer, "hi").await?;

// Already-resolved InputPeer: hash is used directly
let ip: tl::enums::InputPeer = get_it_from_somewhere();
client.send_message(ip, "hi").await?;
}

Accepted invite link formats: https://t.me/+HASH, https://t.me/joinchat/HASH, tg://join?invite=HASH.

Resolution is cache-first. Usernames, phone numbers, and IDs that have been seen before are resolved from memory with no RPC. An RPC is only made on a genuine cache miss.

Bot-API ID encoding

RangePeer type
id > 0User
-1_000_000_000_000 < id < 0Basic group
id <= -1_000_000_000_000Channel or supergroup

Manual resolution

Use client.resolve() when you need a Peer value explicitly. It accepts all the same input types as every other Client method:

#![allow(unused)]
fn main() {
// &str: username, phone, URL, invite link
let peer = client.resolve("@username").await?;
let peer = client.resolve("+12025551234").await?;
let peer = client.resolve("https://t.me/+HASH").await?;
let peer = client.resolve("me").await?;

// i64 / i32: Bot-API numeric ID
let peer = client.resolve(12345678_i64).await?;
let peer = client.resolve(-1001234567890_i64).await?;

// tl::enums::Peer: zero cost, returned as-is
use ferogram::tl;
let raw = tl::enums::Peer::User(tl::types::PeerUser { user_id: 123 });
let peer = client.resolve(raw).await?;

// tl::enums::InputPeer: hash cached, then stripped to Peer
let ip: tl::enums::InputPeer = get_it_from_somewhere();
let peer = client.resolve(ip).await?;
}

client.resolve(peer: &str) is the string-only variant; use it when the input is always a &str. Use resolve() for everything else.

To go from a Peer back to an InputPeer (with access hash):

#![allow(unused)]
fn main() {
let input = client.resolve_to_input_peer(&peer).await?;
}

This returns an error if the peer has not appeared in any prior API response and the access hash is unknown.

Via PeerRef directly (same result):

#![allow(unused)]
fn main() {
use ferogram::PeerRef;
let peer = PeerRef::from("@username").resolve(&client).await?;
let peer = PeerRef::from(12345678_i64).resolve(&client).await?;
}

User: user account wrapper

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

// Wrap from raw TL
if let Some(user) = User::from_raw(raw_tl_user) {
    println!("ID: {}", user.id());
    println!("Name: {}", user.full_name());
    println!("Username: {:?}", user.username());
    println!("Is bot: {}", user.bot());
    println!("Is premium: {}", user.premium());
}
}

User accessor methods

MethodReturn typeDescription
id()i64Telegram user ID
access_hash()Option<i64>Access hash for API calls
first_name()Option<&str>First name
last_name()Option<&str>Last name
full_name()String"First [Last]" combined
username()Option<&str>Primary username (without @)
usernames()Vec<&str>All active usernames
phone()Option<&str>Phone number (if visible)
bot()boolIs a bot account
verified()boolIs a verified account
premium()boolIs a premium account
deleted()boolAccount has been deleted
scam()boolFlagged as scam
restricted()boolAccount is restricted
is_self()boolIs the currently logged-in user
contact()boolIn the logged-in user’s contacts
mutual_contact()boolMutual contact
support()boolTelegram support staff
lang_code()Option<&str>User’s client language code
status()Option<&tl::enums::UserStatus>Online/offline status
photo()Option<&tl::types::UserProfilePhoto>Profile photo
bot_inline_placeholder()Option<&str>Inline mode compose bar hint
bot_inline_geo()boolBot supports inline without location
bot_supports_chats()boolBot can be added to groups
restriction_reason()Vec<&tl::enums::RestrictionReason>Restriction reasons
as_peer()tl::enums::PeerConvert to Peer
as_input_peer()tl::enums::InputPeerConvert to InputPeer

User implements Display as "Full Name (@username)" or "Full Name [user_id]".


Group: basic group wrapper

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

if let Some(group) = Group::from_raw(raw_tl_chat) {
    println!("ID: {}", group.id());
    println!("Title: {}", group.title());
    println!("Members: {}", group.participants_count());
    println!("I am creator: {}", group.creator());
}
}

Group accessor methods

MethodReturn typeDescription
id()i64Group ID
title()&strGroup name
participants_count()i32Member count
creator()boolLogged-in user is the creator
migrated_to()Option<&tl::enums::InputChannel>Points to supergroup after migration
as_peer()tl::enums::PeerConvert to Peer
as_input_peer()tl::enums::InputPeerConvert to InputPeer

Channel: channel / supergroup wrapper

#![allow(unused)]
fn main() {
use ferogram::types::{Channel, ChannelKind};

if let Some(channel) = Channel::from_raw(raw_tl_chat) {
    println!("ID: {}", channel.id());
    println!("Title: {}", channel.title());
    println!("Username: {:?}", channel.username());
    println!("Kind: {:?}", channel.kind());
    println!("Members: {:?}", channel.participants_count());
}
}

Channel accessor methods

MethodReturn typeDescription
id()i64Channel ID
access_hash()Option<i64>Access hash
title()&strChannel / supergroup name
username()Option<&str>Public username (without @)
usernames()Vec<&str>All active usernames
megagroup()boolIs a supergroup (not a broadcast channel)
broadcast()boolIs a broadcast channel
gigagroup()boolIs a broadcast group (gigagroup)
kind()ChannelKindBroadcast / Megagroup / Gigagroup
verified()boolVerified account
restricted()boolIs restricted
signatures()boolPosts have author signatures
participants_count()Option<i32>Approximate member count
photo()Option<&tl::types::ChatPhoto>Channel photo
admin_rights()Option<&tl::types::ChatAdminRights>Your admin rights
restriction_reason()Vec<&tl::enums::RestrictionReason>Restriction reasons
as_peer()tl::enums::PeerConvert to Peer
as_input_peer()tl::enums::InputPeerConvert to InputPeer (requires hash)
as_input_channel()tl::enums::InputChannelConvert to InputChannel

ChannelKind enum

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

match channel.kind() {
    ChannelKind::Broadcast  => { /* Posts only, no member replies */ }
    ChannelKind::Megagroup  => { /* All members can post */ }
    ChannelKind::Gigagroup  => { /* Large public broadcast group */ }
}
}

Chat: unified chat enum

Chat unifies Group and Channel into one enum with shared accessors:

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

if let Some(chat) = Chat::from_raw(raw_tl_chat) {
    println!("ID: {}", chat.id());
    println!("Title: {}", chat.title());

    match &chat {
        Chat::Group(g)   => println!("Basic group, {} members", g.participants_count()),
        Chat::Channel(c) => println!("{:?} channel", c.kind()),
    }
}
}

Chat methods

MethodReturn typeDescription
id()i64ID regardless of variant
title()&strName regardless of variant
as_peer()tl::enums::PeerPeer variant
as_input_peer()tl::enums::InputPeerInputPeer variant

PeerExt / OptionPeerExt: extract numeric ID without match

When you have a raw tl::enums::Peer and just need the i64 ID, use the PeerExt trait instead of writing a full match block every time.

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

// Instead of:
let id = match peer {
    tl::enums::Peer::User(u)    => u.user_id,
    tl::enums::Peer::Chat(c)    => c.chat_id,
    tl::enums::Peer::Channel(c) => c.channel_id,
};

// Just write:
let id = peer.bare_id();
}

OptionPeerExt adds the same .bare_id() to Option<&tl::enums::Peer>, which is what IncomingMessage::sender_id() and IncomingMessage::peer_id() return:

#![allow(unused)]
fn main() {
use ferogram::{PeerExt, OptionPeerExt};

// sender numeric ID: Option<i64>
let sender: Option<i64> = msg.sender_id().bare_id();

// chat numeric ID: Option<i64>
let chat: Option<i64> = msg.peer_id().bare_id();
}

Note: these are native Telegram IDs, not Bot-API-encoded. A channel with native ID 1234567890 is -1001234567890 in the Bot API. Use PeerRef if you need the Bot-API form.

Import summary

TraitImplemented forMethodReturns
PeerExttl::enums::Peer.bare_id()i64
OptionPeerExtOption<&tl::enums::Peer>.bare_id()Option<i64>