use std::{error::Error, sync::Arc};
use crate::gen_types::ResponseParameters;
use anyhow::Result;
use reqwest::multipart::Form;
use serde::{Deserialize, Serialize};
pub use reqwest::multipart::Part;
static TELEGRAM_API: &str = "https://api.telegram.org";
#[derive(Serialize, Deserialize, Debug)]
pub struct Response {
pub ok: bool,
pub result: Option<serde_json::Value>,
pub error_code: Option<i64>,
pub description: Option<String>,
pub parameters: Option<ResponseParameters>,
}
pub type BotResult<T> = Result<T, ApiError>;
struct BotState {
client: reqwest::Client,
token: String,
}
#[derive(Debug)]
enum ErrResponse {
Response(Response),
Err(anyhow::Error),
}
#[derive(Debug)]
pub struct ApiError(ErrResponse);
impl ApiError {
pub(crate) fn from_response(resp: Response) -> Self {
Self(ErrResponse::Response(resp))
}
pub fn get_response<'a>(&'a self) -> Option<&'a Response> {
if let ErrResponse::Response(ref response) = self.0 {
Some(response)
} else {
None
}
}
}
impl From<anyhow::Error> for ApiError {
fn from(value: anyhow::Error) -> Self {
Self(ErrResponse::Err(value))
}
}
impl From<reqwest::Error> for ApiError {
fn from(value: reqwest::Error) -> Self {
Self(ErrResponse::Err(anyhow::anyhow!(value)))
}
}
impl From<serde_json::Error> for ApiError {
fn from(value: serde_json::Error) -> Self {
Self(ErrResponse::Err(anyhow::anyhow!(value)))
}
}
impl From<serde_path_to_error::Error<serde_json::Error>> for ApiError {
fn from(value: serde_path_to_error::Error<serde_json::Error>) -> Self {
Self(ErrResponse::Err(anyhow::anyhow!(value)))
}
}
impl Error for ApiError {}
impl std::fmt::Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
ErrResponse::Response(Response {
description: Some(ref d),
..
}) => f.write_str(&d)?,
ErrResponse::Response(Response { error_code, .. }) => {
f.write_str(&error_code.unwrap_or(-1).to_string())?
}
ErrResponse::Err(ref err) => f.write_str(&err.to_string())?,
};
Ok(())
}
}
pub struct Bot(Arc<BotState>);
impl Default for Response {
fn default() -> Self {
Response {
ok: true,
result: None,
error_code: None,
description: None,
parameters: None,
}
}
}
impl Bot {
pub fn new<T>(token: T) -> Result<Self>
where
T: Into<String>,
{
let client = reqwest::ClientBuilder::new().https_only(true).build()?;
Ok(Self(Arc::new(BotState {
client,
token: token.into(),
})))
}
fn get_endpoint(&self, endpoint: &str) -> String {
format!("{}/bot{}/{}", TELEGRAM_API, self.0.token, endpoint)
}
pub async fn post<T>(&self, endpoint: &str, body: T) -> BotResult<Response>
where
T: Serialize,
{
let endpoint = self.get_endpoint(endpoint);
let resp = self
.0
.client
.post(endpoint)
.query(&body)
.send()
.await
.map_err(|e| e.without_url())?;
let bytes = resp.bytes().await?;
let mut deser = serde_json::Deserializer::from_slice(&bytes);
let resp: Response = serde_path_to_error::deserialize(&mut deser)?;
Ok(resp)
}
pub async fn post_empty(&self, endpoint: &str) -> BotResult<Response> {
let endpoint = self.get_endpoint(endpoint);
let resp = self
.0
.client
.post(endpoint)
.send()
.await
.map_err(|e| e.without_url())?;
let bytes = resp.bytes().await?;
let mut deser = serde_json::Deserializer::from_slice(&bytes);
let resp: Response = serde_path_to_error::deserialize(&mut deser)?;
Ok(resp)
}
pub async fn post_data<T>(&self, endpoint: &str, body: T, data: Form) -> BotResult<Response>
where
T: Serialize,
{
let endpoint = self.get_endpoint(endpoint);
let resp = self
.0
.client
.post(endpoint)
.query(&body)
.multipart(data)
.send()
.await
.map_err(|e| e.without_url())?;
let bytes = resp.bytes().await?;
let mut deser = serde_json::Deserializer::from_slice(&bytes);
let resp: Response = serde_path_to_error::deserialize(&mut deser)?;
Ok(resp)
}
}
impl Clone for Bot {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}