Merge branch 'dev' into dispatcher

This commit is contained in:
Waffle Lapkin 2019-10-06 11:23:52 +03:00 committed by GitHub
commit edeff3305a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 1223 additions and 448 deletions

View file

@ -17,4 +17,5 @@ bytes = "0.4.12"
log = "0.4.8"
futures-preview = "0.3.0-alpha.18"
pin-project = "0.4.0-alpha.7"
async-trait = "0.1.13"
libc = "0.2.62"

View file

@ -26,7 +26,8 @@ impl Bot {
/// let bot = Bot::new("TOKEN");
/// let mut file = File::create("/home/waffle/Pictures/test.png").await?;
///
/// let TgFile { file_path, .. } = bot.get_file("*file_id*").send().await?;
/// let TgFile { file_path, .. } =
/// bot.get_file("*file_id*").send_boxed().await?;
/// bot.download_file(&file_path, &mut file).await?;
/// # Ok(()) }
/// ```

View file

@ -1,10 +1,10 @@
mod api;
mod download;
use reqwest::r#async::Client;
use crate::requests::RequestContext;
mod api;
mod download;
pub struct Bot {
token: String,
client: Client,

View file

@ -5,12 +5,12 @@ extern crate derive_more;
#[macro_use]
extern crate serde;
mod network;
pub use errors::{DownloadError, RequestError};
mod errors;
mod network;
pub mod bot;
pub mod dispatcher;
pub mod requests;
pub mod types;
pub use errors::{DownloadError, RequestError};

View file

@ -6,10 +6,9 @@ use tokio::{
stream::Stream,
};
use crate::{
network::{file_url, TELEGRAM_API_URL},
DownloadError,
};
use crate::DownloadError;
use super::TELEGRAM_API_URL;
pub async fn download_file<D>(
client: &Client,
@ -35,7 +34,10 @@ pub async fn download_file_stream(
token: &str,
path: &str,
) -> Result<impl Stream<Item = Result<Chunk, reqwest::Error>>, reqwest::Error> {
let url = file_url(TELEGRAM_API_URL, token, path);
let resp = client.get(&url).send().await?.error_for_status()?;
Ok(resp.into_body())
Ok(client
.get(&super::file_url(TELEGRAM_API_URL, token, path))
.send()
.await?
.error_for_status()?
.into_body())
}

View file

@ -1,12 +1,12 @@
mod download;
mod request;
mod telegram_response;
pub use download::{download_file, download_file_stream};
pub use request::{request_json, request_multipart};
pub use telegram_response::TelegramResponse;
pub const TELEGRAM_API_URL: &str = "https://api.telegram.org";
mod download;
mod request;
mod telegram_response;
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
/// Creates URL for making HTTPS requests. See the [Telegram documentation].
///

View file

@ -2,11 +2,9 @@ use apply::Apply;
use reqwest::r#async::{multipart::Form, Client, Response};
use serde::{de::DeserializeOwned, Serialize};
use crate::{
network::{method_url, TelegramResponse, TELEGRAM_API_URL},
requests::ResponseResult,
RequestError,
};
use crate::{requests::ResponseResult, RequestError};
use super::{TelegramResponse, TELEGRAM_API_URL};
pub async fn request_multipart<T>(
client: &Client,
@ -19,7 +17,7 @@ where
{
process_response(
client
.post(&method_url(TELEGRAM_API_URL, token, method_name))
.post(&super::method_url(TELEGRAM_API_URL, token, method_name))
.apply(|request_builder| match params {
Some(params) => request_builder.multipart(params),
None => request_builder,
@ -39,7 +37,7 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
) -> ResponseResult<T> {
process_response(
client
.post(&method_url(TELEGRAM_API_URL, token, method_name))
.post(&super::method_url(TELEGRAM_API_URL, token, method_name))
.json(params)
.send()
.await
@ -51,10 +49,9 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
async fn process_response<T: DeserializeOwned>(
mut response: Response,
) -> ResponseResult<T> {
let response = serde_json::from_str::<TelegramResponse<T>>(
serde_json::from_str::<TelegramResponse<T>>(
&response.text().await.map_err(RequestError::NetworkError)?,
)
.map_err(RequestError::InvalidJson)?;
response.into()
.map_err(RequestError::InvalidJson)?
.into()
}

View file

@ -1,7 +1,9 @@
use async_trait::async_trait;
use crate::{
network,
requests::{Request, RequestContext, RequestFuture, ResponseResult},
types::True
requests::{Request, RequestContext, ResponseResult},
types::True,
};
#[derive(Debug, Serialize, Clone)]
@ -32,19 +34,24 @@ pub struct AnswerPreCheckoutQuery<'a> {
pub error_message: Option<String>,
}
impl<'a> Request<'a> for AnswerPreCheckoutQuery<'a> {
#[async_trait]
impl Request for AnswerPreCheckoutQuery<'_> {
type ReturnValue = True;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"answerPreCheckoutQuery",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl AnswerPreCheckoutQuery<'_> {
pub async fn send(self) -> ResponseResult<True> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"answerPreCheckoutQuery",
&self,
)
.await
}
}

View file

@ -1,6 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{Request, RequestContext, RequestFuture, ResponseResult},
requests::{Request, RequestContext, ResponseResult},
types::{ShippingOption, True},
};
@ -33,19 +35,24 @@ pub struct AnswerShippingQuery<'a> {
pub error_message: Option<String>,
}
impl<'a> Request<'a> for AnswerShippingQuery<'a> {
#[async_trait]
impl Request for AnswerShippingQuery<'_> {
type ReturnValue = True;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"answerShippingQuery",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl AnswerShippingQuery<'_> {
pub async fn send(self) -> ResponseResult<True> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"answerShippingQuery",
&self,
)
.await
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{Message, ReplyMarkup},
};
@ -38,19 +38,24 @@ pub struct EditMessageLiveLocation<'a> {
reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for EditMessageLiveLocation<'a> {
#[async_trait]
impl Request for EditMessageLiveLocation<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"editMessageLiveLocation",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl EditMessageLiveLocation<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"editMessageLiveLocation",
&self,
)
.await
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::Message,
};
@ -28,19 +28,24 @@ pub struct ForwardMessage<'a> {
pub disable_notification: Option<bool>,
}
impl<'a> Request<'a> for ForwardMessage<'a> {
#[async_trait]
impl Request for ForwardMessage<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
self.ctx.client,
self.ctx.token,
"forwardMessage",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl ForwardMessage<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
self.ctx.client,
self.ctx.token,
"forwardMessage",
&self,
)
.await
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::Chat,
};
@ -19,19 +19,24 @@ pub struct GetChat<'a> {
chat_id: ChatId,
}
impl<'a> Request<'a> for GetChat<'a> {
#[async_trait]
impl Request for GetChat<'_> {
type ReturnValue = Chat;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getChat",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl GetChat<'_> {
pub async fn send(self) -> ResponseResult<Chat> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getChat",
&self,
)
.await
}
}

