frankenstein

repository·master·Indexed 18 days ago

https://github.com/ayrat555/frankenstein

A complete Rust wrapper for the Telegram Bot API (v10.2) providing one-to-one mappings of Telegram types to Rust structs and enums. It supports both synchronous (blocking) requests via the `client-ureq` feature and asynchronous requests via the `client-reqwest` feature. The library includes builder patterns for API method parameters and handles file uploads using the `FileUpload` enum and `InputFile` struct.

Tokens
15.4K
Snippets
38
Records
54
Agent score
58%

What's inside frankenstein

  1. Use data structures and builders

    master

    Frankenstein's data structures (structs and enums) map one-to-one with the Telegram Bot API. Optional fields in the API are represented as Option<T> in Rust.

    To create parameters for API methods, use the associated builder pattern. Only required fields must be set; optional fields default to None.

    use frankenstein::methods::SendMessageParams;
    
    let send_message_params = SendMessageParams::builder()
        .chat_id(1337)
        .text("hello")
        .build();
  2. Install Frankenstein

    master

    To add Frankenstein to your Rust project, run cargo add frankenstein or manually add it to your Cargo.toml. By default, the crate only includes Telegram types. To perform actual API requests, you must enable either a blocking or an async client via features.

    [dependencies]
    frankenstein = { version = "0.51", features = [] }
  3. Use RichText for formatted content

    master

    The RichText enum is the core abstraction for text formatting. It can be a simple Text(String), a List(Vec<RichText>), or a Object(RichTextObject) which contains specific formatting like Bold, Italic, Code, Url, etc.

    Convenience implementations allow you to convert String, &str, or Vec<RichText> directly into RichText using .into() or RichText::from().

    use frankenstein::RichText;
    
    // Simple text
    let text = RichText::from("plain text");
    
    // Nested list of text
    let list = RichText::from(vec![RichText::from("item 1"), RichText::from("item 2")]);
  4. Use the TelegramApi and AsyncTelegramApi traits

    master

    To interact with the Telegram Bot API, you use the TelegramApi (synchronous) or AsyncTelegramApi (asynchronous) traits. These traits are provided by the trait-sync and trait-async features respectively.

    • Use TelegramApi when working with the client-ureq feature.
    • Use AsyncTelegramApi when working with the client-reqwest feature.
  5. Configure client features for blocking or async

    master

    Frankenstein requires specific features to be enabled to provide HTTP clients:

    Blocking (Synchronous) Clients

    • client-ureq: Uses the ureq crate for blocking HTTP requests.
    • trait-sync: Provides a blocking API trait, useful for implementing custom blocking clients.

    Async Clients

    • client-reqwest: Uses the reqwest crate for asynchronous HTTP requests. Note that file uploads are currently not supported on wasm32 targets.
    • trait-async: Provides an async API trait, used by client-reqwest, useful for implementing custom async clients.

    To use the async client, update your Cargo.toml as follows:

    frankenstein = { version = "0.51", features = ["client-reqwest"] }
  6. Implement the AsyncTelegramApi trait for custom clients

    master

    To create a custom asynchronous Telegram client, you must implement the AsyncTelegramApi trait. This trait provides a high-level interface for all Telegram Bot API methods.

    Key requirements for implementation:

    • The implementation must be Sync.
    • You must implement the core request methods: request, request_with_form_data, and request_with_possible_form_data to handle the actual network communication.
    • The trait is designed to work in both standard and wasm32 environments. In wasm32 targets, the implementation does not need to be Send because the runtime is single-threaded.

    Once implemented, you gain access to all Telegram methods (e.g., get_updates, send_message, send_photo) as asynchronous functions.

    pub trait AsyncTelegramApi
    where
        Self: Sync,
    {
        type Error;
    
        // You must implement these core methods to power the rest of the API
        async fn request<Params, Output>(&self, method: &str, params: Option<Params>) -> Result<Output, Self::Error> 
            where Params: serde::ser::Serialize + std::fmt::Debug + std::marker::Send, Output: serde::de::DeserializeOwned;
    
        async fn request_with_form_data<Params, Output>(&self, method: &str, params: Params, files: Vec<(&str, PathBuf)>) -> Result<Output, Self::Error>
            where Params: serde::ser::Serialize + std::fmt::Debug + std::marker::Send, Output: serde::de::DeserializeOwned;
    
        async fn request_with_possible_form_data<Params, Output>(&self, method_name: &str, params: Params, files: Vec<(&str, PathBuf)>) -> Result<Output, Self::Error>
            where Params: serde::ser::Serialize + std::fmt::Debug + std::marker::Send, Output: serde::de::DeserializeOwned;
    }
  7. Choose a Telegram API client implementation

    master

    Frankenstein supports two different HTTP client backends via Cargo features. You must enable one of these features to access the corresponding client modules and API traits:

    1. reqwest: Enable the client-reqwest feature. This provides an asynchronous client implementation using the reqwest crate.
    2. ureq: Enable the client-ureq feature. This provides a synchronous client implementation using the ureq crate.

    Note: The old AsyncApi and Api structs are deprecated. You should use the specialized client modules instead.

    # For asynchronous reqwest-based client
    frankenstein = { version = "0.51.0", features = ["client-reqwest", "trait-async"] }
    
    # For synchronous ureq-based client
    frankenstein = { version = "0.51.0", features = ["client-ureq", "trait-sync"] }
  8. Initialize a synchronous Bot using ureq

    master

    The Bot struct provides a synchronous implementation of the TelegramApi trait using the ureq HTTP client. You can initialize it using the API key or a custom URL.

    Use Bot::new(api_key) for standard Telegram API URLs, or Bot::new_url(api_url) if you are using a custom endpoint or a local mock server.

    // Using an API key
    let bot = Bot::new("YOUR_API_KEY");
    
    // Using a custom URL
    let bot = Bot::new_url("https://my.custom.proxy/bot");
  9. Initialize the Bot with reqwest

    master

    The Bot struct is the primary asynchronous entry point for interacting with the Telegram API using the reqwest HTTP client. You can initialize it in two ways:

    1. Using an API Key: Use Bot::new(api_key) to create a bot instance configured with the default Telegram API base URL.
    2. Using a Custom URL: Use Bot::new_url(api_url) if you are using a local Telegram Bot API server or a proxy.

    You can also use the Bot::builder() pattern to provide a custom reqwest::Client (e.g., for custom timeouts or proxy settings) and a custom api_url.

    // Using an API key
    let bot = Bot::new("YOUR_API_KEY");
    
    // Using a custom URL (e.g., for a local server)
    let bot = Bot::new_url("http://localhost:8081/");
    
    // Using the builder for advanced configuration
    let bot = Bot::builder()
        .api_url("https://my-proxy.com/")
        .client(custom_reqwest_client)
        .build();
  10. Attach files to an InputRichMessage

    master

    When using InputRichMessage, you can provide local file paths (e.g., PathBuf) within media objects (like InputMediaPhoto or InputMediaVideo).

    If the trait-sync or trait-async features are enabled, you can call replace_input_files() on an InputRichMessage. This method performs two actions:

    1. It returns a Vec<(String, PathBuf)> containing the mapping of generated attachment IDs (e.g., file0, file1) to your local file paths.
    2. It internally replaces the local paths in the message structure with attach://fileN URIs, making the object ready for serialization and transmission to the Telegram API.
    // Assuming features 'trait-sync' or 'trait-async' are enabled
    let mut input_message = InputRichMessage::builder()
        .photo(InputMediaPhoto::builder()
            .media(PathBuf::from("path/to/photo.jpg"))
            .build())
        .build();
    
    // Get the files to upload and transform the message to use attach:// URIs
    let files_to_upload = input_message.replace_input_files();
    
    for (id, path) in files_to_upload {
        println!("Upload {} from {}", id, path.display());
    }
  11. Make API requests with the blocking client

    master

    To make requests using the blocking client, use the Bot struct from frankenstein::client_ureq and the TelegramApi trait. Every method call returns a Result containing either the successful response or an error.

    use frankenstein::TelegramApi;
    use frankenstein::client_ureq::Bot;
    use frankenstein::methods::{GetUpdatesParams, SendMessageParams};
    use frankenstein::types::AllowedUpdate;
    
    let token = "123:ABC";
    let bot = Bot::new(token);
    
    // Send a message
    let send_message_params = SendMessageParams::builder()
        .chat_id(1337)
        .text("hello")
        .build();
    let result = bot.send_message(&send_message_params);
    
    // Get updates (interactions with the bot)
    let update_params = GetUpdatesParams::builder()
        .allowed_updates(vec![AllowedUpdate::Message])
        .build();
    let result = bot.get_updates(&update_params);
  12. Upload files using FileUpload

    master

    For methods that support file uploads, use the FileUpload enum. It has two variants:

    1. FileUpload::String(String): Pass the ID of a file that has already been uploaded to Telegram.
    2. FileUpload::InputFile(InputFile): Upload a new file from a local path using multipart upload.

    The InputFile struct wraps a std::path::PathBuf.

    // Example of the FileUpload enum structure
    pub enum FileUpload {
        InputFile(InputFile),
        String(String),
    }
    
    pub struct InputFile {
        path: std::path::PathBuf
    }