Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: name clients Bot to differentiate from TelegramApi #253

Merged
merged 3 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 20 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,50 +79,42 @@ Optional fields are described as `Option`.
Every struct can be created with the associated builder. Only required fields are required to set, optional fields are set to `None` when not provided:

```rust
use frankenstein::api_params::SendMessageParams;
let send_message_params = SendMessageParams::builder()
.chat_id(message.chat.id)
.chat_id(1337)
.text("hello")
.reply_to_message_id(message.message_id)
.build();
```

For API parameters, the same approach is used. The only difference for parameters is the name of the struct in Frankenstein ends with `Params` postfix.

For example, parameters for `leaveChat` method:

```rust
pub struct LeaveChatParams {
chat_id: ChatId,
}
```

### Making requests

To make a request to the Telegram bot API initialize the `Api` struct.

```rust
use frankenstein::Api;
```rust,no_run
#![cfg(feature = "client-ureq")]
use frankenstein::TelegramApi;
use frankenstein::api_params::{GetUpdatesParams, SendMessageParams};
use frankenstein::client_ureq::Bot;
use frankenstein::objects::AllowedUpdate;

...

let token = "My_token";
let api = Api::new(token);
```
let token = "123:ABC";
let bot = Bot::new(token);

Then use this API object to make requests to the Bot API:
// Send a message
let send_message_params = SendMessageParams::builder()
.chat_id(1337)
.text("hello")
.build();
let result = bot.send_message(&send_message_params);

```rust
// or get the updates (= interactions with the bot)
let update_params = GetUpdatesParams::builder()
.allowed_updates(vec![AllowedUpdate::Message])
.build();

let result = api.get_updates(&update_params);
let result = bot.get_updates(&update_params);
```

Every function returns a `Result` enum with a successful response or failed response.
Every function returns a `Result` with a successful response or failed response.

See a complete example in the `examples` directory.
See more examples in the [`examples`](https://github.com/ayrat555/frankenstein/tree/0.38.0/examples) directory.

### Uploading files

Expand All @@ -144,46 +136,9 @@ It has two variants:
- `FileUpload::String` is used to pass the ID of the already uploaded file
- `FileUpload::InputFile` is used to upload a new file using multipart upload.

### Customizing HTTP clients

Both the async (`reqwest`) and the blocking (`ureq`) HTTP clients can be customized with their builders.

Customizing the blocking client:

```rust
use frankenstein::ureq;
use frankenstein::Api;
use std::time::Duration;

let request_agent = ureq::builder().timeout(Duration::from_secs(100)).build();
let api_url = format!("{}{}", BASE_API_URL, TOKEN);

Api::builder()
.api_url(api_url)
.request_agent(request_agent)
.build()
```

Customizing the async client:

```rust
use frankenstein::reqwest;
use frankenstein::AsyncApi;
use std::time::Duration;

let client = reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(100))
.timeout(Duration::from_secs(100))
.build()
.unwrap();
let api_url = format!("{}{}", BASE_API_URL, TOKEN);