View file

@ -1,6 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{Request, RequestContext, RequestFuture, ResponseResult},
requests::{Request, RequestContext, ResponseResult},
types::File,
};
@ -19,19 +21,24 @@ pub struct GetFile<'a> {
pub file_id: String,
}
impl<'a> Request<'a> for GetFile<'a> {
#[async_trait]
impl Request for GetFile<'_> {
type ReturnValue = File;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getFile",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl GetFile<'_> {
pub async fn send(self) -> ResponseResult<File> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getFile",
&self,
)
.await
}
}

View file

@ -1,6 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{Request, RequestContext, RequestFuture, ResponseResult},
requests::{Request, RequestContext, ResponseResult},
types::User,
};
@ -11,16 +13,24 @@ pub struct GetMe<'a> {
ctx: RequestContext<'a>,
}
impl<'a> Request<'a> for GetMe<'a> {
#[async_trait]
impl Request for GetMe<'_> {
type ReturnValue = User;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(network::request_multipart(
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl GetMe<'_> {
pub async fn send(self) -> ResponseResult<User> {
network::request_multipart(
self.ctx.client,
self.ctx.token,
"getMe",
None,
))
)
.await
}
}

View file

@ -1,6 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{Request, RequestContext, RequestFuture, ResponseResult},
requests::{Request, RequestContext, ResponseResult},
types::Update,
};
@ -27,19 +29,24 @@ pub enum AllowedUpdate {
CallbackQuery,
}
impl<'a> Request<'a> for GetUpdates<'a> {
#[async_trait]
impl Request for GetUpdates<'_> {
type ReturnValue = Vec<Update>;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getUpdates",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl GetUpdates<'_> {
pub async fn send(self) -> ResponseResult<Vec<Update>> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getUpdates",
&self,
)
.await
}
}

View file

@ -1,6 +1,10 @@
use crate::network;
use crate::requests::{Request, RequestContext, RequestFuture, ResponseResult};
use crate::types::UserProfilePhotos;
use async_trait::async_trait;
use crate::{
network,
requests::{Request, RequestContext, ResponseResult},
types::UserProfilePhotos,
};
///Use this method to get a list of profile pictures for a user. Returns a
/// UserProfilePhotos object.
@ -20,19 +24,24 @@ pub struct GetUserProfilePhotos<'a> {
pub limit: Option<i64>,
}
impl<'a> Request<'a> for GetUserProfilePhotos<'a> {
#[async_trait]
impl Request for GetUserProfilePhotos<'_> {
type ReturnValue = UserProfilePhotos;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getUserProfilePhotos",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl GetUserProfilePhotos<'_> {
async fn send(self) -> ResponseResult<UserProfilePhotos> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"getUserProfilePhotos",
&self,
)
.await
}
}
@ -63,11 +72,10 @@ impl<'a> GetUserProfilePhotos<'a> {
}
pub fn limit<T>(mut self, limit: T) -> Self
where
T: Into<i64>,
where
T: Into<i64>,
{
self.limit = Some(limit.into());
self
}
}

View file

@ -1,8 +1,10 @@
use crate::network;
use crate::requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
use async_trait::async_trait;
use crate::{
network,
requests::{ChatId, Request, RequestContext, ResponseResult},
types::True,
};
use crate::types::True;
/// 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
@ -25,19 +27,24 @@ pub struct KickChatMember<'a> {
pub until_date: Option<u64>,
}
impl<'a> Request<'a> for KickChatMember<'a> {
#[async_trait]
impl<'a> Request for KickChatMember<'a> {
type ReturnValue = True;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
self.ctx.client,
self.ctx.token,
"kickChatMember",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl KickChatMember<'_> {
async fn send(self) -> ResponseResult<True> {
network::request_json(
self.ctx.client,
self.ctx.token,
"kickChatMember",
&self,
)
.await
}
}

View file

@ -1,9 +1,6 @@
mod form_builder;
mod utils;
use async_trait::async_trait;
use reqwest::r#async::Client;
use serde::de::DeserializeOwned;
use std::{future::Future, pin::Pin};
use crate::RequestError;
@ -15,28 +12,33 @@ pub use self::{
get_me::GetMe, get_updates::GetUpdates,
get_user_profile_photos::GetUserProfilePhotos,
kick_chat_member::KickChatMember, pin_chat_message::PinChatMessage,
restrict_chat_member::RestrictChatMember,
promote_chat_member::PromoteChatMember,
restrict_chat_member::RestrictChatMember, send_animation::SendAnimation,
send_audio::SendAudio, send_chat_action::SendChatAction,
send_contact::SendContact, send_location::SendLocation,
send_media_group::SendMediaGroup, send_message::SendMessage,
send_photo::SendPhoto, send_poll::SendPoll, send_venue::SendVenue,
send_contact::SendContact, send_document::SendDocument,
send_location::SendLocation, send_media_group::SendMediaGroup,
send_message::SendMessage, send_photo::SendPhoto, send_poll::SendPoll,
send_venue::SendVenue, send_video::SendVideo,
send_video_note::SendVideoNote, send_voice::SendVoice,
stop_message_live_location::StopMessageLiveLocation,
unban_chat_member::UnbanChatMember,
unban_chat_member::UnbanChatMember, unpin_chat_message::UnpinChatMessage,
};
mod form_builder;
mod utils;
pub type ResponseResult<T> = Result<T, RequestError>;
/// Request that can be sent to telegram.
/// `ReturnValue` - a type that will be returned from Telegram.
pub trait Request<'a> {
#[async_trait]
pub trait Request {
type ReturnValue: DeserializeOwned;
/// Send request to telegram
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>>;
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue>;
}
pub type RequestFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Debug, Clone)]
pub struct RequestContext<'a> {
pub client: &'a Client,
@ -91,15 +93,22 @@ mod get_updates;
mod get_user_profile_photos;
mod kick_chat_member;
mod pin_chat_message;
mod promote_chat_member;
mod restrict_chat_member;
mod send_animation;
mod send_audio;
mod send_chat_action;
mod send_contact;
mod send_document;
mod send_location;
mod send_media_group;
mod send_message;
mod send_photo;
mod send_poll;
mod send_venue;
mod send_video;
mod send_video_note;
mod send_voice;
mod stop_message_live_location;
mod unban_chat_member;
mod unpin_chat_message;

View file

@ -1,50 +1,65 @@
use crate::{
requests::{ChatId, RequestContext, RequestFuture, ResponseResult, Request},
types::True
};
use crate::network;
use async_trait::async_trait;
/// Use this method to get up to date information about the chat
/// (current name of the user for one-on-one conversations,
/// current username of a user, group or channel, etc.).
use crate::{
network,
requests::{ChatId, Request, RequestContext, ResponseResult},
types::True,
};
/// Use this method to get up to date information about the chat
/// (current name of the user for one-on-one conversations,
/// current username of a user, group or channel, etc.).
/// Returns a Chat object on success.
#[derive(Debug, Clone, Serialize)]
pub struct PinChatMessage<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
/// Unique identifier for the target chat or username
/// Unique identifier for the target chat or username
/// of the target supergroup or channel (in the format @channelusername)
pub chat_id: ChatId,
pub message_id: i32,
pub disable_notification: Option<bool>
pub disable_notification: Option<bool>,
}
impl<'a> PinChatMessage<'a> {
pub(crate) fn new(
ctx: RequestContext<'a>, chat_id: ChatId, message_id: i32
ctx: RequestContext<'a>,
chat_id: ChatId,
message_id: i32,
) -> Self {
Self { ctx, chat_id, message_id, disable_notification: None }
Self {
ctx,
chat_id,
message_id,
disable_notification: None,
}
}
pub fn disable_notification<T>(mut self, val: T) -> Self
where T: Into<bool>
where
T: Into<bool>,
{
self.disable_notification = Some(val.into());
self
}
}
impl<'a> Request<'a> for PinChatMessage<'a> {
#[async_trait]
impl<'a> Request for PinChatMessage<'a> {
type ReturnValue = True;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"pinChatMessage",
&self,
).await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl PinChatMessage<'_> {
async fn send(self) -> ResponseResult<True> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"pinChatMessage",
&self,
)
.await
}
}

View file

