{"id":44930727,"url":"https://github.com/ankit-chaubey/tgbotrs","last_synced_at":"2026-03-01T18:01:02.665Z","repository":{"id":339026081,"uuid":"1160146372","full_name":"ankit-chaubey/tgbotrs","owner":"ankit-chaubey","description":"A strongly-typed, async Rust client for the Telegram Bot API, generated from the official spec.","archived":false,"fork":false,"pushed_at":"2026-02-24T05:38:39.000Z","size":380,"stargazers_count":13,"open_issues_count":0,"forks_count":15,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-24T13:15:05.049Z","etag":null,"topics":["botapi-rust","tgbotapi","tgbotrs"],"latest_commit_sha":null,"homepage":"https://tgbotrs.ankitchaubey.in/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ankit-chaubey.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-17T15:45:05.000Z","updated_at":"2026-02-24T05:32:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ankit-chaubey/tgbotrs","commit_stats":null,"previous_names":["ankit-chaubey/tgbotrs"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ankit-chaubey/tgbotrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit-chaubey%2Ftgbotrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit-chaubey%2Ftgbotrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit-chaubey%2Ftgbotrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit-chaubey%2Ftgbotrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ankit-chaubey","download_url":"https://codeload.github.com/ankit-chaubey/tgbotrs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit-chaubey%2Ftgbotrs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29974324,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T15:41:30.362Z","status":"ssl_error","status_checked_at":"2026-03-01T15:37:07.343Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["botapi-rust","tgbotapi","tgbotrs"],"created_at":"2026-02-18T06:01:09.402Z","updated_at":"2026-03-01T18:01:02.658Z","avatar_url":"https://github.com/ankit-chaubey.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"https://rustacean.net/assets/rustacean-orig-noshadow.svg\" width=\"110\" alt=\"Ferris the Crab\"/\u003e\n\n\u003ch1\u003etgbotrs\u003c/h1\u003e\n\n\u003cp\u003e\u003cstrong\u003eA fully-featured, auto-generated Telegram Bot API library for Rust 🦀\u003c/strong\u003e\u003c/p\u003e\n\n[![Crates.io](https://img.shields.io/crates/v/tgbotrs?style=for-the-badge\u0026logo=rust\u0026color=f74c00\u0026labelColor=1a1a2e)](https://crates.io/crates/tgbotrs)\n[![docs.rs](https://img.shields.io/docsrs/tgbotrs?style=for-the-badge\u0026logo=docs.rs\u0026color=4a90d9\u0026labelColor=1a1a2e)](https://docs.rs/tgbotrs)\n[![CI](https://img.shields.io/github/actions/workflow/status/ankit-chaubey/tgbotrs/ci.yml?branch=main\u0026style=for-the-badge\u0026logo=github-actions\u0026label=CI\u0026color=2ea44f\u0026labelColor=1a1a2e)](https://github.com/ankit-chaubey/tgbotrs/actions/workflows/ci.yml)\n[![API Sync](https://img.shields.io/github/actions/workflow/status/ankit-chaubey/tgbotrs/auto-regenerate.yml?style=for-the-badge\u0026logo=telegram\u0026label=API+SYNC\u0026color=0088cc\u0026labelColor=1a1a2e)](https://github.com/ankit-chaubey/tgbotrs/actions/workflows/auto-regenerate.yml)\n\n[![Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-9.4-0088cc?style=flat-square\u0026logo=telegram\u0026logoColor=white)](https://core.telegram.org/bots/api)\n[![Rust](https://img.shields.io/badge/Rust-1.75%2B-f74c00?style=flat-square\u0026logo=rust)](https://www.rust-lang.org)\n[![Types](https://img.shields.io/badge/Types-285-7c3aed?style=flat-square)](https://docs.rs/tgbotrs)\n[![Methods](https://img.shields.io/badge/Methods-165-16a34a?style=flat-square)](https://docs.rs/tgbotrs)\n[![Coverage](https://img.shields.io/badge/API%20Coverage-100%25-22c55e?style=flat-square)](https://github.com/ankit-chaubey/tgbotrs/actions)\n[![Downloads](https://img.shields.io/crates/d/tgbotrs?style=flat-square\u0026color=f97316\u0026label=Downloads)](https://crates.io/crates/tgbotrs)\n[![License](https://img.shields.io/badge/License-MIT-eab308?style=flat-square)](LICENSE)\n\n\u003cbr/\u003e\n\n\u003e All **285 types** and **165 methods** of the Telegram Bot API —  \n\u003e strongly typed, fully async, automatically kept in sync with every official release.\n\n\u003cbr/\u003e\n\n[📦 Install](#-installation) · [🚀 Quick Start](#-quick-start) · [📖 Examples](#-examples) · [🔧 API Reference](#-api-reference) · [🔄 Auto-Codegen](#-auto-codegen) · [📚 docs.rs](https://docs.rs/tgbotrs)\n\n\u003c/div\u003e\n\n---\n\n## ✨ Features\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\"\u003e\n\n**🤖 Complete API Coverage**\n- All **285 types** — structs, enums, markers\n- All **165 methods** — fully async\n- All **21 union types** as Rust enums\n- **100 optional params structs** with builder pattern\n\n\u003c/td\u003e\n\u003ctd width=\"50%\"\u003e\n\n**🔄 Auto-Generated \u0026 Always Fresh**\n- Spec sourced from [tgapis/x](https://github.com/tgapis/x/tree/data) — auto-updated every 6 hours\n- Pipeline dispatches regeneration on every new API version\n- PR auto-opened with full semantic diff on every change\n- Zero manual work to stay up-to-date\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n**🦀 Idiomatic Rust**\n- Fully `async/await` with **Tokio**\n- `Into\u003cChatId\u003e` — accepts `i64` or `\"@username\"`\n- `Into\u003cString\u003e` on all text params\n- `Option\u003cT\u003e` for all optional fields\n- `Box\u003cT\u003e` to break recursive type cycles\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n**🛡️ Fully Type-Safe**\n- `ChatId` — integer or username, no stringly typing\n- `InputFile` — file_id / URL / raw bytes\n- `ReplyMarkup` — unified enum for all 4 keyboard types\n- `InputMedia` — typed enum for media groups\n- Compile-time guarantees on every API call\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n**📡 Flexible HTTP Layer**\n- Custom API server support (local Bot API)\n- Multipart file uploads built-in\n- Configurable timeout\n- Flood-wait aware error handling\n- `reqwest` backend\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n**📬 Built-in Polling**\n- Long-polling dispatcher included\n- Spawns a Tokio task per update\n- Configurable timeout, limit, allowed\\_updates\n- Clean concurrent update processing\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n## 📦 Installation\n\nAdd to your `Cargo.toml`:\n\n```toml\n[package]\nname = \"mybot\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ntgbotrs = \"\u003e=0.1.5\"\ntokio   = { version = \"1\", features = [\"full\"] }\n```\n\n\u003e **Requirements:** Rust `1.75+` · Tokio async runtime\n\n---\n\n## 🚀 Quick Start\n\n```rust\nuse tgbotrs::Bot;\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let bot = Bot::new(\"YOUR_BOT_TOKEN\").await?;\n\n    println!(\"✅ Running as @{}\", bot.me.username.as_deref().unwrap_or(\"unknown\"));\n    println!(\"   ID: {}\", bot.me.id);\n\n    // chat_id accepts i64, negative group IDs, or \"@username\"\n    let msg = bot.send_message(123456789i64, \"Hello from tgbotrs! 🦀\", None).await?;\n    println!(\"📨 Sent message #{}\", msg.message_id);\n\n    Ok(())\n}\n```\n\n---\n\n## 📖 Examples\n\n### 🔁 Echo Bot — Long Polling\n\nThe simplest possible bot. Receives every message and echoes it back.\n\n```rust\nuse tgbotrs::{Bot, Poller, UpdateHandler};\n\n#[tokio::main]\nasync fn main() {\n    let bot = Bot::new(std::env::var(\"BOT_TOKEN\").unwrap())\n        .await\n        .expect(\"Invalid token\");\n\n    println!(\"🤖 @{} is running...\", bot.me.username.as_deref().unwrap_or(\"\"));\n\n    let handler: UpdateHandler = Box::new(|bot, update| {\n        Box::pin(async move {\n            let Some(msg) = update.message else { return };\n            let Some(text) = msg.text else { return };\n            let _ = bot.send_message(msg.chat.id, text, None).await;\n        })\n    });\n\n    Poller::new(bot, handler)\n        .timeout(30)\n        .limit(100)\n        .start()\n        .await\n        .unwrap();\n}\n```\n\n---\n\n### 💬 Formatted Messages\n\nSend HTML or MarkdownV2 formatted messages with optional settings.\n\n```rust\nuse tgbotrs::gen_methods::SendMessageParams;\n\nlet params = SendMessageParams::new()\n    .parse_mode(\"HTML\".to_string())\n    .disable_notification(true);\n\nbot.send_message(\n    \"@mychannel\",\n    \"\u003cb\u003eBold\u003c/b\u003e · \u003ci\u003eItalic\u003c/i\u003e · \u003ccode\u003ecode\u003c/code\u003e · \u003ca href='https://example.com'\u003eLink\u003c/a\u003e\",\n    Some(params),\n).await?;\n```\n\n---\n\n### 🎹 Inline Keyboards\n\nButtons embedded inside messages. Perfect for interactive menus.\n\n```rust\nuse tgbotrs::{ReplyMarkup, gen_methods::SendMessageParams};\nuse tgbotrs::types::{InlineKeyboardButton, InlineKeyboardMarkup};\n\nlet keyboard = InlineKeyboardMarkup {\n    inline_keyboard: vec![\n        vec![\n            InlineKeyboardButton {\n                text: \"✅ Accept\".into(),\n                callback_data: Some(\"accept\".into()),\n                ..Default::default()\n            },\n            InlineKeyboardButton {\n                text: \"❌ Decline\".into(),\n                callback_data: Some(\"decline\".into()),\n                ..Default::default()\n            },\n        ],\n        vec![\n            InlineKeyboardButton {\n                text: \"🌐 Visit Website\".into(),\n                url: Some(\"https://ankitchaubey.in\".into()),\n                ..Default::default()\n            },\n        ],\n    ],\n};\n\nlet params = SendMessageParams::new()\n    .parse_mode(\"HTML\".to_string())\n    .reply_markup(ReplyMarkup::InlineKeyboard(keyboard));\n\nbot.send_message(chat_id, \"\u003cb\u003eMake a choice:\u003c/b\u003e\", Some(params)).await?;\n```\n\n---\n\n### ⚡ Callback Queries\n\nHandle button taps from inline keyboards. Always acknowledge with `answer_callback_query`.\n\n```rust\nuse tgbotrs::gen_methods::{AnswerCallbackQueryParams, EditMessageTextParams};\nuse tgbotrs::types::MaybeInaccessibleMessage;\n\nlet handler: UpdateHandler = Box::new(|bot, update| {\n    Box::pin(async move {\n        let Some(cq) = update.callback_query else { return };\n        let data = cq.data.as_deref().unwrap_or(\"\");\n\n        // Always acknowledge — dismisses the loading spinner\n        let _ = bot\n            .answer_callback_query(\n                cq.id.clone(),\n                Some(\n                    AnswerCallbackQueryParams::new()\n                        .text(format!(\"You chose: {}\", data))\n                        .show_alert(false),\n                ),\n            )\n            .await;\n\n        // Edit the original message in-place\n        if let Some(msg) = \u0026cq.message {\n            if let MaybeInaccessibleMessage::Message(m) = msg.as_ref() {\n                let edit_params = EditMessageTextParams::new()\n                    .chat_id(m.chat.id)\n                    .message_id(m.message_id)\n                    .parse_mode(\"HTML\".to_string());\n\n                let _ = bot\n                    .edit_message_text(\n                        format!(\"✅ You selected: \u003cb\u003e{}\u003c/b\u003e\", data),\n                        Some(edit_params),\n                    )\n                    .await;\n            }\n        }\n    })\n});\n```\n\n---\n\n### ⌨️ Reply Keyboards\n\nCustom keyboard shown at the bottom of the screen. Great for persistent menu buttons.\n\n```rust\nuse tgbotrs::{ReplyMarkup, gen_methods::SendMessageParams};\nuse tgbotrs::types::{KeyboardButton, ReplyKeyboardMarkup};\n\nlet keyboard = ReplyKeyboardMarkup {\n    keyboard: vec![\n        vec![\n            KeyboardButton {\n                text: \"📍 Share Location\".into(),\n                request_location: Some(true),\n                ..Default::default()\n            },\n            KeyboardButton {\n                text: \"📱 Share Contact\".into(),\n                request_contact: Some(true),\n                ..Default::default()\n            },\n        ],\n        vec![\n            KeyboardButton { text: \"🏠 Home\".into(), ..Default::default() },\n            KeyboardButton { text: \"⚙️ Settings\".into(), ..Default::default() },\n        ],\n    ],\n    resize_keyboard: Some(true),\n    one_time_keyboard: Some(true),\n    ..Default::default()\n};\n\nlet params = SendMessageParams::new()\n    .reply_markup(ReplyMarkup::ReplyKeyboard(keyboard));\n\nbot.send_message(chat_id, \"Use the keyboard below 👇\", Some(params)).await?;\n```\n\n---\n\n### 📸 Send Photos \u0026 Files\n\nSend files by file\\_id, URL, or raw bytes from disk.\n\n```rust\nuse tgbotrs::{InputFile, gen_methods::SendPhotoParams};\n\nlet params = SendPhotoParams::new()\n    .caption(\"Look at this! 📷\".to_string())\n    .parse_mode(\"HTML\".to_string());\n\n// Fastest — already on Telegram's servers\nbot.send_photo(chat_id, \"AgACAgIAAxkBAAI...\", Some(params.clone())).await?;\n\n// Let Telegram download from a URL\nbot.send_photo(chat_id, \"https://example.com/photo.jpg\", Some(params.clone())).await?;\n\n// Upload raw bytes from disk\nlet data = tokio::fs::read(\"photo.jpg\").await?;\nbot.send_photo(chat_id, InputFile::memory(\"photo.jpg\", data), Some(params)).await?;\n```\n\n---\n\n### 🎬 Media Groups\n\nSend multiple photos or videos as an album in a single message.\n\n```rust\nuse tgbotrs::InputMedia;\nuse tgbotrs::types::{InputMediaPhoto, InputMediaVideo};\n\nlet media = vec![\n    InputMedia::Photo(InputMediaPhoto {\n        r#type: \"photo\".into(),\n        media: \"AgACAgIAAxkBAAI...\".into(),\n        caption: Some(\"First photo 📸\".into()),\n        ..Default::default()\n    }),\n    InputMedia::Video(InputMediaVideo {\n        r#type: \"video\".into(),\n        media: \"BAACAgIAAxkBAAI...\".into(),\n        caption: Some(\"A video 🎬\".into()),\n        ..Default::default()\n    }),\n];\n\nbot.send_media_group(chat_id, media, None).await?;\n```\n\n---\n\n### 📊 Polls\n\nSend polls — regular or quiz style.\n\n```rust\nuse tgbotrs::gen_methods::SendPollParams;\nuse tgbotrs::types::InputPollOption;\n\nlet options = vec![\n    InputPollOption { text: \"🦀 Rust\".into(),   ..Default::default() },\n    InputPollOption { text: \"🐹 Go\".into(),     ..Default::default() },\n    InputPollOption { text: \"🐍 Python\".into(), ..Default::default() },\n];\n\nlet params = SendPollParams::new().is_anonymous(false);\n\nbot.send_poll(chat_id, \"Best language for bots?\", options, Some(params)).await?;\n```\n\n---\n\n### 🏪 Inline Queries\n\nHandle `@yourbot query` inline mode from any chat.\n\n```rust\nuse tgbotrs::types::{\n    InlineQueryResult, InlineQueryResultArticle,\n    InputMessageContent, InputTextMessageContent,\n};\n\nlet results = vec![\n    InlineQueryResult::InlineQueryResultArticle(InlineQueryResultArticle {\n        r#type: \"article\".into(),\n        id: \"1\".into(),\n        title: \"Hello World\".into(),\n        input_message_content: InputMessageContent::InputTextMessageContent(InputTextMessageContent {\n            message_text: \"Hello from inline mode! 👋\".into(),\n            ..Default::default()\n        }),\n        description: Some(\"Send a greeting\".into()),\n        reply_markup: None,\n        url: None,\n        thumbnail_url: None,\n        thumbnail_width: None,\n        thumbnail_height: None,\n    }),\n];\n\nbot.answer_inline_query(query.id.clone(), results, None).await?;\n```\n\n---\n\n### 🛒 Payments \u0026 Telegram Stars\n\nSend invoices using Telegram Stars (`XTR`) or payment providers.\n\n```rust\nuse tgbotrs::gen_methods::SendInvoiceParams;\nuse tgbotrs::types::LabeledPrice;\n\nlet prices = vec![\n    LabeledPrice { label: \"Premium Plan\".into(), amount: 999 },\n];\n\nbot.send_invoice(\n    chat_id,\n    \"Premium Access\",\n    \"30 days of unlimited features\",\n    \"payload_premium_30d\",\n    \"XTR\",   // Telegram Stars\n    prices,\n    None,\n).await?;\n```\n\n---\n\n### 🔔 Webhooks\n\nRegister a webhook URL so Telegram pushes updates to your server instead of you polling. Read detailed [webhook bot examples!](https://github.com/ankit-chaubey/tgbotrs/tree/main/examples/webhook)\n\n```rust\nuse tgbotrs::gen_methods::SetWebhookParams;\n\n// Register webhook\nlet params = SetWebhookParams::new()\n    .max_connections(100i64)\n    .allowed_updates(vec![\"message\".into(), \"callback_query\".into()])\n    .secret_token(\"my_secret_token\".to_string());\n\nbot.set_webhook(\"https://mybot.example.com/webhook\", Some(params)).await?;\n```\n\n**Full webhook server with [axum](https://github.com/tokio-rs/axum):**\n\n```toml\n# Cargo.toml\n[dev-dependencies]\naxum = \"0.7\"\n```\n\n```rust\nuse axum::{extract::State, http::StatusCode, routing::post, Json, Router};\nuse std::sync::Arc;\nuse tgbotrs::{gen_methods::SetWebhookParams, types::Update, Bot};\n\nstruct AppState { bot: Bot }\n\n#[tokio::main]\nasync fn main() {\n    let bot = Bot::new(\"YOUR_BOT_TOKEN\").await.unwrap();\n\n    bot.set_webhook(\n        \"https://yourdomain.com/webhook\",\n        Some(SetWebhookParams::new()),\n    )\n    .await\n    .unwrap();\n\n    let app = Router::new()\n        .route(\"/webhook\", post(handle_update))\n        .with_state(Arc::new(AppState { bot }));\n\n    let listener = tokio::net::TcpListener::bind(\"0.0.0.0:8080\").await.unwrap();\n    axum::serve(listener, app).await.unwrap();\n}\n\nasync fn handle_update(\n    State(state): State\u003cArc\u003cAppState\u003e\u003e,\n    Json(update): Json\u003cUpdate\u003e,\n) -\u003e StatusCode {\n    let bot = state.bot.clone();\n    // Spawn immediately — return 200 fast or Telegram will retry\n    tokio::spawn(async move {\n        if let Some(msg) = update.message {\n            let _ = bot\n                .send_message(msg.chat.id, \"Received via webhook! 🚀\", None)\n                .await;\n        }\n    });\n    StatusCode::OK\n}\n```\n\n\u003e For local testing: `ngrok http 8080` → use the ngrok URL as your webhook\n\n---\n\n### 🌐 Local Bot API Server\n\nPoint the bot at a self-hosted [Telegram Bot API server](https://github.com/tdlib/telegram-bot-api) for higher file size limits and faster speeds.\n\n```rust\nlet bot = Bot::with_api_url(\"YOUR_TOKEN\", \"http://localhost:8081\").await?;\n```\n\n---\n\n### 🛠️ Error Handling\n\nStructured errors with helpers for flood-wait and common API errors.\n\n```rust\nuse tgbotrs::BotError;\n\nmatch bot.send_message(chat_id, \"Hello!\", None).await {\n    Ok(msg) =\u003e println!(\"✅ Sent: #{}\", msg.message_id),\n\n    Err(BotError::Api { code: 403, .. }) =\u003e {\n        eprintln!(\"🚫 Bot was blocked by user\");\n    }\n    Err(BotError::Api { code: 400, description, .. }) =\u003e {\n        eprintln!(\"⚠️  Bad request: {}\", description);\n    }\n    Err(e) if e.is_api_error_code(429) =\u003e {\n        if let Some(secs) = e.flood_wait_seconds() {\n            println!(\"⏳ Flood wait: {} seconds\", secs);\n            tokio::time::sleep(std::time::Duration::from_secs(secs as u64)).await;\n        }\n    }\n    Err(e) =\u003e eprintln!(\"❌ Unexpected error: {}\", e),\n}\n```\n\n---\n\n## 🔧 API Reference\n\n### `Bot` — Core Struct\n\n```rust\npub struct Bot {\n    pub token:   String,  // Bot token from @BotFather\n    pub me:      User,    // Populated via getMe on creation\n    pub api_url: String,  // Default: https://api.telegram.org\n}\n```\n\n| Constructor | Description |\n|---|---|\n| `Bot::new(token)` | Create bot, calls `getMe`, verifies token |\n| `Bot::with_api_url(token, url)` | Create with a custom/local API server |\n| `Bot::new_unverified(token)` | Create without calling `getMe` |\n\n---\n\n### `ChatId` — Flexible Chat Identifier\n\nAnywhere `ChatId` is expected, you can pass any of these:\n\n```rust\nbot.send_message(123456789i64,     \"user by numeric id\", None).await?;\nbot.send_message(-100123456789i64, \"group or channel\",   None).await?;\nbot.send_message(\"@channelname\",   \"by username\",        None).await?;\nbot.send_message(ChatId::Id(123),  \"explicit wrapper\",   None).await?;\n```\n\n---\n\n### `InputFile` — File Sending\n\n```rust\n// Reference a file already on Telegram's servers (fastest)\nInputFile::file_id(\"AgACAgIAAxkBAAI...\")\n\n// Let Telegram download from a URL\nInputFile::url(\"https://example.com/image.png\")\n\n// Upload raw bytes directly\nlet data = tokio::fs::read(\"photo.jpg\").await?;\nInputFile::memory(\"photo.jpg\", data)\n```\n\n---\n\n### `ReplyMarkup` — All Keyboard Types\n\n```rust\n// Inline keyboard — buttons inside messages\nReplyMarkup::InlineKeyboard(InlineKeyboardMarkup { .. })\n\n// Reply keyboard — custom keyboard at bottom of screen\nReplyMarkup::ReplyKeyboard(ReplyKeyboardMarkup { .. })\n\n// Remove the reply keyboard\nReplyMarkup::ReplyKeyboardRemove(ReplyKeyboardRemove { remove_keyboard: true, .. })\n\n// Force the user to reply to a message\nReplyMarkup::ForceReply(ForceReply { force_reply: true, .. })\n```\n\n---\n\n### `Poller` — Long Polling Dispatcher\n\n```rust\nPoller::new(bot, handler)\n    .timeout(30)           // Seconds to long-poll (0 = short poll)\n    .limit(100)            // Max updates per request (1–100)\n    .allowed_updates(vec![ // Filter which update types to receive\n        \"message\".into(),\n        \"callback_query\".into(),\n        \"inline_query\".into(),\n    ])\n    .start()\n    .await?;\n```\n\n---\n\n### `BotError` — Error Variants\n\n```rust\npub enum BotError {\n    Http(reqwest::Error),       // Network / HTTP transport error\n    Json(serde_json::Error),    // Serialization error\n    Api {\n        code: i64,                       // Telegram error code (400, 403, 429…)\n        description: String,             // Human-readable message\n        retry_after: Option\u003ci64\u003e,        // Flood-wait seconds (code 429)\n        migrate_to_chat_id: Option\u003ci64\u003e, // Migration target (code 400)\n    },\n    InvalidToken,               // Token missing ':'\n    Other(String),              // Catch-all\n}\n\n// Helper methods\nerror.is_api_error_code(429)   // → bool\nerror.flood_wait_seconds()     // → Option\u003ci64\u003e\n```\n\n---\n\n### Builder Pattern for Optional Params\n\nEvery method with optional parameters has a `*Params` struct with a fluent builder API:\n\n```rust\n// Pattern: MethodNameParams::new().field(value).field(value)\nlet params = SendMessageParams::new()\n    .parse_mode(\"HTML\".to_string())\n    .disable_notification(true)\n    .protect_content(false)\n    .message_thread_id(123i64)\n    .reply_parameters(ReplyParameters { message_id: 42, ..Default::default() })\n    .reply_markup(ReplyMarkup::ForceReply(ForceReply {\n        force_reply: true,\n        input_field_placeholder: None,\n        selective: None,\n    }));\n```\n\n---\n\n## 📊 Coverage Statistics\n\n| Category | Count | Status |\n|:---|:---:|:---:|\n| **Total Types** | **285** | ✅ 100% |\n| ↳ Struct types | 257 | ✅ |\n| ↳ Union / Enum types | 21 | ✅ |\n| ↳ Marker types | 7 | ✅ |\n| **Total Methods** | **165** | ✅ 100% |\n| ↳ `set*` methods | 30 | ✅ |\n| ↳ `get*` methods | 29 | ✅ |\n| ↳ `send*` methods | 22 | ✅ |\n| ↳ `edit*` methods | 12 | ✅ |\n| ↳ `delete*` methods | 11 | ✅ |\n| ↳ Other methods | 61 | ✅ |\n| **Optional params structs** | 100 | ✅ |\n| **Lines auto-generated** | ~11,258 | — |\n\n---\n\n## 🔄 Auto-Codegen\n\ntgbotrs is the only Rust Telegram library that **automatically stays in sync** with the official API — no manual updates, no lag.\n\nThe spec is sourced from **[tgapis/x](https://github.com/tgapis/x/tree/data)** (`data` branch, `botapi.json`), which scrapes and parses the official Telegram Bot API page and auto-updates every **6 hours**. When a new API version is detected, the `tgapis/x` pipeline dispatches a trigger to this repo — regeneration kicks off immediately, no cron required.\n\n### How It Works\n\n```\ntgapis/x scrapes Telegram Bot API\nevery 6 hours → pushes botapi.json\nto the data branch\n        │\n        ▼\n  ┌─────────────────────┐\n  │  tgapis/x pipeline  │── No change? ──► Stop ✅\n  │  detects new version│\n  └──────────┬──────────┘\n             │ Changed!\n             │ repository_dispatch →\n             │ event: x-data-updated\n             ▼\n  ┌─────────────────────┐\n  │  Fetch botapi.json  │  ← raw.githubusercontent.com/tgapis/x/data/botapi.json\n  │  (always latest)    │\n  └──────────┬──────────┘\n             │\n             ▼\n  ┌─────────────────────┐\n  │  Compare with       │── No change? ──► Stop ✅\n  │  pinned api.json    │\n  └──────────┬──────────┘\n             │ Changed!\n             ▼\n  ┌─────────────────────┐\n  │  diff_spec.py       │  ← Semantic diff (added/removed types \u0026 methods)\n  └──────────┬──────────┘\n             │\n             ▼\n  ┌─────────────────────┐\n  │  codegen.py         │  ← Pure Python, zero pip dependencies\n  │                     │    Generates gen_types.rs + gen_methods.rs\n  └──────────┬──────────┘\n             │\n             ▼\n  ┌─────────────────────┐\n  │  validate.py        │  ← Verify 100% coverage\n  └──────────┬──────────┘\n             │\n             ▼\n  ┌─────────────────────┐\n  │  Open PR with       │  ← Rich report: summary table, per-field diff\n  │  full report        │    New/removed items, checklist\n  └──────────┬──────────┘\n             │\n             ▼\n  ┌─────────────────────┐\n  │  On PR merge:       │\n  │  • Bump semver      │\n  │  • Git tag          │\n  │  • GitHub Release   │\n  │  • crates.io        │\n  └─────────────────────┘\n```\n\n### Regenerate Manually\n\n```sh\n# 1. Pull latest spec from tgapis/x data branch into repo root\ncurl -sSf https://raw.githubusercontent.com/tgapis/x/data/botapi.json -o api.json\n\n# 2. Run codegen (no pip installs needed)\npython3 codegen/codegen.py api.json tgbotrs/src/\n\n# 3. Rebuild\ncargo build\n```\n\n### GitHub Actions Workflows\n\n| Workflow | Trigger | Purpose |\n|:---|:---|:---|\n| `auto-regenerate.yml` | 📡 Pipeline dispatch from `tgapis/x` + manual | Spec sync → diff → codegen → PR |\n| `ci.yml` | Every push / PR | Build, test, lint on 3 OS × 2 Rust versions (spec fetched live from `tgapis/x`) |\n| `release.yml` | PR merged → main | Semver bump → tag → crates.io publish |\n| `notify.yml` | After regen | GitHub Issue with full change summary |\n\n---\n\n## 🤝 Contributing\n\nContributions are welcome!\n\n**Report issues:**\n- 🐛 Bug → [open a bug report](https://github.com/ankit-chaubey/tgbotrs/issues/new?template=bug_report.md)\n- 💡 Feature → [open a feature request](https://github.com/ankit-chaubey/tgbotrs/issues/new?template=feature_request.md)\n- 🔒 Security → email [ankitchaubey.dev@gmail.com](mailto:ankitchaubey.dev@gmail.com) directly\n\n**Development workflow:**\n\n```sh\ngit clone https://github.com/ankit-chaubey/tgbotrs \u0026\u0026 cd tgbotrs\n\ncargo build --workspace                    # Build everything\ncargo test --workspace                     # Run tests\ncargo clippy --workspace -- -D warnings    # Lint\ncargo fmt --all                            # Format\n\n# Pull the latest spec from tgapis/x into repo root and regenerate\ncurl -sSf https://raw.githubusercontent.com/tgapis/x/data/botapi.json -o api.json\npython3 codegen/codegen.py api.json tgbotrs/src/\n\n# Validate 100% coverage\npython3 .github/scripts/validate_generated.py \\\n  api.json tgbotrs/src/gen_types.rs tgbotrs/src/gen_methods.rs\n```\n\n**PR guidelines:**\n- One concern per PR\n- Always run `cargo fmt` and `cargo clippy` before submitting\n- Never edit `gen_types.rs` or `gen_methods.rs` directly — edit `codegen.py` instead\n- Add examples for any new helpers\n\n---\n\n## 📜 Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for the full release history.\n\n---\n\n### Developed by [Ankit Chaubey](https://github.com/ankit-chaubey)\n\n**tgbotrs** started as a personal tool.  \nI was constantly running into limitations, missing features, and unsupported things,  \nso in 2024 I decided to build my own solution.\n\nAfter using **tgbotrs** for a long time (2024-26) and refining it along the way,  \nI felt it could be useful for others too — so I made it public.\n\nIf this helps you in any way, feel free to ⭐ star it or 🍴 fork it 😁\n\nDeveloped and maintained by Ankit Chaubey [(@ankit-chaubey)](https://github.com/ankit-chaubey)\n\n\u003cp align=\"center\"\u003e\n  \u003csub\u003e\n    Rust engineer · Open-source builder · Telegram \u0026 systems enthusiast\n  \u003c/sub\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ankit-chaubey\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/GitHub-ankit--chaubey-181717?style=flat\u0026logo=github\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://t.me/ankify\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Telegram-@ankify-0088cc?style=flat\u0026logo=telegram\u0026logoColor=white\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"mailto:ankitchaubey.dev@gmail.com\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Email-Contact-ea4335?style=flat\u0026logo=gmail\u0026logoColor=white\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://ankitchaubey.in\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Website-ankitchaubey.in-4a90d9?style=flat\u0026logo=google-chrome\u0026logoColor=white\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003chr /\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://docs.rs/tgbotrs\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/docs.rs-tgbotrs-4a90d9?style=flat-square\u0026logo=docs.rs\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://crates.io/crates/tgbotrs\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/crates.io-tgbotrs-f74c00?style=flat-square\u0026logo=rust\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/ankit-chaubey/tgbotrs/stargazers\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/stars/ankit-chaubey/tgbotrs?style=social\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/ankit-chaubey/tgbotrs/network/members\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/forks/ankit-chaubey/tgbotrs?style=social\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## 🙏 Thanks \u0026 Credits\n\nSpecial thanks to **[Paul / PaulSonOfLars](https://github.com/PaulSonOfLars)** — the auto-generation approach at the heart of this library was directly inspired by his excellent Go library **[gotgbot](https://github.com/PaulSonOfLars/gotgbot)** and api-spec gen. Seeing how clean and maintainable a fully-generated, strongly-typed Telegram library can be was the spark for building tgbotrs.\n\n| | |\n|:---|:---|\n| [**Telegram**](https://core.telegram.org/bots/api) | The Bot API this library implements |\n| [**PaulSonOfLars / gotgbot**](https://github.com/PaulSonOfLars/gotgbot) | Inspiration for the codegen-first approach |\n| [**tgapis/x**](https://github.com/tgapis/x/tree/data) | Machine-readable spec source — auto-updated every 6 hours |\n\n---\n\n## 📄 License\n\nMIT License © 2026 [Ankit Chaubey](https://github.com/ankit-chaubey)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n*If tgbotrs saved you time, a ⭐ on GitHub means a lot!*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankit-chaubey%2Ftgbotrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fankit-chaubey%2Ftgbotrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankit-chaubey%2Ftgbotrs/lists"}