Raw API Access
Every Telegram API method is available as a typed struct in ferogram_tl_types::functions. Use client.invoke() to call any of them directly with full compile-time type safety.
Basic usage
#![allow(unused)]
fn main() {
use ferogram_tl_types::functions;
// Get current update state
let state = client.invoke(
&functions::updates::GetState {}
).await?;
println!("pts={} qts={} seq={}", state.pts, state.qts, state.seq);
}
Navigation
All 500+ functions are organized by namespace matching the TL schema:
| TL namespace | Rust path | Examples |
|---|---|---|
auth.* | functions::auth:: | SendCode, SignIn, LogOut |
account.* | functions::account:: | GetPrivacy, UpdateProfile |
users.* | functions::users:: | GetFullUser, GetUsers |
contacts.* | functions::contacts:: | Search, GetContacts, AddContact |
messages.* | functions::messages:: | SendMessage, GetHistory, Search |
updates.* | functions::updates:: | GetState, GetDifference |
photos.* | functions::photos:: | UploadProfilePhoto, GetUserPhotos |
upload.* | functions::upload:: | SaveFilePart, GetFile |
channels.* | functions::channels:: | GetParticipants, EditAdmin |
bots.* | functions::bots:: | SetBotCommands, GetBotCommands |
payments.* | functions::payments:: | GetStarGiftAuctionState (L223) |
stories.* | functions::stories:: | GetStories, CreateAlbum (L223) |
Examples
Get full user info
#![allow(unused)]
fn main() {
use ferogram_tl_types::{functions, enums, types};
let user_full = client.invoke(&functions::users::GetFullUser {
id: enums::InputUser::InputUser(types::InputUser {
user_id: target_user_id,
access_hash: user_access_hash,
}),
}).await?;
let tl::enums::users::UserFull::UserFull(uf) = user_full;
if let enums::UserFull::UserFull(info) = uf.full_user {
println!("About: {:?}", info.about);
println!("Common chats: {}", info.common_chats_count);
println!("Stars rating: {:?}", info.stars_rating);
}
}
Send a message with all parameters
#![allow(unused)]
fn main() {
client.invoke(&functions::messages::SendMessage {
no_webpage: false,
silent: false,
background: false,
clear_draft: true,
noforwards: false,
update_stickersets_order: false,
invert_media: false,
peer: peer_input,
reply_to: None,
message: "Hello from raw API!".into(),
random_id: ferogram::random_i64_pub(),
reply_markup: None,
entities: None,
schedule_date: None,
send_as: None,
quick_reply_shortcut: None,
effect: None,
allow_paid_floodskip: false,
}).await?;
}
Edit admin rights (Layer 223)
In Layer 223, rank is now Option<String>:
#![allow(unused)]
fn main() {
client.invoke(&functions::channels::EditAdmin {
flags: 0,
channel: enums::InputChannel::InputChannel(types::InputChannel {
channel_id, access_hash: ch_hash,
}),
user_id: enums::InputUser::InputUser(types::InputUser {
user_id, access_hash: user_hash,
}),
admin_rights: enums::ChatAdminRights::ChatAdminRights(types::ChatAdminRights {
change_info: true,
post_messages: true,
delete_messages: true,
ban_users: true,
invite_users: true,
pin_messages: true,
manage_call: true,
manage_ranks: true, // new in Layer 223
// ... all others false
edit_messages: false, add_admins: false, anonymous: false,
other: false, manage_topics: false, post_stories: false,
edit_stories: false, delete_stories: false,
manage_direct_messages: false,
}),
rank: Some("Moderator".into()), // Layer 223: optional
}).await?;
}
Set bot commands
#![allow(unused)]
fn main() {
client.invoke(&functions::bots::SetBotCommands {
scope: enums::BotCommandScope::Default,
lang_code: "en".into(),
commands: vec![
types::BotCommand { command: "start".into(), description: "Start the bot".into() },
types::BotCommand { command: "help".into(), description: "Show help".into() },
types::BotCommand { command: "ping".into(), description: "Latency check".into() },
],
}).await?;
}
New in Layer 223: edit chat creator
#![allow(unused)]
fn main() {
client.invoke(&functions::messages::EditChatCreator {
peer: chat_input_peer,
user_id: new_creator_input_user,
password: enums::InputCheckPasswordSRP::InputCheckPasswordEmpty,
}).await?;
}
New in Layer 223: URL auth match code
#![allow(unused)]
fn main() {
let valid = client.invoke(&functions::messages::CheckUrlAuthMatchCode {
url: "https://example.com/login".into(),
match_code: "abc123".into(),
}).await?;
}
Access hashes
Many raw API calls need an access_hash alongside user/channel IDs. The internal peer cache is populated by resolve, get_participants, get_dialogs, etc.:
#![allow(unused)]
fn main() {
// This populates the peer cache
let peer = client.resolve("@username").await?;
// For users
let user_hash = client.inner_peer_cache_users().get(&user_id).copied().unwrap_or(0);
// Simpler: use resolve_to_input_peer for a ready-to-use InputPeer
let input_peer = client.resolve_to_input_peer("@username").await?;
}
Error patterns
#![allow(unused)]
fn main() {
use ferogram::{InvocationError, RpcError};
match client.invoke(&req).await {
Ok(result) => use_result(result),
Err(InvocationError::Rpc(RpcError { code: 400, message, .. })) => {
eprintln!("Bad request: {message}");
}
Err(InvocationError::Rpc(RpcError { code: 403, message, .. })) => {
eprintln!("Forbidden: {message}");
}
Err(InvocationError::Rpc(RpcError { code: 420, message, .. })) => {
// FLOOD_WAIT (only if using NoRetries policy)
let secs: u64 = message
.strip_prefix("FLOOD_WAIT_").and_then(|s| s.parse().ok()).unwrap_or(60);
tokio::time::sleep(Duration::from_secs(secs)).await;
}
Err(e) => return Err(e.into()),
}
}
Peer cache helpers
Two methods let you seed or refresh the internal peer cache without waiting for an update to arrive:
warm_peer_cache_from_dialogs
#![allow(unused)]
fn main() {
client.warm_peer_cache_from_dialogs().await?;
}
Issues a single GetDialogs call and caches all returned channel and user access hashes. Call this at startup if your code needs to address channels by ID before any update has come in for them. Without this, a CHANNEL_INVALID error can appear on the first request to a channel the bot hasn’t interacted with yet in the current session.
This is opt-in; ferogram no longer calls GetDialogs automatically at startup. See the v0.3.5 release notes for why.
cache_users_slice_pub / cache_chats_slice_pub
#![allow(unused)]
fn main() {
client.cache_users_slice_pub(&users_vec).await;
client.cache_chats_slice_pub(&chats_vec).await;
}
Manually insert tl::enums::User or tl::enums::Chat slices into the access-hash cache. Useful when you’ve called a raw RPC that returned user or chat objects and want them to be addressable by subsequent high-level calls.
Update state
sync_update_state
#![allow(unused)]
fn main() {
client.sync_update_state().await;
}
Calls updates.getState and resets the internal pts/qts/seq/date counters to match the server. Called automatically on connect(). Call it manually after resuming from a long hibernation to avoid replaying a large backlog of missed updates.
sync_pts_state
#![allow(unused)]
fn main() {
client.sync_pts_state().await?;
}
The fallible version of sync_update_state. Returns an error if the RPC itself fails. sync_update_state silently discards that error; use sync_pts_state directly when you need to handle or log a failure.