@ -0,0 +1,170 @@
use crate::network;
use crate::requests::{ChatId, Request, RequestContext, ResponseResult};
use crate::types::True;
use async_trait::async_trait;
///Use this method to promote or demote a user in a supergroup or a channel.
/// The bot must be an administrator in the chat for this to work and must have
/// the appropriate admin rights. Pass False for all boolean parameters to
/// demote a user. Returns True on success.
#[derive(Debug, Clone, Serialize)]
pub struct PromoteChatMember<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
///Unique identifier for the target chat or username of the target channel
/// (in the format @channelusername)
pub chat_id: ChatId,
///Unique identifier of the target user
pub user_id: i32,
///Pass True, if the administrator can change chat title, photo and other
/// settings
#[serde(skip_serializing_if = "Option::is_none")]
can_change_info: Option<bool>,
///Pass True, if the administrator can create channel posts, channels only
#[serde(skip_serializing_if = "Option::is_none")]
pub can_post_messages: Option<bool>,
///Pass True, if the administrator can edit messages of other users and
/// can pin messages, channels only
#[serde(skip_serializing_if = "Option::is_none")]
pub can_edit_messages: Option<bool>,
///Pass True, if the administrator can delete messages of other users
#[serde(skip_serializing_if = "Option::is_none")]
pub can_delete_messages: Option<bool>,
///Pass True, if the administrator can invite new users to the chat
#[serde(skip_serializing_if = "Option::is_none")]
pub can_invite_users: Option<bool>,
///Pass True, if the administrator can restrict, ban or unban chat members
#[serde(skip_serializing_if = "Option::is_none")]
pub can_restrict_members: Option<bool>,
///Pass True, if the administrator can pin messages, supergroups only
#[serde(skip_serializing_if = "Option::is_none")]
pub can_pin_messages: Option<bool>,
///Pass True, if the administrator can add new administrators with a
/// subset of his own privileges or demote administrators that he has
/// promoted, directly or indirectly (promoted by administrators that were
/// appointed by him)
#[serde(skip_serializing_if = "Option::is_none")]
pub can_promote_members: Option<bool>,
}
#[async_trait]
impl Request for PromoteChatMember<'_> {
type ReturnValue = True;
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl PromoteChatMember<'_> {
pub async fn send(self) -> ResponseResult<True> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"promoteChatMember",
&self,
)
.await
}
}
impl<'a> PromoteChatMember<'a> {
pub(crate) fn new(
ctx: RequestContext<'a>,
chat_id: ChatId,
user_id: i32,
) -> Self {
Self {
ctx,
chat_id,
user_id,
can_change_info: None,
can_post_messages: None,
can_edit_messages: None,
can_delete_messages: None,
can_invite_users: None,
can_restrict_members: None,
can_pin_messages: None,
can_promote_members: None,
}
}
pub fn chat_id<T>(mut self, chat_id: T) -> Self
where
T: Into<ChatId>,
{
self.chat_id = chat_id.into();
self
}
pub fn user_id<T>(mut self, user_id: T) -> Self
where
T: Into<i32>,
{
self.user_id = user_id.into();
self
}
pub fn can_change_info<T>(mut self, can_change_info: T) -> Self
where
T: Into<bool>,
{
self.can_change_info = Some(can_change_info.into());
self
}
pub fn can_post_messages<T>(mut self, can_post_messages: T) -> Self
where
T: Into<bool>,
{
self.can_post_messages = Some(can_post_messages.into());
self
}
pub fn can_edit_messages<T>(mut self, can_edit_messages: T) -> Self
where
T: Into<bool>,
{
self.can_edit_messages = Some(can_edit_messages.into());
self
}
pub fn can_delete_messages<T>(mut self, can_delete_messages: T) -> Self
where
T: Into<bool>,
{
self.can_delete_messages = Some(can_delete_messages.into());
self
}
pub fn can_invite_users<T>(mut self, can_invite_users: T) -> Self
where
T: Into<bool>,
{
self.can_invite_users = Some(can_invite_users.into());
self
}
pub fn can_restrict_members<T>(mut self, can_restrict_members: T) -> Self
where
T: Into<bool>,
{
self.can_restrict_members = Some(can_restrict_members.into());
self
}
pub fn can_pin_messages<T>(mut self, can_pin_messages: T) -> Self
where
T: Into<bool>,
{
self.can_pin_messages = Some(can_pin_messages.into());
self
}
pub fn can_promote_members<T>(mut self, can_promote_members: T) -> Self
where
T: Into<bool>,
{
self.can_promote_members = Some(can_promote_members.into());
self
}
}

View file

@ -1,8 +1,9 @@
use crate::network;
use crate::requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
use crate::{
network,
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{ChatPermissions, True},
};
use crate::types::{ChatPermissions, True};
use async_trait::async_trait;
/// Use this method to restrict a user in a supergroup. The bot must be an
/// administrator in the supergroup for this to work and must have the
@ -26,19 +27,24 @@ pub struct RestrictChatMember<'a> {
pub until_date: Option<u64>,
}
impl<'a> Request<'a> for RestrictChatMember<'a> {
#[async_trait]
impl<'a> Request for RestrictChatMember<'a> {
type ReturnValue = True;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"restrictChatMember",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl RestrictChatMember<'_> {
async fn send(self) -> ResponseResult<True> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"restrictChatMember",
&self,
)
.await
}
}

View file

@ -0,0 +1,8 @@
use crate::requests::RequestContext;
///TODO: add implementation
#[derive(Debug, Clone, Serialize)]
pub struct SendAnimation<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
}

View file

@ -1,8 +1,10 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
form_builder::FormBuilder, ChatId, Request, RequestContext,
RequestFuture, ResponseResult,
ResponseResult,
},
types::{InputFile, Message, ParseMode, ReplyMarkup},
};
@ -59,48 +61,53 @@ pub struct SendAudio<'a> {
pub reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for SendAudio<'a> {
#[async_trait]
impl Request for SendAudio<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
let mut params = FormBuilder::new()
.add("chat_id", &self.chat_id)
.add_if_some("caption", self.caption.as_ref())
.add_if_some("parse_mode", self.parse_mode.as_ref())
.add_if_some("duration", self.duration.as_ref())
.add_if_some("performer", self.performer.as_ref())
.add_if_some("title", self.title.as_ref())
.add_if_some(
"disable_notification",
self.disable_notification.as_ref(),
)
.add_if_some(
"reply_to_message_id",
self.reply_to_message_id.as_ref(),
);
params = match self.audio {
InputFile::File(file) => params.add_file("audio", &file),
InputFile::Url(url) => params.add("audio", &url),
InputFile::FileId(file_id) => params.add("audio", &file_id),
};
if let Some(thumb) = self.thumb {
params = match thumb {
InputFile::File(file) => params.add_file("thumb", &file),
InputFile::Url(url) => params.add("thumb", &url),
InputFile::FileId(file_id) => params.add("thumb", &file_id),
}
}
let params = params.build();
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
network::request_multipart(
&self.ctx.client,
&self.ctx.token,
"sendAudio",
Some(params),
impl SendAudio<'_> {
pub async fn send(self) -> ResponseResult<Message> {
let mut params = FormBuilder::new()
.add("chat_id", &self.chat_id)
.add_if_some("caption", self.caption.as_ref())
.add_if_some("parse_mode", self.parse_mode.as_ref())
.add_if_some("duration", self.duration.as_ref())
.add_if_some("performer", self.performer.as_ref())
.add_if_some("title", self.title.as_ref())
.add_if_some(
"disable_notification",
self.disable_notification.as_ref(),
)
.await
})
.add_if_some(
"reply_to_message_id",
self.reply_to_message_id.as_ref(),
);
params = match self.audio {
InputFile::File(file) => params.add_file("audio", &file),
InputFile::Url(url) => params.add("audio", &url),
InputFile::FileId(file_id) => params.add("audio", &file_id),
};
if let Some(thumb) = self.thumb {
params = match thumb {
InputFile::File(file) => params.add_file("thumb", &file),
InputFile::Url(url) => params.add("thumb", &url),
InputFile::FileId(file_id) => params.add("thumb", &file_id),
}
}
let params = params.build();
network::request_multipart(
&self.ctx.client,
&self.ctx.token,
"sendAudio",
Some(params),
)
.await
}
}

