Merge branch 'dev' into file_download

This commit is contained in:
Waffle Lapkin 2019-09-20 23:36:49 +03:00 committed by GitHub
commit d5adf4701b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 244 additions and 55 deletions

View file

@ -9,7 +9,7 @@ use bytes::Buf;
use futures::StreamExt; use futures::StreamExt;
use reqwest::r#async::Chunk; use reqwest::r#async::Chunk;
use reqwest::{ use reqwest::{
r#async::{multipart::Form, Client}, r#async::{multipart::Form, Client, Response},
StatusCode, StatusCode,
}; };
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
@ -49,22 +49,18 @@ pub async fn request_multipart<T: DeserializeOwned>(
method_name: &str, method_name: &str,
params: Option<Form>, params: Option<Form>,
) -> ResponseResult<T> { ) -> ResponseResult<T> {
let mut response = client process_response(
.post(&method_url(TELEGRAM_API_URL, token, method_name)) client
.apply(|request_builder| match params { .post(&method_url(TELEGRAM_API_URL, token, method_name))
Some(params) => request_builder.multipart(params), .apply(|request_builder| match params {
None => request_builder, Some(params) => request_builder.multipart(params),
}) None => request_builder,
.send() })
.await .send()
.map_err(RequestError::NetworkError)?; .await
.map_err(RequestError::NetworkError)?,
let response = serde_json::from_str::<TelegramResponse<T>>(
&response.text().await.map_err(RequestError::NetworkError)?,
) )
.map_err(RequestError::InvalidJson)?; .await
response.into()
} }
pub async fn request_json<T: DeserializeOwned, P: Serialize>( pub async fn request_json<T: DeserializeOwned, P: Serialize>(
@ -73,13 +69,20 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
method_name: &str, method_name: &str,
params: &P, params: &P,
) -> ResponseResult<T> { ) -> ResponseResult<T> {
let mut response = client process_response(
.post(&method_url(TELEGRAM_API_URL, token, method_name)) client
.json(params) .post(&method_url(TELEGRAM_API_URL, token, method_name))
.send() .json(params)
.await .send()
.map_err(RequestError::NetworkError)?; .await
.map_err(RequestError::NetworkError)?,
)
.await
}
async fn process_response<T: DeserializeOwned>(
mut response: Response,
) -> ResponseResult<T> {
let response = serde_json::from_str::<TelegramResponse<T>>( let response = serde_json::from_str::<TelegramResponse<T>>(
&response.text().await.map_err(RequestError::NetworkError)?, &response.text().await.map_err(RequestError::NetworkError)?,
) )
@ -92,14 +95,14 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
#[serde(untagged)] #[serde(untagged)]
enum TelegramResponse<R> { enum TelegramResponse<R> {
Ok { Ok {
/// Dummy field. Used for deserialization. /// A dummy field. Used only for deserialization.
#[allow(dead_code)] #[allow(dead_code)]
ok: bool, // TODO: True type ok: bool, // TODO: True type
result: R, result: R,
}, },
Err { Err {
/// Dummy field. Used for deserialization. /// A dummy field. Used only for deserialization.
#[allow(dead_code)] #[allow(dead_code)]
ok: bool, // TODO: False type ok: bool, // TODO: False type

View file

@ -1,8 +1,19 @@
use crate::core::requests::RequestContext; use crate::core::requests::RequestContext;
//TODO:: need implementation
//TODO: complete implementation after user_profile_fotos will be added to
// types/mod.rs
///Use this method to get a list of profile pictures for a user. Returns a
/// UserProfilePhotos object.
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
struct GetUserProfilePhotos<'a> { struct GetUserProfilePhotos<'a> {
#[serde(skip_serializing)] #[serde(skip_serializing)]
ctx: RequestContext<'a>, ctx: RequestContext<'a>,
/// Unique identifier of the target user
user_id: i32,
/// Sequential number of the first photo to be returned. By default, all
/// photos are returned.
offset: Option<i64>,
///Limits the number of photos to be retrieved. Values between 1—100 are
/// accepted. Defaults to 100.
limit: Option<i64>,
} }

View file

@ -1,6 +1,10 @@
use crate::core::requests::RequestContext; use crate::core::requests::RequestContext;
//TODO:: need implementation //TODO:: need implementation
/// Use this method to kick a user from a group, a supergroup or a channel. In
/// the case of supergroups and channels, the user will not be able to return to
/// the group on their own using invite links, etc., unless unbanned first. The
/// bot must be an administrator in the chat for this to work and must have the
/// appropriate admin rights. Returns True on success.
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
struct KickChatMember<'a> { struct KickChatMember<'a> {
#[serde(skip_serializing)] #[serde(skip_serializing)]

View file

@ -1,17 +1,51 @@
use crate::core::types::PhotoSize; use crate::core::types::PhotoSize;
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)] #[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Audio { pub struct Audio {
pub file_id: String, pub file_id: String,
pub duration: u32, pub duration: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub performer: Option<String>, pub performer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>, pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>, pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_size: Option<u32>, pub file_size: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumb: Option<PhotoSize>, pub thumb: Option<PhotoSize>,
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize() {
let json = r#"{
"file_id":"id",
"duration":60,
"performer":"Performer",
"title":"Title",
"mime_type":"MimeType",
"file_size":123456,
"thumb":{
"file_id":"id",
"width":320,
"height":320,
"file_size":3452
}
}"#;
let expected = Audio {
file_id: "id".to_string(),
duration: 60,
performer: Some("Performer".to_string()),
title: Some("Title".to_string()),
mime_type: Some("MimeType".to_string()),
file_size: Some(123456),
thumb: Some(PhotoSize {
file_id: "id".to_string(),
width: 320,
height: 320,
file_size: Some(3452)
})
};
let actual = serde_json::from_str::<Audio>(&json).unwrap();
assert_eq!(actual, expected)
}
}

View file

@ -1,22 +1,51 @@
use crate::core::types::{Message, User}; use crate::core::types::{Message, User};
/// This object represents an incoming callback query from a callback button in
/// an inline keyboard.
#[derive(Debug, Deserialize, PartialEq, Clone)] #[derive(Debug, Deserialize, PartialEq, Clone)]
pub struct CallbackQuery { pub struct CallbackQuery {
/// Unique identifier for this query
pub id: String, pub id: String,
/// Sender
pub from: User, pub from: User,
/// Message with the callback button that originated the query.
/// Note that message content and message date will not be available if the
/// message is too old
pub message: Message,
/// Global identifier, uniquely corresponding to the chat to which the
/// message with the callback button was sent. Useful for high scores
/// in games.
pub chat_instance: String, pub chat_instance: String,
/// Data associated with the callback button. Be aware that a bad client pub message: Option<Message>,
/// can send arbitrary data in this field. pub inline_message_id: Option<String>,
pub data: String, pub data: Option<String>,
pub game_short_name: Option<String>
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize() {
let json = r#"{
"id":"id",
"from":{
"id":12345,
"is_bot":false,
"first_name":"firstName"
},
"inline_message_id":"i_m_id",
"chat_instance":"123456",
"data":"some_data",
"game_short_name":"game_name"
}"#;
let expected = CallbackQuery {
id: "id".to_string(),
from: User {
id: 12345,
is_bot: false,
first_name: "firstName".to_string(),
last_name: None,
username: None,
language_code: None
},
chat_instance: "123456".to_string(),
message: None,
inline_message_id: Some("i_m_id".to_string()),
data: Some("some_data".to_string()),
game_short_name: Some("game_name".to_string())
};
let actual = serde_json::from_str::<CallbackQuery>(json).unwrap();
assert_eq!(actual, expected);
}
} }

View file

@ -1,4 +1,4 @@
use crate::core::types::{ChatMemberStatus, User}; use crate::core::types::User;
/// This object contains information about one member of the chat. /// This object contains information about one member of the chat.
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)] #[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
@ -54,3 +54,74 @@ pub struct ChatMember {
/// his messages, implies can_send_media_messages /// his messages, implies can_send_media_messages
pub can_add_web_page_previews: Option<bool>, pub can_add_web_page_previews: Option<bool>,
} }
#[derive(Deserialize, Debug, Hash, PartialEq, Eq, Clone)]
#[serde(rename_all = "snake_case")]
pub enum ChatMemberStatus {
Creator,
Administrator,
Member,
Restricted,
Left,
Kicked
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize() {
let json = r#"{
"user":{
"id":12345,
"is_bot":false,
"first_name":"firstName"
},
"status":"creator",
"until_date":123456,
"can_be_edited":true,
"can_post_messages":true,
"can_edit_messages":true,
"can_delete_messages":true,
"can_restrict_members":true,
"can_promote_members":true,
"can_change_info":true,
"can_invite_users":true,
"can_pin_messages":true,
"is_member":true,
"can_send_messages":true,
"can_send_media_messages":true,
"can_send_polls":true,
"can_send_other_messages":true,
"can_add_web_page_previews":true
}"#;
let expected = ChatMember {
user: User {
id: 12345,
is_bot: false,
first_name: "firstName".to_string(),
last_name: None,
username: None,
language_code: None
},
status: ChatMemberStatus::Creator,
until_date: Some(123456),
can_be_edited: Some(true),
can_change_info: Some(true),
can_post_messages: Some(true),
can_edit_messages: Some(true),
can_delete_messages: Some(true),
can_invite_users: Some(true),
can_restrict_members: Some(true),
can_pin_messages: Some(true),
can_promote_members: Some(true),
can_send_messages: Some(true),
can_send_media_messages: Some(true),
can_send_other_messages: Some(true),
can_add_web_page_previews: Some(true)
};
let actual = serde_json::from_str::<ChatMember>(&json).unwrap();
assert_eq!(actual, expected)
}
}

