Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	src/dispatching/dispatchers/filter/mod.rs
This commit is contained in:
P0lunin 2019-10-21 13:32:38 +03:00
commit 57d7cc0fe1
28 changed files with 440 additions and 182 deletions

View file

@ -2,20 +2,20 @@ use crate::{
bot::Bot,
requests::{
AnswerCallbackQuery, AnswerPreCheckoutQuery, AnswerShippingQuery,
DeleteChatStickerSet, EditMessageLiveLocation, ForwardMessage,
DeleteChatPhoto, DeleteChatStickerSet, EditMessageLiveLocation,
ExportCharInviteLink, ForwardMessage, GetChat, GetChatAdministrators,
GetChatMember, GetChatMembersCount, GetFile, GetMe, GetUpdates,
KickChatMember, PinChatMessage, PromoteChatMember, RestrictChatMember,
SendAnimation, SendAudio, SendChatAction, SendContact, SendDocument,
SendLocation, SendMediaGroup, SendMessage, SendPhoto, SendPoll,
SendVenue, SendVideo, SendVideoNote, SendVoice, SetChatDescription,
SetChatStickerSet, StopMessageLiveLocation, UnbanChatMember,
UnpinChatMessage, SetChatTitle, DeleteChatPhoto, SetChatPhoto,
ExportCharInviteLink, SetChatPermissions
KickChatMember, LeaveChat, PinChatMessage, PromoteChatMember,
RestrictChatMember, SendAnimation, SendAudio, SendChatAction,
SendContact, SendDocument, SendLocation, SendMediaGroup, SendMessage,
SendPhoto, SendPoll, SendVenue, SendVideo, SendVideoNote, SendVoice,
SetChatDescription, SetChatPermissions, SetChatPhoto,
SetChatStickerSet, SetChatTitle, StopMessageLiveLocation,
UnbanChatMember, UnpinChatMessage,
},
types::{ChatAction, ChatId, ChatPermissions, InputFile, InputMedia},
};
/// Telegram functions
impl Bot {
pub fn get_me(&self) -> GetMe {
GetMe::new(self)
@ -120,6 +120,13 @@ impl Bot {
AnswerPreCheckoutQuery::new(self, pre_checkout_query_id, ok)
}
pub fn get_chat<I>(&self, chat_id: I) -> GetChat
where
I: Into<ChatId>,
{
GetChat::new(self, chat_id)
}
pub fn answer_shipping_query<I, O>(
&self,
shipping_query_id: I,
@ -323,6 +330,16 @@ impl Bot {
GetChatMember::new(self, chat_id, user_id)
}
pub fn get_chat_administrators<C, I>(
&self,
chat_id: C,
) -> GetChatAdministrators
where
C: Into<ChatId>,
{
GetChatAdministrators::new(self, chat_id)
}
pub fn get_chat_members_count<C>(&self, chat_id: C) -> GetChatMembersCount
where
C: Into<ChatId>,
@ -366,21 +383,21 @@ impl Bot {
SetChatTitle::new(self, chat_id, title)
}
pub fn delete_chat_photo<C>(
&self,
chat_id: C,
) -> DeleteChatPhoto
pub fn delete_chat_photo<C>(&self, chat_id: C) -> DeleteChatPhoto
where
C: Into<ChatId>,
{
DeleteChatPhoto::new(self, chat_id)
}
pub fn set_chat_photo<C, P>(
&self,
chat_id: C,
photo: P,
) -> SetChatPhoto
pub fn leave_chat<C>(&self, chat_id: C) -> LeaveChat
where
C: Into<ChatId>,
{
LeaveChat::new(self, chat_id)
}
pub fn set_chat_photo<C, P>(&self, chat_id: C, photo: P) -> SetChatPhoto
where
C: Into<ChatId>,
P: Into<InputFile>,
@ -388,10 +405,7 @@ impl Bot {
SetChatPhoto::new(self, chat_id, photo)
}
pub fn export_chat_invite_link<C>(
&self,
chat_id: C,
) -> ExportCharInviteLink
pub fn export_chat_invite_link<C>(&self, chat_id: C) -> ExportCharInviteLink
where
C: Into<ChatId>,
{

View file

@ -3,9 +3,9 @@ use futures::StreamExt;
use async_trait::async_trait;
use crate::{
dispatcher::{
filter::Filter, handler::Handler, simple::error_policy::ErrorPolicy,
updater::Updater,
dispatching::{
dispatchers::filter::error_policy::ErrorPolicy, filters::Filter,
handler::Handler, updater::Updater,
},
types::{CallbackQuery, ChosenInlineResult, Message, Update, UpdateKind},
};
@ -17,13 +17,13 @@ type Handlers<'a, T, E> =
/// Dispatcher that dispatches updates from telegram.
///
/// This is 'simple' implementation with following limitations:
/// This is 'filter' implementation with following limitations:
/// - Error (`E` generic parameter) _must_ implement [`std::fmt::Debug`]
/// - All 'handlers' are boxed
/// - Handler's fututres are also boxed
/// - [Custom error policy] is also boxed
/// - All errors from [updater] are ignored (TODO: remove this limitation)
/// - All handlers executed in order (this means that in dispatcher have 2
/// - All handlers executed in order (this means that in dispatching have 2
/// upadtes it will first execute some handler into complition with first
/// update and **then** search for handler for second update, this is probably
/// wrong)
@ -37,8 +37,8 @@ type Handlers<'a, T, E> =
/// async fn run() {
/// use std::convert::Infallible;
/// use telebofr::{
/// dispatcher::{
/// simple::{error_policy::ErrorPolicy, Dispatcher},
/// dispatching::{
/// dispatchers::filter::{error_policy::ErrorPolicy, FilterDispatcher},
/// updater::polling,
/// },
/// };
@ -49,9 +49,9 @@ type Handlers<'a, T, E> =
///
/// let bot = Bot::new("TOKEN");
///
/// // create dispatcher which handlers can't fail
/// // create dispatching which handlers can't fail
/// // with error policy that just ignores all errors (that can't ever happen)
/// let mut dp = Dispatcher::<Infallible>::new(ErrorPolicy::Ignore)
/// let mut dp = FilterDispatcher::<Infallible>::new(ErrorPolicy::Ignore)
/// // Add 'handler' that will handle all messages sent to the bot
/// .message_handler(true, |mes: Message| {
/// async move { println!("New message: {:?}", mes) }
@ -67,9 +67,9 @@ type Handlers<'a, T, E> =
///
/// [`std::fmt::Debug`]: std::fmt::Debug
/// [Custom error policy]:
/// crate::dispatcher::simple::error_policy::ErrorPolicy::Custom [updater]:
/// crate::dispatcher::updater
pub struct Dispatcher<'a, E> {
/// crate::dispatching::filter::error_policy::ErrorPolicy::Custom [updater]:
/// crate::dispatching::updater
pub struct FilterDispatcher<'a, E> {
message_handlers: Handlers<'a, Message, E>,
edited_message_handlers: Handlers<'a, Message, E>,
channel_post_handlers: Handlers<'a, Message, E>,
@ -80,12 +80,12 @@ pub struct Dispatcher<'a, E> {
error_policy: ErrorPolicy<'a, E>,
}
impl<'a, E> Dispatcher<'a, E>
impl<'a, E> FilterDispatcher<'a, E>
where
E: std::fmt::Debug, // TODO: Is this really necessary?
{
pub fn new(error_policy: ErrorPolicy<'a, E>) -> Self {
Dispatcher {
FilterDispatcher {
message_handlers: Vec::new(),
edited_message_handlers: Vec::new(),
channel_post_handlers: Vec::new(),
@ -197,16 +197,29 @@ where
match kind {
UpdateKind::Message(mes) => {
self.handle(mes, &self.message_handlers).await;
self.handle(mes, &self.message_handlers)
.await;
}
UpdateKind::EditedMessage(mes) => {
self.handle(mes, &self.edited_message_handlers).await;
self.handle(
mes,
&self.edited_message_handlers,
)
.await;
}
UpdateKind::ChannelPost(post) => {
self.handle(post, &self.channel_post_handlers).await;
self.handle(
post,
&self.channel_post_handlers,
)
.await;
}
UpdateKind::EditedChannelPost(post) => {
self.handle(post, &self.edited_channel_post_handlers).await;
self.handle(
post,
&self.edited_channel_post_handlers,
)
.await;
}
UpdateKind::InlineQuery(query) => {
self.handle(query, &self.inline_query_handlers).await;
@ -249,13 +262,13 @@ where
}
#[async_trait(? Send)]
impl<'a, U, E> crate::dispatcher::Dispatcher<'a, U> for Dispatcher<'a, E>
impl<'a, U, E> crate::dispatching::Dispatcher<'a, U> for FilterDispatcher<'a, E>
where
E: std::fmt::Debug,
U: Updater + 'a,
{
async fn dispatch(&'a mut self, updater: U) {
Dispatcher::dispatch(self, updater).await
FilterDispatcher::dispatch(self, updater).await
}
}
@ -269,8 +282,10 @@ mod tests {
use futures::Stream;
use crate::{
dispatcher::{
simple::{error_policy::ErrorPolicy, Dispatcher},
dispatching::{
dispatchers::filter::{
error_policy::ErrorPolicy, FilterDispatcher,
},
updater::StreamUpdater,
},
types::{
@ -284,7 +299,7 @@ mod tests {
let counter = &AtomicI32::new(0);
let counter2 = &AtomicI32::new(0);
let mut dp = Dispatcher::<Infallible>::new(ErrorPolicy::Ignore)
let mut dp = FilterDispatcher::<Infallible>::new(ErrorPolicy::Ignore)
.message_handler(true, |_mes: Message| {
async move {
counter.fetch_add(1, Ordering::SeqCst);

View file

@ -0,0 +1,3 @@
pub use filter::FilterDispatcher;
pub mod filter;

View file

@ -6,7 +6,7 @@ pub trait Filter<T> {
}
/// ```
/// use telebofr::dispatcher::filter::Filter;
/// use telebofr::dispatching::filters::Filter;
///
/// let closure = |i: &i32| -> bool { *i >= 42 };
/// assert!(closure.test(&42));
@ -22,7 +22,7 @@ impl<T, F: Fn(&T) -> bool> Filter<T> for F {
}
/// ```
/// use telebofr::dispatcher::filter::Filter;
/// use telebofr::dispatching::filters::Filter;
///
/// assert!(true.test(&()));
/// assert_eq!(false.test(&()), false);
@ -42,7 +42,7 @@ impl<T> Filter<T> for bool {
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{And, Filter};
/// use telebofr::dispatching::filters::{And, Filter};
///
/// // Note: bool can be treated as `Filter` that always return self.
/// assert_eq!(And::new(true, false).test(&()), false);
@ -73,13 +73,13 @@ where
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{and, Filter};
/// use telebofr::dispatching::filters::{and, Filter};
///
/// assert!(and(true, true).test(&()));
/// assert_eq!(and(true, false).test(&()), false);
/// ```
///
/// [`And::new`]: crate::dispatcher::filter::And::new
/// [`And::new`]: crate::dispatching::filter::And::new
pub fn and<A, B>(a: A, b: B) -> And<A, B> {
And::new(a, b)
}
@ -93,7 +93,7 @@ pub fn and<A, B>(a: A, b: B) -> And<A, B> {
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{Filter, Or};
/// use telebofr::dispatching::filters::{Filter, Or};
///
/// // Note: bool can be treated as `Filter` that always return self.
/// assert!(Or::new(true, false).test(&()));
@ -124,13 +124,13 @@ where
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{or, Filter};
/// use telebofr::dispatching::filters::{or, Filter};
///
/// assert!(or(true, false).test(&()));
/// assert_eq!(or(false, false).test(&()), false);
/// ```
///
/// [`Or::new`]: crate::dispatcher::filter::Or::new
/// [`Or::new`]: crate::dispatching::filter::Or::new
pub fn or<A, B>(a: A, b: B) -> Or<A, B> {
Or::new(a, b)
}
@ -141,7 +141,7 @@ pub fn or<A, B>(a: A, b: B) -> Or<A, B> {
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{Filter, Not};
/// use telebofr::dispatching::filters::{Filter, Not};
///
/// // Note: bool can be treated as `Filter` that always return self.
/// assert!(Not::new(false).test(&()));
@ -169,13 +169,13 @@ where
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{not, Filter};
/// use telebofr::dispatching::filters::{not, Filter};
///
/// assert!(not(false).test(&()));
/// assert_eq!(not(true).test(&()), false);
/// ```
///
/// [`Not::new`]: crate::dispatcher::filter::Not::new
/// [`Not::new`]: crate::dispatching::filter::Not::new
pub fn not<A>(a: A) -> Not<A> {
Not::new(a)
}
@ -187,7 +187,7 @@ pub fn not<A>(a: A) -> Not<A> {
///
/// ## Examples
/// ```
/// use telebofr::{all, dispatcher::filter::Filter};
/// use telebofr::{all, dispatching::filters::Filter};
///
/// assert!(all![true].test(&()));
/// assert!(all![true, true].test(&()));
@ -199,12 +199,12 @@ pub fn not<A>(a: A) -> Not<A> {
/// assert_eq!(all![false, false].test(&()), false);
/// ```
///
/// [filter]: crate::dispatcher::filter::Filter
/// [filter]: crate::dispatching::filter::Filter
#[macro_export]
macro_rules! all {
($one:expr) => { $one };
($head:expr, $($tail:tt)+) => {
$crate::dispatcher::filter::And::new(
$crate::dispatching::filters::And::new(
$head,
$crate::all!($($tail)+)
)
@ -218,7 +218,7 @@ macro_rules! all {
///
/// ## Examples
/// ```
/// use telebofr::{any, dispatcher::filter::Filter};
/// use telebofr::{any, dispatching::filters::Filter};
///
/// assert!(any![true].test(&()));
/// assert!(any![true, true].test(&()));
@ -230,12 +230,12 @@ macro_rules! all {
/// assert_eq!(any![false, false, false].test(&()), false);
/// ```
///
/// [filter]: crate::dispatcher::filter::Filter
/// [filter]: crate::dispatching::filter::Filter
#[macro_export]
macro_rules! any {
($one:expr) => { $one };
($head:expr, $($tail:tt)+) => {
$crate::dispatcher::filter::Or::new(
$crate::dispatching::filters::Or::new(
$head,
$crate::all!($($tail)+)
)
@ -246,7 +246,7 @@ macro_rules! any {
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{f, And, Filter, Or, F};
/// use telebofr::dispatching::filters::{f, And, Filter, Or, F};
///
/// let flt1 = |i: &i32| -> bool { *i > 17 };
/// let flt2 = |i: &i32| -> bool { *i < 42 };
@ -277,7 +277,7 @@ pub struct F<A>(A);
/// Constructor fn for [F]
///
/// [F]: crate::dispatcher::filter::F;
/// [F]: crate::dispatching::filter::F;
pub fn f<A>(a: A) -> F<A> {
F(a)
}
@ -314,7 +314,7 @@ pub trait FilterExt<T> {
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{Filter, FilterExt};
/// use telebofr::dispatching::filters::{Filter, FilterExt};
///
/// let flt = |i: &i32| -> bool { *i > 0 };
/// let flt = flt.not();
@ -322,7 +322,7 @@ pub trait FilterExt<T> {
/// assert_eq!(flt.test(&1), false);
/// ```
///
/// [`Not::new`]: crate::dispatcher::filter::Not::new
/// [`Not::new`]: crate::dispatching::filter::Not::new
fn not(self) -> Not<Self>
where
Self: Sized,
@ -334,7 +334,7 @@ pub trait FilterExt<T> {
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{Filter, FilterExt};
/// use telebofr::dispatching::filters::{Filter, FilterExt};
///
/// let flt = |i: &i32| -> bool { *i > 0 };
/// let flt = flt.and(|i: &i32| *i < 42);
@ -344,7 +344,7 @@ pub trait FilterExt<T> {
/// assert_eq!(flt.test(&43), false);
/// ```
///
/// [`Not::new`]: crate::dispatcher::filter::And::new
/// [`Not::new`]: crate::dispatching::filter::And::new
fn and<B>(self, other: B) -> And<Self, B>
where
Self: Sized,
@ -356,7 +356,7 @@ pub trait FilterExt<T> {
///
/// ## Examples
/// ```
/// use telebofr::dispatcher::filter::{Filter, FilterExt};
/// use telebofr::dispatching::filters::{Filter, FilterExt};
///
/// let flt = |i: &i32| -> bool { *i < 0 };
/// let flt = flt.or(|i: &i32| *i > 42);
@ -366,7 +366,7 @@ pub trait FilterExt<T> {
/// assert_eq!(flt.test(&17), false);
/// ```
///
/// [`Not::new`]: crate::dispatcher::filter::Or::new
/// [`Not::new`]: crate::dispatching::filter::Or::new
fn or<B>(self, other: B) -> Or<Self, B>
where
Self: Sized,

View file

@ -1,12 +1,12 @@
//! Update dispatching.
use async_trait::async_trait;
pub use filter::Filter;
pub use filters::Filter;
pub use handler::Handler;
pub mod filter;
pub mod dispatchers;
pub mod filters;
pub mod handler;
pub mod simple;
pub mod updater;
#[async_trait(? Send)]

View file

@ -12,6 +12,6 @@ mod errors;
mod network;
mod bot;
pub mod dispatcher;
pub mod dispatching;
pub mod requests;
pub mod types;

View file

@ -1,7 +1,9 @@
use reqwest::StatusCode;
use crate::{
requests::ResponseResult, types::ResponseParameters, RequestError,
requests::ResponseResult,
types::{False, ResponseParameters, True},
RequestError,
};
#[derive(Deserialize)]
@ -10,14 +12,14 @@ pub enum TelegramResponse<R> {
Ok {
/// A dummy field. Used only for deserialization.
#[allow(dead_code)]
ok: bool, // TODO: True type
ok: True,
result: R,
},
Err {
/// A dummy field. Used only for deserialization.
#[allow(dead_code)]
ok: bool, // TODO: False type
ok: False,
description: String,
error_code: u16,

View file

@ -1,10 +1,11 @@
use async_trait::async_trait;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::True,
};
use async_trait::async_trait;
/// Use this method to send answers to callback queries sent from inline
/// keyboards. The answer will be displayed to the user as a notification at the

View file

@ -1,16 +1,18 @@
use async_trait::async_trait;
use crate::bot::Bot;
use crate::types::{ChatId, True};
use crate::requests::{ResponseResult, Request};
use crate::network;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::{ChatId, True},
};
#[derive(Debug, Clone, Serialize)]
pub struct DeleteChatPhoto<'a> {
#[serde(skip_serializing)]
bot: &'a Bot,
chat_id: ChatId
chat_id: ChatId,
}
#[async_trait]
@ -28,15 +30,16 @@ impl DeleteChatPhoto<'_> {
self.bot.client(),
self.bot.token(),
"deleteChatPhoto",
&self
).await
&self,
)
.await
}
}
impl<'a> DeleteChatPhoto<'a> {
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
where
C: Into<ChatId>
C: Into<ChatId>,
{
Self {
bot,

View file

@ -1,10 +1,11 @@
use async_trait::async_trait;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::{ChatId, True},
};
use async_trait::async_trait;
/// Use this method to delete a group sticker set from a supergroup. The bot
/// must be an administrator in the chat for this to work and must have the

View file

@ -1,16 +1,18 @@
use async_trait::async_trait;
use crate::bot::Bot;
use crate::types::ChatId;
use crate::requests::{ResponseResult, Request};
use crate::network;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::ChatId,
};
#[derive(Debug, Clone, Serialize)]
pub struct ExportCharInviteLink<'a> {
#[serde(skip_serializing)]
bot: &'a Bot,
chat_id: ChatId
chat_id: ChatId,
}
#[async_trait]
@ -28,15 +30,16 @@ impl ExportCharInviteLink<'_> {
self.bot.client(),
self.bot.token(),
"exportChatInviteLink",
&self
).await
&self,
)
.await
}
}
impl<'a> ExportCharInviteLink<'a> {
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
where
C: Into<ChatId>
C: Into<ChatId>,
{
Self {
bot,
@ -64,7 +67,8 @@ mod tests {
let method = ExportCharInviteLink::new(&bot, chat_id);
let expected = r#"{"chat_id":123}"#;
let actual = serde_json::to_string::<ExportCharInviteLink>(&method).unwrap();
let actual =
serde_json::to_string::<ExportCharInviteLink>(&method).unwrap();
assert_eq!(actual, expected);
}
}

View file

@ -42,11 +42,21 @@ impl GetChat<'_> {
}
impl<'a> GetChat<'a> {
pub fn chat_id<C>(mut self, value: C) -> Self
pub(crate) fn new<F>(bot: &'a Bot, chat_id: F) -> Self
where
F: Into<ChatId>,
{
Self {
bot,
chat_id: chat_id.into(),
}
}
pub fn chat_id<C>(mut self, chat_id: C) -> Self
where
C: Into<ChatId>,
{
self.chat_id = value.into();
self.chat_id = chat_id.into();
self
}
}

View file

@ -0,0 +1,64 @@
use async_trait::async_trait;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::{ChatId, ChatMember},
};
/// Use this method to get a list of administrators in a chat. On success,
/// returns an Array of ChatMember objects that contains information about all
/// chat administrators except other bots. If the chat is a group or a
/// supergroup and no administrators were appointed, only the creator will be
/// returned
#[derive(Debug, Clone, Serialize)]
pub struct GetChatAdministrators<'a> {
#[serde(skip_serializing)]
bot: &'a Bot,
/// Unique identifier for the target chat or username of the target
/// supergroup or channel (in the format @channelusername)
chat_id: ChatId,
}
#[async_trait]
impl Request for GetChatAdministrators<'_> {
type Output = Vec<ChatMember>;
async fn send_boxed(self) -> ResponseResult<Self::Output> {
self.send().await
}
}
impl GetChatAdministrators<'_> {
async fn send(&self) -> ResponseResult<Vec<ChatMember>> {
network::request_json(
self.bot.client(),
self.bot.token(),
"getChatAdministrators",
&self,
)
.await
}
}
impl<'a> GetChatAdministrators<'a> {
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
where
C: Into<ChatId>,
{
Self {
bot,
chat_id: chat_id.into(),
}
}
pub fn chat_id<C>(mut self, value: C) -> Self
where
C: Into<ChatId>,
{
self.chat_id = value.into();
self
}
}

View file

@ -1,10 +1,11 @@
use async_trait::async_trait;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::{ChatId, ChatMember},
};
use async_trait::async_trait;
/// Use this method to get information about a member of a chat. Returns a
/// ChatMember object on success.

View file

@ -8,7 +8,7 @@ use crate::{
};
#[derive(Debug, Clone)]
/// A simple method for testing your bot's auth token. Requires no parameters.
/// A filter method for testing your bot's auth token. Requires no parameters.
/// Returns basic information about the bot in form of a [`User`] object.
pub struct GetMe<'a> {
bot: &'a Bot,

View file

@ -0,0 +1,59 @@
use async_trait::async_trait;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::ChatId,
};
/// Use this method for your bot to leave a group, supergroup or channel.
/// Returns True on success.
#[derive(Debug, Clone, Serialize)]
pub struct LeaveChat<'a> {
#[serde(skip_serializing)]
bot: &'a Bot,
/// Unique identifier for the target chat or username
/// of the target supergroup or channel (in the format @channelusername)
chat_id: ChatId,
}
#[async_trait]
impl Request for LeaveChat<'_> {
type Output = bool;
async fn send_boxed(self) -> ResponseResult<Self::Output> {
self.send().await
}
}
impl LeaveChat<'_> {
pub async fn send(self) -> ResponseResult<bool> {
network::request_json(
self.bot.client(),
self.bot.token(),
"leaveChat",
&self,
)
.await
}
}
impl<'a> LeaveChat<'a> {
pub(crate) fn new<F>(bot: &'a Bot, chat_id: F) -> Self
where
F: Into<ChatId>,
{
Self {
bot,
chat_id: chat_id.into(),
}
}
pub fn chat_id<C>(mut self, chat_id: C) -> Self
where
C: Into<ChatId>,
{
self.chat_id = chat_id.into();
self
}
}

View file

@ -1,8 +1,5 @@
//! API requests.
use async_trait::async_trait;
use serde::de::DeserializeOwned;
pub use answer_callback_query::*;
pub use answer_pre_checkout_query::*;
pub use answer_shipping_query::*;
@ -12,6 +9,7 @@ pub use edit_message_live_location::*;
pub use export_chat_invite_link::*;
pub use forward_message::*;
pub use get_chat::*;
pub use get_chat_administrators::*;
pub use get_chat_member::*;
pub use get_chat_members_count::*;
pub use get_file::*;
@ -19,6 +17,7 @@ pub use get_me::*;
pub use get_updates::*;
pub use get_user_profile_photos::*;
pub use kick_chat_member::*;
pub use leave_chat::*;
pub use pin_chat_message::*;
pub use promote_chat_member::*;
pub use restrict_chat_member::*;
@ -57,6 +56,7 @@ mod edit_message_live_location;
mod export_chat_invite_link;
mod forward_message;
mod get_chat;
mod get_chat_administrators;
mod get_chat_member;
mod get_chat_members_count;
mod get_file;
@ -64,6 +64,7 @@ mod get_me;
mod get_updates;
mod get_user_profile_photos;
mod kick_chat_member;
mod leave_chat;
mod pin_chat_message;
mod promote_chat_member;
mod restrict_chat_member;
@ -90,6 +91,9 @@ mod stop_message_live_location;
mod unban_chat_member;
mod unpin_chat_message;
use async_trait::async_trait;
use serde::de::DeserializeOwned;
/// A type that is returned from `Request::send_boxed`.
pub type ResponseResult<T> = Result<T, crate::RequestError>;

View file

@ -1,9 +1,11 @@
use async_trait::async_trait;
use crate::bot::Bot;
use crate::types::{ChatId, ChatPermissions, True};
use crate::requests::{ResponseResult, Request};
use crate::network;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::{ChatId, ChatPermissions, True},
};
#[derive(Debug, Clone, Serialize)]
pub struct SetChatPermissions<'a> {
@ -11,7 +13,7 @@ pub struct SetChatPermissions<'a> {
bot: &'a Bot,
chat_id: ChatId,
permissions: ChatPermissions
permissions: ChatPermissions,
}
#[async_trait]
@ -29,17 +31,14 @@ impl SetChatPermissions<'_> {
self.bot.client(),
self.bot.token(),
"setChatPermissions",
&self
).await
&self,
)
.await
}
}
impl<'a> SetChatPermissions<'a> {
pub(crate) fn new<C, CP>(
bot: &'a Bot,
chat_id: C,
permissions: CP,
) -> Self
pub(crate) fn new<C, CP>(bot: &'a Bot, chat_id: C, permissions: CP) -> Self
where
C: Into<ChatId>,
CP: Into<ChatPermissions>,
@ -61,7 +60,7 @@ impl<'a> SetChatPermissions<'a> {
pub fn permissions<CP>(mut self, permissions: CP) -> Self
where
CP: Into<ChatPermissions>
CP: Into<ChatPermissions>,
{
self.permissions = permissions.into();
self
@ -84,12 +83,14 @@ mod tests {
can_add_web_page_previews: None,
can_change_info: None,
can_invite_users: None,
can_pin_messages: None
can_pin_messages: None,
};
let method = SetChatPermissions::new(&bot, chat_id, permissions);
let expected = r#"{"chat_id":123,"permissions":{"can_send_messages":true}}"#;
let actual = serde_json::to_string::<SetChatPermissions>(&method).unwrap();
let expected =
r#"{"chat_id":123,"permissions":{"can_send_messages":true}}"#;
let actual =
serde_json::to_string::<SetChatPermissions>(&method).unwrap();
assert_eq!(actual, expected);
}
}

View file

@ -1,10 +1,11 @@
use async_trait::async_trait;
use crate::bot::Bot;
use crate::types::{ChatId, True, InputFile};
use crate::requests::{Request, ResponseResult};
use crate::network;
use crate::requests::form_builder::FormBuilder;
use crate::{
bot::Bot,
network,
requests::{form_builder::FormBuilder, Request, ResponseResult},
types::{ChatId, InputFile, True},
};
#[derive(Debug, Clone, Serialize)]
pub struct SetChatPhoto<'a> {
@ -35,16 +36,13 @@ impl SetChatPhoto<'_> {
self.bot.token(),
"setChatPhoto",
params.build(),
).await
)
.await
}
}
impl<'a> SetChatPhoto<'a> {
pub(crate) fn new<C, P>(
bot: &'a Bot,
chat_id: C,
photo: P
) -> Self
pub(crate) fn new<C, P>(bot: &'a Bot, chat_id: C, photo: P) -> Self
where
C: Into<ChatId>,
P: Into<InputFile>,
@ -82,7 +80,8 @@ mod tests {
let bot = Bot::new("token");
let chat_id = 123;
let photo_url = "https://some_url".to_string();
let method = SetChatPhoto::new(&bot, chat_id, InputFile::Url(photo_url));
let method =
SetChatPhoto::new(&bot, chat_id, InputFile::Url(photo_url));
let expected = r#"{"chat_id":123,"photo":"https://some_url"}"#;
let actual = serde_json::to_string::<SetChatPhoto>(&method).unwrap();

View file

@ -1,10 +1,11 @@
use async_trait::async_trait;
use crate::{
bot::Bot,
network,
requests::{Request, ResponseResult},
types::{ChatId, True},
};
use async_trait::async_trait;
/// Use this method to set a new group sticker set for a supergroup. The bot
/// must be an administrator in the chat for this to work and must have the

View file

@ -38,14 +38,10 @@ impl SetChatTitle<'_> {
}
impl<'a> SetChatTitle<'a> {
pub(crate) fn new<C, T>(
bot: &'a Bot,
chat_id: C,
title: T
) -> Self
where
C: Into<ChatId>,
T: Into<String>,
pub(crate) fn new<C, T>(bot: &'a Bot, chat_id: C, title: T) -> Self
where
C: Into<ChatId>,
T: Into<String>,
{
Self {
bot,

View file

@ -1,4 +1,4 @@
/// This object represents one button of the reply keyboard. For simple text
/// This object represents one button of the reply keyboard. For filter text
/// buttons String can be used instead of this object to specify text of the
/// button. Optional fields are mutually exclusive.
#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq, Clone)]

View file

@ -13,49 +13,14 @@ pub use chat_photo::*;
pub use chosen_inline_result::*;
pub use contact::*;
pub use document::*;
pub use encrypted_credentials::*;
pub use encrypted_passport_element::*;
pub use file::*;
pub use force_reply::*;
pub use game::*;
pub use game_high_score::*;
pub use inline_keyboard_button::*;
pub use inline_keyboard_markup::*;
pub use input_file::*;
pub use input_media::*;
pub use input_message_content::*;
pub use invoice::*;
pub use keyboard_button::*;
pub use label_price::*;
pub use location::*;
pub use login_url::*;
pub use mask_position::*;
pub use message::*;
pub use message_entity::*;
pub use order_info::*;
pub use parse_mode::*;
pub use photo_size::*;
pub use poll::*;
pub use pre_checkout_query::*;
pub use reply_keyboard_markup::*;
pub use reply_keyboard_remove::*;
pub use reply_markup::*;
pub use response_parameters::*;
pub use send_invoice::*;
pub use shipping_address::*;
pub use shipping_option::*;
pub use shipping_query::*;
pub use sticker::*;
pub use sticker_set::*;
pub use successful_payment::*;
pub use unit_true::*;
pub use update::*;
pub use user::*;
pub use user_profile_photos::*;
pub use venue::*;
pub use video::*;
pub use video_note::*;
pub use voice::*;
pub use webhook_info::*;
pub use inline_query::*;
pub use inline_query_result::*;
pub use inline_query_result_article::*;
@ -78,11 +43,45 @@ pub use inline_query_result_photo::*;
pub use inline_query_result_venue::*;
pub use inline_query_result_video::*;
pub use inline_query_result_voice::*;
pub use encrypted_credentials::*;
pub use encrypted_passport_element::*;
pub use input_file::*;
pub use input_media::*;
pub use input_message_content::*;
pub use invoice::*;
pub use keyboard_button::*;
pub use label_price::*;
pub use location::*;
pub use login_url::*;
pub use mask_position::*;
pub use message::*;
pub use message_entity::*;
pub use order_info::*;
pub use parse_mode::*;
pub use passport_data::*;
pub use passport_file::*;
pub use photo_size::*;
pub use poll::*;
pub use pre_checkout_query::*;
pub use reply_keyboard_markup::*;
pub use reply_keyboard_remove::*;
pub use reply_markup::*;
pub use response_parameters::*;
pub use send_invoice::*;
pub use shipping_address::*;
pub use shipping_option::*;
pub use shipping_query::*;
pub use sticker::*;
pub use sticker_set::*;
pub use successful_payment::*;
pub use unit_false::*;
pub use unit_true::*;
pub use update::*;
pub use user::*;
pub use user_profile_photos::*;
pub use venue::*;
pub use video::*;
pub use video_note::*;
pub use voice::*;
pub use webhook_info::*;
mod animation;
mod audio;
@ -130,6 +129,7 @@ mod shipping_query;
mod sticker;
mod sticker_set;
mod successful_payment;
mod unit_false;
mod unit_true;
mod update;
mod user;

80
src/types/unit_false.rs Normal file
View file

@ -0,0 +1,80 @@
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct False;
impl std::convert::TryFrom<bool> for False {
type Error = ();
fn try_from(value: bool) -> Result<Self, Self::Error> {
#[allow(clippy::match_bool)]
match value {
true => Err(()),
false => Ok(False),
}
}
}
impl<'de> Deserialize<'de> for False {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_bool(FalseVisitor)
}
}
struct FalseVisitor;
impl<'de> Visitor<'de> for FalseVisitor {
type Value = False;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter,
) -> std::fmt::Result {
write!(formatter, "bool, equal to `false`")
}
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
#[allow(clippy::match_bool)]
match value {
true => Err(E::custom("expected `false`, found `true`")),
false => Ok(False),
}
}
}
impl Serialize for False {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bool(false)
}
}
#[cfg(test)]
mod tests {
use serde_json::{from_str, to_string};
use super::False;
#[test]
fn unit_false_de() {
let json = "false";
let expected = False;
let actual = from_str(json).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn unit_false_se() {
let actual = to_string(&False).unwrap();
let expected = "false";
assert_eq!(expected, actual);
}
}

View file

@ -3,7 +3,7 @@ use serde::{
ser::{Serialize, Serializer},
};
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct True;
impl std::convert::TryFrom<bool> for True {