View file

@ -1,9 +1,9 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
types::True
requests::{ChatId, Request, RequestContext, ResponseResult},
types::True,
};
///Use this method when you need to tell the user that something is happening
@ -40,19 +40,24 @@ pub enum ChatAction {
UploadVideoNote,
}
impl<'a> Request<'a> for SendChatAction<'a> {
#[async_trait]
impl Request for SendChatAction<'_> {
type ReturnValue = True;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendChatAction",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendChatAction<'_> {
pub async fn send(self) -> ResponseResult<True> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendChatAction",
&self,
)
.await
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{Message, ReplyMarkup},
};
@ -42,19 +42,24 @@ pub struct SendContact<'a> {
pub reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for SendContact<'a> {
#[async_trait]
impl Request for SendContact<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendContact",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendContact<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendContact",
&self,
)
.await
}
}

View file

@ -0,0 +1,8 @@
use crate::requests::RequestContext;
///TODO: add implementation
#[derive(Debug, Clone, Serialize)]
pub struct SendDocument<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
}

View file

@ -1,13 +1,12 @@
use async_trait::async_trait;
use serde::Serialize;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{Message, ReplyMarkup},
};
use serde::Serialize;
#[derive(Debug, Clone, Serialize)]
/// Use this method to send point on the map. On success, the sent [`Message`]
/// is returned.
@ -38,19 +37,24 @@ pub struct SendLocation<'a> {
reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for SendLocation<'a> {
#[async_trait]
impl Request for SendLocation<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendLocation",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendLocation<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendLocation",
&self,
)
.await
}
}

View file

@ -1,10 +1,11 @@
use apply::Apply;
use async_trait::async_trait;
use crate::{
network::request_multipart,
requests::{
form_builder::FormBuilder, ChatId, Request, RequestContext,
RequestFuture, ResponseResult,
ResponseResult,
},
types::{InputFile, InputMedia, Message},
};
@ -21,49 +22,51 @@ pub struct SendMediaGroup<'a> {
pub reply_to_message_id: Option<i32>,
}
impl<'a> Request<'a> for SendMediaGroup<'a> {
#[async_trait]
impl Request for SendMediaGroup<'_> {
type ReturnValue = Vec<Message>;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
let params = FormBuilder::new()
.add("chat_id", &self.chat_id)
.apply(|form| {
self.media.iter().map(|e| e.media()).fold(
form,
|acc, file| {
if let InputFile::File(path) = file {
acc.add_file(
&path
.file_name()
.unwrap()
.to_string_lossy(),
path,
)
} else {
acc
}
},
)
})
.add("media", &self.media)
.add_if_some(
"disable_notification",
self.disable_notification.as_ref(),
)
.add_if_some(
"reply_to_message_id",
self.reply_to_message_id.as_ref(),
)
.build();
request_multipart(
&self.ctx.client,
&self.ctx.token,
"sendMediaGroup",
Some(params),
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendMediaGroup<'_> {
pub async fn send(self) -> ResponseResult<Vec<Message>> {
let params = FormBuilder::new()
.add("chat_id", &self.chat_id)
.apply(|form| {
self.media
.iter()
.map(|e| e.media())
.fold(form, |acc, file| {
if let InputFile::File(path) = file {
acc.add_file(
&path.file_name().unwrap().to_string_lossy(),
path,
)
} else {
acc
}
})
})
.add("media", &self.media)
.add_if_some(
"disable_notification",
self.disable_notification.as_ref(),
)
.await
})
.add_if_some(
"reply_to_message_id",
self.reply_to_message_id.as_ref(),
)
.build();
request_multipart(
&self.ctx.client,
&self.ctx.token,
"sendMediaGroup",
Some(params),
)
.await
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{Message, ParseMode, ReplyMarkup},
};
@ -43,19 +43,24 @@ pub struct SendMessage<'a> {
pub reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for SendMessage<'a> {
#[async_trait]
impl Request for SendMessage<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
self.ctx.client,
self.ctx.token,
"sendMessage",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendMessage<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
self.ctx.client,
self.ctx.token,
"sendMessage",
&self,
)
.await
}
}

View file

