update dialogue_bot example

This commit is contained in:
p0lunin 2021-12-27 17:12:31 +02:00
parent b10288f751
commit 50f37cf43b
9 changed files with 94 additions and 125 deletions

View file

@ -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"] }

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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<AutoSend<Bot>>,
ans: String,
) -> TransitionOut<Dialogue> {
match ans.parse::<u8>() {
Ok(ans) => {
cx.answer("What's your location?").await?;
next(ReceiveLocationState::up(state, ans))
}
_ => {
cx.answer("Send me a number.").await?;
next(state)
}
}
}

View file

@ -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<AutoSend<Bot>>,
ans: String,
) -> TransitionOut<Dialogue> {
cx.answer("How old are you?").await?;
next(ReceiveAgeState::up(state, ans))
}

View file

@ -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<AutoSend<Bot>>,
ans: String,
) -> TransitionOut<Dialogue> {
cx.answer(format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, ans))
.await?;
exit()
}

View file

@ -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<AutoSend<Bot>>,
_ans: String,
) -> TransitionOut<Dialogue> {
cx.answer("Let's start! What's your full name?").await?;
next(ReceiveFullNameState)
}

View file

@ -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<Bot>;
type Store = SqliteStorage<Json>;
type BotDialogue = Dialogue<State, Store>;
#[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::<Message, Store, State>().branch(dptree::endpoint(handle_message))
})
.dispatch()
.await;
}
async fn handle_message(
cx: UpdateWithCx<AutoSend<Bot>, Message>,
dialogue: Dialogue,
) -> TransitionOut<Dialogue> {
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::<u8>() {
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(())
}

View file