mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Refine network/mod.rs
This commit is contained in:
parent
ce87928a90
commit
cad60fa2fb
6 changed files with 123 additions and 85 deletions
|
@ -1,59 +1,63 @@
|
|||
use reqwest::StatusCode;
|
||||
use reqwest::r#async::Client;
|
||||
use serde_json::Value;
|
||||
use futures::compat::Future01CompatExt;
|
||||
use apply::Apply;
|
||||
use serde::de::DeserializeOwned;
|
||||
use super::requests::Request;
|
||||
use apply::Apply;
|
||||
use futures::compat::Future01CompatExt;
|
||||
use reqwest::r#async::Client;
|
||||
use reqwest::StatusCode;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::Value;
|
||||
|
||||
const TELEGRAM_URL_START: &str = "https://api.telegram.org/bot";
|
||||
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Api {
|
||||
pub enum RequestError {
|
||||
ApiError {
|
||||
status_code: StatusCode,
|
||||
description: Option<String>,
|
||||
description: String,
|
||||
},
|
||||
Send(reqwest::Error),
|
||||
InvalidJson(reqwest::Error),
|
||||
NetworkError(reqwest::Error),
|
||||
InvalidJson(serde_json::Error),
|
||||
}
|
||||
|
||||
pub type Response<T> = Result<T, Error>;
|
||||
pub type ResponseResult<T> = Result<T, RequestError>;
|
||||
|
||||
pub async fn request<R: DeserializeOwned, Req: Request<R>>(
|
||||
pub async fn request<T: DeserializeOwned, R: Request<T>>(
|
||||
client: &Client,
|
||||
request: Req,
|
||||
) -> Response<T> {
|
||||
request: R,
|
||||
) -> ResponseResult<T> {
|
||||
let mut response = client
|
||||
.post(&format!(
|
||||
"{}{token}/{method}",
|
||||
TELEGRAM_URL_START,
|
||||
"{url}/bot{token}/{method}",
|
||||
url = TELEGRAM_API_URL,
|
||||
token = request.token(),
|
||||
method = request.name(),
|
||||
))
|
||||
.apply(|req| if let Some(params) = request.params() {
|
||||
req.multipart(params)
|
||||
} else { req })
|
||||
.apply(|request_builder| {
|
||||
if let Some(params) = request.params() {
|
||||
request_builder.multipart(params)
|
||||
} else {
|
||||
request_builder
|
||||
}
|
||||
})
|
||||
.send()
|
||||
.compat()
|
||||
.await
|
||||
.map_err(Error::Send)?;
|
||||
.map_err(RequestError::NetworkError)?;
|
||||
|
||||
let response_json = response
|
||||
.json::<Value>()
|
||||
.compat()
|
||||
.await
|
||||
.map_err(Error::InvalidJson)?;
|
||||
let response_json = serde_json::from_str::<Value>(
|
||||
&response
|
||||
.text()
|
||||
.compat()
|
||||
.await
|
||||
.map_err(RequestError::NetworkError)?,
|
||||
)
|
||||
.map_err(RequestError::InvalidJson)?;
|
||||
|
||||
if response_json["ok"] == "false" {
|
||||
return Err(Error::Api {
|
||||
Err(RequestError::ApiError {
|
||||
status_code: response.status(),
|
||||
description: match response_json.get("description") {
|
||||
None => None,
|
||||
Some(description) => Some(description.to_string()),
|
||||
},
|
||||
});
|
||||
description: response_json["description"].to_string(),
|
||||
})
|
||||
} else {
|
||||
Ok(serde_json::from_value(response_json["result"].clone()).unwrap())
|
||||
}
|
||||
|
||||
Ok(serde_json::from_value(response_json["result"].clone()).unwrap())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::core::payments::{Invoice, SuccessfulPayment};
|
||||
use serde::Deserialize;
|
||||
use crate::core::payments::{SuccessfulPayment, Invoice};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct User {
|
||||
|
@ -16,7 +16,7 @@ pub struct Chat {
|
|||
id: i64,
|
||||
chat_type: String,
|
||||
title: Option<String>,
|
||||
username:Option<String>,
|
||||
username: Option<String>,
|
||||
first_name: Option<String>,
|
||||
last_name: Option<String>,
|
||||
photo: Option<ChatPhoto>,
|
||||
|
@ -55,7 +55,7 @@ pub struct Message {
|
|||
sticker: Option<Stickers>,
|
||||
video: Option<Video>,
|
||||
voice: Option<Voice>,
|
||||
video_note: Option< VideoNote>,
|
||||
video_note: Option<VideoNote>,
|
||||
caption: Option<String>,
|
||||
contact: Option<Contact>,
|
||||
location: Option<Location>,
|
||||
|
@ -104,4 +104,4 @@ pub struct Document {
|
|||
file_name: Option<String>,
|
||||
mime_type: Option<String>,
|
||||
file_size: Option<i64>,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
use serde::Deserialize;
|
||||
use crate::core::other::User;
|
||||
|
||||
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SendInvoice {
|
||||
|
@ -40,7 +37,7 @@ pub struct AnswerShippingQuery {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct AnswerPreCheckoutQuery {
|
||||
pub struct AnswerPreCheckoutQuery {
|
||||
pre_checkout_query_id: String,
|
||||
ok: bool,
|
||||
error_message: Option<String>,
|
||||
|
@ -76,7 +73,7 @@ pub struct OrderInfo {
|
|||
name: String,
|
||||
phone_number: String,
|
||||
email: String,
|
||||
shipping_address: ShippingAddress
|
||||
shipping_address: ShippingAddress,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -113,5 +110,5 @@ pub struct PreCheckoutQuery {
|
|||
total_amount: i64,
|
||||
invoice_payload: String,
|
||||
shipping_option_id: Option<String>,
|
||||
order_info: Option<OrderInfo>
|
||||
}
|
||||
order_info: Option<OrderInfo>,
|
||||
}
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
use crate::core::other::User;
|
||||
use super::Request;
|
||||
use crate::core::other::User;
|
||||
|
||||
use reqwest::r#async::multipart::Form;
|
||||
|
||||
|
||||
#[derive(Debug, Constructor, PartialEq, Eq)]
|
||||
pub struct GetMe {
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl Request<User> for GetMe {
|
||||
fn name(&self) -> &str { "getMe" }
|
||||
fn params(self) -> Option<Form> { None }
|
||||
fn token(&self) -> &str { &self.token }
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
"getMe"
|
||||
}
|
||||
fn params(self) -> Option<Form> {
|
||||
None
|
||||
}
|
||||
fn token(&self) -> &str {
|
||||
&self.token
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use reqwest::r#async::multipart::Form;
|
||||
|
||||
|
||||
/// Request that can be sended to telegram.
|
||||
/// `R` - return type.
|
||||
pub trait Request<R: serde::de::DeserializeOwned> {
|
||||
|
@ -24,4 +23,4 @@ pub enum ChatId {
|
|||
}
|
||||
|
||||
pub mod get_me;
|
||||
pub mod send_message;
|
||||
pub mod send_message;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use crate::core::other::Message;
|
||||
use super::{ChatId, Request};
|
||||
use crate::core::other::Message;
|
||||
|
||||
use reqwest::r#async::multipart::Form;
|
||||
|
||||
|
||||
#[derive(Debug, TypedBuilder, PartialEq, Eq)]
|
||||
pub struct SendMessage {
|
||||
token: String,
|
||||
|
@ -23,39 +22,69 @@ pub struct SendMessage {
|
|||
}
|
||||
|
||||
impl Request<Message> for SendMessage {
|
||||
fn name(&self) -> &str { "getMe" }
|
||||
fn name(&self) -> &str {
|
||||
"getMe"
|
||||
}
|
||||
fn params(self) -> Option<Form> {
|
||||
use apply::Apply;
|
||||
use apply::Apply;
|
||||
|
||||
// TODO: we need better serialization
|
||||
let params = Form::new()
|
||||
.text("chat_id", format!("{:?}", self.chat_id))
|
||||
.text("text", self.text)
|
||||
.apply(|f| if let Some(parse_mode) = self.parse_mode {
|
||||
f.text("parse_mode", parse_mode);
|
||||
f
|
||||
} else { f })
|
||||
.apply(|f| if let Some(disable_web_page_preview) = self.disable_web_page_preview {
|
||||
f.text("disable_web_page_preview", format!("{:?}", disable_web_page_preview));
|
||||
f
|
||||
} else { f })
|
||||
.apply(|f| if let Some(disable_notification) = self.disable_notification {
|
||||
f.text("disable_notification", format!("{:?}", disable_notification));
|
||||
f
|
||||
} else { f })
|
||||
.apply(|f| if let Some(reply_to_message_id) = self.reply_to_message_id {
|
||||
f.text("reply_to_message_id", format!("{:?}", reply_to_message_id));
|
||||
f
|
||||
} else { f })
|
||||
.apply(|f| if let Some(reply_markup) = self.reply_markup {
|
||||
unimplemented!();
|
||||
//f.text("reply_markup", );
|
||||
f
|
||||
} else { f });
|
||||
.apply(|f| {
|
||||
if let Some(parse_mode) = self.parse_mode {
|
||||
f.text("parse_mode", parse_mode);
|
||||
f
|
||||
} else {
|
||||
f
|
||||
}
|
||||
})
|
||||
.apply(|f| {
|
||||
if let Some(disable_web_page_preview) = self.disable_web_page_preview {
|
||||
f.text(
|
||||
"disable_web_page_preview",
|
||||
format!("{:?}", disable_web_page_preview),
|
||||
);
|
||||
f
|
||||
} else {
|
||||
f
|
||||
}
|
||||
})
|
||||
.apply(|f| {
|
||||
if let Some(disable_notification) = self.disable_notification {
|
||||
f.text(
|
||||
"disable_notification",
|
||||
format!("{:?}", disable_notification),
|
||||
);
|
||||
f
|
||||
} else {
|
||||
f
|
||||
}
|
||||
})
|
||||
.apply(|f| {
|
||||
if let Some(reply_to_message_id) = self.reply_to_message_id {
|
||||
f.text("reply_to_message_id", format!("{:?}", reply_to_message_id));
|
||||
f
|
||||
} else {
|
||||
f
|
||||
}
|
||||
})
|
||||
.apply(|f| {
|
||||
if let Some(reply_markup) = self.reply_markup {
|
||||
unimplemented!();
|
||||
//f.text("reply_markup", );
|
||||
f
|
||||
} else {
|
||||
f
|
||||
}
|
||||
});
|
||||
|
||||
Some(params)
|
||||
}
|
||||
fn token(&self) -> &str { &self.token }
|
||||
fn token(&self) -> &str {
|
||||
&self.token
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -64,7 +93,11 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn default() {
|
||||
let sm = SendMessage::builder().token("TOKEN").chat_id(123456.into()).text("text").build();
|
||||
let sm = SendMessage::builder()
|
||||
.token("TOKEN")
|
||||
.chat_id(123456.into())
|
||||
.text("text")
|
||||
.build();
|
||||
let r = SendMessage {
|
||||
token: String::from("TOKEN"),
|
||||
chat_id: ChatId::Id(123456),
|
||||
|
@ -73,9 +106,9 @@ mod test {
|
|||
disable_web_page_preview: None,
|
||||
disable_notification: None,
|
||||
reply_to_message_id: None,
|
||||
reply_markup: None
|
||||
reply_markup: None,
|
||||
};
|
||||
|
||||
assert_eq!(sm, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue