mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Properly parse telegram answer
This commit is contained in:
parent
c3052ed036
commit
dc755b6ec0
8 changed files with 82 additions and 46 deletions
|
@ -1,10 +1,19 @@
|
||||||
use apply::Apply;
|
use crate::core::{
|
||||||
use reqwest::r#async::{multipart::Form, Client};
|
requests::{RequestError, ResponseResult},
|
||||||
use serde::de::DeserializeOwned;
|
types::ResponseParameters,
|
||||||
use serde_json::Value;
|
};
|
||||||
|
|
||||||
|
use apply::Apply;
|
||||||
|
use serde_json::Value;
|
||||||
|
use serde::{
|
||||||
|
Serialize,
|
||||||
|
de::DeserializeOwned
|
||||||
|
};
|
||||||
|
use reqwest::{
|
||||||
|
StatusCode,
|
||||||
|
r#async::{Client, multipart::Form},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::core::requests::{RequestError, ResponseResult};
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
||||||
|
|
||||||
|
@ -48,21 +57,22 @@ pub async fn request_multipart<T: DeserializeOwned>(
|
||||||
.await
|
.await
|
||||||
.map_err(RequestError::NetworkError)?;
|
.map_err(RequestError::NetworkError)?;
|
||||||
|
|
||||||
let response_json = serde_json::from_str::<Value>(
|
let response = serde_json::from_str::<TelegramResponse<T>>(
|
||||||
&response
|
&response
|
||||||
.text()
|
.text()
|
||||||
.await
|
.await
|
||||||
.map_err(RequestError::NetworkError)?,
|
.map_err(RequestError::NetworkError)?,
|
||||||
)
|
)
|
||||||
.map_err(RequestError::InvalidJson)?;
|
.map_err(RequestError::InvalidJson)?;
|
||||||
|
|
||||||
if response_json["ok"] == "false" {
|
match response {
|
||||||
Err(RequestError::ApiError {
|
TelegramResponse::Ok { result, .. } => Ok(result),
|
||||||
status_code: response.status(),
|
TelegramResponse::Err {
|
||||||
description: response_json["description"].to_string(),
|
description,
|
||||||
})
|
error_code,
|
||||||
} else {
|
response_parameters,
|
||||||
Ok(serde_json::from_value(response_json["result"].clone()).unwrap())
|
..
|
||||||
|
} => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,24 +89,40 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
||||||
.await
|
.await
|
||||||
.map_err(RequestError::NetworkError)?;
|
.map_err(RequestError::NetworkError)?;
|
||||||
|
|
||||||
let response_json = serde_json::from_str::<Value>(
|
let response = serde_json::from_str::<TelegramResponse<T>>(
|
||||||
&response
|
&response
|
||||||
.text()
|
.text()
|
||||||
.await
|
.await
|
||||||
.map_err(RequestError::NetworkError)?,
|
.map_err(RequestError::NetworkError)?,
|
||||||
)
|
)
|
||||||
.map_err(RequestError::InvalidJson)?;
|
.map_err(RequestError::InvalidJson)?;
|
||||||
|
|
||||||
if response_json["ok"] == "false" {
|
match response {
|
||||||
Err(RequestError::ApiError {
|
TelegramResponse::Ok { result, .. } => Ok(result),
|
||||||
status_code: response.status(),
|
TelegramResponse::Err {
|
||||||
description: response_json["description"].to_string(),
|
description,
|
||||||
})
|
error_code,
|
||||||
} else {
|
response_parameters,
|
||||||
Ok(serde_json::from_value(response_json["result"].clone()).unwrap())
|
..
|
||||||
|
} => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum TelegramResponse<R> {
|
||||||
|
Ok {
|
||||||
|
ok: bool, // true
|
||||||
|
result: R,
|
||||||
|
},
|
||||||
|
Err {
|
||||||
|
ok: bool, // false
|
||||||
|
description: String,
|
||||||
|
error_code: u16,
|
||||||
|
response_parameters: Option<ResponseParameters>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -37,14 +37,14 @@ impl<'a> Request<'a> for ForwardMessage<'a> {
|
||||||
type ReturnValue = Message;
|
type ReturnValue = Message;
|
||||||
|
|
||||||
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
Box::pin(
|
Box::pin(async move {
|
||||||
network::request_json(
|
network::request_json(
|
||||||
self.ctx.client,
|
self.ctx.client,
|
||||||
self.ctx.token,
|
self.ctx.token,
|
||||||
"forwardMessage",
|
"forwardMessage",
|
||||||
&self,
|
&self,
|
||||||
)
|
).await
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ mod utils;
|
||||||
#[derive(Debug, Display)]
|
#[derive(Debug, Display)]
|
||||||
pub enum RequestError {
|
pub enum RequestError {
|
||||||
#[display(fmt = "Telegram error #{}: {}", status_code, description)]
|
#[display(fmt = "Telegram error #{}: {}", status_code, description)]
|
||||||
ApiError {
|
ApiError { // TODO: add response parameters
|
||||||
status_code: StatusCode,
|
status_code: StatusCode,
|
||||||
description: String,
|
description: String,
|
||||||
},
|
},
|
||||||
|
|
|
@ -52,25 +52,25 @@ impl<'a> Request<'a> for SendMessage<'a> {
|
||||||
type ReturnValue = Message;
|
type ReturnValue = Message;
|
||||||
|
|
||||||
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
Box::pin(
|
Box::pin(async move {
|
||||||
network::request_json(
|
network::request_json(
|
||||||
self.ctx.client,
|
self.ctx.client,
|
||||||
self.ctx.token,
|
self.ctx.token,
|
||||||
"sendMessage",
|
"sendMessage",
|
||||||
&self,
|
&self,
|
||||||
)
|
).await
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SendMessage<'a> {
|
impl<'a> SendMessage<'a> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
info: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
text: String,
|
text: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
SendMessage {
|
SendMessage {
|
||||||
ctx: info,
|
ctx,
|
||||||
chat_id,
|
chat_id,
|
||||||
text,
|
text,
|
||||||
parse_mode: None,
|
parse_mode: None,
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::core::types::{ChatPermissions, ChatPhoto, Message};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
|
||||||
pub struct Chat {
|
pub struct Chat {
|
||||||
#[serde(rename = "chat_id")]
|
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub kind: ChatKind,
|
pub kind: ChatKind,
|
||||||
|
@ -103,7 +102,7 @@ mod tests {
|
||||||
photo: None,
|
photo: None,
|
||||||
};
|
};
|
||||||
let actual = from_str(
|
let actual = from_str(
|
||||||
r#"{"chat_id":-1,"type":"channel","username":"channelname"}"#,
|
r#"{"id":-1,"type":"channel","username":"channelname"}"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
|
@ -123,12 +122,12 @@ mod tests {
|
||||||
photo: None
|
photo: None
|
||||||
},
|
},
|
||||||
from_str(
|
from_str(
|
||||||
r#"{"chat_id":0,"type":"private","username":"username","first_name":"Anon"}"#
|
r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"#
|
||||||
).unwrap());
|
).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn private_chat_de_wrong_type_field() {
|
fn private_chat_de_wrong_type_field() {
|
||||||
assert!(from_str::<Chat>(r#"{"chat_id":0,"type":"WRONG"}"#).is_err());
|
assert!(from_str::<Chat>(r#"{"id":0,"type":"WRONG"}"#).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,7 @@ mod tests {
|
||||||
reply_markup: None,
|
reply_markup: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let actual = from_str::<Message>(r#"{"message_id":0,"date":0,"chat":{"chat_id":0,"type":"private"},"text":"Hello","entities":[]}"#).unwrap();
|
let actual = from_str::<Message>(r#"{"message_id":0,"date":0,"chat":{"id":0,"type":"private"},"text":"Hello","entities":[]}"#).unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,9 +230,17 @@ mod tests {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let actual = from_str::<Message>(
|
let actual = from_str::<Message>(
|
||||||
r#"{"message_id":1,"date":1,"chat":{"chat_id":1,"type":"private"},"forward_date":1,"forward_from":{"id":123,"is_bot":false,"first_name":"Name"},"text":"Message","entities":[]}"#,
|
r#"{"message_id":1,"date":1,"chat":{"id":1,"type":"private"},"forward_date":1,"forward_from":{"id":123,"is_bot":false,"first_name":"Name"},"text":"Message","entities":[]}"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sent_message_de() {
|
||||||
|
// actual message from telegram
|
||||||
|
let json = "{\"message_id\":6534,\"from\":{\"id\":457569668,\"is_bot\":true,\"first_name\":\"\\u0424\\u044b\\u0440\\u044c\\u043a\",\"username\":\"BloodyTestBot\"},\"chat\":{\"id\":218485655,\"first_name\":\"\\u0412\\u0430\\u0444\\u0435\\u043b\\u044c\",\"username\":\"WaffleLapkin\",\"type\":\"private\"},\"date\":1567898953,\"text\":\"text\"}";
|
||||||
|
let actual: Result<Message, _>= from_str(json);
|
||||||
|
assert!(actual.is_ok());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub use self::{
|
||||||
successful_payment::SuccessfulPayment,
|
successful_payment::SuccessfulPayment,
|
||||||
user::User,
|
user::User,
|
||||||
video::Video,
|
video::Video,
|
||||||
|
response_parameters::ResponseParameters
|
||||||
};
|
};
|
||||||
|
|
||||||
mod answer_pre_checkout_query;
|
mod answer_pre_checkout_query;
|
||||||
|
@ -55,3 +56,4 @@ mod sticker;
|
||||||
mod successful_payment;
|
mod successful_payment;
|
||||||
mod user;
|
mod user;
|
||||||
mod video;
|
mod video;
|
||||||
|
mod response_parameters;
|
|
@ -1,5 +1,4 @@
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
||||||
#[serde(tag = "type")]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ResponseParameters {
|
pub enum ResponseParameters {
|
||||||
MigrateToChatId(i64),
|
MigrateToChatId(i64),
|
||||||
|
@ -8,23 +7,25 @@ pub enum ResponseParameters {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn migrate_to_chat_id_deserialization() {
|
fn migrate_to_chat_id_deserialization() {
|
||||||
let expected_struct = ResponseParameters::MigrateToChatId(123456);
|
let expected = ResponseParameters::MigrateToChatId(123456);
|
||||||
let actual_json: ResponseParameters = serde_json::from_str(
|
let actual: ResponseParameters = serde_json::from_str(
|
||||||
r#"{"migrate_to_chat_id":123456}"#
|
r#"{"migrate_to_chat_id":123456}"#
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
assert_eq!(expected_json, actual_json);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn retry_after_deserialization() {
|
fn retry_after_deserialization() {
|
||||||
let expected_struct = ResponseParameters::RetryAfter(123456);
|
let expected = ResponseParameters::RetryAfter(123456);
|
||||||
let actual_json: ResponseParameters = serde_json::from_str(
|
let actual: ResponseParameters = serde_json::from_str(
|
||||||
r#"{"retry_after":123456}"#
|
r#"{"retry_after":123456}"#
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
assert_eq!(expected_json, actual_json);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue