mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-23 06:51:01 +01:00
Simpler example for Redis
This commit is contained in:
parent
82d0958c91
commit
c58aa22f7b
4 changed files with 132 additions and 232 deletions
|
@ -1,20 +0,0 @@
|
|||
[package]
|
||||
name = "dialogue_bot_redis"
|
||||
version = "0.1.0"
|
||||
authors = ["Temirkhan Myrzamadi <hirrolot@gmail.com>"]
|
||||
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
|
|
@ -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<State> = DialogueDispatcherHandlerCx<
|
||||
Message,
|
||||
State,
|
||||
<RedisStorage<Bincode> as Storage<Dialogue>>::Error,
|
||||
>;
|
||||
type Res = ResponseResult<DialogueStage<Dialogue>>;
|
||||
|
||||
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<ReceiveAgeState>) -> 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<ReceiveFavouriteMusicState>) -> 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<Dialogue>) -> 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-<name>", e. g. "serializer-cbor"
|
||||
// or "serializer-bincode"
|
||||
RedisStorage::open("redis://127.0.0.1:6379", Bincode)
|
||||
.await
|
||||
.unwrap(),
|
||||
),
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
}
|
13
examples/redis_remember_bot/Cargo.toml
Normal file
13
examples/redis_remember_bot/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "redis_remember_bot"
|
||||
version = "0.1.0"
|
||||
authors = ["Maximilian Siling <mouse-art@ya.ru>"]
|
||||
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"
|
119
examples/redis_remember_bot/src/main.rs
Normal file
119
examples/redis_remember_bot/src/main.rs
Normal file
|
@ -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 = <RedisStorage<Bincode> as Storage<Dialogue>>::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum Error {
|
||||
#[error("error from Telegram: {0}")]
|
||||
TelegramError(#[from] RequestError),
|
||||
#[error("error from storage: {0}")]
|
||||
StorageError(#[from] StorageError),
|
||||
}
|
||||
|
||||
type Cx<State> = DialogueDispatcherHandlerCx<Message, State, StorageError>;
|
||||
|
||||
type Res = Result<DialogueStage<Dialogue>, Error>;
|
||||
|
||||
async fn handle_message(cx: Cx<Dialogue>) -> 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-<name>", e. g. "serializer-cbor"
|
||||
// or "serializer-bincode"
|
||||
RedisStorage::open("redis://127.0.0.1:6379", Bincode)
|
||||
.await
|
||||
.unwrap(),
|
||||
),
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
}
|
Loading…
Reference in a new issue