diff --git a/examples/dialogue_bot/Cargo.toml b/examples/dialogue_bot/Cargo.toml index 8d2ea09a..a885196b 100644 --- a/examples/dialogue_bot/Cargo.toml +++ b/examples/dialogue_bot/Cargo.toml @@ -7,7 +7,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -teloxide = { path = "../../", features = ["frunk", "macros", "auto-send"] } +teloxide = { path = "../../", features = ["frunk", "macros", "auto-send", "sqlite-storage"] } +anyhow = "1.0.52" +serde = "1" futures = "0.3.5" tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/dialogue_bot/src/dialogue/mod.rs b/examples/dialogue_bot/src/dialogue/mod.rs deleted file mode 100644 index 43ad8db3..00000000 --- a/examples/dialogue_bot/src/dialogue/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -mod states; - -use crate::dialogue::states::{ - ReceiveAgeState, ReceiveFullNameState, ReceiveLocationState, StartState, -}; -use derive_more::From; -use teloxide::macros::Transition; - -#[derive(Transition, Clone, From)] -pub enum Dialogue { - Start(StartState), - ReceiveFullName(ReceiveFullNameState), - ReceiveAge(ReceiveAgeState), - ReceiveLocation(ReceiveLocationState), -} - -impl Default for Dialogue { - fn default() -> Self { - Self::Start(StartState) - } -} diff --git a/examples/dialogue_bot/src/dialogue/states/mod.rs b/examples/dialogue_bot/src/dialogue/states/mod.rs deleted file mode 100644 index 4872cc8e..00000000 --- a/examples/dialogue_bot/src/dialogue/states/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod receive_age; -mod receive_full_name; -mod receive_location; -mod start; - -pub use receive_age::ReceiveAgeState; -pub use receive_full_name::ReceiveFullNameState; -pub use receive_location::ReceiveLocationState; -pub use start::StartState; diff --git a/examples/dialogue_bot/src/dialogue/states/receive_age.rs b/examples/dialogue_bot/src/dialogue/states/receive_age.rs deleted file mode 100644 index 099b3407..00000000 --- a/examples/dialogue_bot/src/dialogue/states/receive_age.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::dialogue::{states::receive_location::ReceiveLocationState, Dialogue}; -use teloxide::prelude::*; - -#[derive(Clone, Generic)] -pub struct ReceiveAgeState { - pub full_name: String, -} - -#[teloxide(subtransition)] -async fn receive_age_state( - state: ReceiveAgeState, - cx: TransitionIn>, - ans: String, -) -> TransitionOut { - match ans.parse::() { - Ok(ans) => { - cx.answer("What's your location?").await?; - next(ReceiveLocationState::up(state, ans)) - } - _ => { - cx.answer("Send me a number.").await?; - next(state) - } - } -} diff --git a/examples/dialogue_bot/src/dialogue/states/receive_full_name.rs b/examples/dialogue_bot/src/dialogue/states/receive_full_name.rs deleted file mode 100644 index 2ea60a1c..00000000 --- a/examples/dialogue_bot/src/dialogue/states/receive_full_name.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::dialogue::{states::receive_age::ReceiveAgeState, Dialogue}; -use teloxide::prelude::*; - -#[derive(Clone, Generic)] -pub struct ReceiveFullNameState; - -#[teloxide(subtransition)] -async fn receive_full_name( - state: ReceiveFullNameState, - cx: TransitionIn>, - ans: String, -) -> TransitionOut { - cx.answer("How old are you?").await?; - next(ReceiveAgeState::up(state, ans)) -} diff --git a/examples/dialogue_bot/src/dialogue/states/receive_location.rs b/examples/dialogue_bot/src/dialogue/states/receive_location.rs deleted file mode 100644 index 3c1f6407..00000000 --- a/examples/dialogue_bot/src/dialogue/states/receive_location.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::dialogue::Dialogue; -use teloxide::prelude::*; - -#[derive(Clone, Generic)] -pub struct ReceiveLocationState { - pub full_name: String, - pub age: u8, -} - -#[teloxide(subtransition)] -async fn receive_location( - state: ReceiveLocationState, - cx: TransitionIn>, - ans: String, -) -> TransitionOut { - cx.answer(format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, ans)) - .await?; - exit() -} diff --git a/examples/dialogue_bot/src/dialogue/states/start.rs b/examples/dialogue_bot/src/dialogue/states/start.rs deleted file mode 100644 index f3f12e0c..00000000 --- a/examples/dialogue_bot/src/dialogue/states/start.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::dialogue::{states::ReceiveFullNameState, Dialogue}; -use teloxide::prelude::*; - -#[derive(Clone)] -pub struct StartState; - -#[teloxide(subtransition)] -async fn start( - _state: StartState, - cx: TransitionIn>, - _ans: String, -) -> TransitionOut { - cx.answer("Let's start! What's your full name?").await?; - next(ReceiveFullNameState) -} diff --git a/examples/dialogue_bot/src/main.rs b/examples/dialogue_bot/src/main.rs index cc2e6e76..311826f1 100644 --- a/examples/dialogue_bot/src/main.rs +++ b/examples/dialogue_bot/src/main.rs @@ -13,17 +13,30 @@ // Age: 223 // Location: Middle-earth // ``` +use crate::state::State; +use teloxide::{ + dispatching2::dialogue::{serializer::Json, SqliteStorage}, + prelude::*, +}; -#![allow(clippy::trivial_regex)] -#![allow(dead_code)] +// FIXME: naming +type MyBot = AutoSend; +type Store = SqliteStorage; +type BotDialogue = Dialogue; -#[macro_use] -extern crate frunk; +#[derive(Clone, serde::Serialize, serde::Deserialize)] +pub enum State { + Start, + ReceiveFullName, + ReceiveAge { full_name: String }, + ReceiveLocation { full_name: String, age: u8 }, +} -mod dialogue; - -use crate::dialogue::Dialogue; -use teloxide::prelude::*; +impl Default for State { + fn default() -> Self { + Self::Start + } +} #[tokio::main] async fn main() { @@ -35,22 +48,80 @@ async fn run() { log::info!("Starting dialogue_bot..."); let bot = Bot::from_env().auto_send(); + let storage = SqliteStorage::open("db.sqlite", Json).await.unwrap(); - teloxide::dialogues_repl(bot, |message, dialogue| async move { - handle_message(message, dialogue).await.expect("Something wrong with the bot!") - }) - .await; + Dispatcher::new(bot) + .dependencies(dptree::deps![storage]) + .messages_handler(|h| { + h.add_dialogue::().branch(dptree::endpoint(handle_message)) + }) + .dispatch() + .await; } -async fn handle_message( - cx: UpdateWithCx, Message>, - dialogue: Dialogue, -) -> TransitionOut { - match cx.update.text().map(ToOwned::to_owned) { +async fn handle_message(bot: MyBot, mes: Message, dialogue: BotDialogue) -> anyhow::Result<()> { + match mes.text().map(ToOwned::to_owned) { None => { - cx.answer("Send me a text message.").await?; - next(dialogue) + bot.send_message(mes.chat_id(), "Send me a text message.").await?; + Ok(()) } - Some(ans) => dialogue.react(cx, ans).await, + Some(_) => match dialogue.current_state_or_default().await? { + State::Start => handle_start(bot, mes, dialogue).await, + State::ReceiveFullName => handle_receive_full_name(bot, mes, dialogue).await, + State::ReceiveAge { full_name } => { + handle_receive_age(bot, mes, dialogue, full_name).await + } + State::ReceiveLocation { full_name, age } => { + handle_receive_location(bot, mes, dialogue, full_name, age).await + } + }, } } + +async fn handle_start(bot: MyBot, mes: Message, dialogue: BotDialogue) -> anyhow::Result<()> { + bot.send_message(mes.chat_id(), "Let's start! What's your full name?").await?; + dialogue.next(State::ReceiveFullName).await?; + Ok(()) +} + +async fn handle_receive_full_name( + bot: MyBot, + mes: Message, + dialogue: BotDialogue, +) -> anyhow::Result<()> { + bot.send_message(mes.chat_id(), "How old are you?").await?; + dialogue.next(State::ReceiveAge { full_name: mes.text().unwrap().into() }).await?; + Ok(()) +} + +async fn handle_receive_age( + bot: MyBot, + mes: Message, + dialogue: BotDialogue, + full_name: String, +) -> anyhow::Result<()> { + match mes.text().unwrap().parse::() { + Ok(age) => { + bot.send_message(mes.chat_id(), "What's your location?").await?; + dialogue.next(State::ReceiveLocation { full_name, age }).await?; + } + _ => { + bot.send_message(mes.chat_id(), "Send me a number.").await?; + } + } + Ok(()) +} + +async fn handle_receive_location( + bot: MyBot, + mes: Message, + dialogue: BotDialogue, + full_name: String, + age: u8, +) -> anyhow::Result<()> { + let location = mes.text().unwrap(); + let message = format!("Full name: {}\nAge: {}\nLocation: {}", full_name, age, location); + bot.send_message(mes.chat_id(), message).await?; + dialogue.exit().await?; + Ok(()) +} diff --git a/examples/dialogue_bot/src/state.rs b/examples/dialogue_bot/src/state.rs new file mode 100644 index 00000000..e69de29b