teloxide/examples/simple_dialogue/src/main.rs

168 lines
4.6 KiB
Rust
Raw Normal View History

2020-01-29 23:54:40 +01:00
#[macro_use]
extern crate strum_macros;
2020-02-10 21:54:08 +01:00
#[macro_use]
extern crate smart_default;
2020-01-29 23:54:40 +01:00
use std::fmt::{self, Display, Formatter};
use teloxide::{
prelude::*,
types::{KeyboardButton, ReplyKeyboardMarkup},
};
// ============================================================================
// [Favourite music kinds]
// ============================================================================
#[derive(Copy, Clone, Display, EnumString)]
enum FavouriteMusic {
Rock,
Metal,
Pop,
Other,
}
impl FavouriteMusic {
fn markup() -> ReplyKeyboardMarkup {
2020-02-02 19:54:11 +01:00
ReplyKeyboardMarkup::default().append_row(vec![
KeyboardButton::new("Rock"),
KeyboardButton::new("Metal"),
KeyboardButton::new("Pop"),
KeyboardButton::new("Other"),
])
2020-01-29 23:54:40 +01:00
}
}
// ============================================================================
// [A UserInfo's data]
2020-01-29 23:54:40 +01:00
// ============================================================================
2020-02-11 11:58:32 +01:00
// TODO: implement a type-safe UserInfo without lots of .unwrap
2020-01-29 23:54:40 +01:00
#[derive(Default)]
struct UserInfo {
2020-01-29 23:54:40 +01:00
full_name: Option<String>,
age: Option<u8>,
favourite_music: Option<FavouriteMusic>,
}
impl Display for UserInfo {
2020-01-29 23:54:40 +01:00
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(
f,
"Your full name: {}, your age: {}, your favourite music: {}",
self.full_name.as_ref().unwrap(),
self.age.unwrap(),
self.favourite_music.unwrap()
)
}
}
2020-02-03 15:53:12 +01:00
// ============================================================================
2020-02-04 16:38:25 +01:00
// [States of a dialogue]
2020-02-03 15:53:12 +01:00
// ============================================================================
2020-02-10 21:54:08 +01:00
#[derive(SmartDefault)]
2020-02-04 16:38:25 +01:00
enum State {
2020-02-10 21:54:08 +01:00
#[default]
2020-02-03 15:53:12 +01:00
Start,
FullName,
Age,
FavouriteMusic,
}
// ============================================================================
2020-02-04 16:38:25 +01:00
// [Control a dialogue]
2020-02-03 15:53:12 +01:00
// ============================================================================
type Ctx = DialogueHandlerCtx<Message, State, UserInfo>;
type Res = Result<DialogueStage<State, UserInfo>, RequestError>;
2020-02-02 19:54:11 +01:00
2020-01-29 23:54:40 +01:00
async fn send_favourite_music_types(ctx: &Ctx) -> Result<(), RequestError> {
2020-02-10 22:13:09 +01:00
ctx.answer("Good. Now choose your favourite music:")
2020-01-29 23:54:40 +01:00
.reply_markup(FavouriteMusic::markup())
.send()
.await?;
Ok(())
}
2020-02-04 16:38:25 +01:00
async fn start(mut ctx: Ctx) -> Res {
2020-02-10 22:13:09 +01:00
ctx.answer("Let's start! First, what's your full name?")
.send()
2020-02-02 19:54:11 +01:00
.await?;
2020-02-10 23:04:57 +01:00
state!(ctx, State::FullName);
2020-02-10 21:54:08 +01:00
next(ctx.dialogue)
2020-01-29 23:54:40 +01:00
}
async fn full_name(mut ctx: Ctx) -> Res {
2020-02-10 22:13:09 +01:00
ctx.answer("What a wonderful name! Your age?")
.send()
.await?;
2020-02-04 16:38:25 +01:00
ctx.dialogue.data.full_name = Some(ctx.update.text().unwrap().to_owned());
2020-02-10 23:04:57 +01:00
state!(ctx, State::Age);
2020-02-10 21:54:08 +01:00
next(ctx.dialogue)
2020-01-29 23:54:40 +01:00
}
async fn age(mut ctx: Ctx) -> Res {
match ctx.update.text().unwrap().parse() {
Ok(ok) => {
send_favourite_music_types(&ctx).await?;
2020-02-04 16:38:25 +01:00
ctx.dialogue.data.age = Some(ok);
2020-02-10 23:04:57 +01:00
state!(ctx, State::FavouriteMusic);
2020-01-29 23:54:40 +01:00
}
2020-02-10 22:13:09 +01:00
Err(_) => ctx
.answer("Oh, please, enter a number!")
.send()
.await
.map(|_| ())?,
2020-01-29 23:54:40 +01:00
}
2020-02-10 21:54:08 +01:00
next(ctx.dialogue)
2020-01-29 23:54:40 +01:00
}
async fn favourite_music(mut ctx: Ctx) -> Res {
match ctx.update.text().unwrap().parse() {
Ok(ok) => {
2020-02-04 16:38:25 +01:00
ctx.dialogue.data.favourite_music = Some(ok);
2020-02-10 22:13:09 +01:00
ctx.answer(format!("Fine. {}", ctx.dialogue.data))
.send()
.await?;
2020-02-10 21:54:08 +01:00
exit()
2020-01-29 23:54:40 +01:00
}
Err(_) => {
2020-02-10 22:13:09 +01:00
ctx.answer("Oh, please, enter from the keyboard!")
.send()
.await?;
2020-02-10 21:54:08 +01:00
next(ctx.dialogue)
2020-01-29 23:54:40 +01:00
}
}
}
async fn handle_message(ctx: Ctx) -> Res {
2020-02-04 16:38:25 +01:00
match ctx.dialogue.state {
State::Start => start(ctx).await,
State::FullName => full_name(ctx).await,
State::Age => age(ctx).await,
State::FavouriteMusic => favourite_music(ctx).await,
2020-01-29 23:54:40 +01:00
}
}
// ============================================================================
// [Run!]
// ============================================================================
#[tokio::main]
async fn main() {
2020-02-04 16:38:25 +01:00
std::env::set_var("RUST_LOG", "simple_dialogue=trace");
2020-02-11 23:50:10 +01:00
std::env::set_var("RUST_LOG", "teloxide=error");
2020-01-29 23:54:40 +01:00
pretty_env_logger::init();
2020-02-04 16:38:25 +01:00
log::info!("Starting the simple_dialogue bot!");
2020-01-29 23:54:40 +01:00
Dispatcher::new(Bot::new("YourAwesomeToken"))
2020-02-10 21:54:08 +01:00
.message_handler(&DialogueDispatcher::new(|ctx| async move {
2020-02-01 05:05:18 +01:00
handle_message(ctx)
.await
.expect("Something wrong with the bot!")
2020-01-29 23:54:40 +01:00
}))
.dispatch()
.await;
}