mirror of
https://github.com/teloxide/teloxide.git
synced 2025-03-24 23:57:38 +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…
Add table
Reference in a new issue