mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Merge branch 'dev' of github.com:async-telegram-bot/async-telegram-bot into dev
This commit is contained in:
commit
17d8ef43a0
42 changed files with 606 additions and 491 deletions
|
@ -4,16 +4,12 @@ use crate::core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use apply::Apply;
|
use apply::Apply;
|
||||||
use serde_json::Value;
|
|
||||||
use serde::{
|
|
||||||
Serialize,
|
|
||||||
de::DeserializeOwned
|
|
||||||
};
|
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
|
r#async::{multipart::Form, Client},
|
||||||
StatusCode,
|
StatusCode,
|
||||||
r#async::{Client, multipart::Form},
|
|
||||||
};
|
};
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
||||||
|
|
||||||
|
@ -58,12 +54,9 @@ pub async fn request_multipart<T: DeserializeOwned>(
|
||||||
.map_err(RequestError::NetworkError)?;
|
.map_err(RequestError::NetworkError)?;
|
||||||
|
|
||||||
let response = serde_json::from_str::<TelegramResponse<T>>(
|
let response = serde_json::from_str::<TelegramResponse<T>>(
|
||||||
&response
|
&response.text().await.map_err(RequestError::NetworkError)?,
|
||||||
.text()
|
|
||||||
.await
|
|
||||||
.map_err(RequestError::NetworkError)?,
|
|
||||||
)
|
)
|
||||||
.map_err(RequestError::InvalidJson)?;
|
.map_err(RequestError::InvalidJson)?;
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
TelegramResponse::Ok { result, .. } => Ok(result),
|
TelegramResponse::Ok { result, .. } => Ok(result),
|
||||||
|
@ -72,7 +65,10 @@ pub async fn request_multipart<T: DeserializeOwned>(
|
||||||
error_code,
|
error_code,
|
||||||
response_parameters,
|
response_parameters,
|
||||||
..
|
..
|
||||||
} => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() })
|
} => Err(RequestError::ApiError {
|
||||||
|
description,
|
||||||
|
status_code: StatusCode::from_u16(error_code).unwrap(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +86,7 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
||||||
.map_err(RequestError::NetworkError)?;
|
.map_err(RequestError::NetworkError)?;
|
||||||
|
|
||||||
let response = serde_json::from_str::<TelegramResponse<T>>(
|
let response = serde_json::from_str::<TelegramResponse<T>>(
|
||||||
&response
|
&response.text().await.map_err(RequestError::NetworkError)?,
|
||||||
.text()
|
|
||||||
.await
|
|
||||||
.map_err(RequestError::NetworkError)?,
|
|
||||||
)
|
)
|
||||||
.map_err(RequestError::InvalidJson)?;
|
.map_err(RequestError::InvalidJson)?;
|
||||||
|
|
||||||
|
@ -104,7 +97,10 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
||||||
error_code,
|
error_code,
|
||||||
response_parameters,
|
response_parameters,
|
||||||
..
|
..
|
||||||
} => Err(RequestError::ApiError { description, status_code: StatusCode::from_u16(error_code).unwrap() })
|
} => Err(RequestError::ApiError {
|
||||||
|
description,
|
||||||
|
status_code: StatusCode::from_u16(error_code).unwrap(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use serde::Serialize;
|
|
||||||
use crate::core::requests::{RequestContext, ChatId, Request, RequestFuture, ResponseResult};
|
|
||||||
use crate::core::types::{Message, ReplyMarkup};
|
|
||||||
use crate::core::network;
|
use crate::core::network;
|
||||||
|
use crate::core::requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
};
|
||||||
|
use crate::core::types::{Message, ReplyMarkup};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
/// Use this method to edit live location messages. A location can be edited
|
/// Use this method to edit live location messages. A location can be edited
|
||||||
|
@ -13,16 +15,16 @@ pub struct EditMessageLiveLocation<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Required if inline_message_id is not specified. Unique identifier for
|
/// Required if inline_message_id is not specified. Unique identifier for
|
||||||
/// the target chat or username of the target channel (in the format
|
/// the target chat or username of the target channel (in the format
|
||||||
/// @channelusername)
|
/// @channelusername)
|
||||||
chat_id: Option<ChatId>,
|
chat_id: Option<ChatId>,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Required if inline_message_id is not specified. Identifier of the
|
/// Required if inline_message_id is not specified. Identifier of the
|
||||||
/// message to edit
|
/// message to edit
|
||||||
message_id: Option<i64>,
|
message_id: Option<i64>,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Required if chat_id and message_id are not specified. Identifier of
|
/// Required if chat_id and message_id are not specified. Identifier of
|
||||||
/// the inline message
|
/// the inline message
|
||||||
inline_message_id: Option<String>,
|
inline_message_id: Option<String>,
|
||||||
|
@ -30,9 +32,9 @@ pub struct EditMessageLiveLocation<'a> {
|
||||||
latitude: f64,
|
latitude: f64,
|
||||||
/// Longitude of new location
|
/// Longitude of new location
|
||||||
longitude: f64,
|
longitude: f64,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// A JSON-serialized object for a new inline keyboard.
|
/// A JSON-serialized object for a new inline keyboard.
|
||||||
reply_markup: Option<ReplyMarkup>
|
reply_markup: Option<ReplyMarkup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Request<'a> for EditMessageLiveLocation<'a> {
|
impl<'a> Request<'a> for EditMessageLiveLocation<'a> {
|
||||||
|
@ -44,14 +46,15 @@ impl<'a> Request<'a> for EditMessageLiveLocation<'a> {
|
||||||
&self.ctx.client,
|
&self.ctx.client,
|
||||||
&self.ctx.token,
|
&self.ctx.token,
|
||||||
"editMessageLiveLocation",
|
"editMessageLiveLocation",
|
||||||
&self
|
&self,
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EditMessageLiveLocation<'a> {
|
impl<'a> EditMessageLiveLocation<'a> {
|
||||||
pub(crate) fn new (
|
pub(crate) fn new(
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
latitude: f64,
|
latitude: f64,
|
||||||
longitude: f64,
|
longitude: f64,
|
||||||
|
@ -63,7 +66,7 @@ impl<'a> EditMessageLiveLocation<'a> {
|
||||||
inline_message_id: None,
|
inline_message_id: None,
|
||||||
latitude,
|
latitude,
|
||||||
longitude,
|
longitude,
|
||||||
reply_markup: None
|
reply_markup: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +81,8 @@ impl<'a> EditMessageLiveLocation<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inline_message_id<T>(mut self, inline_message_id: T) -> Self
|
pub fn inline_message_id<T>(mut self, inline_message_id: T) -> Self
|
||||||
where T: Into<String>
|
where
|
||||||
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.inline_message_id = Some(inline_message_id.into());
|
self.inline_message_id = Some(inline_message_id.into());
|
||||||
self
|
self
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
types::{ParseMode, InputMedia},
|
requests::{utils, ChatId},
|
||||||
requests::{ChatId, utils},
|
types::{InputMedia, ParseMode},
|
||||||
};
|
};
|
||||||
|
|
||||||
use reqwest::r#async::multipart::Form;
|
use reqwest::r#async::multipart::Form;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
|
||||||
/// This is a convenient struct that builds `reqwest::r#async::multipart::Form`
|
/// This is a convenient struct that builds `reqwest::r#async::multipart::Form`
|
||||||
/// from scratch.
|
/// from scratch.
|
||||||
pub struct FormBuilder {
|
pub struct FormBuilder {
|
||||||
|
@ -22,20 +21,19 @@ impl FormBuilder {
|
||||||
|
|
||||||
/// Add the supplied key-value pair to this `FormBuilder`.
|
/// Add the supplied key-value pair to this `FormBuilder`.
|
||||||
pub fn add<T>(self, name: &str, value: &T) -> Self
|
pub fn add<T>(self, name: &str, value: &T) -> Self
|
||||||
where T: ToFormValue + ?Sized
|
where
|
||||||
|
T: ToFormValue + ?Sized,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
form: self.form.text(
|
form: self.form.text(name.to_owned(), value.to_form_value()),
|
||||||
name.to_owned(),
|
|
||||||
value.to_form_value()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a key-value pair to the supplied `FormBuilder` if `value` is some.
|
/// Adds a key-value pair to the supplied `FormBuilder` if `value` is some.
|
||||||
/// Don't forget to implement `serde::Serialize` for `T`!
|
/// Don't forget to implement `serde::Serialize` for `T`!
|
||||||
pub fn add_if_some<T>(self, name: &str, value: Option<&T>) -> Self
|
pub fn add_if_some<T>(self, name: &str, value: Option<&T>) -> Self
|
||||||
where T: ToFormValue + ?Sized
|
where
|
||||||
|
T: ToFormValue + ?Sized,
|
||||||
{
|
{
|
||||||
match value {
|
match value {
|
||||||
None => Self { form: self.form },
|
None => Self { form: self.form },
|
||||||
|
@ -45,7 +43,9 @@ impl FormBuilder {
|
||||||
|
|
||||||
pub fn add_file(self, name: &str, path_to_file: &PathBuf) -> Self {
|
pub fn add_file(self, name: &str, path_to_file: &PathBuf) -> Self {
|
||||||
Self {
|
Self {
|
||||||
form: self.form.part(name.to_owned(), utils::file_to_part(path_to_file))
|
form: self
|
||||||
|
.form
|
||||||
|
.part(name.to_owned(), utils::file_to_part(path_to_file)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,9 +70,7 @@ macro_rules! impl_for_struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_for_struct!(
|
impl_for_struct!(bool, i32, i64, Vec<InputMedia>);
|
||||||
bool, i32, i64, Vec<InputMedia>
|
|
||||||
);
|
|
||||||
|
|
||||||
impl ToFormValue for str {
|
impl ToFormValue for str {
|
||||||
fn to_form_value(&self) -> String {
|
fn to_form_value(&self) -> String {
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
types::Message,
|
|
||||||
requests::{
|
requests::{
|
||||||
ChatId,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
Request,
|
RequestFuture, ResponseResult,
|
||||||
RequestFuture,
|
|
||||||
RequestContext,
|
|
||||||
ResponseResult,
|
|
||||||
form_builder::FormBuilder,
|
|
||||||
},
|
},
|
||||||
|
types::Message,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
/// Use this method to forward messages of any kind. On success, the sent
|
/// Use this method to forward messages of any kind. On success, the sent
|
||||||
/// [`Message`] is returned.
|
/// [`Message`] is returned.
|
||||||
|
@ -28,8 +23,9 @@ pub struct ForwardMessage<'a> {
|
||||||
/// Message identifier in the chat specified in from_chat_id
|
/// Message identifier in the chat specified in from_chat_id
|
||||||
pub message_id: i64,
|
pub message_id: i64,
|
||||||
|
|
||||||
/// Sends the message silently. Users will receive a notification with no sound.
|
/// Sends the message silently. Users will receive a notification with no
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
/// sound.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,22 +39,25 @@ impl<'a> Request<'a> for ForwardMessage<'a> {
|
||||||
self.ctx.token,
|
self.ctx.token,
|
||||||
"forwardMessage",
|
"forwardMessage",
|
||||||
&self,
|
&self,
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ForwardMessage<'a> {
|
impl<'a> ForwardMessage<'a> {
|
||||||
pub(crate) fn new(ctx: RequestContext<'a>,
|
pub(crate) fn new(
|
||||||
chat_id: ChatId,
|
ctx: RequestContext<'a>,
|
||||||
from_chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
message_id: i64) -> Self {
|
from_chat_id: ChatId,
|
||||||
|
message_id: i64,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
chat_id,
|
chat_id,
|
||||||
from_chat_id,
|
from_chat_id,
|
||||||
message_id,
|
message_id,
|
||||||
disable_notification: None
|
disable_notification: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
src/core/requests/get_file.rs
Normal file
8
src/core/requests/get_file.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct GetFile<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
|
@ -1,15 +1,9 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
|
requests::{Request, RequestContext, RequestFuture, ResponseResult},
|
||||||
types::User,
|
types::User,
|
||||||
requests::{
|
|
||||||
Request,
|
|
||||||
RequestFuture,
|
|
||||||
RequestContext,
|
|
||||||
ResponseResult
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// A simple method for testing your bot's auth token. Requires no parameters.
|
/// A simple method for testing your bot's auth token. Requires no parameters.
|
||||||
/// Returns basic information about the bot in form of a [`User`] object.
|
/// Returns basic information about the bot in form of a [`User`] object.
|
||||||
|
@ -21,14 +15,12 @@ impl<'a> Request<'a> for GetMe<'a> {
|
||||||
type ReturnValue = User;
|
type ReturnValue = User;
|
||||||
|
|
||||||
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
Box::pin(
|
Box::pin(network::request_multipart(
|
||||||
network::request_multipart(
|
self.ctx.client,
|
||||||
self.ctx.client,
|
self.ctx.token,
|
||||||
self.ctx.token,
|
"getMe",
|
||||||
"getMe",
|
None,
|
||||||
None
|
))
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
src/core/requests/get_user_profile_photos.rs
Normal file
8
src/core/requests/get_user_profile_photos.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct GetUserProfilePhotos<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
8
src/core/requests/kick_chat_member.rs
Normal file
8
src/core/requests/kick_chat_member.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct KickChatMember<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
|
@ -1,19 +1,17 @@
|
||||||
use std::pin::Pin;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
use reqwest::{
|
use reqwest::{r#async::Client, StatusCode};
|
||||||
r#async::Client, StatusCode
|
|
||||||
};
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
|
|
||||||
mod form_builder;
|
mod form_builder;
|
||||||
mod utils;
|
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 { // TODO: add response parameters
|
ApiError {
|
||||||
|
// TODO: add response parameters
|
||||||
status_code: StatusCode,
|
status_code: StatusCode,
|
||||||
description: String,
|
description: String,
|
||||||
},
|
},
|
||||||
|
@ -82,18 +80,30 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn chat_id_channel_username_serialization() {
|
fn chat_id_channel_username_serialization() {
|
||||||
let expected_json = String::from(r#""@username""#);
|
let expected_json = String::from(r#""@username""#);
|
||||||
let actual_json = serde_json::to_string(&ChatId::ChannelUsername(String::from("@username"))).unwrap();
|
let actual_json = serde_json::to_string(&ChatId::ChannelUsername(
|
||||||
|
String::from("@username"),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(expected_json, actual_json)
|
assert_eq!(expected_json, actual_json)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod get_me;
|
|
||||||
pub mod send_message;
|
|
||||||
pub mod forward_message;
|
|
||||||
pub mod send_photo;
|
|
||||||
pub mod send_media_group;
|
|
||||||
pub mod send_audio;
|
|
||||||
pub mod send_location;
|
|
||||||
pub mod edit_message_live_location;
|
pub mod edit_message_live_location;
|
||||||
|
pub mod forward_message;
|
||||||
|
pub mod get_file;
|
||||||
|
pub mod get_me;
|
||||||
|
pub mod get_user_profile_photos;
|
||||||
|
pub mod kick_chat_member;
|
||||||
|
pub mod restrict_chat_member;
|
||||||
|
pub mod send_audio;
|
||||||
|
pub mod send_chat_action;
|
||||||
|
pub mod send_contact;
|
||||||
|
pub mod send_location;
|
||||||
|
pub mod send_media_group;
|
||||||
|
pub mod send_message;
|
||||||
|
pub mod send_photo;
|
||||||
|
pub mod send_poll;
|
||||||
|
pub mod send_venue;
|
||||||
pub mod stop_message_live_location;
|
pub mod stop_message_live_location;
|
||||||
|
pub mod unban_chat_member;
|
||||||
|
|
8
src/core/requests/restrict_chat_member.rs
Normal file
8
src/core/requests/restrict_chat_member.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct RestrictChatMember<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
requests::{ChatId, Request, RequestFuture, ResponseResult, RequestContext},
|
|
||||||
requests::form_builder::FormBuilder,
|
requests::form_builder::FormBuilder,
|
||||||
types::{InputFile, ParseMode, Message, ReplyMarkup},
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
types::{InputFile, Message, ParseMode, ReplyMarkup},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Use this method to send audio files, if you want Telegram clients to display
|
/// Use this method to send audio files, if you want Telegram clients to display
|
||||||
|
@ -71,11 +73,11 @@ impl<'a> Request<'a> for SendAudio<'a> {
|
||||||
.add_if_some("title", self.title.as_ref())
|
.add_if_some("title", self.title.as_ref())
|
||||||
.add_if_some(
|
.add_if_some(
|
||||||
"disable_notification",
|
"disable_notification",
|
||||||
self.disable_notification.as_ref()
|
self.disable_notification.as_ref(),
|
||||||
)
|
)
|
||||||
.add_if_some(
|
.add_if_some(
|
||||||
"reply_to_message_id",
|
"reply_to_message_id",
|
||||||
self.reply_to_message_id.as_ref()
|
self.reply_to_message_id.as_ref(),
|
||||||
);
|
);
|
||||||
params = match self.audio {
|
params = match self.audio {
|
||||||
InputFile::File(file) => params.add_file("audio", &file),
|
InputFile::File(file) => params.add_file("audio", &file),
|
||||||
|
@ -95,8 +97,9 @@ impl<'a> Request<'a> for SendAudio<'a> {
|
||||||
&self.ctx.client,
|
&self.ctx.client,
|
||||||
&self.ctx.token,
|
&self.ctx.token,
|
||||||
"sendAudio",
|
"sendAudio",
|
||||||
Some(params)
|
Some(params),
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +122,7 @@ impl<'a> SendAudio<'a> {
|
||||||
thumb: None,
|
thumb: None,
|
||||||
disable_notification: None,
|
disable_notification: None,
|
||||||
reply_to_message_id: None,
|
reply_to_message_id: None,
|
||||||
reply_markup: None
|
reply_markup: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,12 +166,18 @@ impl<'a> SendAudio<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_notification<T: Into<bool>>(mut self, disable_notification: T) -> Self {
|
pub fn disable_notification<T: Into<bool>>(
|
||||||
|
mut self,
|
||||||
|
disable_notification: T,
|
||||||
|
) -> Self {
|
||||||
self.disable_notification = Some(disable_notification.into());
|
self.disable_notification = Some(disable_notification.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_to_message_id<T: Into<i64>>(mut self, reply_to_message_id: T) -> Self {
|
pub fn reply_to_message_id<T: Into<i64>>(
|
||||||
|
mut self,
|
||||||
|
reply_to_message_id: T,
|
||||||
|
) -> Self {
|
||||||
self.reply_to_message_id = Some(reply_to_message_id.into());
|
self.reply_to_message_id = Some(reply_to_message_id.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
8
src/core/requests/send_chat_action.rs
Normal file
8
src/core/requests/send_chat_action.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct SendChatAction<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
7
src/core/requests/send_contact.rs
Normal file
7
src/core/requests/send_contact.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct SendContact<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
requests::{RequestContext, ChatId, Request, RequestFuture, ResponseResult},
|
|
||||||
types::{Message, ReplyMarkup},
|
|
||||||
network,
|
network,
|
||||||
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
types::{Message, ReplyMarkup},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -20,19 +22,19 @@ pub struct SendLocation<'a> {
|
||||||
latitude: f64,
|
latitude: f64,
|
||||||
/// Longitude of the location
|
/// Longitude of the location
|
||||||
longitude: f64,
|
longitude: f64,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Period in seconds for which the location will be updated
|
/// Period in seconds for which the location will be updated
|
||||||
/// (see [Live Locations](https://telegram.org/blog/live-locations)),
|
/// (see [Live Locations](https://telegram.org/blog/live-locations)),
|
||||||
/// should be between 60 and 86400.
|
/// should be between 60 and 86400.
|
||||||
live_period: Option<i32>,
|
live_period: Option<i32>,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Sends the message silently. Users will receive a notification with
|
/// Sends the message silently. Users will receive a notification with
|
||||||
/// no sound.
|
/// no sound.
|
||||||
disable_notification: Option<bool>,
|
disable_notification: Option<bool>,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// If the message is a reply, ID of the original message
|
/// If the message is a reply, ID of the original message
|
||||||
reply_to_message_id: Option<i64>,
|
reply_to_message_id: Option<i64>,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
reply_markup: Option<ReplyMarkup>,
|
reply_markup: Option<ReplyMarkup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +47,9 @@ impl<'a> Request<'a> for SendLocation<'a> {
|
||||||
&self.ctx.client,
|
&self.ctx.client,
|
||||||
&self.ctx.token,
|
&self.ctx.token,
|
||||||
"sendLocation",
|
"sendLocation",
|
||||||
&self
|
&self,
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +69,7 @@ impl<'a> SendLocation<'a> {
|
||||||
live_period: None,
|
live_period: None,
|
||||||
disable_notification: None,
|
disable_notification: None,
|
||||||
reply_to_message_id: None,
|
reply_to_message_id: None,
|
||||||
reply_markup: None
|
reply_markup: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network::request_multipart,
|
network::request_multipart,
|
||||||
types::{Message, InputMedia, InputFile},
|
|
||||||
requests::{
|
requests::{
|
||||||
ChatId,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
Request,
|
RequestFuture, ResponseResult,
|
||||||
RequestFuture,
|
},
|
||||||
RequestContext,
|
types::{InputFile, InputMedia, Message},
|
||||||
ResponseResult,
|
|
||||||
form_builder::FormBuilder,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
use apply::Apply;
|
use apply::Apply;
|
||||||
|
|
||||||
|
@ -32,28 +28,40 @@ impl<'a> Request<'a> for SendMediaGroup<'a> {
|
||||||
let params = FormBuilder::new()
|
let params = FormBuilder::new()
|
||||||
.add("chat_id", &self.chat_id)
|
.add("chat_id", &self.chat_id)
|
||||||
.apply(|form| {
|
.apply(|form| {
|
||||||
self.media
|
self.media.iter().map(|e| e.media()).fold(
|
||||||
.iter()
|
form,
|
||||||
.map(|e| e.media())
|
|acc, file| {
|
||||||
.fold(form, |acc, file| {
|
|
||||||
if let InputFile::File(path) = file {
|
if let InputFile::File(path) = file {
|
||||||
acc.add_file(
|
acc.add_file(
|
||||||
&path
|
&path
|
||||||
.file_name()
|
.file_name()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string_lossy(),
|
.to_string_lossy(),
|
||||||
path
|
path,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.add("media", &self.media)
|
.add("media", &self.media)
|
||||||
.add_if_some("disable_notification", self.disable_notification.as_ref())
|
.add_if_some(
|
||||||
.add_if_some("reply_to_message_id", self.reply_to_message_id.as_ref())
|
"disable_notification",
|
||||||
|
self.disable_notification.as_ref(),
|
||||||
|
)
|
||||||
|
.add_if_some(
|
||||||
|
"reply_to_message_id",
|
||||||
|
self.reply_to_message_id.as_ref(),
|
||||||
|
)
|
||||||
.build();
|
.build();
|
||||||
request_multipart(&self.ctx.client, &self.ctx.token, "sendMediaGroup", Some(params)).await
|
request_multipart(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"sendMediaGroup",
|
||||||
|
Some(params),
|
||||||
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
types::{Message, ParseMode, ReplyMarkup},
|
|
||||||
requests::{
|
requests::{
|
||||||
form_builder::FormBuilder,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
ChatId,
|
RequestFuture, ResponseResult,
|
||||||
Request,
|
|
||||||
RequestFuture,
|
|
||||||
RequestContext,
|
|
||||||
ResponseResult,
|
|
||||||
},
|
},
|
||||||
|
types::{Message, ParseMode, ReplyMarkup},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
/// Use this method to send text messages. On success, the sent [`Message`] is returned.
|
/// Use this method to send text messages. On success, the sent [`Message`] is
|
||||||
|
/// returned.
|
||||||
pub struct SendMessage<'a> {
|
pub struct SendMessage<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
|
@ -32,18 +28,19 @@ pub struct SendMessage<'a> {
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::core::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::core::types::ParseMode
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub parse_mode: Option<ParseMode>,
|
pub parse_mode: Option<ParseMode>,
|
||||||
/// Disables link previews for links in this message
|
/// Disables link previews for links in this message
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub disable_web_page_preview: Option<bool>,
|
pub disable_web_page_preview: Option<bool>,
|
||||||
/// Sends the message silently. Users will receive a notification with no sound.
|
/// Sends the message silently. Users will receive a notification with no
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
/// sound.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
/// If the message is a reply, ID of the original message
|
/// If the message is a reply, ID of the original message
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub reply_to_message_id: Option<i64>,
|
pub reply_to_message_id: Option<i64>,
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub reply_markup: Option<ReplyMarkup>,
|
pub reply_markup: Option<ReplyMarkup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +54,8 @@ impl<'a> Request<'a> for SendMessage<'a> {
|
||||||
self.ctx.token,
|
self.ctx.token,
|
||||||
"sendMessage",
|
"sendMessage",
|
||||||
&self,
|
&self,
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,16 @@ use std::path::Path;
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
types::{ParseMode, Message, InputFile, ReplyMarkup},
|
|
||||||
requests::{
|
requests::{
|
||||||
ChatId,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
Request,
|
RequestFuture, ResponseResult,
|
||||||
RequestFuture,
|
|
||||||
RequestContext,
|
|
||||||
ResponseResult,
|
|
||||||
form_builder::FormBuilder,
|
|
||||||
},
|
},
|
||||||
|
types::{InputFile, Message, ParseMode, ReplyMarkup},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// Use this method to send photos. On success, the sent [`Message`] is returned.
|
/// Use this method to send photos. On success, the sent [`Message`] is
|
||||||
|
/// returned.
|
||||||
pub struct SendPhoto<'a> {
|
pub struct SendPhoto<'a> {
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
|
|
||||||
|
@ -23,13 +19,14 @@ pub struct SendPhoto<'a> {
|
||||||
/// (in the format @channelusername)
|
/// (in the format @channelusername)
|
||||||
pub chat_id: ChatId,
|
pub chat_id: ChatId,
|
||||||
/// Photo to send.
|
/// Photo to send.
|
||||||
/// [`InputFile::FileId`] - Pass a file_id as String to send a photo that exists on the
|
/// [`InputFile::FileId`] - Pass a file_id as String to send a photo that
|
||||||
/// Telegram servers (recommended)
|
/// exists on the Telegram servers (recommended)
|
||||||
/// [`InputFile::Url`] - Pass an HTTP URL as a String for Telegram
|
/// [`InputFile::Url`] - Pass an HTTP URL as a String for Telegram
|
||||||
/// to get a photo from the Internet
|
/// to get a photo from the Internet
|
||||||
/// [`InputFile::File`] - Upload a new photo.
|
/// [`InputFile::File`] - Upload a new photo.
|
||||||
pub photo: InputFile,
|
pub photo: InputFile,
|
||||||
/// Photo caption (may also be used when resending photos by file_id), 0-1024 characters
|
/// Photo caption (may also be used when resending photos by file_id),
|
||||||
|
/// 0-1024 characters
|
||||||
pub caption: Option<String>,
|
pub caption: Option<String>,
|
||||||
/// Send [Markdown] or [HTML],
|
/// Send [Markdown] or [HTML],
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
|
@ -40,7 +37,8 @@ pub struct SendPhoto<'a> {
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::core::types::ParseMode
|
||||||
pub parse_mode: Option<ParseMode>,
|
pub parse_mode: Option<ParseMode>,
|
||||||
/// Sends the message silently. Users will receive a notification with no sound.
|
/// Sends the message silently. Users will receive a notification with no
|
||||||
|
/// sound.
|
||||||
pub disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
/// If the message is a reply, ID of the original message
|
/// If the message is a reply, ID of the original message
|
||||||
pub reply_to_message_id: Option<i64>,
|
pub reply_to_message_id: Option<i64>,
|
||||||
|
@ -58,13 +56,13 @@ impl<'a> Request<'a> for SendPhoto<'a> {
|
||||||
.add_if_some("parse_mode", self.parse_mode.as_ref())
|
.add_if_some("parse_mode", self.parse_mode.as_ref())
|
||||||
.add_if_some(
|
.add_if_some(
|
||||||
"disable_notification",
|
"disable_notification",
|
||||||
self.disable_notification.as_ref()
|
self.disable_notification.as_ref(),
|
||||||
)
|
)
|
||||||
.add_if_some(
|
.add_if_some(
|
||||||
"reply_to_message_id",
|
"reply_to_message_id",
|
||||||
self.reply_to_message_id.as_ref()
|
self.reply_to_message_id.as_ref(),
|
||||||
);
|
);
|
||||||
|
|
||||||
params = match self.photo {
|
params = match self.photo {
|
||||||
InputFile::File(path) => params.add_file("photo", &path),
|
InputFile::File(path) => params.add_file("photo", &path),
|
||||||
InputFile::Url(url) => params.add("photo", &url),
|
InputFile::Url(url) => params.add("photo", &url),
|
||||||
|
@ -76,8 +74,9 @@ impl<'a> Request<'a> for SendPhoto<'a> {
|
||||||
&self.ctx.client,
|
&self.ctx.client,
|
||||||
&self.ctx.token,
|
&self.ctx.token,
|
||||||
"sendPhoto",
|
"sendPhoto",
|
||||||
Some(params)
|
Some(params),
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +85,7 @@ impl<'a> SendPhoto<'a> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
photo: InputFile
|
photo: InputFile,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -96,7 +95,7 @@ impl<'a> SendPhoto<'a> {
|
||||||
parse_mode: None,
|
parse_mode: None,
|
||||||
disable_notification: None,
|
disable_notification: None,
|
||||||
reply_to_message_id: None,
|
reply_to_message_id: None,
|
||||||
reply_markup: None
|
reply_markup: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,12 +119,18 @@ impl<'a> SendPhoto<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_notification<T: Into<bool>>(mut self, disable_notification: T) -> Self {
|
pub fn disable_notification<T: Into<bool>>(
|
||||||
|
mut self,
|
||||||
|
disable_notification: T,
|
||||||
|
) -> Self {
|
||||||
self.disable_notification = Some(disable_notification.into());
|
self.disable_notification = Some(disable_notification.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_to_message_id<T: Into<i64>>(mut self, reply_to_message_id: T) -> Self {
|
pub fn reply_to_message_id<T: Into<i64>>(
|
||||||
|
mut self,
|
||||||
|
reply_to_message_id: T,
|
||||||
|
) -> Self {
|
||||||
self.reply_to_message_id = Some(reply_to_message_id.into());
|
self.reply_to_message_id = Some(reply_to_message_id.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
8
src/core/requests/send_poll.rs
Normal file
8
src/core/requests/send_poll.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct SendPoll<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
43
src/core/requests/send_venue.rs
Normal file
43
src/core/requests/send_venue.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::core::requests::{ChatId, RequestContext};
|
||||||
|
|
||||||
|
//TODO:: need implementation
|
||||||
|
///Use this method to send information about a venue. On success, the sent
|
||||||
|
/// Message is returned.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct SendVenue<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
/// Integer or String Yes Unique identifier for the target chat or
|
||||||
|
/// username of the target channel (in the format @channelusername)
|
||||||
|
chat_id: ChatId,
|
||||||
|
/// Float number Yes Latitude of the venue
|
||||||
|
latitude: f64,
|
||||||
|
///Float number Yes Longitude of the venue
|
||||||
|
longitude: f64,
|
||||||
|
/// Yes Name of the venue
|
||||||
|
title: String,
|
||||||
|
///String Yes Address of the venue
|
||||||
|
address: String,
|
||||||
|
/// String Optional Foursquare identifier of the venue
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
foursquare_id: Option<String>,
|
||||||
|
/// String Optional Foursquare type of the venue, if known. (For
|
||||||
|
/// example, “arts_entertainment/default”, “arts_entertainment/aquarium” or
|
||||||
|
/// “food/icecream”.)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
foursquare_type: Option<String>,
|
||||||
|
/// Boolean Optional Sends the message silently. Users will receive a
|
||||||
|
/// notification with no sound.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
disable_notification: Option<bool>,
|
||||||
|
/// Integer Optional If the message is a reply, ID of the original
|
||||||
|
/// message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
reply_to_message_id: Option<i32>,
|
||||||
|
/// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or
|
||||||
|
/// ForceReply Optional Additional interface options. A JSON-serialized
|
||||||
|
/// object for an inline keyboard, custom reply keyboard, instructions to
|
||||||
|
/// remove reply keyboard or to force a reply from the user.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
reply_markup: Option<()>, //TODO: need concrete type
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use std::path::Path;
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
ChatId, form_builder::FormBuilder, Request, RequestContext,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
RequestFuture, ResponseResult,
|
RequestFuture, ResponseResult,
|
||||||
},
|
},
|
||||||
types::{InlineKeyboardMarkup, Message, ParseMode},
|
types::{InlineKeyboardMarkup, Message, ParseMode},
|
||||||
|
@ -14,23 +14,24 @@ use crate::core::{
|
||||||
/// returned, otherwise True is returned.
|
/// returned, otherwise True is returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct StopMessageLiveLocation<'a> {
|
struct StopMessageLiveLocation<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
/// Required if inline_message_id is not specified. Unique identifier for
|
/// Required if inline_message_id is not specified. Unique identifier for
|
||||||
/// the target chat or username of the target channel (in the format
|
/// the target chat or username of the target channel (in the format
|
||||||
/// @channelusername)
|
/// @channelusername)
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub chat_id: Option<ChatId>,
|
pub chat_id: Option<ChatId>,
|
||||||
/// Required if inline_message_id is not specified. Identifier of the
|
/// Required if inline_message_id is not specified. Identifier of the
|
||||||
/// message with live location to stop
|
/// message with live location to stop
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub message_id: Option<i32>,
|
pub message_id: Option<i32>,
|
||||||
/// Required if chat_id and message_id are not specified. Identifier of the
|
/// Required if chat_id and message_id are not specified. Identifier of the
|
||||||
/// inline message
|
/// inline message
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub inline_message_id: Option<String>,
|
pub inline_message_id: Option<String>,
|
||||||
/// A JSON-serialized object InlineKeyboardMarkup for a new inline
|
/// A JSON-serialized object InlineKeyboardMarkup for a new inline
|
||||||
/// keyboard.
|
/// keyboard.
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub reply_markup: Option<InlineKeyboardMarkup>,
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +52,7 @@ impl<'a> Request<'a> for StopMessageLiveLocation<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StopMessageLiveLocation<'a> {
|
impl<'a> StopMessageLiveLocation<'a> {
|
||||||
fn new(
|
fn new(ctx: RequestContext<'a>) -> Self {
|
||||||
ctx: RequestContext<'a>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
chat_id: None,
|
chat_id: None,
|
||||||
|
@ -64,30 +63,34 @@ impl<'a> StopMessageLiveLocation<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||||
where T: Into<ChatId>
|
where
|
||||||
|
T: Into<ChatId>,
|
||||||
{
|
{
|
||||||
self.chat_id = chat_id.into();
|
self.chat_id = Some(chat_id.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message_id<T>(mut self, message_id: T) -> Self
|
pub fn message_id<T>(mut self, message_id: T) -> Self
|
||||||
where T: Into<i32>
|
where
|
||||||
|
T: Into<i32>,
|
||||||
{
|
{
|
||||||
self.message_id = Some(message_id.into());
|
self.message_id = Some(message_id.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inline_message_id<T>(mut self, inline_message_id: T) -> Self
|
pub fn inline_message_id<T>(mut self, inline_message_id: T) -> Self
|
||||||
where T: Into<String>
|
where
|
||||||
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.inline_message_id = Some(inline_message_id.into());
|
self.inline_message_id = Some(inline_message_id.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_markup<T>(mut self, reply_markup: T) -> Self
|
pub fn reply_markup<T>(mut self, reply_markup: T) -> Self
|
||||||
where T: Into<InlineKeyboardMarkup>
|
where
|
||||||
|
T: Into<InlineKeyboardMarkup>,
|
||||||
{
|
{
|
||||||
self.inline_message_id = Some(reply_markup.into());
|
self.reply_markup = Some(reply_markup.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
8
src/core/requests/unban_chat_member.rs
Normal file
8
src/core/requests/unban_chat_member.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::core::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct UnbanChatMember<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
use tokio::codec::FramedRead;
|
|
||||||
use std::fs::File;
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use tokio::prelude::*;
|
|
||||||
use reqwest::r#async::multipart::Part;
|
use reqwest::r#async::multipart::Part;
|
||||||
|
use std::fs::File;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use tokio::codec::FramedRead;
|
||||||
|
use tokio::prelude::*;
|
||||||
|
|
||||||
struct FileDecoder;
|
struct FileDecoder;
|
||||||
|
|
||||||
|
@ -11,9 +11,12 @@ impl tokio::codec::Decoder for FileDecoder {
|
||||||
type Item = Bytes;
|
type Item = Bytes;
|
||||||
type Error = std::io::Error;
|
type Error = std::io::Error;
|
||||||
|
|
||||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
fn decode(
|
||||||
|
&mut self,
|
||||||
|
src: &mut BytesMut,
|
||||||
|
) -> Result<Option<Self::Item>, Self::Error> {
|
||||||
if src.is_empty() {
|
if src.is_empty() {
|
||||||
return Ok(None)
|
return Ok(None);
|
||||||
}
|
}
|
||||||
Ok(Some(src.take().freeze()))
|
Ok(Some(src.take().freeze()))
|
||||||
}
|
}
|
||||||
|
@ -21,9 +24,19 @@ impl tokio::codec::Decoder for FileDecoder {
|
||||||
|
|
||||||
pub fn file_to_part(path_to_file: &PathBuf) -> Part {
|
pub fn file_to_part(path_to_file: &PathBuf) -> Part {
|
||||||
let file = tokio::fs::File::open(path_to_file.clone())
|
let file = tokio::fs::File::open(path_to_file.clone())
|
||||||
.map(|file| FramedRead::new(file.unwrap() /* TODO: this can cause panics */, FileDecoder))
|
.map(|file| {
|
||||||
|
FramedRead::new(
|
||||||
|
file.unwrap(), /* TODO: this can cause panics */
|
||||||
|
FileDecoder,
|
||||||
|
)
|
||||||
|
})
|
||||||
.flatten_stream();
|
.flatten_stream();
|
||||||
let part = Part::stream(file)
|
let part = Part::stream(file).file_name(
|
||||||
.file_name(path_to_file.file_name().unwrap().to_string_lossy().into_owned());
|
path_to_file
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
);
|
||||||
part
|
part
|
||||||
}
|
}
|
||||||
|
|
13
src/core/types/animation.rs
Normal file
13
src/core/types/animation.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use crate::core::types::PhotoSize;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
pub struct Animation {
|
||||||
|
pub file_id: String,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub duration: u32,
|
||||||
|
pub thumb: PhotoSize,
|
||||||
|
pub file_name: Option<String>,
|
||||||
|
pub mime_type: Option<String>,
|
||||||
|
pub file_size: Option<u32>
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone )]
|
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
||||||
pub struct AnswerPreCheckoutQuery {
|
pub struct AnswerPreCheckoutQuery {
|
||||||
pub pre_checkout_query_id: String,
|
pub pre_checkout_query_id: String,
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
|
|
|
@ -32,7 +32,6 @@ pub enum ChatKind {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
|
@ -101,10 +100,9 @@ mod tests {
|
||||||
},
|
},
|
||||||
photo: None,
|
photo: None,
|
||||||
};
|
};
|
||||||
let actual = from_str(
|
let actual =
|
||||||
r#"{"id":-1,"type":"channel","username":"channelname"}"#,
|
from_str(r#"{"id":-1,"type":"channel","username":"channelname"}"#)
|
||||||
)
|
.unwrap();
|
||||||
.unwrap();
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use serde::Deserialization;
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
|
||||||
#[derive(Debug, Deserialization, Clone)]
|
|
||||||
/// This object represents a phone contact.
|
/// This object represents a phone contact.
|
||||||
struct Contact {
|
pub struct Contact {
|
||||||
/// Contact's phone number
|
/// Contact's phone number
|
||||||
pub phone_number: String,
|
pub phone_number: String,
|
||||||
/// Contact's first name
|
/// Contact's first name
|
||||||
|
|
6
src/core/types/file.rs
Normal file
6
src/core/types/file.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
|
||||||
|
pub struct File {
|
||||||
|
pub file_id: String,
|
||||||
|
pub file_size: u32,
|
||||||
|
pub file_path: String
|
||||||
|
}
|
|
@ -15,4 +15,4 @@ pub struct ForceReply {
|
||||||
/// [`Message`] object; 2) if the bot's message is a reply
|
/// [`Message`] object; 2) if the bot's message is a reply
|
||||||
/// (has reply_to_message_id), sender of the original message.
|
/// (has reply_to_message_id), sender of the original message.
|
||||||
pub selective: Option<bool>,
|
pub selective: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ pub struct InlineKeyboardButton {
|
||||||
pub kind: InlineKeyboardButtonKind,
|
pub kind: InlineKeyboardButtonKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Eq, Hash, Deserialize)]
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, PartialOrd, Serialize, Eq, Hash, Deserialize,
|
||||||
|
)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum InlineKeyboardButtonKind {
|
pub enum InlineKeyboardButtonKind {
|
||||||
/// HTTP or tg:// url to be opened when button is pressed
|
/// HTTP or tg:// url to be opened when button is pressed
|
||||||
|
@ -27,13 +29,14 @@ pub enum InlineKeyboardButtonKind {
|
||||||
/// the user will be automatically returned to the chat they switched from,
|
/// the user will be automatically returned to the chat they switched from,
|
||||||
/// skipping the chat selection screen.
|
/// skipping the chat selection screen.
|
||||||
SwitchInlineQuery(String),
|
SwitchInlineQuery(String),
|
||||||
/// Optional. If set, pressing the button will insert the bot‘s username and
|
/// Optional. If set, pressing the button will insert the bot‘s username
|
||||||
/// the specified inline query in the current chat's input field. Can be
|
/// and the specified inline query in the current chat's input field.
|
||||||
/// empty, in which case only the bot’s username will be inserted.
|
/// Can be empty, in which case only the bot’s username will be
|
||||||
|
/// inserted.
|
||||||
///
|
///
|
||||||
///This offers a quick way for the user to open your bot in inline mode in
|
///This offers a quick way for the user to open your bot in inline mode in
|
||||||
/// the same chat – good for selecting something from multiple options.
|
/// the same chat – good for selecting something from multiple options.
|
||||||
SwitchInlineQueryCurrentChat(String),
|
SwitchInlineQueryCurrentChat(String),
|
||||||
// CallbackGame(CallbackGame), TODO: разобраться, что с этим делать
|
/* CallbackGame(CallbackGame), TODO: разобраться, что с этим делать
|
||||||
// TODO: add LoginUrl, pay
|
* TODO: add LoginUrl, pay */
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,11 +168,11 @@ pub enum InputMedia {
|
||||||
impl InputMedia {
|
impl InputMedia {
|
||||||
pub fn media(&self) -> &InputFile {
|
pub fn media(&self) -> &InputFile {
|
||||||
match self {
|
match self {
|
||||||
InputMedia::Photo { media, .. } |
|
InputMedia::Photo { media, .. }
|
||||||
InputMedia::Document { media, .. } |
|
| InputMedia::Document { media, .. }
|
||||||
InputMedia::Audio { media, .. } |
|
| InputMedia::Audio { media, .. }
|
||||||
InputMedia::Animation { media, .. } |
|
| InputMedia::Animation { media, .. }
|
||||||
InputMedia::Video { media, .. } => media,
|
| InputMedia::Video { media, .. } => media,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,4 +16,4 @@ pub struct KeyboardButton {
|
||||||
/// Optional. If True, the user's current location will be sent when the
|
/// Optional. If True, the user's current location will be sent when the
|
||||||
/// button is pressed. Available in private chats only
|
/// button is pressed. Available in private chats only
|
||||||
pub request_location: Option<bool>,
|
pub request_location: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ pub enum MessageKind {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
pub enum Sender {
|
pub enum Sender {
|
||||||
/// If message is sent from Chat
|
/// If message is sent from Chat
|
||||||
#[serde(rename = "from")]
|
#[serde(rename = "from")]
|
||||||
|
@ -106,7 +106,7 @@ pub enum ForwardKind {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
pub enum ForwardedFrom {
|
pub enum ForwardedFrom {
|
||||||
#[serde(rename = "forward_from")]
|
#[serde(rename = "forward_from")]
|
||||||
User(User),
|
User(User),
|
||||||
|
@ -125,13 +125,13 @@ pub enum MediaKind {
|
||||||
document: (),
|
document: (),
|
||||||
caption: Option<String>,
|
caption: Option<String>,
|
||||||
#[serde(default = "Vec::new")]
|
#[serde(default = "Vec::new")]
|
||||||
caption_entities: Vec<MessageEntity>
|
caption_entities: Vec<MessageEntity>,
|
||||||
},
|
},
|
||||||
Audio {
|
Audio {
|
||||||
audio: Audio,
|
audio: Audio,
|
||||||
caption: Option<String>,
|
caption: Option<String>,
|
||||||
#[serde(default = "Vec::new")]
|
#[serde(default = "Vec::new")]
|
||||||
caption_entities: Vec<MessageEntity>
|
caption_entities: Vec<MessageEntity>,
|
||||||
},
|
},
|
||||||
Contact {
|
Contact {
|
||||||
contact: Contact,
|
contact: Contact,
|
||||||
|
@ -140,7 +140,7 @@ pub enum MediaKind {
|
||||||
document: Document,
|
document: Document,
|
||||||
caption: Option<String>,
|
caption: Option<String>,
|
||||||
#[serde(default = "Vec::new")]
|
#[serde(default = "Vec::new")]
|
||||||
caption_entities: Vec<MessageEntity>
|
caption_entities: Vec<MessageEntity>,
|
||||||
},
|
},
|
||||||
Game {
|
Game {
|
||||||
game: Game,
|
game: Game,
|
||||||
|
@ -193,232 +193,192 @@ mod tests {
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sent_message_de() {
|
fn de_media_forwarded() {
|
||||||
let expected = Message {
|
|
||||||
id: 6534,
|
|
||||||
date: 1567898953,
|
|
||||||
chat: Chat {
|
|
||||||
id: 218485655,
|
|
||||||
photo: None,
|
|
||||||
kind: ChatKind::Private {
|
|
||||||
type_: (),
|
|
||||||
first_name: Some("W".to_string()),
|
|
||||||
last_name: None,
|
|
||||||
username: Some("WaffleLapkin".to_string()),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
message_kind: MessageKind::IncomingMessage {
|
|
||||||
from: Sender::User(User {
|
|
||||||
id: 457569668,
|
|
||||||
is_bot: true,
|
|
||||||
first_name: "BT".to_string(),
|
|
||||||
last_name: None,
|
|
||||||
username: Some("BloodyTestBot".to_string()),
|
|
||||||
language_code: None,
|
|
||||||
}),
|
|
||||||
forward_kind: ForwardKind::Origin {
|
|
||||||
reply_to_message: None,
|
|
||||||
},
|
|
||||||
edit_date: None,
|
|
||||||
media_kind: MediaKind::Text {
|
|
||||||
text: "text".to_string(),
|
|
||||||
entities: vec![],
|
|
||||||
},
|
|
||||||
reply_markup: None,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// actual message from telegram
|
|
||||||
let json = r#"{
|
let json = r#"{
|
||||||
"message_id": 6534,
|
"message_id": 198283,
|
||||||
"from": {
|
"from": {
|
||||||
"id": 457569668,
|
"id": 250918540,
|
||||||
"is_bot": true,
|
"is_bot": false,
|
||||||
"first_name": "BT",
|
"first_name": "Андрей",
|
||||||
"username": "BloodyTestBot"
|
"last_name": "Власов",
|
||||||
},
|
"username": "aka_dude",
|
||||||
"chat": {
|
"language_code": "en"
|
||||||
"id": 218485655,
|
},
|
||||||
"first_name": "W",
|
"chat": {
|
||||||
"username": "WaffleLapkin",
|
"id": 250918540,
|
||||||
"type": "private"
|
"first_name": "Андрей",
|
||||||
},
|
"last_name": "Власов",
|
||||||
"date": 1567898953,
|
"username": "aka_dude",
|
||||||
"text": "text"
|
"type": "private"
|
||||||
}"#;
|
},
|
||||||
let actual = from_str::<Message>(json).unwrap();
|
"date": 1567927221,
|
||||||
assert_eq!(expected, actual);
|
"video": {
|
||||||
|
"duration": 13,
|
||||||
|
"width": 512,
|
||||||
|
"height": 640,
|
||||||
|
"mime_type": "video/mp4",
|
||||||
|
"thumb": {
|
||||||
|
"file_id": "AAQCAAOmBAACBf2oS53pByA-I4CWWCObDwAEAQAHbQADMWcAAhYE",
|
||||||
|
"file_size": 10339,
|
||||||
|
"width": 256,
|
||||||
|
"height": 320
|
||||||
|
},
|
||||||
|
"file_id": "BAADAgADpgQAAgX9qEud6QcgPiOAlhYE",
|
||||||
|
"file_size": 1381334
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let message = from_str::<Message>(json);
|
||||||
|
assert!(message.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn media_message_de() {
|
fn de_media_group_forwarded() {
|
||||||
let json = r#"{
|
let json = r#"{
|
||||||
"message_id": 198283,
|
"message_id": 198283,
|
||||||
"from": {
|
"from": {
|
||||||
"id": 250918540,
|
"id": 250918540,
|
||||||
"is_bot": false,
|
"is_bot": false,
|
||||||
"first_name": "Андрей",
|
"first_name": "Андрей",
|
||||||
"last_name": "Власов",
|
"last_name": "Власов",
|
||||||
"username": "aka_dude",
|
"username": "aka_dude",
|
||||||
"language_code": "en"
|
"language_code": "en"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"id": 250918540,
|
"id": 250918540,
|
||||||
"first_name": "Андрей",
|
"first_name": "Андрей",
|
||||||
"last_name": "Власов",
|
"last_name": "Власов",
|
||||||
"username": "aka_dude",
|
"username": "aka_dude",
|
||||||
"type": "private"
|
"type": "private"
|
||||||
},
|
},
|
||||||
"date": 1567927221,
|
"date": 1567927221,
|
||||||
"video": {
|
"media_group_id": "12543417770506682",
|
||||||
"duration": 13,
|
"video": {
|
||||||
"width": 512,
|
"duration": 13,
|
||||||
"height": 640,
|
"width": 512,
|
||||||
"mime_type": "video/mp4",
|
"height": 640,
|
||||||
"thumb": {
|
"mime_type": "video/mp4",
|
||||||
"file_id": "AAQCAAOmBAACBf2oS53pByA-I4CWWCObDwAEAQAHbQADMWcAAhYE",
|
"thumb": {
|
||||||
"file_size": 10339,
|
"file_id": "AAQCAAOmBAACBf2oS53pByA-I4CWWCObDwAEAQAHbQADMWcAAhYE",
|
||||||
"width": 256,
|
"file_size": 10339,
|
||||||
"height": 320
|
"width": 256,
|
||||||
},
|
"height": 320
|
||||||
"file_id": "BAADAgADpgQAAgX9qEud6QcgPiOAlhYE",
|
|
||||||
"file_size": 1381334
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
let actual = from_str::<Message>(json).unwrap();
|
|
||||||
let expected = Message {
|
|
||||||
id: 198283,
|
|
||||||
date: 1567927221,
|
|
||||||
chat: Chat {
|
|
||||||
id: 250918540,
|
|
||||||
photo: None,
|
|
||||||
kind: ChatKind::Private {
|
|
||||||
first_name: Some("Андрей".to_string()),
|
|
||||||
last_name: Some("Власов".to_string()),
|
|
||||||
username: Some("aka_dude".to_string()),
|
|
||||||
type_: ()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
message_kind: MessageKind::IncomingMessage {
|
"file_id": "BAADAgADpgQAAgX9qEud6QcgPiOAlhYE",
|
||||||
from: Sender::User(User {
|
"file_size": 1381334
|
||||||
id: 250918540,
|
}
|
||||||
is_bot: false,
|
}"#;
|
||||||
first_name: "Андрей".to_string(),
|
let message = from_str::<Message>(json);
|
||||||
last_name: Some("Власов".to_string()),
|
assert!(message.is_ok());
|
||||||
username: Some("aka_dude".to_string()),
|
|
||||||
language_code: Some("en".to_string())
|
|
||||||
}),
|
|
||||||
forward_kind: ForwardKind::Origin { reply_to_message: None },
|
|
||||||
edit_date: None,
|
|
||||||
media_kind: MediaKind::Video {
|
|
||||||
video: Video {
|
|
||||||
duration: 13,
|
|
||||||
width: 512,
|
|
||||||
height: 640,
|
|
||||||
mime_type: Some("video/mp4".to_string()),
|
|
||||||
thumb: Some(PhotoSize {
|
|
||||||
file_id: "AAQCAAOmBAACBf2oS53pByA-I4CWWCObDwAEAQAHbQADMWcAAhYE".to_string(),
|
|
||||||
file_size: Some(10339),
|
|
||||||
width: 256,
|
|
||||||
height: 320
|
|
||||||
}),
|
|
||||||
file_id: "BAADAgADpgQAAgX9qEud6QcgPiOAlhYE".to_string(),
|
|
||||||
file_size: Some(1381334)
|
|
||||||
},
|
|
||||||
caption: None,
|
|
||||||
caption_entities: vec![],
|
|
||||||
media_group_id: None
|
|
||||||
},
|
|
||||||
reply_markup: None
|
|
||||||
},
|
|
||||||
|
|
||||||
};
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn media_group_message_de() {
|
fn de_text() {
|
||||||
let json = r#"{
|
let json = r#"{
|
||||||
"message_id": 198283,
|
"message_id": 199785,
|
||||||
"from": {
|
"from": {
|
||||||
"id": 250918540,
|
"id": 250918540,
|
||||||
"is_bot": false,
|
"is_bot": false,
|
||||||
"first_name": "Андрей",
|
"first_name": "Андрей",
|
||||||
"last_name": "Власов",
|
"last_name": "Власов",
|
||||||
"username": "aka_dude",
|
"username": "aka_dude",
|
||||||
"language_code": "en"
|
"language_code": "en"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"id": 250918540,
|
"id": 250918540,
|
||||||
"first_name": "Андрей",
|
"first_name": "Андрей",
|
||||||
"last_name": "Власов",
|
"last_name": "Власов",
|
||||||
"username": "aka_dude",
|
"username": "aka_dude",
|
||||||
"type": "private"
|
"type": "private"
|
||||||
},
|
},
|
||||||
"date": 1567927221,
|
"date": 1568289890,
|
||||||
"media_group_id": "12543417770506682",
|
"text": "Лол кек 😂"
|
||||||
"video": {
|
}"#;
|
||||||
"duration": 13,
|
let message = from_str::<Message>(json);
|
||||||
"width": 512,
|
assert!(message.is_ok());
|
||||||
"height": 640,
|
}
|
||||||
"mime_type": "video/mp4",
|
|
||||||
"thumb": {
|
#[test]
|
||||||
"file_id": "AAQCAAOmBAACBf2oS53pByA-I4CWWCObDwAEAQAHbQADMWcAAhYE",
|
fn de_sticker() {
|
||||||
"file_size": 10339,
|
let json = r#"{
|
||||||
"width": 256,
|
"message_id": 199787,
|
||||||
"height": 320
|
"from": {
|
||||||
},
|
"id": 250918540,
|
||||||
"file_id": "BAADAgADpgQAAgX9qEud6QcgPiOAlhYE",
|
"is_bot": false,
|
||||||
"file_size": 1381334
|
"first_name": "Андрей",
|
||||||
}
|
"last_name": "Власов",
|
||||||
}"#;
|
"username": "aka_dude",
|
||||||
let actual = from_str::<Message>(json).unwrap();
|
"language_code": "en"
|
||||||
let expected = Message {
|
},
|
||||||
id: 198283,
|
"chat": {
|
||||||
date: 1567927221,
|
"id": 250918540,
|
||||||
chat: Chat {
|
"first_name": "Андрей",
|
||||||
id: 250918540,
|
"last_name": "Власов",
|
||||||
photo: None,
|
"username": "aka_dude",
|
||||||
kind: ChatKind::Private {
|
"type": "private"
|
||||||
first_name: Some("Андрей".to_string()),
|
},
|
||||||
last_name: Some("Власов".to_string()),
|
"date": 1568290188,
|
||||||
username: Some("aka_dude".to_string()),
|
"sticker": {
|
||||||
type_: ()
|
"width": 512,
|
||||||
}
|
"height": 512,
|
||||||
},
|
"emoji": "😡",
|
||||||
message_kind: MessageKind::IncomingMessage {
|
"set_name": "AdvenTimeAnim",
|
||||||
from: Sender::User(User {
|
"is_animated": true,
|
||||||
id: 250918540,
|
"thumb": {
|
||||||
is_bot: false,
|
"file_id": "AAQCAAMjAAOw0PgMaabKAcaXKCBLubkPAAQBAAdtAAPGKwACFgQ",
|
||||||
first_name: "Андрей".to_string(),
|
"file_size": 4118,
|
||||||
last_name: Some("Власов".to_string()),
|
"width": 128,
|
||||||
username: Some("aka_dude".to_string()),
|
"height": 128
|
||||||
language_code: Some("en".to_string())
|
},
|
||||||
}),
|
"file_id": "CAADAgADIwADsND4DGmmygHGlyggFgQ",
|
||||||
forward_kind: ForwardKind::Origin { reply_to_message: None },
|
"file_size": 16639
|
||||||
edit_date: None,
|
}
|
||||||
media_kind: MediaKind::Video {
|
}"#;
|
||||||
video: Video {
|
let message = from_str::<Message>(json);
|
||||||
duration: 13,
|
assert!(message.is_ok());
|
||||||
width: 512,
|
}
|
||||||
height: 640,
|
|
||||||
mime_type: Some("video/mp4".to_string()),
|
#[test]
|
||||||
thumb: Some(PhotoSize {
|
fn de_image() {
|
||||||
file_id: "AAQCAAOmBAACBf2oS53pByA-I4CWWCObDwAEAQAHbQADMWcAAhYE".to_string(),
|
let json = r#"{
|
||||||
file_size: Some(10339),
|
"message_id": 199791,
|
||||||
width: 256,
|
"from": {
|
||||||
height: 320
|
"id": 250918540,
|
||||||
}),
|
"is_bot": false,
|
||||||
file_id: "BAADAgADpgQAAgX9qEud6QcgPiOAlhYE".to_string(),
|
"first_name": "Андрей",
|
||||||
file_size: Some(1381334)
|
"last_name": "Власов",
|
||||||
},
|
"username": "aka_dude",
|
||||||
caption: None,
|
"language_code": "en"
|
||||||
caption_entities: vec![],
|
},
|
||||||
media_group_id: Some("12543417770506682".to_string())
|
"chat": {
|
||||||
},
|
"id": 250918540,
|
||||||
reply_markup: None
|
"first_name": "Андрей",
|
||||||
},
|
"last_name": "Власов",
|
||||||
|
"username": "aka_dude",
|
||||||
};
|
"type": "private"
|
||||||
assert_eq!(actual, expected);
|
},
|
||||||
|
"date": 1568290622,
|
||||||
|
"photo": [
|
||||||
|
{
|
||||||
|
"file_id": "AgADAgAD36sxG-PX0UvQSXIn9rccdw-ACA4ABAEAAwIAA20AAybcBAABFgQ",
|
||||||
|
"file_size": 18188,
|
||||||
|
"width": 320,
|
||||||
|
"height": 239
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file_id": "AgADAgAD36sxG-PX0UvQSXIn9rccdw-ACA4ABAEAAwIAA3gAAyfcBAABFgQ",
|
||||||
|
"file_size": 62123,
|
||||||
|
"width": 800,
|
||||||
|
"height": 598
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file_id": "AgADAgAD36sxG-PX0UvQSXIn9rccdw-ACA4ABAEAAwIAA3kAAyTcBAABFgQ",
|
||||||
|
"file_size": 75245,
|
||||||
|
"width": 962,
|
||||||
|
"height": 719
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
let message = from_str::<Message>(json);
|
||||||
|
assert!(message.is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,13 @@ pub use self::{
|
||||||
chat_permissions::ChatPermissions,
|
chat_permissions::ChatPermissions,
|
||||||
chat_photo::ChatPhoto,
|
chat_photo::ChatPhoto,
|
||||||
document::Document,
|
document::Document,
|
||||||
|
force_reply::ForceReply,
|
||||||
|
inline_keyboard_button::InlineKeyboardButton,
|
||||||
|
inline_keyboard_markup::InlineKeyboardMarkup,
|
||||||
input_file::InputFile,
|
input_file::InputFile,
|
||||||
input_media::InputMedia,
|
input_media::InputMedia,
|
||||||
invoice::Invoice,
|
invoice::Invoice,
|
||||||
|
keyboard_button::KeyboardButton,
|
||||||
label_price::LabeledPrice,
|
label_price::LabeledPrice,
|
||||||
message::{
|
message::{
|
||||||
ForwardKind, ForwardedFrom, MediaKind, Message, MessageKind, Sender,
|
ForwardKind, ForwardedFrom, MediaKind, Message, MessageKind, Sender,
|
||||||
|
@ -19,7 +23,11 @@ pub use self::{
|
||||||
order_info::OrderInfo,
|
order_info::OrderInfo,
|
||||||
parse_mode::ParseMode,
|
parse_mode::ParseMode,
|
||||||
photo_size::PhotoSize,
|
photo_size::PhotoSize,
|
||||||
|
poll::{Poll, PollOption},
|
||||||
pre_checkout_query::PreCheckoutQuery,
|
pre_checkout_query::PreCheckoutQuery,
|
||||||
|
reply_keyboard_markup::ReplyKeyboardMarkup,
|
||||||
|
reply_keyboard_remove::ReplyKeyboardRemove,
|
||||||
|
reply_markup::ReplyMarkup,
|
||||||
response_parameters::ResponseParameters,
|
response_parameters::ResponseParameters,
|
||||||
send_invoice::SendInvoice,
|
send_invoice::SendInvoice,
|
||||||
shipping_address::ShippingAddress,
|
shipping_address::ShippingAddress,
|
||||||
|
@ -51,8 +59,8 @@ mod chat_permissions;
|
||||||
mod chat_photo;
|
mod chat_photo;
|
||||||
mod document;
|
mod document;
|
||||||
mod force_reply;
|
mod force_reply;
|
||||||
mod inline_keyboard_markup;
|
|
||||||
mod inline_keyboard_button;
|
mod inline_keyboard_button;
|
||||||
|
mod inline_keyboard_markup;
|
||||||
mod input_file;
|
mod input_file;
|
||||||
mod input_media;
|
mod input_media;
|
||||||
mod invoice;
|
mod invoice;
|
||||||
|
@ -64,11 +72,12 @@ mod not_implemented_types;
|
||||||
mod order_info;
|
mod order_info;
|
||||||
mod parse_mode;
|
mod parse_mode;
|
||||||
mod photo_size;
|
mod photo_size;
|
||||||
|
mod poll;
|
||||||
mod pre_checkout_query;
|
mod pre_checkout_query;
|
||||||
mod response_parameters;
|
|
||||||
mod reply_markup;
|
|
||||||
mod reply_keyboard_markup;
|
mod reply_keyboard_markup;
|
||||||
mod reply_keyboard_remove;
|
mod reply_keyboard_remove;
|
||||||
|
mod reply_markup;
|
||||||
|
mod response_parameters;
|
||||||
mod send_invoice;
|
mod send_invoice;
|
||||||
mod shipping_address;
|
mod shipping_address;
|
||||||
mod shipping_option;
|
mod shipping_option;
|
||||||
|
|
|
@ -1,35 +1,5 @@
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct Location;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct InlineKeyboardMarkup;
|
|
||||||
|
|
||||||
#[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 Poll;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct Animation;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct Game;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct Contact;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct VideoNote;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct Venue;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct Voice;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct MaskPosition;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
||||||
pub struct ChatMemberStatus;
|
pub struct ChatMemberStatus;
|
||||||
|
|
|
@ -69,7 +69,6 @@ pub enum ParseMode {
|
||||||
Markdown,
|
Markdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
13
src/core/types/poll.rs
Normal file
13
src/core/types/poll.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
pub struct Poll {
|
||||||
|
pub id: String,
|
||||||
|
pub question: String,
|
||||||
|
pub options: Vec<PollOption>,
|
||||||
|
pub is_closed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
pub struct PollOption {
|
||||||
|
pub text: String,
|
||||||
|
pub voter_count: i32,
|
||||||
|
}
|
|
@ -3,23 +3,24 @@ use crate::core::types::KeyboardButton;
|
||||||
/// This object represents a custom keyboard with reply options.
|
/// This object represents a custom keyboard with reply options.
|
||||||
#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq, Clone)]
|
||||||
pub struct ReplyKeyboardMarkup {
|
pub struct ReplyKeyboardMarkup {
|
||||||
/// Array of button rows, each represented by an Array of [`KeyboardButton`]
|
/// Array of button rows, each represented by an Array of
|
||||||
/// objects
|
/// [`KeyboardButton`] objects
|
||||||
pub keyboard: Vec<Vec<KeyboardButton>>,
|
pub keyboard: Vec<Vec<KeyboardButton>>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Optional. Requests clients to resize the keyboard vertically for optimal
|
/// Optional. Requests clients to resize the keyboard vertically for
|
||||||
/// fit (e.g., make the keyboard smaller if there are just two rows of
|
/// optimal fit (e.g., make the keyboard smaller if there are just two
|
||||||
/// buttons). Defaults to false, in which case the custom keyboard is always
|
/// rows of buttons). Defaults to false, in which case the custom
|
||||||
/// of the same height as the app's standard keyboard.
|
/// keyboard is always of the same height as the app's standard
|
||||||
|
/// keyboard.
|
||||||
pub resize_keyboard: Option<bool>,
|
pub resize_keyboard: Option<bool>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Optional. Requests clients to hide the keyboard as soon as it's been
|
/// Optional. Requests clients to hide the keyboard as soon as it's been
|
||||||
/// used. The keyboard will still be available, but clients will
|
/// used. The keyboard will still be available, but clients will
|
||||||
/// automatically display the usual letter-keyboard in the chat – the user
|
/// automatically display the usual letter-keyboard in the chat – the user
|
||||||
/// can press a special button in the input field to see the custom keyboard
|
/// can press a special button in the input field to see the custom
|
||||||
/// again. Defaults to false.
|
/// keyboard again. Defaults to false.
|
||||||
pub one_time_keyboard: Option<bool>,
|
pub one_time_keyboard: Option<bool>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -32,4 +33,4 @@ pub struct ReplyKeyboardMarkup {
|
||||||
/// the request with a keyboard to select the new language. Other users in
|
/// the request with a keyboard to select the new language. Other users in
|
||||||
/// the group don’t see the keyboard.
|
/// the group don’t see the keyboard.
|
||||||
pub selective: Option<bool>,
|
pub selective: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,4 +20,4 @@ pub struct ReplyKeyboardRemove {
|
||||||
/// the request with a keyboard to select the new language. Other users in
|
/// the request with a keyboard to select the new language. Other users in
|
||||||
/// the group don’t see the keyboard.
|
/// the group don’t see the keyboard.
|
||||||
pub selective: Option<bool>,
|
pub selective: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
use crate::core::types::{
|
use crate::core::types::{
|
||||||
InlineKeyboardMarkup,
|
ForceReply, InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove,
|
||||||
ReplyKeyboardMarkup,
|
|
||||||
ReplyKeyboardRemove,
|
|
||||||
ForceReply,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
|
@ -12,9 +12,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn migrate_to_chat_id_deserialization() {
|
fn migrate_to_chat_id_deserialization() {
|
||||||
let expected = ResponseParameters::MigrateToChatId(123456);
|
let expected = ResponseParameters::MigrateToChatId(123456);
|
||||||
let actual: ResponseParameters = serde_json::from_str(
|
let actual: ResponseParameters =
|
||||||
r#"{"migrate_to_chat_id":123456}"#
|
serde_json::from_str(r#"{"migrate_to_chat_id":123456}"#).unwrap();
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
@ -22,10 +21,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn retry_after_deserialization() {
|
fn retry_after_deserialization() {
|
||||||
let expected = ResponseParameters::RetryAfter(123456);
|
let expected = ResponseParameters::RetryAfter(123456);
|
||||||
let actual: ResponseParameters = serde_json::from_str(
|
let actual: ResponseParameters =
|
||||||
r#"{"retry_after":123456}"#
|
serde_json::from_str(r#"{"retry_after":123456}"#).unwrap();
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
src/core/types/user_profile_photos.rs
Normal file
7
src/core/types/user_profile_photos.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use crate::core::types::PhotoSize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
||||||
|
pub struct UserProfilePhotos {
|
||||||
|
pub total_count: u32,
|
||||||
|
pub photos: Vec<Vec<PhotoSize>>
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::core::types::PhotoSize;
|
use crate::core::types::PhotoSize;
|
||||||
|
|
||||||
|
|
||||||
/// This object represents a video file.
|
/// This object represents a video file.
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
||||||
pub struct Video {
|
pub struct Video {
|
||||||
|
|
Loading…
Reference in a new issue