View file

@ -11,13 +11,26 @@ pub struct Message {
pub date: i32, pub date: i32,
pub chat: Chat, pub chat: Chat,
#[serde(flatten)] #[serde(flatten)]
pub message_kind: MessageKind, pub kind: MessageKind,
}
impl Message {
fn text(&self) -> Option<&str> {
if let MessageKind::Common {
media_kind: MediaKind::Text {
ref text, ..
}, .. } = self.kind {
Some(text)
} else {
None
}
}
} }
#[derive(Debug, Deserialize, PartialEq, Clone)] #[derive(Debug, Deserialize, PartialEq, Clone)]
#[serde(untagged)] #[serde(untagged)]
pub enum MessageKind { pub enum MessageKind {
IncomingMessage { Common {
#[serde(flatten)] #[serde(flatten)]
from: Sender, from: Sender,
#[serde(flatten)] #[serde(flatten)]
@ -55,8 +68,8 @@ pub enum MessageKind {
migrate_to_chat_id: i64, migrate_to_chat_id: i64,
migrate_from_chat_id: i64, migrate_from_chat_id: i64,
}, },
PinnedMessage { Pinned {
pinned_message: Box<Message>, pinned: Box<Message>,
}, },
Invoice { Invoice {
invoice: Invoice, invoice: Invoice,

View file

@ -4,7 +4,7 @@ pub use self::{
audio::Audio, audio::Audio,
callback_query::CallbackQuery, callback_query::CallbackQuery,
chat::{Chat, ChatKind, NonPrivateChatKind}, chat::{Chat, ChatKind, NonPrivateChatKind},
chat_member::ChatMember, chat_member::{ChatMember, ChatMemberStatus},
chat_permissions::ChatPermissions, chat_permissions::ChatPermissions,
chat_photo::ChatPhoto, chat_photo::ChatPhoto,
chosen_inline_result::ChosenInlineResult, chosen_inline_result::ChosenInlineResult,

View file

@ -1,5 +1,2 @@
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)] #[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
pub struct PassportData; pub struct PassportData;
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
pub struct ChatMemberStatus;

View file

@ -7,3 +7,30 @@ pub struct User {
pub username: Option<String>, pub username: Option<String>,
pub language_code: Option<String>, pub language_code: Option<String>,
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize() {
let json = r#"{
"id":12345,
"is_bot":false,
"first_name":"firstName",
"last_name":"lastName",
"username":"Username",
"language_code":"languageCode"
}"#;
let expected = User {
id: 12345,
is_bot: false,
first_name: "firstName".to_string(),
last_name: Some("lastName".to_string()),
username: Some("Username".to_string()),
language_code: Some("languageCode".to_string())
};
let actual = serde_json::from_str::<User>(&json).unwrap();
assert_eq!(actual, expected)
}
}