From 2bd67ff0b3b366ec437331e26654a6145631f1f0 Mon Sep 17 00:00:00 2001 From: p0lunin Date: Thu, 16 Jan 2020 13:02:53 +0200 Subject: [PATCH 01/11] fixed command_filter --- src/dispatching/filters/command.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/dispatching/filters/command.rs b/src/dispatching/filters/command.rs index b807050a..758c746b 100644 --- a/src/dispatching/filters/command.rs +++ b/src/dispatching/filters/command.rs @@ -7,10 +7,7 @@ pub struct CommandFilter { impl Filter for CommandFilter { fn test(&self, value: &Message) -> bool { match value.text() { - Some(text) => match text.split_whitespace().next() { - Some(command) => self.command == command, - None => false, - }, + Some(text) => text.starts_with(&self.command), None => false, } } @@ -25,9 +22,10 @@ impl CommandFilter { command: '/'.to_string() + &command.into(), } } - pub fn with_prefix(command: T, prefix: T) -> Self + pub fn with_prefix(command: T, prefix: U) -> Self where T: Into, + U: Into { Self { command: prefix.into() + &command.into(), @@ -44,14 +42,14 @@ mod tests { #[test] fn commands_are_equal() { - let filter = CommandFilter::new("command".to_string()); + let filter = CommandFilter::new("command"); let message = create_message_with_text("/command".to_string()); assert!(filter.test(&message)); } #[test] fn commands_are_not_equal() { - let filter = CommandFilter::new("command".to_string()); + let filter = CommandFilter::new("command"); let message = create_message_with_text("/not_equal_command".to_string()); assert_eq!(filter.test(&message), false); @@ -59,7 +57,7 @@ mod tests { #[test] fn command_have_args() { - let filter = CommandFilter::new("command".to_string()); + let filter = CommandFilter::new("command"); let message = create_message_with_text("/command arg1 arg2".to_string()); assert!(filter.test(&message)); @@ -67,11 +65,18 @@ mod tests { #[test] fn message_have_only_whitespace() { - let filter = CommandFilter::new("command".to_string()); + let filter = CommandFilter::new("command"); let message = create_message_with_text(" ".to_string()); assert_eq!(filter.test(&message), false); } + #[test] + fn another_prefix() { + let filter = CommandFilter::with_prefix("command", "!"); + let message = create_message_with_text("!command".to_string()); + assert!(filter.test(&message)); + } + fn create_message_with_text(text: String) -> Message { Message { id: 0, From c3d90abe7d6e6f6237fcda2302cbf44fa361c5ec Mon Sep 17 00:00:00 2001 From: p0lunin Date: Fri, 17 Jan 2020 14:54:51 +0200 Subject: [PATCH 02/11] added RegexFilter --- Cargo.toml | 7 ++- src/dispatching/filters/mod.rs | 5 ++ src/dispatching/filters/regex_filter.rs | 73 +++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/dispatching/filters/regex_filter.rs diff --git a/Cargo.toml b/Cargo.toml index d921bb1b..dbedf53c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +regex_filter = ["regex"] + [dependencies] serde_json = "1.0.44" serde = { version = "1.0.101", features = ["derive"] } @@ -23,4 +26,6 @@ futures = "0.3.1" pin-project = "0.4.6" serde_with_macros = "1.0.1" either = "1.5.3" -mime = "0.3.16" \ No newline at end of file +mime = "0.3.16" + +regex = {version = "1.3.3", optional = true} diff --git a/src/dispatching/filters/mod.rs b/src/dispatching/filters/mod.rs index 4f8207a8..22f7aad7 100644 --- a/src/dispatching/filters/mod.rs +++ b/src/dispatching/filters/mod.rs @@ -2,6 +2,8 @@ pub use main::*; +#[cfg(feature = "regex_filter")] +pub use regex_filter::*; pub use command::*; pub use message_caption::*; pub use message_text::*; @@ -13,3 +15,6 @@ mod command; mod message_caption; mod message_text; mod message_text_caption; +#[cfg(feature = "regex_filter")] +mod regex_filter; + diff --git a/src/dispatching/filters/regex_filter.rs b/src/dispatching/filters/regex_filter.rs new file mode 100644 index 00000000..12764503 --- /dev/null +++ b/src/dispatching/filters/regex_filter.rs @@ -0,0 +1,73 @@ +use regex::Regex; +use crate::dispatching::Filter; +use crate::types::Message; + +// TODO: docs +pub struct RegexFilter { + regexp: Regex +} + +impl Filter for RegexFilter { + fn test(&self, value: &Message) -> bool { + self.regexp.is_match(value.text()?) + } +} + +impl RegexFilter { + pub fn new(regexp: Regex) -> Self { + Self { + regexp + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::types::{ + Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User, + }; + + #[test] + fn match_true() { + let filter = RegexFilter::new(Regex::new(r"\w+").unwrap()); + let message = create_message_with_text("text".to_string()); + assert!(filter.test(&message)); + } + + fn create_message_with_text(text: String) -> Message { + Message { + id: 0, + date: 0, + chat: Chat { + id: 0, + kind: ChatKind::Private { + type_: (), + username: None, + first_name: None, + last_name: None, + }, + photo: None, + }, + kind: MessageKind::Common { + from: Sender::User(User { + id: 0, + is_bot: false, + first_name: "".to_string(), + last_name: None, + username: None, + language_code: None, + }), + forward_kind: ForwardKind::Origin { + reply_to_message: None, + }, + edit_date: None, + media_kind: MediaKind::Text { + text, + entities: vec![], + }, + reply_markup: None, + }, + } + } +} \ No newline at end of file From 3e983a804c3c177361f73c1645ab0245c2a24c24 Mon Sep 17 00:00:00 2001 From: p0lunin Date: Fri, 17 Jan 2020 15:24:29 +0200 Subject: [PATCH 03/11] fix wrong return type RegexFilter::test --- src/dispatching/filters/regex_filter.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dispatching/filters/regex_filter.rs b/src/dispatching/filters/regex_filter.rs index 12764503..71ce4d3f 100644 --- a/src/dispatching/filters/regex_filter.rs +++ b/src/dispatching/filters/regex_filter.rs @@ -1,23 +1,23 @@ +use crate::{dispatching::Filter, types::Message}; use regex::Regex; -use crate::dispatching::Filter; -use crate::types::Message; // TODO: docs pub struct RegexFilter { - regexp: Regex + regexp: Regex, } impl Filter for RegexFilter { fn test(&self, value: &Message) -> bool { - self.regexp.is_match(value.text()?) + match value.text() { + Some(text) => self.regexp.is_match(text), + None => false, + } } } impl RegexFilter { pub fn new(regexp: Regex) -> Self { - Self { - regexp - } + Self { regexp } } } @@ -70,4 +70,4 @@ mod tests { }, } } -} \ No newline at end of file +} From 5f16c7b9ff962b97e18dfe6844381e0154018132 Mon Sep 17 00:00:00 2001 From: p0lunin Date: Fri, 17 Jan 2020 15:24:54 +0200 Subject: [PATCH 04/11] fmt --- src/dispatching/filters/command.rs | 2 +- src/dispatching/filters/mod.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/dispatching/filters/command.rs b/src/dispatching/filters/command.rs index 758c746b..12d7fb14 100644 --- a/src/dispatching/filters/command.rs +++ b/src/dispatching/filters/command.rs @@ -25,7 +25,7 @@ impl CommandFilter { pub fn with_prefix(command: T, prefix: U) -> Self where T: Into, - U: Into + U: Into, { Self { command: prefix.into() + &command.into(), diff --git a/src/dispatching/filters/mod.rs b/src/dispatching/filters/mod.rs index 22f7aad7..c8d62294 100644 --- a/src/dispatching/filters/mod.rs +++ b/src/dispatching/filters/mod.rs @@ -2,12 +2,12 @@ pub use main::*; -#[cfg(feature = "regex_filter")] -pub use regex_filter::*; pub use command::*; pub use message_caption::*; pub use message_text::*; pub use message_text_caption::*; +#[cfg(feature = "regex_filter")] +pub use regex_filter::*; mod main; @@ -17,4 +17,3 @@ mod message_text; mod message_text_caption; #[cfg(feature = "regex_filter")] mod regex_filter; - From c3c9b92f878da632d73695504cb07d6f04310412 Mon Sep 17 00:00:00 2001 From: p0lunin Date: Fri, 17 Jan 2020 15:35:07 +0200 Subject: [PATCH 05/11] change position of arguments to more readable --- src/dispatching/filters/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dispatching/filters/command.rs b/src/dispatching/filters/command.rs index 12d7fb14..04524b66 100644 --- a/src/dispatching/filters/command.rs +++ b/src/dispatching/filters/command.rs @@ -22,7 +22,7 @@ impl CommandFilter { command: '/'.to_string() + &command.into(), } } - pub fn with_prefix(command: T, prefix: U) -> Self + pub fn with_prefix(prefix: U, command: T) -> Self where T: Into, U: Into, From eb430b04872fd02665984a57b2a5da44c5bcd98e Mon Sep 17 00:00:00 2001 From: p0lunin Date: Fri, 17 Jan 2020 15:35:30 +0200 Subject: [PATCH 06/11] docs for CommandFilter --- src/dispatching/filters/command.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dispatching/filters/command.rs b/src/dispatching/filters/command.rs index 04524b66..d4896ca5 100644 --- a/src/dispatching/filters/command.rs +++ b/src/dispatching/filters/command.rs @@ -1,5 +1,15 @@ use crate::{dispatching::Filter, types::Message}; +/// Filter which find command in message text +/// +/// *NB:* filter compare only text of message, not caption of media message +/// +/// Examples: +/// ``` +/// use teloxide::dispatching::filters::CommandFilter; +/// CommandFilter::new("start"); // return true if text message starts with "/start" +/// CommandFilter::with_prefix("!", "ban"); // return true if text message starts with "!ban" +/// ``` pub struct CommandFilter { command: String, } From 583d021be7a63af71ab522be4dbc1903a1ed3304 Mon Sep 17 00:00:00 2001 From: p0lunin Date: Fri, 17 Jan 2020 15:36:22 +0200 Subject: [PATCH 07/11] update docs for CommandFilter --- src/dispatching/filters/command.rs | 4 ++-- src/dispatching/mod.rs | 4 ++-- src/dispatching/updaters.rs | 30 ++++++++++++++++-------------- src/types/message_entity.rs | 10 ++++++---- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/dispatching/filters/command.rs b/src/dispatching/filters/command.rs index d4896ca5..1fd5bab5 100644 --- a/src/dispatching/filters/command.rs +++ b/src/dispatching/filters/command.rs @@ -7,8 +7,8 @@ use crate::{dispatching::Filter, types::Message}; /// Examples: /// ``` /// use teloxide::dispatching::filters::CommandFilter; -/// CommandFilter::new("start"); // return true if text message starts with "/start" -/// CommandFilter::with_prefix("!", "ban"); // return true if text message starts with "!ban" +/// CommandFilter::new("start"); // filter will return true if text message starts with "/start" +/// CommandFilter::with_prefix("!", "ban"); // filter will return true if text message starts with "!ban" /// ``` pub struct CommandFilter { command: String, diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index 8cdd83ae..8819a0ea 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -1,12 +1,12 @@ //! Update dispatching. -mod dispatchers; +//mod dispatchers; pub mod error_handlers; pub mod filters; mod handler; pub mod updaters; -pub use dispatchers::filter::FilterDispatcher; +//pub use dispatchers::filter::FilterDispatcher; pub use error_handlers::ErrorHandler; pub use filters::Filter; pub use handler::Handler; diff --git a/src/dispatching/updaters.rs b/src/dispatching/updaters.rs index 56dbe2e9..28a65901 100644 --- a/src/dispatching/updaters.rs +++ b/src/dispatching/updaters.rs @@ -146,23 +146,25 @@ pub fn polling( stream::unfold( (allowed_updates, bot, 0), - move |(mut allowed_updates, bot, mut offset)| async move { - let mut req = bot.get_updates().offset(offset); - req.timeout = timeout; - req.limit = limit; - req.allowed_updates = allowed_updates.take(); + move |(mut allowed_updates, bot, mut offset)| { + async move { + let mut req = bot.get_updates().offset(offset); + req.timeout = timeout; + req.limit = limit; + req.allowed_updates = allowed_updates.take(); - let updates = match req.send().await { - Err(err) => vec![Err(err)], - Ok(updates) => { - if let Some(upd) = updates.last() { - offset = upd.id + 1; + let updates = match req.send().await { + Err(err) => vec![Err(err)], + Ok(updates) => { + if let Some(upd) = updates.last() { + offset = upd.id + 1; + } + updates.into_iter().map(Ok).collect::>() } - updates.into_iter().map(Ok).collect::>() - } - }; + }; - Some((stream::iter(updates), (allowed_updates, bot, offset))) + Some((stream::iter(updates), (allowed_updates, bot, offset))) + } }, ) .flatten() diff --git a/src/types/message_entity.rs b/src/types/message_entity.rs index c87471d6..9a649176 100644 --- a/src/types/message_entity.rs +++ b/src/types/message_entity.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::types::{User, Message}; +use crate::types::{Message, User}; /// This object represents one special entity in a text message. For example, /// hashtags, usernames, URLs, etc. @@ -42,14 +42,16 @@ pub enum MessageEntityKind { impl MessageEntity { pub fn text_from(&self, message: &Message) -> Option { let text = message.text(); - Some(String::from(&text?[self.offset..self.offset+self.length])) + Some(String::from(&text?[self.offset..self.offset + self.length])) } } #[cfg(test)] mod tests { use super::*; - use crate::types::{Chat, ChatKind, MessageKind, Sender, ForwardKind, MediaKind}; + use crate::types::{ + Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, + }; #[test] fn recursive_kind() { @@ -111,7 +113,7 @@ mod tests { entities: vec![MessageEntity { kind: MessageEntityKind::Mention, offset: 3, - length: 3 + length: 3, }], }, reply_markup: None, From 4db07fc189e50b4e54707d0ee0d4292c405be65e Mon Sep 17 00:00:00 2001 From: p0lunin Date: Tue, 21 Jan 2020 10:41:10 +0200 Subject: [PATCH 08/11] delete filters --- src/dispatching/filters/command.rs | 125 ------ src/dispatching/filters/main.rs | 379 ------------------ src/dispatching/filters/message_caption.rs | 100 ----- src/dispatching/filters/message_text.rs | 95 ----- .../filters/message_text_caption.rs | 152 ------- src/dispatching/filters/mod.rs | 19 - src/dispatching/filters/regex_filter.rs | 73 ---- src/dispatching/mod.rs | 2 - 8 files changed, 945 deletions(-) delete mode 100644 src/dispatching/filters/command.rs delete mode 100644 src/dispatching/filters/main.rs delete mode 100644 src/dispatching/filters/message_caption.rs delete mode 100644 src/dispatching/filters/message_text.rs delete mode 100644 src/dispatching/filters/message_text_caption.rs delete mode 100644 src/dispatching/filters/mod.rs delete mode 100644 src/dispatching/filters/regex_filter.rs diff --git a/src/dispatching/filters/command.rs b/src/dispatching/filters/command.rs deleted file mode 100644 index 1fd5bab5..00000000 --- a/src/dispatching/filters/command.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::{dispatching::Filter, types::Message}; - -/// Filter which find command in message text -/// -/// *NB:* filter compare only text of message, not caption of media message -/// -/// Examples: -/// ``` -/// use teloxide::dispatching::filters::CommandFilter; -/// CommandFilter::new("start"); // filter will return true if text message starts with "/start" -/// CommandFilter::with_prefix("!", "ban"); // filter will return true if text message starts with "!ban" -/// ``` -pub struct CommandFilter { - command: String, -} - -impl Filter for CommandFilter { - fn test(&self, value: &Message) -> bool { - match value.text() { - Some(text) => text.starts_with(&self.command), - None => false, - } - } -} - -impl CommandFilter { - pub fn new(command: T) -> Self - where - T: Into, - { - Self { - command: '/'.to_string() + &command.into(), - } - } - pub fn with_prefix(prefix: U, command: T) -> Self - where - T: Into, - U: Into, - { - Self { - command: prefix.into() + &command.into(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::{ - Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User, - }; - - #[test] - fn commands_are_equal() { - let filter = CommandFilter::new("command"); - let message = create_message_with_text("/command".to_string()); - assert!(filter.test(&message)); - } - - #[test] - fn commands_are_not_equal() { - let filter = CommandFilter::new("command"); - let message = - create_message_with_text("/not_equal_command".to_string()); - assert_eq!(filter.test(&message), false); - } - - #[test] - fn command_have_args() { - let filter = CommandFilter::new("command"); - let message = - create_message_with_text("/command arg1 arg2".to_string()); - assert!(filter.test(&message)); - } - - #[test] - fn message_have_only_whitespace() { - let filter = CommandFilter::new("command"); - let message = create_message_with_text(" ".to_string()); - assert_eq!(filter.test(&message), false); - } - - #[test] - fn another_prefix() { - let filter = CommandFilter::with_prefix("command", "!"); - let message = create_message_with_text("!command".to_string()); - assert!(filter.test(&message)); - } - - fn create_message_with_text(text: String) -> Message { - Message { - id: 0, - date: 0, - chat: Chat { - id: 0, - kind: ChatKind::Private { - type_: (), - username: None, - first_name: None, - last_name: None, - }, - photo: None, - }, - kind: MessageKind::Common { - from: Sender::User(User { - id: 0, - is_bot: false, - first_name: "".to_string(), - last_name: None, - username: None, - language_code: None, - }), - forward_kind: ForwardKind::Origin { - reply_to_message: None, - }, - edit_date: None, - media_kind: MediaKind::Text { - text, - entities: vec![], - }, - reply_markup: None, - }, - } - } -} diff --git a/src/dispatching/filters/main.rs b/src/dispatching/filters/main.rs deleted file mode 100644 index 1a3b9b19..00000000 --- a/src/dispatching/filters/main.rs +++ /dev/null @@ -1,379 +0,0 @@ -/// Filter that determines that particular event -/// is suitable for particular handler. -pub trait Filter { - /// Passes (return true) if event is suitable (otherwise return false) - fn test(&self, value: &T) -> bool; -} - -/// ``` -/// use teloxide::dispatching::filters::Filter; -/// -/// let closure = |i: &i32| -> bool { *i >= 42 }; -/// assert!(closure.test(&42)); -/// assert!(closure.test(&100)); -/// -/// assert_eq!(closure.test(&41), false); -/// assert_eq!(closure.test(&0), false); -/// ``` -impl bool> Filter for F { - fn test(&self, value: &T) -> bool { - (self)(value) - } -} - -/// ``` -/// use teloxide::dispatching::filters::Filter; -/// -/// assert!(true.test(&())); -/// assert_eq!(false.test(&()), false); -/// ``` -impl Filter for bool { - fn test(&self, _: &T) -> bool { - *self - } -} - -/// And filter. -/// -/// Passes if both underlying filters pass. -/// -/// **NOTE**: if one of filters don't pass -/// it is **not** guaranteed that other will be executed. -/// -/// ## Examples -/// ``` -/// use teloxide::dispatching::filters::{And, Filter}; -/// -/// // Note: bool can be treated as `Filter` that always return self. -/// assert_eq!(And::new(true, false).test(&()), false); -/// assert_eq!(And::new(true, false).test(&()), false); -/// assert!(And::new(true, true).test(&())); -/// assert!(And::new(true, And::new(|_: &()| true, true)).test(&())); -/// ``` -#[derive(Debug, Clone, Copy)] -pub struct And(A, B); - -impl And { - pub fn new(a: A, b: B) -> Self { - And(a, b) - } -} - -impl Filter for And -where - A: Filter, - B: Filter, -{ - fn test(&self, value: &T) -> bool { - self.0.test(value) && self.1.test(value) - } -} - -/// Alias for [`And::new`] -/// -/// ## Examples -/// ``` -/// use teloxide::dispatching::filters::{and, Filter}; -/// -/// assert!(and(true, true).test(&())); -/// assert_eq!(and(true, false).test(&()), false); -/// ``` -/// -/// [`And::new`]: crate::dispatching::filters::And::new -pub fn and(a: A, b: B) -> And { - And::new(a, b) -} - -/// Or filter. -/// -/// Passes if at least one underlying filters passes. -/// -/// **NOTE**: if one of filters passes -/// it is **not** guaranteed that other will be executed. -/// -/// ## Examples -/// ``` -/// use teloxide::dispatching::filters::{Filter, Or}; -/// -/// // Note: bool can be treated as `Filter` that always return self. -/// assert!(Or::new(true, false).test(&())); -/// assert!(Or::new(false, true).test(&())); -/// assert!(Or::new(false, Or::new(|_: &()| true, false)).test(&())); -/// assert_eq!(Or::new(false, false).test(&()), false); -/// ``` -#[derive(Debug, Clone, Copy)] -pub struct Or(A, B); - -impl Or { - pub fn new(a: A, b: B) -> Self { - Or(a, b) - } -} - -impl Filter for Or -where - A: Filter, - B: Filter, -{ - fn test(&self, value: &T) -> bool { - self.0.test(value) || self.1.test(value) - } -} - -/// Alias for [`Or::new`] -/// -/// ## Examples -/// ``` -/// use teloxide::dispatching::filters::{or, Filter}; -/// -/// assert!(or(true, false).test(&())); -/// assert_eq!(or(false, false).test(&()), false); -/// ``` -/// -/// [`Or::new`]: crate::dispatching::filters::Or::new -pub fn or(a: A, b: B) -> Or { - Or::new(a, b) -} - -/// Not filter. -/// -/// Passes if underlying filter don't pass. -/// -/// ## Examples -/// ``` -/// use teloxide::dispatching::filters::{Filter, Not}; -/// -/// // Note: bool can be treated as `Filter` that always return self. -/// assert!(Not::new(false).test(&())); -/// assert_eq!(Not::new(true).test(&()), false); -/// ``` -#[derive(Debug, Clone, Copy)] -pub struct Not(A); - -impl Not { - pub fn new(a: A) -> Self { - Not(a) - } -} - -impl Filter for Not -where - A: Filter, -{ - fn test(&self, value: &T) -> bool { - !self.0.test(value) - } -} - -/// Alias for [`Not::new`] -/// -/// ## Examples -/// ``` -/// use teloxide::dispatching::filters::{not, Filter}; -/// -/// assert!(not(false).test(&())); -/// assert_eq!(not(true).test(&()), false); -/// ``` -/// -/// [`Not::new`]: crate::dispatching::filters::Not::new -pub fn not(a: A) -> Not { - Not::new(a) -} - -/// Return [filter] that passes if and only if all of the given filters passes. -/// -/// **NOTE**: if one of filters don't pass -/// it is **not** guaranteed that other will be executed. -/// -/// ## Examples -/// ``` -/// use teloxide::{all, dispatching::filters::Filter}; -/// -/// assert!(all![true].test(&())); -/// assert!(all![true, true].test(&())); -/// assert!(all![true, true, true].test(&())); -/// -/// assert_eq!(all![false].test(&()), false); -/// assert_eq!(all![true, false].test(&()), false); -/// assert_eq!(all![false, true].test(&()), false); -/// assert_eq!(all![false, false].test(&()), false); -/// ``` -/// -/// [filter]: crate::dispatching::filters::Filter -#[macro_export] -macro_rules! all { - ($one:expr) => { $one }; - ($head:expr, $($tail:tt)+) => { - $crate::dispatching::filters::And::new( - $head, - $crate::all!($($tail)+) - ) - }; -} - -/// Return [filter] that passes if any of the given filters passes. -/// -/// **NOTE**: if one of filters passes -/// it is **not** guaranteed that other will be executed. -/// -/// ## Examples -/// ``` -/// use teloxide::{any, dispatching::filters::Filter}; -/// -/// assert!(any![true].test(&())); -/// assert!(any![true, true].test(&())); -/// assert!(any![false, true].test(&())); -/// assert!(any![true, false, true].test(&())); -/// -/// assert_eq!(any![false].test(&()), false); -/// assert_eq!(any![false, false].test(&()), false); -/// assert_eq!(any![false, false, false].test(&()), false); -/// ``` -/// -/// [filter]: crate::dispatching::filters::Filter -#[macro_export] -macro_rules! any { - ($one:expr) => { $one }; - ($head:expr, $($tail:tt)+) => { - $crate::dispatching::filters::Or::new( - $head, - $crate::all!($($tail)+) - ) - }; -} - -/// Simple wrapper around `Filter` that adds `|` and `&` operators. -/// -/// ## Examples -/// ``` -/// use teloxide::dispatching::filters::{f, And, Filter, Or, F}; -/// -/// let flt1 = |i: &i32| -> bool { *i > 17 }; -/// let flt2 = |i: &i32| -> bool { *i < 42 }; -/// let flt3 = |i: &i32| -> bool { *i % 2 == 0 }; -/// -/// let and = f(flt1) & flt2; -/// assert!(and.test(&19)); // both filters pass -/// -/// assert_eq!(and.test(&50), false); // `flt2` doesn't pass -/// assert_eq!(and.test(&16), false); // `flt1` doesn't pass -/// -/// let or = f(flt1) | flt3; -/// assert!(or.test(&19)); // `flt1` passes -/// assert!(or.test(&16)); // `flt2` passes -/// assert!(or.test(&20)); // both pass -/// -/// assert_eq!(or.test(&17), false); // both don't pass -/// -/// // Note: only first filter in chain should be wrapped in `f(...)` -/// let complicated: F, _>> = f(flt1) & flt2 | flt3; -/// assert!(complicated.test(&2)); // `flt3` passes -/// assert!(complicated.test(&21)); // `flt1` and `flt2` pass -/// -/// assert_eq!(complicated.test(&15), false); // `flt1` and `flt3` don't pass -/// assert_eq!(complicated.test(&43), false); // `flt2` and `flt3` don't pass -/// ``` -pub struct F(A); - -/// Constructor fn for [F] -/// -/// [F]: crate::dispatching::filters::F; -pub fn f(a: A) -> F { - F(a) -} - -impl Filter for F -where - A: Filter, -{ - fn test(&self, value: &T) -> bool { - self.0.test(value) - } -} - -impl std::ops::BitAnd for F { - type Output = F>; - - fn bitand(self, other: B) -> Self::Output { - f(and(self.0, other)) - } -} - -impl std::ops::BitOr for F { - type Output = F>; - - fn bitor(self, other: B) -> Self::Output { - f(or(self.0, other)) - } -} - -/* workaround for `E0207` compiler error */ -/// Extensions for filters -pub trait FilterExt { - /// Alias for [`Not::new`] - /// - /// ## Examples - /// ``` - /// use teloxide::dispatching::filters::{Filter, FilterExt}; - /// - /// let flt = |i: &i32| -> bool { *i > 0 }; - /// let flt = flt.not(); - /// assert!(flt.test(&-1)); - /// assert_eq!(flt.test(&1), false); - /// ``` - /// - /// [`Not::new`]: crate::dispatching::filters::Not::new - fn not(self) -> Not - where - Self: Sized, - { - Not::new(self) - } - - /// Alias for [`And::new`] - /// - /// ## Examples - /// ``` - /// use teloxide::dispatching::filters::{Filter, FilterExt}; - /// - /// let flt = |i: &i32| -> bool { *i > 0 }; - /// let flt = flt.and(|i: &i32| *i < 42); - /// - /// assert!(flt.test(&1)); - /// assert_eq!(flt.test(&-1), false); - /// assert_eq!(flt.test(&43), false); - /// ``` - /// - /// [`Not::new`]: crate::dispatching::filters::And::new - fn and(self, other: B) -> And - where - Self: Sized, - { - And::new(self, other) - } - - /// Alias for [`Or::new`] - /// - /// ## Examples - /// ``` - /// use teloxide::dispatching::filters::{Filter, FilterExt}; - /// - /// let flt = |i: &i32| -> bool { *i < 0 }; - /// let flt = flt.or(|i: &i32| *i > 42); - /// - /// assert!(flt.test(&-1)); - /// assert!(flt.test(&43)); - /// assert_eq!(flt.test(&17), false); - /// ``` - /// - /// [`Not::new`]: crate::dispatching::filters::Or::new - fn or(self, other: B) -> Or - where - Self: Sized, - { - Or::new(self, other) - } -} - -// All methods implemented via defaults -impl FilterExt for F where F: Filter {} diff --git a/src/dispatching/filters/message_caption.rs b/src/dispatching/filters/message_caption.rs deleted file mode 100644 index 02255bc6..00000000 --- a/src/dispatching/filters/message_caption.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::{dispatching::Filter, types::Message}; - -/// Filter which compare caption of media with another text. -/// Returns true if the caption of media is equal to another text, otherwise -/// false. -/// -/// NOTE: filter compares only caption of media, does not compare text of -/// message! -/// -/// If you want to compare text of message use -/// [MessageTextFilter] -/// -/// If you want to compare text and caption use -/// [MessageTextCaptionFilter] -/// -/// [MessageTextFilter]: crate::dispatching::filters::MessageTextFilter -/// [MessageTextCaptionFilter]: -/// crate::dispatching::filters::MessageTextCaptionFilter -pub struct MessageCaptionFilter { - text: String, -} - -impl Filter for MessageCaptionFilter { - fn test(&self, value: &Message) -> bool { - match value.caption() { - Some(caption) => self.text == caption, - None => false, - } - } -} - -impl MessageCaptionFilter { - pub fn new(text: T) -> Self - where - T: Into, - { - Self { text: text.into() } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::{ - Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User, - }; - - #[test] - fn captions_are_equal() { - let filter = MessageCaptionFilter::new("caption".to_string()); - let message = create_message_with_caption("caption".to_string()); - assert!(filter.test(&message)); - } - - #[test] - fn captions_are_not_equal() { - let filter = MessageCaptionFilter::new("caption".to_string()); - let message = - create_message_with_caption("not equal caption".to_string()); - assert_eq!(filter.test(&message), false); - } - - fn create_message_with_caption(caption: String) -> Message { - Message { - id: 0, - date: 0, - chat: Chat { - id: 0, - kind: ChatKind::Private { - type_: (), - username: None, - first_name: None, - last_name: None, - }, - photo: None, - }, - kind: MessageKind::Common { - from: Sender::User(User { - id: 0, - is_bot: false, - first_name: "".to_string(), - last_name: None, - username: None, - language_code: None, - }), - forward_kind: ForwardKind::Origin { - reply_to_message: None, - }, - edit_date: None, - media_kind: MediaKind::Photo { - photo: vec![], - caption: Some(caption), - caption_entities: vec![], - media_group_id: None, - }, - reply_markup: None, - }, - } - } -} diff --git a/src/dispatching/filters/message_text.rs b/src/dispatching/filters/message_text.rs deleted file mode 100644 index e713d9bc..00000000 --- a/src/dispatching/filters/message_text.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::{dispatching::Filter, types::Message}; - -/// Filter which compare message text with another text. -/// Returns true if the message text is equal to another text, otherwise false. -/// -/// NOTE: filter compares only text message, does not compare caption of media! -/// -/// If you want to compare caption use -/// [MessageCaptionFilter] -/// -/// If you want to compare text and caption use -/// [MessageTextCaptionFilter] -/// -/// [MessageCaptionFilter]: crate::dispatching::filters::MessageCaptionFilter -/// [MessageTextCaptionFilter]: -/// crate::dispatching::filters::MessageTextCaptionFilter -pub struct MessageTextFilter { - text: String, -} - -impl Filter for MessageTextFilter { - fn test(&self, value: &Message) -> bool { - match value.text() { - Some(text) => self.text == text, - None => false, - } - } -} - -impl MessageTextFilter { - pub fn new(text: T) -> Self - where - T: Into, - { - Self { text: text.into() } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::{ - Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User, - }; - - #[test] - fn texts_are_equal() { - let filter = MessageTextFilter::new("text"); - let message = create_message_with_text("text".to_string()); - assert!(filter.test(&message)); - } - - #[test] - fn texts_are_not_equal() { - let filter = MessageTextFilter::new("text"); - let message = create_message_with_text("not equal text".to_string()); - assert_eq!(filter.test(&message), false); - } - - fn create_message_with_text(text: String) -> Message { - Message { - id: 0, - date: 0, - chat: Chat { - id: 0, - kind: ChatKind::Private { - type_: (), - username: None, - first_name: None, - last_name: None, - }, - photo: None, - }, - kind: MessageKind::Common { - from: Sender::User(User { - id: 0, - is_bot: false, - first_name: "".to_string(), - last_name: None, - username: None, - language_code: None, - }), - forward_kind: ForwardKind::Origin { - reply_to_message: None, - }, - edit_date: None, - media_kind: MediaKind::Text { - text, - entities: vec![], - }, - reply_markup: None, - }, - } - } -} diff --git a/src/dispatching/filters/message_text_caption.rs b/src/dispatching/filters/message_text_caption.rs deleted file mode 100644 index eabcfb71..00000000 --- a/src/dispatching/filters/message_text_caption.rs +++ /dev/null @@ -1,152 +0,0 @@ -use crate::{dispatching::Filter, types::Message}; - -/// Filter which compare message text or caption of media with another text. -/// Returns true if the message text or caption of media is equal to another -/// text, otherwise false. -/// -/// NOTE: filter compares text of message or if it is not exists, compares -/// caption of the message! -/// -/// If you want to compare only caption use -/// [MessageCaptionFilter] -/// -/// If you want to compare only text use -/// [MessageTextFilter] -/// -/// [MessageCaptionFilter]: crate::dispatching::filters::MessageCaptionFilter -/// [MessageTextFilter]: crate::dispatching::filters::MessageTextFilter -pub struct MessageTextCaptionFilter { - text: String, -} - -impl Filter for MessageTextCaptionFilter { - fn test(&self, value: &Message) -> bool { - match value.text() { - Some(text) => self.text == text, - None => match value.caption() { - Some(caption) => self.text == caption, - None => false, - }, - } - } -} - -impl MessageTextCaptionFilter { - pub fn new(text: T) -> Self - where - T: Into, - { - Self { text: text.into() } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::{ - Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User, - }; - - #[test] - fn texts_are_equal() { - let filter = MessageTextCaptionFilter::new("text"); - let message = create_message_with_text("text".to_string()); - assert!(filter.test(&message)); - } - - #[test] - fn texts_are_not_equal() { - let filter = MessageTextCaptionFilter::new("text"); - let message = create_message_with_text("not equal text".to_string()); - assert_eq!(filter.test(&message), false); - } - - fn create_message_with_text(text: String) -> Message { - Message { - id: 0, - date: 0, - chat: Chat { - id: 0, - kind: ChatKind::Private { - type_: (), - username: None, - first_name: None, - last_name: None, - }, - photo: None, - }, - kind: MessageKind::Common { - from: Sender::User(User { - id: 0, - is_bot: false, - first_name: "".to_string(), - last_name: None, - username: None, - language_code: None, - }), - forward_kind: ForwardKind::Origin { - reply_to_message: None, - }, - edit_date: None, - media_kind: MediaKind::Text { - text, - entities: vec![], - }, - reply_markup: None, - }, - } - } - - #[test] - fn captions_are_equal() { - let filter = MessageTextCaptionFilter::new("caption".to_string()); - let message = create_message_with_caption("caption".to_string()); - assert!(filter.test(&message)); - } - - #[test] - fn captions_are_not_equal() { - let filter = MessageTextCaptionFilter::new("caption".to_string()); - let message = - create_message_with_caption("not equal caption".to_string()); - assert_eq!(filter.test(&message), false); - } - - fn create_message_with_caption(caption: String) -> Message { - Message { - id: 0, - date: 0, - chat: Chat { - id: 0, - kind: ChatKind::Private { - type_: (), - username: None, - first_name: None, - last_name: None, - }, - photo: None, - }, - kind: MessageKind::Common { - from: Sender::User(User { - id: 0, - is_bot: false, - first_name: "".to_string(), - last_name: None, - username: None, - language_code: None, - }), - forward_kind: ForwardKind::Origin { - reply_to_message: None, - }, - edit_date: None, - media_kind: MediaKind::Photo { - photo: vec![], - caption: Some(caption), - caption_entities: vec![], - media_group_id: None, - }, - reply_markup: None, - }, - } - } -} diff --git a/src/dispatching/filters/mod.rs b/src/dispatching/filters/mod.rs deleted file mode 100644 index c8d62294..00000000 --- a/src/dispatching/filters/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Filters of messages. - -pub use main::*; - -pub use command::*; -pub use message_caption::*; -pub use message_text::*; -pub use message_text_caption::*; -#[cfg(feature = "regex_filter")] -pub use regex_filter::*; - -mod main; - -mod command; -mod message_caption; -mod message_text; -mod message_text_caption; -#[cfg(feature = "regex_filter")] -mod regex_filter; diff --git a/src/dispatching/filters/regex_filter.rs b/src/dispatching/filters/regex_filter.rs deleted file mode 100644 index 71ce4d3f..00000000 --- a/src/dispatching/filters/regex_filter.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::{dispatching::Filter, types::Message}; -use regex::Regex; - -// TODO: docs -pub struct RegexFilter { - regexp: Regex, -} - -impl Filter for RegexFilter { - fn test(&self, value: &Message) -> bool { - match value.text() { - Some(text) => self.regexp.is_match(text), - None => false, - } - } -} - -impl RegexFilter { - pub fn new(regexp: Regex) -> Self { - Self { regexp } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::{ - Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User, - }; - - #[test] - fn match_true() { - let filter = RegexFilter::new(Regex::new(r"\w+").unwrap()); - let message = create_message_with_text("text".to_string()); - assert!(filter.test(&message)); - } - - fn create_message_with_text(text: String) -> Message { - Message { - id: 0, - date: 0, - chat: Chat { - id: 0, - kind: ChatKind::Private { - type_: (), - username: None, - first_name: None, - last_name: None, - }, - photo: None, - }, - kind: MessageKind::Common { - from: Sender::User(User { - id: 0, - is_bot: false, - first_name: "".to_string(), - last_name: None, - username: None, - language_code: None, - }), - forward_kind: ForwardKind::Origin { - reply_to_message: None, - }, - edit_date: None, - media_kind: MediaKind::Text { - text, - entities: vec![], - }, - reply_markup: None, - }, - } - } -} diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index 8819a0ea..b1099d8f 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -2,12 +2,10 @@ //mod dispatchers; pub mod error_handlers; -pub mod filters; mod handler; pub mod updaters; //pub use dispatchers::filter::FilterDispatcher; pub use error_handlers::ErrorHandler; -pub use filters::Filter; pub use handler::Handler; pub use updaters::Updater; From 9c020a23cb021018426e391924fdb73483a7104e Mon Sep 17 00:00:00 2001 From: p0lunin Date: Tue, 21 Jan 2020 10:42:43 +0200 Subject: [PATCH 09/11] remove feature regex-filter --- Cargo.toml | 5 ----- src/dispatching/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dbedf53c..bbd9acae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[features] -regex_filter = ["regex"] - [dependencies] serde_json = "1.0.44" serde = { version = "1.0.101", features = ["derive"] } @@ -27,5 +24,3 @@ pin-project = "0.4.6" serde_with_macros = "1.0.1" either = "1.5.3" mime = "0.3.16" - -regex = {version = "1.3.3", optional = true} diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index b1099d8f..1a8d9c07 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -1,11 +1,11 @@ //! Update dispatching. -//mod dispatchers; +mod dispatchers; pub mod error_handlers; mod handler; pub mod updaters; -//pub use dispatchers::filter::FilterDispatcher; +pub use dispatchers::filter::FilterDispatcher; pub use error_handlers::ErrorHandler; pub use handler::Handler; pub use updaters::Updater; From 841f80ea78225e36218588daf7e24e26f23228da Mon Sep 17 00:00:00 2001 From: p0lunin Date: Tue, 21 Jan 2020 10:44:41 +0200 Subject: [PATCH 10/11] merge from dev --- src/dispatching/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dispatching/mod.rs b/src/dispatching/mod.rs index 70d1e0c4..cfd4f87b 100644 --- a/src/dispatching/mod.rs +++ b/src/dispatching/mod.rs @@ -8,9 +8,7 @@ pub enum DispatchResult { } pub mod chat; -pub mod filters; mod handler; pub mod update_listeners; -pub use filters::Filter; pub use handler::*; From 05c27aa9d05bdb80e6fe137a6499555181c47787 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Wed, 22 Jan 2020 01:56:00 +0600 Subject: [PATCH 11/11] Fix rustfmt --- src/dispatching/update_listeners.rs | 30 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/dispatching/update_listeners.rs b/src/dispatching/update_listeners.rs index 815e8e96..9a0722f6 100644 --- a/src/dispatching/update_listeners.rs +++ b/src/dispatching/update_listeners.rs @@ -146,25 +146,23 @@ pub fn polling( stream::unfold( (allowed_updates, bot, 0), - move |(mut allowed_updates, bot, mut offset)| { - async move { - let mut req = bot.get_updates().offset(offset); - req.timeout = timeout; - req.limit = limit; - req.allowed_updates = allowed_updates.take(); + move |(mut allowed_updates, bot, mut offset)| async move { + let mut req = bot.get_updates().offset(offset); + req.timeout = timeout; + req.limit = limit; + req.allowed_updates = allowed_updates.take(); - let updates = match req.send().await { - Err(err) => vec![Err(err)], - Ok(updates) => { - if let Some(upd) = updates.last() { - offset = upd.id + 1; - } - updates.into_iter().map(Ok).collect::>() + let updates = match req.send().await { + Err(err) => vec![Err(err)], + Ok(updates) => { + if let Some(upd) = updates.last() { + offset = upd.id + 1; } - }; + updates.into_iter().map(Ok).collect::>() + } + }; - Some((stream::iter(updates), (allowed_updates, bot, offset))) - } + Some((stream::iter(updates), (allowed_updates, bot, offset))) }, ) .flatten()