@ -1,8 +1,10 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
form_builder::FormBuilder, ChatId, Request, RequestContext,
RequestFuture, ResponseResult,
ResponseResult,
},
types::{InputFile, Message, ParseMode, ReplyMarkup},
};
@ -43,39 +45,44 @@ pub struct SendPhoto<'a> {
pub reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for SendPhoto<'a> {
#[async_trait]
impl Request for SendPhoto<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
let mut params = FormBuilder::new()
.add("chat_id", &self.chat_id)
.add_if_some("caption", self.caption.as_ref())
.add_if_some("parse_mode", self.parse_mode.as_ref())
.add_if_some(
"disable_notification",
self.disable_notification.as_ref(),
)
.add_if_some(
"reply_to_message_id",
self.reply_to_message_id.as_ref(),
);
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
params = match self.photo {
InputFile::File(path) => params.add_file("photo", &path),
InputFile::Url(url) => params.add("photo", &url),
InputFile::FileId(file_id) => params.add("photo", &file_id),
};
let params = params.build();
network::request_multipart(
&self.ctx.client,
&self.ctx.token,
"sendPhoto",
Some(params),
impl SendPhoto<'_> {
pub async fn send(self) -> ResponseResult<Message> {
let mut params = FormBuilder::new()
.add("chat_id", &self.chat_id)
.add_if_some("caption", self.caption.as_ref())
.add_if_some("parse_mode", self.parse_mode.as_ref())
.add_if_some(
"disable_notification",
self.disable_notification.as_ref(),
)
.await
})
.add_if_some(
"reply_to_message_id",
self.reply_to_message_id.as_ref(),
);
params = match self.photo {
InputFile::File(path) => params.add_file("photo", &path),
InputFile::Url(url) => params.add("photo", &url),
InputFile::FileId(file_id) => params.add("photo", &file_id),
};
let params = params.build();
network::request_multipart(
&self.ctx.client,
&self.ctx.token,
"sendPhoto",
Some(params),
)
.await
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{Message, ReplyMarkup},
};
@ -32,19 +32,24 @@ pub struct SendPoll<'a> {
reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for SendPoll<'a> {
#[async_trait]
impl Request for SendPoll<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendPoll",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendPoll<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendPoll",
&self,
)
.await
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{Message, ReplyMarkup},
};
@ -47,19 +47,24 @@ pub struct SendVenue<'a> {
pub reply_markup: Option<ReplyMarkup>,
}
impl<'a> Request<'a> for SendVenue<'a> {
#[async_trait]
impl Request for SendVenue<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendVenue",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendVenue<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendVenue",
&self,
)
.await
}
}

View file

@ -0,0 +1,8 @@
use crate::requests::RequestContext;
///TODO: add implementation
#[derive(Debug, Clone, Serialize)]
pub struct SendVideo<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
}

View file

@ -0,0 +1,154 @@
use crate::network;
use crate::requests::{ChatId, Request, RequestContext, ResponseResult};
use crate::types::{Message, ReplyMarkup};
use async_trait::async_trait;
///As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1
/// minute long. Use this method to send video messages. On success, the sent
/// Message is returned.
#[derive(Debug, Clone, Serialize)]
pub struct SendVideoNote<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
///Unique identifier for the target chat or username of the target channel
/// (in the format @channelusername)
pub chat_id: ChatId,
///Video note to send. Pass a file_id as String to send a video note that
/// exists on the Telegram servers (recommended) or upload a new video
/// using multipart/form-data. More info on Sending Files ». Sending video
/// notes by a URL is currently unsupported
pub video_note: String, // InputFile or String
///Duration of sent video in seconds
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<u64>,
/// Video width and height, i.e. diameter of the video message
#[serde(skip_serializing_if = "Option::is_none")]
pub length: Option<u64>,
///Thumbnail of the file sent; can be ignored if thumbnail generation for
/// the file is supported server-side. The thumbnail should be in JPEG
/// format and less than 200 kB in size. A thumbnails width and height
/// should not exceed 320. Ignored if the file is not uploaded using
/// multipart/form-data. Thumbnails cant be reused and can be only
/// uploaded as a new file, so you can pass “attach://<file_attach_name>”
/// if the thumbnail was uploaded using multipart/form-data under
/// <file_attach_name>. More info on Sending Files »
#[serde(skip_serializing_if = "Option::is_none")]
pub thumb: Option<String>, // InputFile or String
///Sends the message silently. Users will receive a notification with no
/// sound.
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_notification: Option<bool>,
///If the message is a reply, ID of the original message
#[serde(skip_serializing_if = "Option::is_none")]
pub reply_to_message_id: Option<i32>,
/// 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")]
pub reply_markup: Option<ReplyMarkup>,
}
#[async_trait]
impl Request for SendVideoNote<'_> {
type ReturnValue = Message;
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendVideoNote<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendVideoNote",
&self,
)
.await
}
}
impl<'a> SendVideoNote<'a> {
pub(crate) fn new(
ctx: RequestContext<'a>,
chat_id: ChatId,
video_note: String,
) -> Self {
Self {
ctx,
chat_id,
video_note,
duration: None,
length: None,
thumb: None,
disable_notification: None,
reply_to_message_id: None,
reply_markup: None,
}
}
pub fn chat_id<T>(mut self, chat_id: T) -> Self
where
T: Into<ChatId>,
{
self.chat_id = chat_id.into();
self
}
pub fn video_note<T>(mut self, video_note: T) -> Self
where
T: Into<String>,
{
self.video_note = video_note.into();
self
}
pub fn duration<T>(mut self, duration: T) -> Self
where
T: Into<u64>,
{
self.duration = Some(duration.into());
self
}
pub fn length<T>(mut self, length: T) -> Self
where
T: Into<u64>,
{
self.length = Some(length.into());
self
}
pub fn thumb<T>(mut self, thumb: T) -> Self
where
T: Into<String>,
{
self.thumb = Some(thumb.into());
self
}
pub fn disable_notification<T>(mut self, disable_notification: T) -> Self
where
T: Into<bool>,
{
self.disable_notification = Some(disable_notification.into());
self
}
pub fn reply_to_message_id<T>(mut self, reply_to_message_id: T) -> Self
where
T: Into<i32>,
{
self.reply_to_message_id = Some(reply_to_message_id.into());
self
}
pub fn reply_markup<T>(mut self, reply_markup: T) -> Self
where
T: Into<ReplyMarkup>,
{
self.reply_markup = Some(reply_markup.into());
self
}
}

