From 0897edae0918056a738cd52248b819fc44c7c46e Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 19 Jun 2020 17:36:06 +0600 Subject: [PATCH 1/7] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5cdd699a..0edc7d54 100644 --- a/README.md +++ b/README.md @@ -399,7 +399,11 @@ Associated links: Of course, you can. The [`enable_logging!`](https://docs.rs/teloxide/latest/teloxide/macro.enable_logging.html) and [`enable_logging_with_filter!`](https://docs.rs/teloxide/latest/teloxide/macro.enable_logging_with_filter.html) macros are just convenient utilities, not necessary to use them. You can setup a different logger, for example, [fern](https://crates.io/crates/fern), as usual, e.g. teloxide has no specific requirements as it depends only on [log](https://crates.io/crates/log). ## Community bots -Feel free to push your own bot into our collection: https://github.com/teloxide/community-bots. Later you will be able to play with them right in our official chat: https://t.me/teloxide. +Feel free to push your own bot into our collection! + + - [Rust subreddit reader](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/subreddit_reader) + - [with_webserver - teloxide + warp](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/with_webserver) + - [vzmuinebot - Telegram bot for food menu navigate](https://github.com/ArtHome12/vzmuinebot) ## Contributing See [CONRIBUTING.md](https://github.com/teloxide/teloxide/blob/master/CONTRIBUTING.md). From c5c659738fa45a7b2b35979da619ea2a69e01501 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 19 Jun 2020 17:37:08 +0600 Subject: [PATCH 2/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0edc7d54..db0335b3 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ Of course, you can. The [`enable_logging!`](https://docs.rs/teloxide/latest/telo Feel free to push your own bot into our collection! - [Rust subreddit reader](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/subreddit_reader) - - [with_webserver - teloxide + warp](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/with_webserver) + - [with_webserver - How to use teloxide with warp](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/with_webserver) - [vzmuinebot - Telegram bot for food menu navigate](https://github.com/ArtHome12/vzmuinebot) ## Contributing From 1289ef9be1a6c5d8dd0e3bfc7614c922d3254ade Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 19 Jun 2020 17:37:40 +0600 Subject: [PATCH 3/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db0335b3..8f6df8ca 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ Of course, you can. The [`enable_logging!`](https://docs.rs/teloxide/latest/telo Feel free to push your own bot into our collection! - [Rust subreddit reader](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/subreddit_reader) - - [with_webserver - How to use teloxide with warp](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/with_webserver) + - [with_webserver - An example of the teloxide + warp combination](https://github.com/steadylearner/Rust-Full-Stack/tree/master/commits/teloxide/with_webserver) - [vzmuinebot - Telegram bot for food menu navigate](https://github.com/ArtHome12/vzmuinebot) ## Contributing From eabc77caab6fb792961c0204e57cfd7445e4deee Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 26 Jun 2020 18:18:31 +0600 Subject: [PATCH 4/7] Remove wrap_dialogue! --- examples/dialogue_bot/src/main.rs | 15 +++-- examples/dialogue_bot/src/states.rs | 5 -- examples/dialogue_bot/src/transitions.rs | 2 +- .../dialogue/dialogue_dispatcher.rs | 35 ++++++++---- src/dispatching/dialogue/dialogue_stage.rs | 14 +---- src/dispatching/dialogue/mod.rs | 57 +------------------ src/prelude.rs | 4 +- 7 files changed, 42 insertions(+), 90 deletions(-) diff --git a/examples/dialogue_bot/src/main.rs b/examples/dialogue_bot/src/main.rs index fac687fb..a907142d 100644 --- a/examples/dialogue_bot/src/main.rs +++ b/examples/dialogue_bot/src/main.rs @@ -38,18 +38,21 @@ async fn run() { let bot = Bot::from_env(); Dispatcher::new(bot) - .messages_handler(DialogueDispatcher::new(|cx| async move { - let DialogueWithCx { cx, dialogue } = cx; + .messages_handler(DialogueDispatcher::new( + |cx| async move { + let DialogueWithCx { cx, dialogue } = cx; - // Unwrap without panic because of std::convert::Infallible. - let Wrapper(dialogue) = dialogue.unwrap(); + // Unwrap without panic because of std::convert::Infallible. + let dialogue = dialogue.unwrap(); - dispatch!( + dispatch!( [cx, dialogue] -> [start, receive_full_name, receive_age, receive_favourite_music] ) .expect("Something wrong with the bot!") - })) + }, + || Dialogue::inject(StartState), + )) .dispatch() .await; } diff --git a/examples/dialogue_bot/src/states.rs b/examples/dialogue_bot/src/states.rs index c920392a..e40d2778 100644 --- a/examples/dialogue_bot/src/states.rs +++ b/examples/dialogue_bot/src/states.rs @@ -42,8 +42,3 @@ pub type Dialogue = Coprod!( ReceiveAgeState, ReceiveFavouriteMusicState, ); - -wrap_dialogue!( - Wrapper(Dialogue), - default Self(Dialogue::inject(StartState)), -); diff --git a/examples/dialogue_bot/src/transitions.rs b/examples/dialogue_bot/src/transitions.rs index 4a20fafb..9960bc8d 100644 --- a/examples/dialogue_bot/src/transitions.rs +++ b/examples/dialogue_bot/src/transitions.rs @@ -3,7 +3,7 @@ use teloxide::prelude::*; use super::{favourite_music::FavouriteMusic, states::*}; pub type In = TransitionIn; -pub type Out = TransitionOut; +pub type Out = TransitionOut; pub async fn start(cx: In) -> Out { let (cx, dialogue) = cx.unpack(); diff --git a/src/dispatching/dialogue/dialogue_dispatcher.rs b/src/dispatching/dialogue/dialogue_dispatcher.rs index 385545c2..91f473b6 100644 --- a/src/dispatching/dialogue/dialogue_dispatcher.rs +++ b/src/dispatching/dialogue/dialogue_dispatcher.rs @@ -26,6 +26,7 @@ use std::sync::{Arc, Mutex}; pub struct DialogueDispatcher { storage: Arc, handler: Arc, + default: Arc D + Send + Sync + 'static>, _phantom: PhantomData>, /// A lock-free map to handle updates from the same chat sequentially, but @@ -41,17 +42,22 @@ impl DialogueDispatcher, H, Upd> where H: DialogueDispatcherHandler + Send + Sync + 'static, Upd: GetChatId + Send + 'static, - D: Default + Send + 'static, + D: Send + 'static, { - /// Creates a dispatcher with the specified `handler` and [`InMemStorage`] - /// (a default storage). + /// Creates a dispatcher with the specified `handler, [`InMemStorage`] + /// (a default storage), and a function that returns a default dialogue, + /// used when a user initiates a new dialogue. /// /// [`InMemStorage`]: crate::dispatching::dialogue::InMemStorage #[must_use] - pub fn new(handler: H) -> Self { + pub fn new(handler: H, default: F) -> Self + where + F: Fn() -> D + Send + Sync + 'static, + { Self { storage: InMemStorage::new(), handler: Arc::new(handler), + default: Arc::new(default), senders: Arc::new(Map::new()), _phantom: PhantomData, } @@ -62,16 +68,22 @@ impl DialogueDispatcher where H: DialogueDispatcherHandler + Send + Sync + 'static, Upd: GetChatId + Send + 'static, - D: Default + Send + 'static, + D: Send + 'static, S: Storage + Send + Sync + 'static, S::Error: Send + 'static, { - /// Creates a dispatcher with the specified `handler` and `storage`. + /// Creates a dispatcher with the specified `handler`, `storage`, and a + /// function that returns a default dialogue, used when a user initiates a + /// new dialogue. #[must_use] - pub fn with_storage(handler: H, storage: Arc) -> Self { + pub fn with_storage(handler: H, storage: Arc, default: F) -> Self + where + F: Fn() -> D + Send + Sync + 'static, + { Self { storage, handler: Arc::new(handler), + default: Arc::new(default), senders: Arc::new(Map::new()), _phantom: PhantomData, } @@ -83,11 +95,13 @@ where let storage = Arc::clone(&self.storage); let handler = Arc::clone(&self.handler); + let default = Arc::clone(&self.default); let senders = Arc::clone(&self.senders); tokio::spawn(rx.for_each(move |cx: UpdateWithCx| { let storage = Arc::clone(&storage); let handler = Arc::clone(&handler); + let default = Arc::clone(&default); let senders = Arc::clone(&senders); async move { @@ -96,7 +110,7 @@ where let dialogue = Arc::clone(&storage) .remove_dialogue(chat_id) .await - .map(Option::unwrap_or_default); + .map(|opt| opt.unwrap_or_else(move || default())); match handler.handle(DialogueWithCx { cx, dialogue }).await { DialogueStage::Next(new_dialogue) => { @@ -131,7 +145,7 @@ impl DispatcherHandler for DialogueDispatcher where H: DialogueDispatcherHandler + Send + Sync + 'static, Upd: GetChatId + Send + 'static, - D: Default + Send + 'static, + D: Send + 'static, S: Storage + Send + Sync + 'static, S::Error: Send + 'static, { @@ -189,7 +203,7 @@ mod tests { #[tokio::test] async fn updates_from_same_chat_executed_sequentially() { - #[derive(Debug)] + #[derive(Debug, Default)] struct MyUpdate { chat_id: i64, unique_number: u32, @@ -232,6 +246,7 @@ mod tests { DialogueStage::Next(()) }, + || (), ); let updates = stream::iter( diff --git a/src/dispatching/dialogue/dialogue_stage.rs b/src/dispatching/dialogue/dialogue_stage.rs index 2fd3c8e7..7778e334 100644 --- a/src/dispatching/dialogue/dialogue_stage.rs +++ b/src/dispatching/dialogue/dialogue_stage.rs @@ -11,29 +11,21 @@ pub enum DialogueStage { Exit, } -/// A dialogue wrapper to bypass orphan rules. -pub trait DialogueWrapper { - fn new(dialogue: D) -> Self; -} - /// Returns a new dialogue state. /// /// See [the module-level documentation for the design /// overview](crate::dispatching::dialogue). -pub fn next( - new_state: State, -) -> TransitionOut +pub fn next(new_state: State) -> TransitionOut where Dialogue: CoprodInjector, - DWrapper: DialogueWrapper, { - Ok(DialogueStage::Next(DWrapper::new(Dialogue::inject(new_state)))) + Ok(DialogueStage::Next(Dialogue::inject(new_state))) } /// Exits a dialogue. /// /// See [the module-level documentation for the design /// overview](crate::dispatching::dialogue). -pub fn exit() -> TransitionOut { +pub fn exit() -> TransitionOut { Ok(DialogueStage::Exit) } diff --git a/src/dispatching/dialogue/mod.rs b/src/dispatching/dialogue/mod.rs index fd770d5e..e35c23de 100644 --- a/src/dispatching/dialogue/mod.rs +++ b/src/dispatching/dialogue/mod.rs @@ -52,7 +52,7 @@ mod storage; use crate::{requests::ResponseResult, types::Message}; pub use dialogue_dispatcher::DialogueDispatcher; pub use dialogue_dispatcher_handler::DialogueDispatcherHandler; -pub use dialogue_stage::{exit, next, DialogueStage, DialogueWrapper}; +pub use dialogue_stage::{exit, next, DialogueStage}; pub use dialogue_with_cx::DialogueWithCx; pub use get_chat_id::GetChatId; pub use storage::{InMemStorage, Storage}; @@ -121,59 +121,6 @@ macro_rules! dispatch { }; } -/// Generates a dialogue wrapper and implements `Default` for it. -/// -/// The reason is to bypass orphan rules to be able to pass a user-defined -/// dialogue into [`DialogueDispatcher`]. Since a dialogue is -/// [`frunk::Coproduct`], we cannot directly satisfy the `D: Default` -/// constraint. -/// -/// # Examples -/// ``` -/// use teloxide::prelude::*; -/// -/// struct StartState; -/// struct ReceiveWordState; -/// struct ReceiveNumberState; -/// struct ExitState; -/// -/// type Dialogue = Coprod!( -/// StartState, -/// ReceiveWordState, -/// ReceiveNumberState, -/// ); -/// -/// wrap_dialogue!( -/// Wrapper(Dialogue), -/// default Self(Dialogue::inject(StartState)), -/// ); -/// -/// let start_state = Wrapper::default(); -/// ``` -/// -/// [`DialogueDispatcher`]: crate::dispatching::dialogue::DialogueDispatcher -/// [`frunk::Coproduct`]: https://docs.rs/frunk/0.3.1/frunk/coproduct/enum.Coproduct.html -#[macro_export] -macro_rules! wrap_dialogue { - ($name:ident($dialogue:ident), default $default_block:expr, ) => { - pub struct $name(pub $dialogue); - - impl teloxide::dispatching::dialogue::DialogueWrapper<$dialogue> - for $name - { - fn new(d: $dialogue) -> Wrapper { - $name(d) - } - } - - impl Default for $name { - fn default() -> $name { - $default_block - } - } - }; -} - /// Generates `.up(field)` methods for dialogue states. /// /// Given inductively defined states, this macro generates `.up(field)` methods @@ -227,4 +174,4 @@ macro_rules! up { pub type TransitionIn = DialogueWithCx; // A type returned from a FSM transition function. -pub type TransitionOut = ResponseResult>; +pub type TransitionOut = ResponseResult>; diff --git a/src/prelude.rs b/src/prelude.rs index 43e29238..64d04656 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -5,14 +5,14 @@ pub use crate::{ dispatching::{ dialogue::{ exit, next, DialogueDispatcher, DialogueStage, DialogueWithCx, - DialogueWrapper, GetChatId, TransitionIn, TransitionOut, + GetChatId, TransitionIn, TransitionOut, }, Dispatcher, DispatcherHandlerRx, DispatcherHandlerRxExt, UpdateWithCx, }, error_handlers::{LoggingErrorHandler, OnError}, requests::{Request, ResponseResult}, types::{Message, Update}, - up, wrap_dialogue, Bot, RequestError, + up, Bot, RequestError, }; pub use frunk::{Coprod, Coproduct}; From 9cab81b8bf7d5ef45bdbb8361d466541e728564d Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 26 Jun 2020 18:33:40 +0600 Subject: [PATCH 5/7] Fix the docs --- src/dispatching/dialogue/mod.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/dispatching/dialogue/mod.rs b/src/dispatching/dialogue/mod.rs index e35c23de..7917c551 100644 --- a/src/dispatching/dialogue/mod.rs +++ b/src/dispatching/dialogue/mod.rs @@ -74,13 +74,8 @@ pub use storage::{InMemStorage, Storage}; /// ReceiveNumberState, /// ); /// -/// wrap_dialogue!( -/// Wrapper(Dialogue), -/// default Self(Dialogue::inject(StartState)), -/// ); -/// /// pub type In = TransitionIn; -/// pub type Out = TransitionOut; +/// pub type Out = TransitionOut; /// /// pub async fn start(cx: In) -> Out { todo!() } /// pub async fn receive_word(cx: In) -> Out { todo!() } From f022e73a08ea0f58731499ec4052a3a1afab0fae Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 26 Jun 2020 18:35:43 +0600 Subject: [PATCH 6/7] Update README.md --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8f6df8ca..44869f16 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,6 @@ - [Dialogues](https://github.com/teloxide/teloxide#dialogues) - [Recommendations](https://github.com/teloxide/teloxide#recommendations) - [FAQ](https://github.com/teloxide/teloxide#faq) - - [Where I can ask questions?](https://github.com/teloxide/teloxide#where-i-can-ask-questions) - - [Why Rust?](https://github.com/teloxide/teloxide#why-rust) - - [Can I use different loggers?](https://github.com/teloxide/teloxide#can-i-use-different-loggers) - [Community bots](https://github.com/teloxide/teloxide#community-bots) - [Contributing](https://github.com/teloxide/teloxide#contributing) @@ -382,21 +379,25 @@ async fn main() { The second one produces very strange compiler messages because of the `#[tokio::main]` macro. However, the examples in this README use the second variant for brevity. ## FAQ -### Where I can ask questions? -[Issues](https://github.com/teloxide/teloxide/issues) is a good place for well-formed questions, for example, about the library design, enhancements, bug reports. But if you can't compile your bot due to compilation errors and need quick help, feel free to ask in [our official group](https://t.me/teloxide). +Q: Where I can ask questions? -### Why Rust? -Most programming languages have their own implementations of Telegram bots frameworks, so why not Rust? We think Rust provides enough good ecosystem and the language itself to be suitable for writing bots. +A: [Issues](https://github.com/teloxide/teloxide/issues) is a good place for well-formed questions, for example, about the library design, enhancements, bug reports. But if you can't compile your bot due to compilation errors and need quick help, feel free to ask in [our official group](https://t.me/teloxide). -### Can I use webhooks? -teloxide doesn't provide special API for working with webhooks due to their nature with lots of subtle settings. Instead, you setup your webhook by yourself, as shown in [webhook_ping_pong_bot](examples/ngrok_ping_pong_bot/src/main.rs). +Q: Why Rust? + +A: Most programming languages have their own implementations of Telegram bots frameworks, so why not Rust? We think Rust provides enough good ecosystem and the language itself to be suitable for writing bots. + +Q: Can I use webhooks? + +A: teloxide doesn't provide special API for working with webhooks due to their nature with lots of subtle settings. Instead, you setup your webhook by yourself, as shown in [webhook_ping_pong_bot](examples/ngrok_ping_pong_bot/src/main.rs). Associated links: - [Marvin's Marvellous Guide to All Things Webhook](https://core.telegram.org/bots/webhooks) - [Using self-signed certificates](https://core.telegram.org/bots/self-signed) -### Can I use different loggers? -Of course, you can. The [`enable_logging!`](https://docs.rs/teloxide/latest/teloxide/macro.enable_logging.html) and [`enable_logging_with_filter!`](https://docs.rs/teloxide/latest/teloxide/macro.enable_logging_with_filter.html) macros are just convenient utilities, not necessary to use them. You can setup a different logger, for example, [fern](https://crates.io/crates/fern), as usual, e.g. teloxide has no specific requirements as it depends only on [log](https://crates.io/crates/log). +Q: Can I use different loggers? + +A: Of course, you can. The [`enable_logging!`](https://docs.rs/teloxide/latest/teloxide/macro.enable_logging.html) and [`enable_logging_with_filter!`](https://docs.rs/teloxide/latest/teloxide/macro.enable_logging_with_filter.html) macros are just convenient utilities, not necessary to use them. You can setup a different logger, for example, [fern](https://crates.io/crates/fern), as usual, e.g. teloxide has no specific requirements as it depends only on [log](https://crates.io/crates/log). ## Community bots Feel free to push your own bot into our collection! From 5b489747d5895b7a9f8bfb871650263384af44f6 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 26 Jun 2020 18:36:50 +0600 Subject: [PATCH 7/7] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 44869f16..dee44aeb 100644 --- a/README.md +++ b/README.md @@ -222,11 +222,6 @@ pub type Dialogue = Coprod!( ReceiveAgeState, ReceiveFavouriteMusicState, ); - -wrap_dialogue!( - Wrapper(Dialogue), - default Self(Dialogue::inject(StartState)), -); ``` The [`wrap_dialogue!`](https://docs.rs/teloxide/latest/teloxide/macro.wrap_dialogue.html) macro generates a new-type of `Dialogue` with a default implementation. @@ -345,7 +340,7 @@ async fn main() { [start, receive_full_name, receive_age, receive_favourite_music] ) .expect("Something wrong with the bot!") - })) + }, || Dialogue::inject(StartState))) .dispatch() .await; }