db_remember/
db_remember.rs

1// Set the `DB_REMEMBER_REDIS` environmental variable if you want to use Redis.
2// Otherwise, the default is Sqlite.
3
4use teloxide::{
5    dispatching::dialogue::{
6        serializer::{Bincode, Json},
7        ErasedStorage, RedisStorage, SqliteStorage, Storage,
8    },
9    prelude::*,
10    utils::command::BotCommands,
11};
12
13type MyDialogue = Dialogue<State, ErasedStorage<State>>;
14type MyStorage = std::sync::Arc<ErasedStorage<State>>;
15type HandlerResult = Result<(), Box<dyn std::error::Error + Send + Sync>>;
16
17#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
18pub enum State {
19    #[default]
20    Start,
21    GotNumber(i32),
22}
23
24/// These commands are supported:
25#[derive(Clone, BotCommands)]
26#[command(rename_rule = "lowercase")]
27pub enum Command {
28    /// Get your number.
29    Get,
30    /// Reset your number.
31    Reset,
32}
33
34#[tokio::main]
35async fn main() {
36    pretty_env_logger::init();
37    log::info!("Starting DB remember bot...");
38
39    let bot = Bot::from_env();
40
41    let storage: MyStorage = if std::env::var("DB_REMEMBER_REDIS").is_ok() {
42        RedisStorage::open("redis://127.0.0.1:6379", Bincode).await.unwrap().erase()
43    } else {
44        SqliteStorage::open("db.sqlite", Json).await.unwrap().erase()
45    };
46
47    let handler = Update::filter_message()
48        .enter_dialogue::<Message, ErasedStorage<State>, State>()
49        .branch(dptree::case![State::Start].endpoint(start))
50        .branch(
51            dptree::case![State::GotNumber(n)]
52                .branch(dptree::entry().filter_command::<Command>().endpoint(got_number))
53                .branch(dptree::endpoint(invalid_command)),
54        );
55
56    Dispatcher::builder(bot, handler)
57        .dependencies(dptree::deps![storage])
58        .enable_ctrlc_handler()
59        .build()
60        .dispatch()
61        .await;
62}
63
64async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult {
65    match msg.text().map(|text| text.parse::<i32>()) {
66        Some(Ok(n)) => {
67            dialogue.update(State::GotNumber(n)).await?;
68            bot.send_message(
69                msg.chat.id,
70                format!("Remembered number {n}. Now use /get or /reset."),
71            )
72            .await?;
73        }
74        _ => {
75            bot.send_message(msg.chat.id, "Please, send me a number.").await?;
76        }
77    }
78
79    Ok(())
80}
81
82async fn got_number(
83    bot: Bot,
84    dialogue: MyDialogue,
85    num: i32, // Available from `State::GotNumber`.
86    msg: Message,
87    cmd: Command,
88) -> HandlerResult {
89    match cmd {
90        Command::Get => {
91            bot.send_message(msg.chat.id, format!("Here is your number: {num}.")).await?;
92        }
93        Command::Reset => {
94            dialogue.reset().await?;
95            bot.send_message(msg.chat.id, "Number reset.").await?;
96        }
97    }
98    Ok(())
99}
100
101async fn invalid_command(bot: Bot, msg: Message) -> HandlerResult {
102    bot.send_message(msg.chat.id, "Please, send /get or /reset.").await?;
103    Ok(())
104}