155
src/requests/send_voice.rs Normal file
View file

@ -0,0 +1,155 @@
use crate::network;
use crate::requests::{ChatId, Request, RequestContext, ResponseResult};
use crate::types::{Message, ReplyMarkup, ParseMode};
use async_trait::async_trait;
///Use this method to send audio files, if you want Telegram clients to display
/// the file as a playable voice message. For this to work, your audio must be
/// in an .ogg file encoded with OPUS (other formats may be sent as Audio or
/// Document). On success, the sent Message is returned. Bots can currently send
/// voice messages of up to 50 MB in size, this limit may be changed in the
/// future.
#[derive(Debug, Clone, Serialize)]
pub struct SendVoice<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
/// Unique identifier for the target chat or username of the target channel
/// (in the format @channelusername)
pub chat_id: ChatId,
/// Audio file to send. Pass a file_id as String to send a file that exists
/// on the Telegram servers (recommended), pass an HTTP URL as a String for
/// Telegram to get a file from the Internet, or upload a new one using
/// multipart/form-data. More info on Sending Files »
pub voice: String, //InputFile or String
/// Voice message caption, 0-1024 characters
#[serde(skip_serializing_if = "Option::is_none")]
pub caption: Option<String>,
/// Send Markdown or HTML, if you want Telegram apps to show bold, italic,
/// fixed-width text or inline URLs in the media caption.
#[serde(skip_serializing_if = "Option::is_none")]
pub parse_mode: Option<ParseMode>,
///Duration of the voice message in seconds
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<u64>,
/// Sends the message silently. Users will receive a notification with
/// no sound.
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_notification: Option<bool>,
/// If the message is a reply, ID of the original message
#[serde(skip_serializing_if = "Option::is_none")]
pub 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")]
pub reply_markup: Option<ReplyMarkup>,
}
#[async_trait]
impl Request for SendVoice<'_> {
type ReturnValue = Message;
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl SendVoice<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"sendVoice",
&self,
)
.await
}
}
impl<'a> SendVoice<'a> {
pub(crate) fn new(
ctx: RequestContext<'a>,
chat_id: ChatId,
voice: String,
) -> Self {
Self {
ctx,
chat_id,
voice,
caption: None,
parse_mode: None,
duration: None,
disable_notification: None,
reply_to_message_id: None,
reply_markup: None,
}
}
pub fn chat_id<T>(mut self, chat_id: T) -> Self
where
T: Into<ChatId>,
{
self.chat_id = chat_id.into();
self
}
pub fn voice<T>(mut self, voice: T) -> Self
where
T: Into<String>,
{
self.voice = voice.into();
self
}
pub fn caption<T>(mut self, caption: T) -> Self
where
T: Into<String>,
{
self.caption = Some(caption.into());
self
}
pub fn parse_mode<T>(mut self, parse_mode: T) -> Self
where
T: Into<ParseMode>,
{
self.parse_mode = Some(parse_mode.into());
self
}
pub fn duration<T>(mut self, duration: T) -> Self
where
T: Into<u64>,
{
self.duration = Some(duration.into());
self
}
pub fn disable_notification<T>(mut self, disable_notification: T) -> Self
where
T: Into<bool>,
{
self.disable_notification = Some(disable_notification.into());
self
}
pub fn reply_to_message_id<T>(mut self, reply_to_message_id: T) -> Self
where
T: Into<i32>,
{
self.reply_to_message_id = Some(reply_to_message_id.into());
self
}
pub fn reply_markup<T>(mut self, reply_markup: T) -> Self
where
T: Into<ReplyMarkup>,
{
self.reply_markup = Some(reply_markup.into());
self
}
}

View file

@ -1,8 +1,8 @@
use async_trait::async_trait;
use crate::{
network,
requests::{
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
},
requests::{ChatId, Request, RequestContext, ResponseResult},
types::{InlineKeyboardMarkup, Message},
};
@ -32,19 +32,24 @@ pub struct StopMessageLiveLocation<'a> {
pub reply_markup: Option<InlineKeyboardMarkup>,
}
impl<'a> Request<'a> for StopMessageLiveLocation<'a> {
#[async_trait]
impl Request for StopMessageLiveLocation<'_> {
type ReturnValue = Message;
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
Box::pin(async move {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"stopMessageLiveLocation",
&self,
)
.await
})
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl StopMessageLiveLocation<'_> {
pub async fn send(self) -> ResponseResult<Message> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"stopMessageLiveLocation",
&self,
)
.await
}
}

View file

@ -1,8 +1,70 @@
use crate::requests::RequestContext;
//TODO:: need implementation
use async_trait::async_trait;
use crate::network;
use crate::requests::{ChatId, Request, RequestContext, ResponseResult};
/// Use this method to unban a previously kicked user in a supergroup or
/// channel. The user will not return to the group or channel automatically, but
/// will be able to join via link, etc. The bot must be an administrator for
/// this to work. Returns True on success.
#[derive(Debug, Clone, Serialize)]
pub struct UnbanChatMember<'a> {
#[serde(skip_serializing)]
ctx: RequestContext<'a>,
///Unique identifier for the target group or username of the target
/// supergroup or channel (in the format @channelusername)
pub chat_id: ChatId,
/// Unique identifier of the target user
pub user_id: i32,
}
#[async_trait]
impl Request for UnbanChatMember<'_> {
type ReturnValue = bool;
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl UnbanChatMember<'_> {
pub async fn send(self) -> ResponseResult<bool> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"unbanChatMember",
&self,
)
.await
}
}
impl<'a> UnbanChatMember<'a> {
pub(crate) fn new(
ctx: RequestContext<'a>,
chat_id: ChatId,
user_id: i32,
) -> Self {
Self {
ctx,
chat_id,
user_id,
}
}
pub fn chat_id<T>(mut self, chat_id: T) -> Self
where
T: Into<ChatId>,
{
self.chat_id = chat_id.into();
self
}
pub fn user_id<T>(mut self, user_id: T) -> Self
where
T: Into<i32>,
{
self.user_id = user_id.into();
self
}
}

