diff --git a/examples/dialogue_bot_redis/Cargo.toml b/examples/dialogue_bot_redis/Cargo.toml deleted file mode 100644 index 5e10af40..00000000 --- a/examples/dialogue_bot_redis/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "dialogue_bot_redis" -version = "0.1.0" -authors = ["Temirkhan Myrzamadi "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -log = "0.4.8" -tokio = "0.2.9" -pretty_env_logger = "0.4.0" -smart-default = "0.6.0" -parse-display = "0.1.1" -# You can also choose "cbor-serializer" or built-in JSON serializer -teloxide = { path = "../../", features = ["redis-storage", "bincode-serializer"] } -serde = "1.0.104" - -[profile.release] -lto = true diff --git a/examples/dialogue_bot_redis/src/main.rs b/examples/dialogue_bot_redis/src/main.rs deleted file mode 100644 index cc8bdd59..00000000 --- a/examples/dialogue_bot_redis/src/main.rs +++ /dev/null @@ -1,212 +0,0 @@ -// This is a bot that asks your full name, your age, your favourite kind of -// music and sends all the gathered information back. -// -// # Example -// ``` -// - Let's start! First, what's your full name? -// - Luke Skywalker -// - What a wonderful name! Your age? -// - 26 -// - Good. Now choose your favourite music -// *A keyboard of music kinds is displayed* -// *You select Metal* -// - Metal -// - Fine. Your full name: Luke Skywalker, your age: 26, your favourite music: Metal -// ``` - -#![allow(clippy::trivial_regex)] - -#[macro_use] -extern crate smart_default; - -use serde::{Deserialize, Serialize}; -use std::sync::Arc; - -use teloxide::{ - dispatching::dialogue::{serializer::Bincode, RedisStorage, Storage}, - prelude::*, - types::{KeyboardButton, ReplyKeyboardMarkup}, -}; - -use parse_display::{Display, FromStr}; - -// ============================================================================ -// [Favourite music kinds] -// ============================================================================ - -#[derive(Copy, Clone, Display, FromStr)] -enum FavouriteMusic { - Rock, - Metal, - Pop, - Other, -} - -impl FavouriteMusic { - fn markup() -> ReplyKeyboardMarkup { - ReplyKeyboardMarkup::default().one_time_keyboard(true).append_row(vec![ - KeyboardButton::new("Rock"), - KeyboardButton::new("Metal"), - KeyboardButton::new("Pop"), - KeyboardButton::new("Other"), - ]) - } -} - -// ============================================================================ -// [A type-safe finite automaton] -// ============================================================================ - -#[derive(Clone, Serialize, Deserialize)] -struct ReceiveAgeState { - full_name: String, -} - -#[derive(Clone, Serialize, Deserialize)] -struct ReceiveFavouriteMusicState { - data: ReceiveAgeState, - age: u8, -} - -#[derive(Display)] -#[display( - "Your full name: {data.data.full_name}, your age: {data.age}, your \ - favourite music: {favourite_music}" -)] -struct ExitState { - data: ReceiveFavouriteMusicState, - favourite_music: FavouriteMusic, -} - -#[derive(SmartDefault, Serialize, Deserialize)] -enum Dialogue { - #[default] - Start, - ReceiveFullName, - ReceiveAge(ReceiveAgeState), - ReceiveFavouriteMusic(ReceiveFavouriteMusicState), -} - -// ============================================================================ -// [Control a dialogue] -// ============================================================================ - -type Cx = DialogueDispatcherHandlerCx< - Message, - State, - as Storage>::Error, ->; -type Res = ResponseResult>; - -async fn start(cx: Cx<()>) -> Res { - cx.answer("Let's start! First, what's your full name?").send().await?; - next(Dialogue::ReceiveFullName) -} - -async fn full_name(cx: Cx<()>) -> Res { - match cx.update.text() { - None => { - cx.answer("Please, send me a text message!").send().await?; - next(Dialogue::ReceiveFullName) - } - Some(full_name) => { - cx.answer("What a wonderful name! Your age?").send().await?; - next(Dialogue::ReceiveAge(ReceiveAgeState { - full_name: full_name.to_owned(), - })) - } - } -} - -async fn age(cx: Cx) -> Res { - match cx.update.text().unwrap().parse() { - Ok(age) => { - cx.answer("Good. Now choose your favourite music:") - .reply_markup(FavouriteMusic::markup()) - .send() - .await?; - next(Dialogue::ReceiveFavouriteMusic(ReceiveFavouriteMusicState { - data: cx.dialogue.unwrap(), - age, - })) - } - Err(_) => { - cx.answer("Oh, please, enter a number!").send().await?; - next(Dialogue::ReceiveAge(cx.dialogue.unwrap())) - } - } -} - -async fn favourite_music(cx: Cx) -> Res { - match cx.update.text().unwrap().parse() { - Ok(favourite_music) => { - cx.answer(format!( - "Fine. {}", - ExitState { - data: cx.dialogue.as_ref().unwrap().clone(), - favourite_music - } - )) - .send() - .await?; - exit() - } - Err(_) => { - cx.answer("Oh, please, enter from the keyboard!").send().await?; - next(Dialogue::ReceiveFavouriteMusic(cx.dialogue.unwrap())) - } - } -} - -async fn handle_message(cx: Cx) -> Res { - let DialogueDispatcherHandlerCx { bot, update, dialogue } = cx; - match dialogue.unwrap() { - Dialogue::Start => { - start(DialogueDispatcherHandlerCx::new(bot, update, ())).await - } - Dialogue::ReceiveFullName => { - full_name(DialogueDispatcherHandlerCx::new(bot, update, ())).await - } - Dialogue::ReceiveAge(s) => { - age(DialogueDispatcherHandlerCx::new(bot, update, s)).await - } - Dialogue::ReceiveFavouriteMusic(s) => { - favourite_music(DialogueDispatcherHandlerCx::new(bot, update, s)) - .await - } - } -} - -// ============================================================================ -// [Run!] -// ============================================================================ - -#[tokio::main] -async fn main() { - run().await; -} - -async fn run() { - teloxide::enable_logging!(); - log::info!("Starting dialogue_bot!"); - - let bot = Bot::from_env(); - - Dispatcher::new(bot) - .messages_handler(DialogueDispatcher::with_storage( - |cx| async move { - handle_message(cx).await.expect("Something wrong with the bot!") - }, - Arc::new( - // You can also choose Serializer::JSON or Serializer::CBOR - // All serializer but JSON require enabling feature - // "serializer-", e. g. "serializer-cbor" - // or "serializer-bincode" - RedisStorage::open("redis://127.0.0.1:6379", Bincode) - .await - .unwrap(), - ), - )) - .dispatch() - .await; -} diff --git a/examples/redis_remember_bot/Cargo.toml b/examples/redis_remember_bot/Cargo.toml new file mode 100644 index 00000000..a8f6bd3c --- /dev/null +++ b/examples/redis_remember_bot/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "redis_remember_bot" +version = "0.1.0" +authors = ["Maximilian Siling "] +edition = "2018" + +[dependencies] +tokio = "0.2.9" +smart-default = "0.6.0" +# You can also choose "cbor-serializer" or built-in JSON serializer +teloxide = { path = "../../", features = ["redis-storage", "bincode-serializer"] } +serde = "1.0.104" +thiserror = "1.0.15" diff --git a/examples/redis_remember_bot/src/main.rs b/examples/redis_remember_bot/src/main.rs new file mode 100644 index 00000000..0de999e8 --- /dev/null +++ b/examples/redis_remember_bot/src/main.rs @@ -0,0 +1,119 @@ +use serde::{Deserialize, Serialize}; +use smart_default::SmartDefault; +use std::sync::Arc; +use teloxide::{ + dispatching::dialogue::{serializer::Bincode, RedisStorage, Storage}, + prelude::*, +}; +use thiserror::Error; + +#[derive(SmartDefault, Serialize, Deserialize)] +enum Dialogue { + #[default] + Start, + HaveNumber(i32), +} + +type StorageError = as Storage>::Error; + +#[derive(Debug, Error)] +enum Error { + #[error("error from Telegram: {0}")] + TelegramError(#[from] RequestError), + #[error("error from storage: {0}")] + StorageError(#[from] StorageError), +} + +type Cx = DialogueDispatcherHandlerCx; + +type Res = Result, Error>; + +async fn handle_message(cx: Cx) -> Res { + let DialogueDispatcherHandlerCx { bot, update, dialogue } = cx; + let text = match update.text() { + Some(text) => text, + None => { + bot.send_message( + update.chat_id(), + "Please, send me a text message", + ) + .send() + .await?; + return next(Dialogue::Start); + } + }; + + match dialogue? { + Dialogue::Start => { + if let Ok(number) = text.parse() { + bot.send_message( + update.chat_id(), + format!( + "Remembered number {}. Now use /get or /reset", + number + ), + ) + .send() + .await?; + next(Dialogue::HaveNumber(number)) + } else { + bot.send_message(update.chat_id(), "Please, send me a number") + .send() + .await?; + next(Dialogue::Start) + } + } + Dialogue::HaveNumber(num) => { + if text.starts_with("/get") { + bot.send_message( + update.chat_id(), + format!("Here is your number: {}", num), + ) + .send() + .await?; + next(Dialogue::HaveNumber(num)) + } else if text.starts_with("/reset") { + bot.send_message(update.chat_id(), format!("Resetted number")) + .send() + .await?; + next(Dialogue::Start) + } else { + bot.send_message( + update.chat_id(), + "Please, send /get or /reset", + ) + .send() + .await?; + next(Dialogue::HaveNumber(num)) + } + } + } +} + +#[tokio::main] +async fn main() { + run().await; +} + +async fn run() { + let bot = Bot::from_env(); + Dispatcher::new(bot) + .messages_handler(DialogueDispatcher::with_storage( + |cx| async move { + handle_message(cx) + .await + .expect("Something is wrong with the bot!") + }, + Arc::new( + // You can also choose serializer::JSON or serializer::CBOR + // All serializers but JSON require enabling feature + // "serializer-", e. g. "serializer-cbor" + // or "serializer-bincode" + RedisStorage::open("redis://127.0.0.1:6379", Bincode) + .await + .unwrap(), + ), + )) + .dispatch() + .await; +}