AsyncApi::builder().api_url(api_url).client(client).build()
```

### Documentation

Frankenstein implements all Telegram bot API methods. To see which parameters you should pass, check [docs.rs](https://docs.rs/frankenstein/0.38.0/frankenstein/api_traits/telegram_api/trait.TelegramApi.html#provided-methods)
Frankenstein implements all Telegram bot API methods. To see which parameters you should pass, check the [official Telegram Bot API documentation](https://core.telegram.org/bots/api#available-methods) or [docs.rs/frankenstein](https://docs.rs/frankenstein/0.38.0/frankenstein/trait.TelegramApi.html#provided-methods)

You can check out real-world bots created using this library:

Expand All @@ -192,24 +147,6 @@ You can check out real-world bots created using this library:
- [wdr-maus-downloader](https://github.com/EdJoPaTo/wdr-maus-downloader) - checks for a new episode of the WDR Maus and downloads it.
- [weather_bot_rust](https://github.com/pxp9/weather_bot_rust) - A Telegram bot that provides weather info around the world.

## Replacing the default HTTP client

The library uses `ureq` HTTP client by default, but it can be easily replaced with any HTTP client of your choice.
This is described here for the `trait-sync` and can be done similarly with `trait-async` based on your needs.

```toml
frankenstein = { version = "0.39", features = ["trait-sync"] }
```

Then implement the `TelegramApi` trait for your HTTP client which requires two functions:

- `request_with_form_data` is used to upload files
- `request` is used for requests without file uploads

You can check [the default `TelegramApi` trait implementation](https://github.com/ayrat555/frankenstein/blob/aac88c01d06aa945393db7255ef2485a7c764d47/src/api_impl.rs) for `ureq`.

Also, you can take a look at the [implementation for `isahc` HTTP client](https://github.com/ayrat555/frankenstein/blob/master/examples/api_trait_implementation.rs) in the `examples` directory.

## Contributing

1. [Fork it!](https://github.com/ayrat555/frankenstein/fork)
Expand Down
10 changes: 5 additions & 5 deletions examples/api_trait_implementation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ static TOKEN: &str = "TOKEN";
static BASE_API_URL: &str = "https://api.telegram.org/bot";
static CHAT_ID: i64 = 1;

pub struct Api {
pub struct MyApiClient {
pub api_url: String,
}

Expand All @@ -20,7 +20,7 @@ pub enum Error {
Api(ErrorResponse),
}

impl Api {
impl MyApiClient {
#[must_use]
pub fn new(api_key: &str) -> Self {
let api_url = format!("{BASE_API_URL}{api_key}");
Expand All @@ -47,7 +47,7 @@ impl From<isahc::Error> for Error {
}
}

impl TelegramApi for Api {
impl TelegramApi for MyApiClient {
type Error = Error;

fn request<Params, Output>(
Expand Down Expand Up @@ -105,14 +105,14 @@ impl TelegramApi for Api {
}

fn main() {
let api = Api::new(TOKEN);
let bot = MyApiClient::new(TOKEN);

let params = SendMessageParams::builder()
.chat_id(CHAT_ID)
.text("Hello!")
.build();

let result = api.send_message(&params);
let result = bot.send_message(&params);

eprintln!("{result:?}");
}
11 changes: 6 additions & 5 deletions examples/async_custom_client.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::time::Duration;

use frankenstein::{AsyncApi, AsyncTelegramApi};
use frankenstein::client_reqwest::Bot;
use frankenstein::AsyncTelegramApi;

static TOKEN: &str = "API_TOKEN";
static BASE_API_URL: &str = "https://api.telegram.org/bot";

#[tokio::main]
async fn main() {
let api = custom_client();
let bot = custom_client();

match api.get_me().await {
match bot.get_me().await {
Ok(response) => {
let user = response.result;
println!(
Expand All @@ -24,13 +25,13 @@ async fn main() {
}
}

fn custom_client() -> AsyncApi {
fn custom_client() -> Bot {
let client = frankenstein::reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(100))
.timeout(Duration::from_secs(100))
.build()
.unwrap();
let api_url = format!("{BASE_API_URL}{TOKEN}");

AsyncApi::builder().api_url(api_url).client(client).build()
Bot::builder().api_url(api_url).client(client).build()
}
7 changes: 4 additions & 3 deletions examples/async_file_upload.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use frankenstein::api_params::SendPhotoParams;
use frankenstein::{AsyncApi, AsyncTelegramApi};
use frankenstein::client_reqwest::Bot;
use frankenstein::AsyncTelegramApi;

static TOKEN: &str = "TOKEN";
static CHAT_ID: i64 = 1;

#[tokio::main]
async fn main() {
let api = AsyncApi::new(TOKEN);
let bot = Bot::new(TOKEN);

let file = std::path::PathBuf::from("./frankenstein_logo.png");

Expand All @@ -15,7 +16,7 @@ async fn main() {
.photo(file)
.build();

match api.send_photo(&params).await {
match bot.send_photo(&params).await {
Ok(response) => {
println!("Photo was uploaded {response:?}");
}
Expand Down
7 changes: 4 additions & 3 deletions examples/async_get_me.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use frankenstein::{AsyncApi, AsyncTelegramApi};
use frankenstein::client_reqwest::Bot;
use frankenstein::AsyncTelegramApi;

static TOKEN: &str = "API_TOKEN";

#[tokio::main]
async fn main() {
let api = AsyncApi::new(TOKEN);
let bot = Bot::new(TOKEN);

match api.get_me().await {
match bot.get_me().await {
Ok(response) => {
let user = response.result;
println!(
Expand Down
15 changes: 8 additions & 7 deletions examples/async_reply_to_message_updates.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
use frankenstein::api_params::{GetUpdatesParams, ReplyParameters, SendMessageParams};
use frankenstein::client_reqwest::Bot;
use frankenstein::objects::{Message, UpdateContent};
use frankenstein::{AsyncApi, AsyncTelegramApi};
use frankenstein::AsyncTelegramApi;

static TOKEN: &str = "API_TOKEN";

#[tokio::main]
async fn main() {
let api = AsyncApi::new(TOKEN);
let bot = Bot::new(TOKEN);

let mut update_params = GetUpdatesParams::builder().build();

loop {
let result = api.get_updates(&update_params).await;
let result = bot.get_updates(&update_params).await;

println!("result: {result:?}");

match result {
Ok(response) => {
for update in response.result {
if let UpdateContent::Message(message) = update.content {
let api_clone = api.clone();
let bot_clone = bot.clone();

tokio::spawn(async move {
process_message(message, api_clone).await;
process_message(message, bot_clone).await;
});
}
update_params.offset = Some(i64::from(update.update_id) + 1);
Expand All @@ -35,7 +36,7 @@ async fn main() {
}
}

async fn process_message(message: Message, api: AsyncApi) {
async fn process_message(message: Message, bot: Bot) {
let reply_parameters = ReplyParameters::builder()
.message_id(message.message_id)
.build();
Expand All @@ -44,7 +45,7 @@ async fn process_message(message: Message, api: AsyncApi) {
.text("hello")
.reply_parameters(reply_parameters)
.build();
if let Err(error) = api.send_message(&send_message_params).await {
if let Err(error) = bot.send_message(&send_message_params).await {
println!("Failed to send message: {error:?}");
}
}
11 changes: 6 additions & 5 deletions examples/custom_client.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::time::Duration;

use frankenstein::{Api, TelegramApi};
use frankenstein::client_ureq::Bot;
use frankenstein::TelegramApi;

static TOKEN: &str = "API_TOKEN";
static BASE_API_URL: &str = "https://api.telegram.org/bot";

fn main() {
let api = custom_client();
let bot = custom_client();

match api.get_me() {
match bot.get_me() {
Ok(response) => {
let user = response.result;
println!(
Expand All @@ -23,15 +24,15 @@ fn main() {
}
}

fn custom_client() -> Api {
fn custom_client() -> Bot {
let config = frankenstein::ureq::Agent::config_builder()
.http_status_as_error(false)
.timeout_global(Some(Duration::from_secs(100)))
.build();
let request_agent = frankenstein::ureq::Agent::new_with_config(config);
let api_url = format!("{BASE_API_URL}{TOKEN}");

Api::builder()
Bot::builder()
.api_url(api_url)
.request_agent(request_agent)
.build()
Expand Down
7 changes: 4 additions & 3 deletions examples/get_me.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use frankenstein::{Api, TelegramApi};
use frankenstein::client_ureq::Bot;
use frankenstein::TelegramApi;

static TOKEN: &str = "API_TOKEN";

fn main() {
let api = Api::new(TOKEN);
let bot = Bot::new(TOKEN);

match api.get_me() {
match bot.get_me() {
Ok(response) => {
let user = response.result;
println!(
Expand Down
7 changes: 4 additions & 3 deletions examples/inline_keyboard.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use frankenstein::api_params::{ReplyMarkup, SendMessageParams};
use frankenstein::client_ureq::Bot;
use frankenstein::objects::{InlineKeyboardButton, InlineKeyboardMarkup};
use frankenstein::{Api, TelegramApi};
use frankenstein::TelegramApi;

// replace with your token
static TOKEN: &str = "TOKEN";
// replace with your chat id
static CHAT_ID: i64 = 275_808_073;

fn main() {
let api = Api::new(TOKEN);
let bot = Bot::new(TOKEN);

let mut keyboard: Vec<Vec<InlineKeyboardButton>> = Vec::new();

Expand Down Expand Up @@ -38,5 +39,5 @@ fn main() {
.reply_markup(ReplyMarkup::InlineKeyboardMarkup(inline_keyboard))
.build();

api.send_message(&send_message_params).unwrap();
bot.send_message(&send_message_params).unwrap();
}
Loading