View file

@ -0,0 +1,50 @@
use async_trait::async_trait;
use crate::{
network,
requests::{ChatId, Request, RequestContext, ResponseResult},
types::True,
};
#[derive(Debug, Clone, Serialize)]
pub struct UnpinChatMessage<'a> {
#[serde(skip_serializing)]
pub ctx: RequestContext<'a>,
pub chat_id: ChatId,
}
#[async_trait]
impl Request for UnpinChatMessage<'_> {
type ReturnValue = True;
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
self.send().await
}
}
impl UnpinChatMessage<'_> {
pub async fn send(self) -> ResponseResult<True> {
network::request_json(
&self.ctx.client,
&self.ctx.token,
"unpinChatMessage",
&self,
)
.await
}
}
impl<'a> UnpinChatMessage<'a> {
pub(crate) fn new(ctx: RequestContext<'a>, chat_id: ChatId) -> Self {
Self { ctx, chat_id }
}
pub fn chat_id<T>(mut self, chat_id: T) -> Self
where
T: Into<ChatId>,
{
self.chat_id = chat_id.into();
self
}
}

View file

@ -80,9 +80,10 @@ where
#[cfg(test)]
mod tests {
use crate::types::*;
use serde_json::from_str;
use crate::types::*;
#[test]
fn channel_de() {
let expected = Chat {
@ -113,9 +114,9 @@ mod tests {
type_: (),
username: Some("username".into()),
first_name: Some("Anon".into()),
last_name: None
last_name: None,
},
photo: None
photo: None,
},
from_str(
r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"#

View file

@ -4,64 +4,68 @@ use super::passport_file::PassportFile;
pub struct EncryptedPassportElement {
pub hash: String,
#[serde(flatten)]
pub kind: EncryptedPassportElementKind
pub kind: EncryptedPassportElementKind,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum EncryptedPassportElementKind {
PersonalDetails {
data: String
data: String,
},
Passport {
data: String,
front_side: PassportFile,
selfie: PassportFile,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
DriverLicense {
data: String,
front_side: PassportFile,
reverse_side: PassportFile,
selfie: PassportFile,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
IdentityCard {
data: String,
front_side: PassportFile,
reverse_side: PassportFile,
selfie: PassportFile,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
InternalPassport {
data: String,
front_side: PassportFile,
selfie: PassportFile,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
Address {
data: String
data: String,
},
UtilityBill {
files: Vec<PassportFile>,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
BankStatement {
files: Vec<PassportFile>,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
RentalAgreement {
files: Vec<PassportFile>,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
PassportRegistration {
files: Vec<PassportFile>,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
TemporaryRegistration {
files: Vec<PassportFile>,
translation: Option<Vec<PassportFile>>
translation: Option<Vec<PassportFile>>,
},
PhoneNumber {
phone_number: String,
},
Email {
email: String,
},
PhoneNumber { phone_number: String },
Email { email: String }
}

View file

@ -203,9 +203,10 @@ pub enum MediaKind {
#[cfg(test)]
mod tests {
use crate::types::*;
use serde_json::from_str;
use crate::types::*;
#[test]
fn de_media_forwarded() {
let json = r#"{

View file

@ -12,7 +12,9 @@ pub use self::{
contact::Contact,
document::Document,
encrypted_credintials::EncryptedCredentials,
encrypted_passport_element::{EncryptedPassportElement, EncryptedPassportElementKind},
encrypted_passport_element::{
EncryptedPassportElement, EncryptedPassportElementKind,
},
file::File,
force_reply::ForceReply,
game::Game,

View file

@ -16,7 +16,7 @@ impl std::convert::TryFrom<bool> for True {
fn try_from(value: bool) -> Result<Self, Self::Error> {
match value {
true => Ok(True),
false => Err(())
false => Err(()),
}
}
}
@ -24,7 +24,7 @@ impl std::convert::TryFrom<bool> for True {
impl<'de> Deserialize<'de> for True {
fn deserialize<D>(des: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
D: Deserializer<'de>,
{
des.deserialize_bool(TrueVisitor)
}
@ -41,11 +41,11 @@ impl<'de> Visitor<'de> for TrueVisitor {
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
where
E: de::Error
E: de::Error,
{
match value {
true => Ok(True),
false => Err(E::custom("expected `true`, found `false`"))
false => Err(E::custom("expected `true`, found `false`")),
}
}
}
@ -61,9 +61,10 @@ impl Serialize for True {
#[cfg(test)]
mod tests {
use super::True;
use serde_json::{from_str, to_string};
use super::True;
#[test]
fn unit_true_de() {
let json = "true";

View file

@ -15,7 +15,8 @@ pub enum UpdateKind {
EditedMessage(Message),
ChannelPost(Message),
EditedChannelPost(Message),
InlineQuery(()), // TODO
InlineQuery(()),
// TODO
ChosenInlineResult(ChosenInlineResult),
CallbackQuery(CallbackQuery),
}