From f67d960d43acecfde901553c5ffb93fb0fc120d7 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Thu, 23 Jul 2020 21:40:36 +0600 Subject: [PATCH 01/14] Make a test in examples/dialogue_bot --- examples/dialogue_bot/Cargo.toml | 3 - examples/dialogue_bot/src/favourite_music.rs | 22 ------ examples/dialogue_bot/src/main.rs | 1 - examples/dialogue_bot/src/states.rs | 40 +++++------ examples/dialogue_bot/src/transitions.rs | 72 +++++++++++--------- 5 files changed, 54 insertions(+), 84 deletions(-) delete mode 100644 examples/dialogue_bot/src/favourite_music.rs diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index 6bf24e5d..bcf96df5 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -10,11 +10,8 @@ edition = "2018" log = "0.4.8" tokio = "0.2.9" pretty_env_logger = "0.4.0" - smart-default = "0.6.0" derive_more = "0.99.9" -parse-display = "0.1.1" - teloxide = { path = "../../" } [profile.release] diff --git a/examples/dialogue_bot/src/favourite_music.rs b/examples/dialogue_bot/src/favourite_music.rs deleted file mode 100644 index 5b102332..00000000 --- a/examples/dialogue_bot/src/favourite_music.rs +++ /dev/null @@ -1,22 +0,0 @@ -use parse_display::{Display, FromStr}; - -use teloxide::types::{KeyboardButton, ReplyKeyboardMarkup}; - -#[derive(Copy, Clone, Display, FromStr)] -pub enum FavouriteMusic { - Rock, - Metal, - Pop, - Other, -} - -impl FavouriteMusic { - pub fn markup() -> ReplyKeyboardMarkup { - ReplyKeyboardMarkup::default().append_row(vec![ - KeyboardButton::new("Rock"), - KeyboardButton::new("Metal"), - KeyboardButton::new("Pop"), - KeyboardButton::new("Other"), - ]) - } -} diff --git a/examples/dialogue_bot/src/main.rs b/examples/dialogue_bot/src/main.rs index f9b9683d..e043bdf9 100644 --- a/examples/dialogue_bot/src/main.rs +++ b/examples/dialogue_bot/src/main.rs @@ -22,7 +22,6 @@ extern crate smart_default; #[macro_use] extern crate derive_more; -mod favourite_music; mod states; mod transitions; diff --git a/examples/dialogue_bot/src/states.rs b/examples/dialogue_bot/src/states.rs index 705226bd..ddd185cc 100644 --- a/examples/dialogue_bot/src/states.rs +++ b/examples/dialogue_bot/src/states.rs @@ -1,47 +1,39 @@ use teloxide::prelude::*; -use super::favourite_music::FavouriteMusic; -use parse_display::Display; - #[derive(Default)] pub struct StartState; -pub struct ReceiveFullNameState { +pub struct ReceiveDaysOfWeekState { rest: StartState, } -pub struct ReceiveAgeState { - rest: ReceiveFullNameState, - full_name: String, +pub struct Receive10x5AnswerState { + rest: ReceiveDaysOfWeekState, + days_of_week: u8, } -pub struct ReceiveFavouriteMusicState { - rest: ReceiveAgeState, - age: u8, +pub struct ReceiveGandalfAlternativeNameState { + rest: Receive10x5AnswerState, + _10x5_answer: u8, } -#[derive(Display)] -#[display( - "Your full name: {rest.rest.full_name}, your age: {rest.age}, your \ - favourite music: {favourite_music}" -)] pub struct ExitState { - rest: ReceiveFavouriteMusicState, - favourite_music: FavouriteMusic, + rest: ReceiveGandalfAlternativeNameState, + gandalf_alternative_name: String, } up!( - StartState -> ReceiveFullNameState, - ReceiveFullNameState + [full_name: String] -> ReceiveAgeState, - ReceiveAgeState + [age: u8] -> ReceiveFavouriteMusicState, - ReceiveFavouriteMusicState + [favourite_music: FavouriteMusic] -> ExitState, + StartState -> ReceiveDaysOfWeekState, + ReceiveDaysOfWeekState + [days_of_week: u8] -> Receive10x5AnswerState, + Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState, + ReceiveGandalfAlternativeNameState + [gandalf_alternative_name: String] -> ExitState, ); #[derive(SmartDefault, From)] pub enum Dialogue { #[default] Start(StartState), - ReceiveFullName(ReceiveFullNameState), - ReceiveAge(ReceiveAgeState), - ReceiveFavouriteMusic(ReceiveFavouriteMusicState), + ReceiveDaysOfWeek(ReceiveDaysOfWeekState), + Receive10x5Answer(Receive10x5AnswerState), + ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), } diff --git a/examples/dialogue_bot/src/transitions.rs b/examples/dialogue_bot/src/transitions.rs index 6f6a5747..4c5724af 100644 --- a/examples/dialogue_bot/src/transitions.rs +++ b/examples/dialogue_bot/src/transitions.rs @@ -1,56 +1,56 @@ use teloxide::prelude::*; -use super::{favourite_music::FavouriteMusic, states::*}; +use super::states::*; pub type Cx = UpdateWithCx; pub type Out = TransitionOut; async fn start(cx: Cx, state: StartState) -> Out { - cx.answer_str("Let's start! First, what's your full name?").await?; + cx.answer_str("Let's start our test! How many days per week are there?") + .await?; next(state.up()) } -async fn receive_full_name(cx: Cx, state: ReceiveFullNameState) -> Out { - match cx.update.text_owned() { - Some(full_name) => { - cx.answer_str("What a wonderful name! Your age?").await?; - next(state.up(full_name)) - } - _ => { - cx.answer_str("Please, enter a text message!").await?; - next(state) - } - } -} - -async fn receive_age(cx: Cx, state: ReceiveAgeState) -> Out { +async fn receive_days_of_week(cx: Cx, state: ReceiveDaysOfWeekState) -> Out { match cx.update.text().map(str::parse) { - Some(Ok(age)) => { - cx.answer("Good. Now choose your favourite music:") - .reply_markup(FavouriteMusic::markup()) - .send() - .await?; - next(state.up(age)) + Some(Ok(ans)) if ans == 7 => { + cx.answer_str("10*5 = ?").await?; + next(state.up(ans)) } _ => { - cx.answer_str("Please, enter a number!").await?; + cx.answer_str("Try again.").await?; next(state) } } } -async fn receive_favourite_music( +async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { + match cx.update.text().map(str::parse) { + Some(Ok(ans)) if ans == 50 => { + cx.answer_str("What's an alternative name of Gandalf?").await?; + next(state.up(ans)) + } + _ => { + cx.answer_str("Try again.").await?; + next(state) + } + } +} + +async fn receive_gandalf_alternative_name( cx: Cx, - state: ReceiveFavouriteMusicState, + state: ReceiveGandalfAlternativeNameState, ) -> Out { - match cx.update.text().map(str::parse) { - Some(Ok(favourite_music)) => { - cx.answer_str(format!("Fine. {}", state.up(favourite_music))) - .await?; + match cx.update.text() { + Some(ans) if ans == "Mithrandir" => { + cx.answer_str( + "Congratulations! You've successfully passed the test!", + ) + .await?; exit() } _ => { - cx.answer_str("Please, enter from the keyboard!").await?; + cx.answer_str("Try again.").await?; next(state) } } @@ -59,10 +59,14 @@ async fn receive_favourite_music( pub async fn dispatch(cx: Cx, dialogue: Dialogue) -> Out { match dialogue { Dialogue::Start(state) => start(cx, state).await, - Dialogue::ReceiveFullName(state) => receive_full_name(cx, state).await, - Dialogue::ReceiveAge(state) => receive_age(cx, state).await, - Dialogue::ReceiveFavouriteMusic(state) => { - receive_favourite_music(cx, state).await + Dialogue::ReceiveDaysOfWeek(state) => { + receive_days_of_week(cx, state).await + } + Dialogue::Receive10x5Answer(state) => { + receive_10x5_answer(cx, state).await + } + Dialogue::ReceiveGandalfAlternativeName(state) => { + receive_gandalf_alternative_name(cx, state).await } } } From 324f0afaf46dba7fc55c6062137e40e7f04df397 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Thu, 23 Jul 2020 21:43:01 +0600 Subject: [PATCH 02/14] Update README.md --- README.md | 133 ++++++++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 23960a3d..90430861 100644 --- a/README.md +++ b/README.md @@ -188,44 +188,39 @@ States and transition functions are placed into separated modules. For example: #[derive(Default)] pub struct StartState; -pub struct ReceiveFullNameState { +pub struct ReceiveDaysOfWeekState { rest: StartState, } -pub struct ReceiveAgeState { - rest: ReceiveFullNameState, - full_name: String, +pub struct Receive10x5AnswerState { + rest: ReceiveDaysOfWeekState, + days_of_week: u8, } -pub struct ReceiveFavouriteMusicState { - rest: ReceiveAgeState, - age: u8, +pub struct ReceiveGandalfAlternativeNameState { + rest: Receive10x5AnswerState, + _10x5_answer: u8, } -#[derive(Display)] -#[display( - "Your full name: {rest.rest.full_name}, your age: {rest.age}, your \ - favourite music: {favourite_music}" -)] pub struct ExitState { - rest: ReceiveFavouriteMusicState, - favourite_music: FavouriteMusic, + rest: ReceiveGandalfAlternativeNameState, + gandalf_alternative_name: String, } up!( - StartState -> ReceiveFullNameState, - ReceiveFullNameState + [full_name: String] -> ReceiveAgeState, - ReceiveAgeState + [age: u8] -> ReceiveFavouriteMusicState, - ReceiveFavouriteMusicState + [favourite_music: FavouriteMusic] -> ExitState, + StartState -> ReceiveDaysOfWeekState, + ReceiveDaysOfWeekState + [days_of_week: u8] -> Receive10x5AnswerState, + Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState, + ReceiveGandalfAlternativeNameState + [gandalf_alternative_name: String] -> ExitState, ); #[derive(SmartDefault, From)] pub enum Dialogue { #[default] Start(StartState), - ReceiveFullName(ReceiveFullNameState), - ReceiveAge(ReceiveAgeState), - ReceiveFavouriteMusic(ReceiveFavouriteMusicState), + ReceiveDaysOfWeek(ReceiveDaysOfWeekState), + Receive10x5Answer(Receive10x5AnswerState), + ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), } ``` @@ -239,51 +234,51 @@ pub type Cx = UpdateWithCx; pub type Out = TransitionOut; async fn start(cx: Cx, state: StartState) -> Out { - cx.answer_str("Let's start! First, what's your full name?").await?; + cx.answer_str("Let's start our test! How many days per week are there?") + .await?; next(state.up()) } -async fn receive_full_name(cx: Cx, state: ReceiveFullNameState) -> Out { - match cx.update.text_owned() { - Some(full_name) => { - cx.answer_str("What a wonderful name! Your age?").await?; - next(state.up(full_name)) - } - _ => { - cx.answer_str("Please, enter a text message!").await?; - next(state) - } - } -} - -async fn receive_age(cx: Cx, state: ReceiveAgeState) -> Out { +async fn receive_days_of_week(cx: Cx, state: ReceiveDaysOfWeekState) -> Out { match cx.update.text().map(str::parse) { - Some(Ok(age)) => { - cx.answer("Good. Now choose your favourite music:") - .reply_markup(FavouriteMusic::markup()) - .send() - .await?; - next(state.up(age)) + Some(Ok(ans)) if ans == 7 => { + cx.answer_str("10*5 = ?").await?; + next(state.up(ans)) } _ => { - cx.answer_str("Please, enter a number!").await?; + cx.answer_str("Try again.").await?; next(state) } } } -async fn receive_favourite_music( +async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { + match cx.update.text().map(str::parse) { + Some(Ok(ans)) if ans == 50 => { + cx.answer_str("What's an alternative name of Gandalf?").await?; + next(state.up(ans)) + } + _ => { + cx.answer_str("Try again.").await?; + next(state) + } + } +} + +async fn receive_gandalf_alternative_name( cx: Cx, - state: ReceiveFavouriteMusicState, + state: ReceiveGandalfAlternativeNameState, ) -> Out { - match cx.update.text().map(str::parse) { - Some(Ok(favourite_music)) => { - cx.answer_str(format!("Fine. {}", state.up(favourite_music))) - .await?; + match cx.update.text() { + Some(ans) if ans == "Mithrandir" => { + cx.answer_str( + "Congratulations! You've successfully passed the test!", + ) + .await?; exit() } _ => { - cx.answer_str("Please, enter from the keyboard!").await?; + cx.answer_str("Try again.").await?; next(state) } } @@ -292,41 +287,19 @@ async fn receive_favourite_music( pub async fn dispatch(cx: Cx, dialogue: Dialogue) -> Out { match dialogue { Dialogue::Start(state) => start(cx, state).await, - Dialogue::ReceiveFullName(state) => receive_full_name(cx, state).await, - Dialogue::ReceiveAge(state) => receive_age(cx, state).await, - Dialogue::ReceiveFavouriteMusic(state) => { - receive_favourite_music(cx, state).await + Dialogue::ReceiveDaysOfWeek(state) => { + receive_days_of_week(cx, state).await + } + Dialogue::Receive10x5Answer(state) => { + receive_10x5_answer(cx, state).await + } + Dialogue::ReceiveGandalfAlternativeName(state) => { + receive_gandalf_alternative_name(cx, state).await } } } ``` -([dialogue_bot/src/favourite_music.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/favourite_music.rs)) -```rust -// Imports are omitted... - -#[derive(Copy, Clone, Display, FromStr)] -pub enum FavouriteMusic { - Rock, - Metal, - Pop, - Other, -} - -impl FavouriteMusic { - pub fn markup() -> ReplyKeyboardMarkup { - ReplyKeyboardMarkup::default().append_row(vec![ - KeyboardButton::new("Rock"), - KeyboardButton::new("Metal"), - KeyboardButton::new("Pop"), - KeyboardButton::new("Other"), - ]) - } -} -``` - - - ([dialogue_bot/src/main.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/main.rs)) ```rust // Imports are omitted... From 462b42c526e3c11b45acd100ce96fea8a143b6f5 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 01:04:42 +0600 Subject: [PATCH 03/14] Trying to compile examples/dialogue_bot... --- examples/dialogue_bot/Cargo.toml | 1 + examples/dialogue_bot/src/main.rs | 7 ++++++- examples/dialogue_bot/src/states.rs | 7 +++++++ examples/dialogue_bot/src/transitions.rs | 15 --------------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index bcf96df5..2942fa93 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -13,6 +13,7 @@ pretty_env_logger = "0.4.0" smart-default = "0.6.0" derive_more = "0.99.9" teloxide = { path = "../../" } +teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } [profile.release] lto = true \ No newline at end of file diff --git a/examples/dialogue_bot/src/main.rs b/examples/dialogue_bot/src/main.rs index e043bdf9..559faecf 100644 --- a/examples/dialogue_bot/src/main.rs +++ b/examples/dialogue_bot/src/main.rs @@ -21,6 +21,8 @@ extern crate smart_default; #[macro_use] extern crate derive_more; +#[macro_use] +extern crate teloxide_macros; mod states; mod transitions; @@ -46,7 +48,10 @@ async fn run() { .messages_handler(DialogueDispatcher::new( |input: TransitionIn| async move { // Unwrap without panic because of std::convert::Infallible. - dispatch(input.cx, input.dialogue.unwrap()) + input + .dialogue + .unwrap() + .dispatch(input.cx) .await .expect("Something wrong with the bot!") }, diff --git a/examples/dialogue_bot/src/states.rs b/examples/dialogue_bot/src/states.rs index ddd185cc..ab424e49 100644 --- a/examples/dialogue_bot/src/states.rs +++ b/examples/dialogue_bot/src/states.rs @@ -1,5 +1,7 @@ use teloxide::prelude::*; +use super::transitions::*; + #[derive(Default)] pub struct StartState; @@ -29,11 +31,16 @@ up!( ReceiveGandalfAlternativeNameState + [gandalf_alternative_name: String] -> ExitState, ); +#[bot_dialogue] #[derive(SmartDefault, From)] pub enum Dialogue { #[default] + #[handler(start)] Start(StartState), + #[handler(receive_days_of_week)] ReceiveDaysOfWeek(ReceiveDaysOfWeekState), + #[handler(receive_10x5_answer)] Receive10x5Answer(Receive10x5AnswerState), + #[handler(receive_gandalf_alternative_name)] ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), } diff --git a/examples/dialogue_bot/src/transitions.rs b/examples/dialogue_bot/src/transitions.rs index 4c5724af..765f986d 100644 --- a/examples/dialogue_bot/src/transitions.rs +++ b/examples/dialogue_bot/src/transitions.rs @@ -55,18 +55,3 @@ async fn receive_gandalf_alternative_name( } } } - -pub async fn dispatch(cx: Cx, dialogue: Dialogue) -> Out { - match dialogue { - Dialogue::Start(state) => start(cx, state).await, - Dialogue::ReceiveDaysOfWeek(state) => { - receive_days_of_week(cx, state).await - } - Dialogue::Receive10x5Answer(state) => { - receive_10x5_answer(cx, state).await - } - Dialogue::ReceiveGandalfAlternativeName(state) => { - receive_gandalf_alternative_name(cx, state).await - } - } -} From e6534ac1b84b559ea44a1be1be64e08fd4064863 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 12:36:24 +0600 Subject: [PATCH 04/14] Download teloxide-macros from 'ponos' --- examples/dialogue_bot/Cargo.toml | 2 +- examples/dialogue_bot/src/states.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index 2942fa93..459ea860 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -13,7 +13,7 @@ pretty_env_logger = "0.4.0" smart-default = "0.6.0" derive_more = "0.99.9" teloxide = { path = "../../" } -teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } +teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "ponos" } [profile.release] lto = true \ No newline at end of file diff --git a/examples/dialogue_bot/src/states.rs b/examples/dialogue_bot/src/states.rs index ab424e49..5a464ffc 100644 --- a/examples/dialogue_bot/src/states.rs +++ b/examples/dialogue_bot/src/states.rs @@ -37,10 +37,13 @@ pub enum Dialogue { #[default] #[handler(start)] Start(StartState), + #[handler(receive_days_of_week)] ReceiveDaysOfWeek(ReceiveDaysOfWeekState), + #[handler(receive_10x5_answer)] Receive10x5Answer(Receive10x5AnswerState), + #[handler(receive_gandalf_alternative_name)] ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), } From 008e8505c5fac6857119ac69aeb394cd8d0f5518 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 18:04:20 +0600 Subject: [PATCH 05/14] Add BotDialogue --- examples/dialogue_bot/Cargo.toml | 3 ++- examples/dialogue_bot/src/states.rs | 19 ------------------- examples/dialogue_bot/src/transitions.rs | 16 ++++++++++++++++ src/dispatching/dialogue/bot_dialogue.rs | 14 ++++++++++++++ src/dispatching/dialogue/mod.rs | 2 ++ src/prelude.rs | 4 ++-- 6 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 src/dispatching/dialogue/bot_dialogue.rs diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index 459ea860..eac96fa6 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -10,10 +10,11 @@ edition = "2018" log = "0.4.8" tokio = "0.2.9" pretty_env_logger = "0.4.0" +futures = "0.3.5" smart-default = "0.6.0" derive_more = "0.99.9" teloxide = { path = "../../" } -teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "ponos" } +teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } [profile.release] lto = true \ No newline at end of file diff --git a/examples/dialogue_bot/src/states.rs b/examples/dialogue_bot/src/states.rs index 5a464ffc..bca02350 100644 --- a/examples/dialogue_bot/src/states.rs +++ b/examples/dialogue_bot/src/states.rs @@ -1,7 +1,5 @@ use teloxide::prelude::*; -use super::transitions::*; - #[derive(Default)] pub struct StartState; @@ -30,20 +28,3 @@ up!( Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState, ReceiveGandalfAlternativeNameState + [gandalf_alternative_name: String] -> ExitState, ); - -#[bot_dialogue] -#[derive(SmartDefault, From)] -pub enum Dialogue { - #[default] - #[handler(start)] - Start(StartState), - - #[handler(receive_days_of_week)] - ReceiveDaysOfWeek(ReceiveDaysOfWeekState), - - #[handler(receive_10x5_answer)] - Receive10x5Answer(Receive10x5AnswerState), - - #[handler(receive_gandalf_alternative_name)] - ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), -} diff --git a/examples/dialogue_bot/src/transitions.rs b/examples/dialogue_bot/src/transitions.rs index 765f986d..ef97c18d 100644 --- a/examples/dialogue_bot/src/transitions.rs +++ b/examples/dialogue_bot/src/transitions.rs @@ -55,3 +55,19 @@ async fn receive_gandalf_alternative_name( } } } + +#[derive(BotDialogue, SmartDefault, From)] +pub enum Dialogue { + #[default] + #[handler(start)] + Start(StartState), + + #[handler(receive_days_of_week)] + ReceiveDaysOfWeek(ReceiveDaysOfWeekState), + + #[handler(receive_10x5_answer)] + Receive10x5Answer(Receive10x5AnswerState), + + #[handler(receive_gandalf_alternative_name)] + ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), +} diff --git a/src/dispatching/dialogue/bot_dialogue.rs b/src/dispatching/dialogue/bot_dialogue.rs new file mode 100644 index 00000000..6fc7b918 --- /dev/null +++ b/src/dispatching/dialogue/bot_dialogue.rs @@ -0,0 +1,14 @@ +use crate::{ + dispatching::{dialogue::TransitionOut, UpdateWithCx}, + types::Message, +}; +use futures::future::BoxFuture; + +/// Represents a dialogue FSM. +pub trait BotDialogue: Default { + /// Turns itself into another state, depending on the input message. + fn dispatch( + self, + cx: UpdateWithCx, + ) -> BoxFuture<'static, TransitionOut>; +} diff --git a/src/dispatching/dialogue/mod.rs b/src/dispatching/dialogue/mod.rs index 09d753fd..36b700b4 100644 --- a/src/dispatching/dialogue/mod.rs +++ b/src/dispatching/dialogue/mod.rs @@ -42,6 +42,7 @@ #![allow(clippy::type_complexity)] +mod bot_dialogue; mod dialogue_dispatcher; mod dialogue_dispatcher_handler; mod dialogue_stage; @@ -50,6 +51,7 @@ mod get_chat_id; mod storage; use crate::{requests::ResponseResult, types::Message}; +pub use bot_dialogue::BotDialogue; pub use dialogue_dispatcher::DialogueDispatcher; pub use dialogue_dispatcher_handler::DialogueDispatcherHandler; pub use dialogue_stage::{exit, next, DialogueStage}; diff --git a/src/prelude.rs b/src/prelude.rs index 7ea96bdf..7ebec1f1 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -3,8 +3,8 @@ pub use crate::{ dispatching::{ dialogue::{ - exit, next, DialogueDispatcher, DialogueStage, DialogueWithCx, - GetChatId, TransitionIn, TransitionOut, + exit, next, BotDialogue, DialogueDispatcher, DialogueStage, + DialogueWithCx, GetChatId, TransitionIn, TransitionOut, }, Dispatcher, DispatcherHandlerRx, DispatcherHandlerRxExt, UpdateWithCx, }, From 34633ad57b0b47650bc0ed232ca8baa344538309 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 18:25:52 +0600 Subject: [PATCH 06/14] Refactor examples/dialogue_bot --- examples/dialogue_bot/Cargo.toml | 3 ++- examples/dialogue_bot/src/main.rs | 1 - examples/dialogue_bot/src/states.rs | 21 +++++++++++++++ examples/dialogue_bot/src/transitions.rs | 33 ++++++++---------------- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index eac96fa6..157a2086 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -14,7 +14,8 @@ futures = "0.3.5" smart-default = "0.6.0" derive_more = "0.99.9" teloxide = { path = "../../" } -teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } +teloxide-macros = { path = "../../../teloxide-macros" } +#teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } [profile.release] lto = true \ No newline at end of file diff --git a/examples/dialogue_bot/src/main.rs b/examples/dialogue_bot/src/main.rs index 559faecf..8c7d80d5 100644 --- a/examples/dialogue_bot/src/main.rs +++ b/examples/dialogue_bot/src/main.rs @@ -28,7 +28,6 @@ mod states; mod transitions; use states::*; -use transitions::*; use std::convert::Infallible; use teloxide::prelude::*; diff --git a/examples/dialogue_bot/src/states.rs b/examples/dialogue_bot/src/states.rs index bca02350..610f03e7 100644 --- a/examples/dialogue_bot/src/states.rs +++ b/examples/dialogue_bot/src/states.rs @@ -1,5 +1,26 @@ use teloxide::prelude::*; +use super::transitions::{ + receive_10x5_answer, receive_days_of_week, + receive_gandalf_alternative_name, start, +}; + +#[derive(BotDialogue, SmartDefault, From)] +pub enum Dialogue { + #[default] + #[handler(start)] + Start(StartState), + + #[handler(receive_days_of_week)] + ReceiveDaysOfWeek(ReceiveDaysOfWeekState), + + #[handler(receive_10x5_answer)] + Receive10x5Answer(Receive10x5AnswerState), + + #[handler(receive_gandalf_alternative_name)] + ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), +} + #[derive(Default)] pub struct StartState; diff --git a/examples/dialogue_bot/src/transitions.rs b/examples/dialogue_bot/src/transitions.rs index ef97c18d..d98d8aed 100644 --- a/examples/dialogue_bot/src/transitions.rs +++ b/examples/dialogue_bot/src/transitions.rs @@ -1,17 +1,22 @@ +use crate::states::{ + Dialogue, Receive10x5AnswerState, ReceiveDaysOfWeekState, + ReceiveGandalfAlternativeNameState, StartState, +}; use teloxide::prelude::*; -use super::states::*; - pub type Cx = UpdateWithCx; pub type Out = TransitionOut; -async fn start(cx: Cx, state: StartState) -> Out { +pub async fn start(cx: Cx, state: StartState) -> Out { cx.answer_str("Let's start our test! How many days per week are there?") .await?; next(state.up()) } -async fn receive_days_of_week(cx: Cx, state: ReceiveDaysOfWeekState) -> Out { +pub async fn receive_days_of_week( + cx: Cx, + state: ReceiveDaysOfWeekState, +) -> Out { match cx.update.text().map(str::parse) { Some(Ok(ans)) if ans == 7 => { cx.answer_str("10*5 = ?").await?; @@ -24,7 +29,7 @@ async fn receive_days_of_week(cx: Cx, state: ReceiveDaysOfWeekState) -> Out { } } -async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { +pub async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { match cx.update.text().map(str::parse) { Some(Ok(ans)) if ans == 50 => { cx.answer_str("What's an alternative name of Gandalf?").await?; @@ -37,7 +42,7 @@ async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { } } -async fn receive_gandalf_alternative_name( +pub async fn receive_gandalf_alternative_name( cx: Cx, state: ReceiveGandalfAlternativeNameState, ) -> Out { @@ -55,19 +60,3 @@ async fn receive_gandalf_alternative_name( } } } - -#[derive(BotDialogue, SmartDefault, From)] -pub enum Dialogue { - #[default] - #[handler(start)] - Start(StartState), - - #[handler(receive_days_of_week)] - ReceiveDaysOfWeek(ReceiveDaysOfWeekState), - - #[handler(receive_10x5_answer)] - Receive10x5Answer(Receive10x5AnswerState), - - #[handler(receive_gandalf_alternative_name)] - ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), -} From 49b7a50218e213082d6703146a8fc22997a64503 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 18:28:34 +0600 Subject: [PATCH 07/14] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6c2d69..655b7cd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.3.0] - ??? ### Added - `BotBuilder`, which allows setting a default `ParseMode`. + - The `BotDialogue` trait. + - Automatic `dispatch` function generation via `#[derive(BotDialogue)]` + `#[handler(handler_fn)]`. ### Deprecated - `Bot::{from_env_with_client, new, with_client}`. From aec1abde624458ce2dc65427fea6326ba380a9f9 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 18:31:00 +0600 Subject: [PATCH 08/14] Update examples/dialogue_bot --- examples/dialogue_bot/src/states.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/dialogue_bot/src/states.rs b/examples/dialogue_bot/src/states.rs index 610f03e7..a27ee3cc 100644 --- a/examples/dialogue_bot/src/states.rs +++ b/examples/dialogue_bot/src/states.rs @@ -8,16 +8,16 @@ use super::transitions::{ #[derive(BotDialogue, SmartDefault, From)] pub enum Dialogue { #[default] - #[handler(start)] + #[transition(start)] Start(StartState), - #[handler(receive_days_of_week)] + #[transition(receive_days_of_week)] ReceiveDaysOfWeek(ReceiveDaysOfWeekState), - #[handler(receive_10x5_answer)] + #[transition(receive_10x5_answer)] Receive10x5Answer(Receive10x5AnswerState), - #[handler(receive_gandalf_alternative_name)] + #[transition(receive_gandalf_alternative_name)] ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), } From 701b89bf8f5d5180c514ad894990ce6ceb74b089 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 18:35:50 +0600 Subject: [PATCH 09/14] Fix examples/dialogue_bot/Cargo.toml --- examples/dialogue_bot/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index 157a2086..eac96fa6 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -14,8 +14,7 @@ futures = "0.3.5" smart-default = "0.6.0" derive_more = "0.99.9" teloxide = { path = "../../" } -teloxide-macros = { path = "../../../teloxide-macros" } -#teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } +teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } [profile.release] lto = true \ No newline at end of file From b1c97600aec4121387047a9604c319f248f475e5 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 18:36:24 +0600 Subject: [PATCH 10/14] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 655b7cd2..0087853c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `BotBuilder`, which allows setting a default `ParseMode`. - The `BotDialogue` trait. - - Automatic `dispatch` function generation via `#[derive(BotDialogue)]` + `#[handler(handler_fn)]`. + - Automatic `dispatch` function generation via `#[derive(BotDialogue)]` + `#[transition(transition_fn)]`. ### Deprecated - `Bot::{from_env_with_client, new, with_client}`. From 0b203bd5134204b2e9c1caa57ef0e0153d0a6419 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 18:40:05 +0600 Subject: [PATCH 11/14] Update README.md --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 90430861..60d8c6b8 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,22 @@ States and transition functions are placed into separated modules. For example: ```rust // Imports are omitted... +#[derive(BotDialogue, SmartDefault, From)] +pub enum Dialogue { + #[default] + #[transition(start)] + Start(StartState), + + #[transition(receive_days_of_week)] + ReceiveDaysOfWeek(ReceiveDaysOfWeekState), + + #[transition(receive_10x5_answer)] + Receive10x5Answer(Receive10x5AnswerState), + + #[transition(receive_gandalf_alternative_name)] + ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), +} + #[derive(Default)] pub struct StartState; @@ -213,15 +229,6 @@ up!( Receive10x5AnswerState + [_10x5_answer: u8] -> ReceiveGandalfAlternativeNameState, ReceiveGandalfAlternativeNameState + [gandalf_alternative_name: String] -> ExitState, ); - -#[derive(SmartDefault, From)] -pub enum Dialogue { - #[default] - Start(StartState), - ReceiveDaysOfWeek(ReceiveDaysOfWeekState), - Receive10x5Answer(Receive10x5AnswerState), - ReceiveGandalfAlternativeName(ReceiveGandalfAlternativeNameState), -} ``` The handy `up!` macro automatically generates functions that complete one state to another by appending a field. Here are the transition functions: @@ -233,13 +240,16 @@ The handy `up!` macro automatically generates functions that complete one state pub type Cx = UpdateWithCx; pub type Out = TransitionOut; -async fn start(cx: Cx, state: StartState) -> Out { +pub async fn start(cx: Cx, state: StartState) -> Out { cx.answer_str("Let's start our test! How many days per week are there?") .await?; next(state.up()) } -async fn receive_days_of_week(cx: Cx, state: ReceiveDaysOfWeekState) -> Out { +pub async fn receive_days_of_week( + cx: Cx, + state: ReceiveDaysOfWeekState, +) -> Out { match cx.update.text().map(str::parse) { Some(Ok(ans)) if ans == 7 => { cx.answer_str("10*5 = ?").await?; @@ -252,7 +262,7 @@ async fn receive_days_of_week(cx: Cx, state: ReceiveDaysOfWeekState) -> Out { } } -async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { +pub async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { match cx.update.text().map(str::parse) { Some(Ok(ans)) if ans == 50 => { cx.answer_str("What's an alternative name of Gandalf?").await?; @@ -265,7 +275,7 @@ async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { } } -async fn receive_gandalf_alternative_name( +pub async fn receive_gandalf_alternative_name( cx: Cx, state: ReceiveGandalfAlternativeNameState, ) -> Out { @@ -283,21 +293,6 @@ async fn receive_gandalf_alternative_name( } } } - -pub async fn dispatch(cx: Cx, dialogue: Dialogue) -> Out { - match dialogue { - Dialogue::Start(state) => start(cx, state).await, - Dialogue::ReceiveDaysOfWeek(state) => { - receive_days_of_week(cx, state).await - } - Dialogue::Receive10x5Answer(state) => { - receive_10x5_answer(cx, state).await - } - Dialogue::ReceiveGandalfAlternativeName(state) => { - receive_gandalf_alternative_name(cx, state).await - } - } -} ``` ([dialogue_bot/src/main.rs](https://github.com/teloxide/teloxide/blob/master/examples/dialogue_bot/src/main.rs)) @@ -315,7 +310,10 @@ async fn main() { .messages_handler(DialogueDispatcher::new( |input: TransitionIn| async move { // Unwrap without panic because of std::convert::Infallible. - dispatch(input.cx, input.dialogue.unwrap()) + input + .dialogue + .unwrap() + .dispatch(input.cx) .await .expect("Something wrong with the bot!") }, From 18b813c159823d4c2584afb156b7afe341fb6989 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 19:26:49 +0600 Subject: [PATCH 12/14] Unify TransitionIn with TransitionOut --- examples/dialogue_bot/src/main.rs | 2 +- examples/dialogue_bot/src/transitions.rs | 12 +++++++----- examples/redis_remember_bot/src/main.rs | 2 +- examples/redis_remember_bot/src/transitions.rs | 11 +++++++---- src/dispatching/dialogue/mod.rs | 5 +++-- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/examples/dialogue_bot/src/main.rs b/examples/dialogue_bot/src/main.rs index 8c7d80d5..6fb15ace 100644 --- a/examples/dialogue_bot/src/main.rs +++ b/examples/dialogue_bot/src/main.rs @@ -45,7 +45,7 @@ async fn run() { Dispatcher::new(bot) .messages_handler(DialogueDispatcher::new( - |input: TransitionIn| async move { + |input: DialogueWithCx| async move { // Unwrap without panic because of std::convert::Infallible. input .dialogue diff --git a/examples/dialogue_bot/src/transitions.rs b/examples/dialogue_bot/src/transitions.rs index d98d8aed..a3265652 100644 --- a/examples/dialogue_bot/src/transitions.rs +++ b/examples/dialogue_bot/src/transitions.rs @@ -4,17 +4,16 @@ use crate::states::{ }; use teloxide::prelude::*; -pub type Cx = UpdateWithCx; pub type Out = TransitionOut; -pub async fn start(cx: Cx, state: StartState) -> Out { +pub async fn start(cx: TransitionIn, state: StartState) -> Out { cx.answer_str("Let's start our test! How many days per week are there?") .await?; next(state.up()) } pub async fn receive_days_of_week( - cx: Cx, + cx: TransitionIn, state: ReceiveDaysOfWeekState, ) -> Out { match cx.update.text().map(str::parse) { @@ -29,7 +28,10 @@ pub async fn receive_days_of_week( } } -pub async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { +pub async fn receive_10x5_answer( + cx: TransitionIn, + state: Receive10x5AnswerState, +) -> Out { match cx.update.text().map(str::parse) { Some(Ok(ans)) if ans == 50 => { cx.answer_str("What's an alternative name of Gandalf?").await?; @@ -43,7 +45,7 @@ pub async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { } pub async fn receive_gandalf_alternative_name( - cx: Cx, + cx: TransitionIn, state: ReceiveGandalfAlternativeNameState, ) -> Out { match cx.update.text() { diff --git a/examples/redis_remember_bot/src/main.rs b/examples/redis_remember_bot/src/main.rs index bffaa8aa..2b86b295 100644 --- a/examples/redis_remember_bot/src/main.rs +++ b/examples/redis_remember_bot/src/main.rs @@ -25,7 +25,7 @@ enum Error { StorageError(#[from] StorageError), } -type In = TransitionIn; +type In = DialogueWithCx; async fn handle_message(input: In) -> Out { let (cx, dialogue) = input.unpack(); diff --git a/examples/redis_remember_bot/src/transitions.rs b/examples/redis_remember_bot/src/transitions.rs index 594471e1..c294e9e4 100644 --- a/examples/redis_remember_bot/src/transitions.rs +++ b/examples/redis_remember_bot/src/transitions.rs @@ -2,10 +2,9 @@ use teloxide::prelude::*; use super::states::*; -pub type Cx = UpdateWithCx; pub type Out = TransitionOut; -async fn start(cx: Cx, state: StartState, text: &str) -> Out { +async fn start(cx: TransitionIn, state: StartState, text: &str) -> Out { if let Ok(number) = text.parse() { cx.answer_str(format!( "Remembered number {}. Now use /get or /reset", @@ -19,7 +18,11 @@ async fn start(cx: Cx, state: StartState, text: &str) -> Out { } } -async fn have_number(cx: Cx, state: HaveNumberState, text: &str) -> Out { +async fn have_number( + cx: TransitionIn, + state: HaveNumberState, + text: &str, +) -> Out { let num = state.number; if text.starts_with("/get") { @@ -34,7 +37,7 @@ async fn have_number(cx: Cx, state: HaveNumberState, text: &str) -> Out { } } -pub async fn dispatch(cx: Cx, dialogue: Dialogue, text: &str) -> Out { +pub async fn dispatch(cx: TransitionIn, dialogue: Dialogue, text: &str) -> Out { match dialogue { Dialogue::Start(state) => start(cx, state, text).await, Dialogue::HaveNumber(state) => have_number(cx, state, text).await, diff --git a/src/dispatching/dialogue/mod.rs b/src/dispatching/dialogue/mod.rs index 36b700b4..2687d489 100644 --- a/src/dispatching/dialogue/mod.rs +++ b/src/dispatching/dialogue/mod.rs @@ -61,6 +61,7 @@ pub use get_chat_id::GetChatId; #[cfg(feature = "redis-storage")] pub use storage::{RedisStorage, RedisStorageError}; +use crate::dispatching::UpdateWithCx; pub use storage::{serializer, InMemStorage, Serializer, Storage}; /// Generates `.up(field)` methods for dialogue states. @@ -112,8 +113,8 @@ macro_rules! up { }; } -/// A type passed into a FSM transition function. -pub type TransitionIn = DialogueWithCx; +/// An input passed into a FSM transition function. +pub type TransitionIn = UpdateWithCx; // A type returned from a FSM transition function. pub type TransitionOut = ResponseResult>; From 7e4014260adb7369055e50755a58800470caf98b Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 19:40:07 +0600 Subject: [PATCH 13/14] Fix Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8f2a86cc..079b5d0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ serde_cbor = { version = "0.11.1", optional = true } bincode = { version = "1.3.1", optional = true } #teloxide-macros = "0.3.1" -teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "dev" } +teloxide-macros = { git = "http://github.com/teloxide/teloxide-macros", branch = "master" } [dev-dependencies] smart-default = "0.6.0" From 1a343e377f30baa98029809e9dbc37437b683184 Mon Sep 17 00:00:00 2001 From: Temirkhan Myrzamadi Date: Fri, 24 Jul 2020 19:46:13 +0600 Subject: [PATCH 14/14] Update README.md --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 60d8c6b8..d928011f 100644 --- a/README.md +++ b/README.md @@ -237,17 +237,16 @@ The handy `up!` macro automatically generates functions that complete one state ```rust // Imports are omitted... -pub type Cx = UpdateWithCx; pub type Out = TransitionOut; -pub async fn start(cx: Cx, state: StartState) -> Out { +pub async fn start(cx: TransitionIn, state: StartState) -> Out { cx.answer_str("Let's start our test! How many days per week are there?") .await?; next(state.up()) } pub async fn receive_days_of_week( - cx: Cx, + cx: TransitionIn, state: ReceiveDaysOfWeekState, ) -> Out { match cx.update.text().map(str::parse) { @@ -262,7 +261,10 @@ pub async fn receive_days_of_week( } } -pub async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { +pub async fn receive_10x5_answer( + cx: TransitionIn, + state: Receive10x5AnswerState, +) -> Out { match cx.update.text().map(str::parse) { Some(Ok(ans)) if ans == 50 => { cx.answer_str("What's an alternative name of Gandalf?").await?; @@ -276,7 +278,7 @@ pub async fn receive_10x5_answer(cx: Cx, state: Receive10x5AnswerState) -> Out { } pub async fn receive_gandalf_alternative_name( - cx: Cx, + cx: TransitionIn, state: ReceiveGandalfAlternativeNameState, ) -> Out { match cx.update.text() { @@ -308,7 +310,7 @@ async fn main() { Dispatcher::new(bot) .messages_handler(DialogueDispatcher::new( - |input: TransitionIn| async move { + |input: DialogueWithCx| async move { // Unwrap without panic because of std::convert::Infallible. input .dialogue