buttons/
buttons.rs

1use std::error::Error;
2use teloxide::{
3    payloads::SendMessageSetters,
4    prelude::*,
5    sugar::bot::BotMessagesExt,
6    types::{
7        InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultArticle, InputMessageContent,
8        InputMessageContentText, Me,
9    },
10    utils::command::BotCommands,
11};
12
13/// These commands are supported:
14#[derive(BotCommands)]
15#[command(rename_rule = "lowercase")]
16enum Command {
17    /// Display this text
18    Help,
19    /// Start
20    Start,
21}
22
23#[tokio::main]
24async fn main() -> Result<(), Box<dyn Error>> {
25    pretty_env_logger::init();
26    log::info!("Starting buttons bot...");
27
28    let bot = Bot::from_env();
29
30    let handler = dptree::entry()
31        .branch(Update::filter_message().endpoint(message_handler))
32        .branch(Update::filter_callback_query().endpoint(callback_handler))
33        .branch(Update::filter_inline_query().endpoint(inline_query_handler));
34
35    Dispatcher::builder(bot, handler).enable_ctrlc_handler().build().dispatch().await;
36    Ok(())
37}
38
39/// Creates a keyboard made by buttons in a big column.
40fn make_keyboard() -> InlineKeyboardMarkup {
41    let mut keyboard: Vec<Vec<InlineKeyboardButton>> = vec![];
42
43    let debian_versions = [
44        "Buzz", "Rex", "Bo", "Hamm", "Slink", "Potato", "Woody", "Sarge", "Etch", "Lenny",
45        "Squeeze", "Wheezy", "Jessie", "Stretch", "Buster", "Bullseye",
46    ];
47
48    for versions in debian_versions.chunks(3) {
49        let row = versions
50            .iter()
51            .map(|&version| InlineKeyboardButton::callback(version.to_owned(), version.to_owned()))
52            .collect();
53
54        keyboard.push(row);
55    }
56
57    InlineKeyboardMarkup::new(keyboard)
58}
59
60/// Parse the text wrote on Telegram and check if that text is a valid command
61/// or not, then match the command. If the command is `/start` it writes a
62/// markup with the `InlineKeyboardMarkup`.
63async fn message_handler(
64    bot: Bot,
65    msg: Message,
66    me: Me,
67) -> Result<(), Box<dyn Error + Send + Sync>> {
68    if let Some(text) = msg.text() {
69        match BotCommands::parse(text, me.username()) {
70            Ok(Command::Help) => {
71                // Just send the description of all commands.
72                bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?;
73            }
74            Ok(Command::Start) => {
75                // Create a list of buttons and send them.
76                let keyboard = make_keyboard();
77                bot.send_message(msg.chat.id, "Debian versions:").reply_markup(keyboard).await?;
78            }
79
80            Err(_) => {
81                bot.send_message(msg.chat.id, "Command not found!").await?;
82            }
83        }
84    }
85
86    Ok(())
87}
88
89async fn inline_query_handler(
90    bot: Bot,
91    q: InlineQuery,
92) -> Result<(), Box<dyn Error + Send + Sync>> {
93    let choose_debian_version = InlineQueryResultArticle::new(
94        "0",
95        "Chose debian version",
96        InputMessageContent::Text(InputMessageContentText::new("Debian versions:")),
97    )
98    .reply_markup(make_keyboard());
99
100    bot.answer_inline_query(q.id, vec![choose_debian_version.into()]).await?;
101
102    Ok(())
103}
104
105/// When it receives a callback from a button it edits the message with all
106/// those buttons writing a text with the selected Debian version.
107///
108/// **IMPORTANT**: do not send privacy-sensitive data this way!!!
109/// Anyone can read data stored in the callback button.
110async fn callback_handler(bot: Bot, q: CallbackQuery) -> Result<(), Box<dyn Error + Send + Sync>> {
111    if let Some(ref version) = q.data {
112        let text = format!("You chose: {version}");
113
114        // Tell telegram that we've seen this query, to remove 🕑 icons from the
115        // clients. You could also use `answer_callback_query`'s optional
116        // parameters to tweak what happens on the client side.
117        bot.answer_callback_query(q.id.clone()).await?;
118
119        // Edit text of the message to which the buttons were attached
120        if let Some(message) = q.regular_message() {
121            bot.edit_text(message, text).await?;
122        } else if let Some(id) = q.inline_message_id {
123            bot.edit_message_text_inline(id, text).await?;
124        }
125
126        log::info!("You chose: {version}");
127    }
128
129    Ok(())
130}