use std::{future::Future, sync::Arc};
use reqwest::Client;
use serde::{de::DeserializeOwned, Serialize};
use crate::{
net,
requests::{MultipartPayload, Payload, ResponseResult},
serde_multipart,
};
mod api;
mod download;
const TELOXIDE_TOKEN: &str = "TELOXIDE_TOKEN";
const TELOXIDE_API_URL: &str = "TELOXIDE_API_URL";
#[must_use]
#[derive(Debug, Clone)]
pub struct Bot {
token: Arc<str>,
api_url: Arc<reqwest::Url>,
client: Client,
}
impl Bot {
pub fn new<S>(token: S) -> Self
where
S: Into<String>,
{
let client = net::default_reqwest_settings().build().expect("Client creation failed");
Self::with_client(token, client)
}
pub fn with_client<S>(token: S, client: Client) -> Self
where
S: Into<String>,
{
let token = Into::<String>::into(token).into();
let api_url = Arc::new(
reqwest::Url::parse(net::TELEGRAM_API_URL)
.expect("Failed to parse the default TBA URL"),
);
Self { token, api_url, client }
}
pub fn from_env() -> Self {
Self::from_env_with_client(crate::net::client_from_env())
}
pub fn from_env_with_client(client: Client) -> Self {
let bot = Self::with_client(get_env(TELOXIDE_TOKEN), client);
match std::env::var(TELOXIDE_API_URL) {
Ok(env_api_url) => {
let env_api_url = if env_api_url.ends_with('/') {
env_api_url.trim_end_matches('/')
} else {
&env_api_url
};
let api_url = reqwest::Url::parse(env_api_url)
.expect("Failed to parse the `TELOXIDE_API_URL` env variable");
bot.set_api_url(api_url)
}
Err(_) => bot,
}
}
pub fn set_api_url(mut self, url: reqwest::Url) -> Self {
self.api_url = Arc::new(url);
self
}
}
impl Bot {
#[must_use]
pub fn token(&self) -> &str {
&self.token
}
#[must_use]
pub fn client(&self) -> &Client {
&self.client
}
#[must_use]
pub fn api_url(&self) -> reqwest::Url {
reqwest::Url::clone(&*self.api_url)
}
}
impl Bot {
pub(crate) fn execute_json<P>(
&self,
payload: &P,
) -> impl Future<Output = ResponseResult<P::Output>> + 'static
where
P: Payload + Serialize,
P::Output: DeserializeOwned + 'static,
{
let client = self.client.clone();
let token = Arc::clone(&self.token);
let api_url = Arc::clone(&self.api_url);
let timeout_hint = payload.timeout_hint();
let params = stacker::maybe_grow(256 * 1024, 1024 * 1024, || serde_json::to_vec(payload))
.expect("serialization of request to be infallible");
async move {
net::request_json(
&client,
token.as_ref(),
reqwest::Url::clone(&*api_url),
P::NAME,
params,
timeout_hint,
)
.await
}
}
pub(crate) fn execute_multipart<P>(
&self,
payload: &mut P,
) -> impl Future<Output = ResponseResult<P::Output>>
where
P: MultipartPayload + Serialize,
P::Output: DeserializeOwned + 'static,
{
let client = self.client.clone();
let token = Arc::clone(&self.token);
let api_url = Arc::clone(&self.api_url);
let timeout_hint = payload.timeout_hint();
let params = serde_multipart::to_form(payload);
async move {
let params = params?.await;
net::request_multipart(
&client,
token.as_ref(),
reqwest::Url::clone(&*api_url),
P::NAME,
params,
timeout_hint,
)
.await
}
}
pub(crate) fn execute_multipart_ref<P>(
&self,
payload: &P,
) -> impl Future<Output = ResponseResult<P::Output>>
where
P: MultipartPayload + Serialize,
P::Output: DeserializeOwned + 'static,
{
let client = self.client.clone();
let token = Arc::clone(&self.token);
let api_url = self.api_url.clone();
let timeout_hint = payload.timeout_hint();
let params = serde_multipart::to_form_ref(payload);
async move {
let params = params?.await;
net::request_multipart(
&client,
token.as_ref(),
reqwest::Url::clone(&*api_url),
P::NAME,
params,
timeout_hint,
)
.await
}
}
}
fn get_env(env: &'static str) -> String {
std::env::var(env).unwrap_or_else(|_| panic!("Cannot get the {env} env variable"))
}