use std::error::Error; use teloxide::{ payloads::SendMessageSetters, prelude::*, types::{ InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultArticle, InputMessageContent, InputMessageContentText, Me, }, utils::command::BotCommands, }; /// These commands are supported: #[derive(BotCommands)] #[command(rename_rule = "lowercase")] enum Command { /// Display this text Help, /// Start Start, } #[tokio::main] async fn main() -> Result<(), Box> { pretty_env_logger::init(); log::info!("Starting buttons bot..."); let bot = Bot::from_env(); let handler = dptree::entry() .branch(Update::filter_message().endpoint(message_handler)) .branch(Update::filter_callback_query().endpoint(callback_handler)) .branch(Update::filter_inline_query().endpoint(inline_query_handler)); Dispatcher::builder(bot, handler).enable_ctrlc_handler().build().dispatch().await; Ok(()) } /// Creates a keyboard made by buttons in a big column. fn make_keyboard() -> InlineKeyboardMarkup { let mut keyboard: Vec> = vec![]; let debian_versions = [ "Buzz", "Rex", "Bo", "Hamm", "Slink", "Potato", "Woody", "Sarge", "Etch", "Lenny", "Squeeze", "Wheezy", "Jessie", "Stretch", "Buster", "Bullseye", ]; for versions in debian_versions.chunks(3) { let row = versions .iter() .map(|&version| InlineKeyboardButton::callback(version.to_owned(), version.to_owned())) .collect(); keyboard.push(row); } InlineKeyboardMarkup::new(keyboard) } /// Parse the text wrote on Telegram and check if that text is a valid command /// or not, then match the command. If the command is `/start` it writes a /// markup with the `InlineKeyboardMarkup`. async fn message_handler( bot: Bot, msg: Message, me: Me, ) -> Result<(), Box> { if let Some(text) = msg.text() { match BotCommands::parse(text, me.username()) { Ok(Command::Help) => { // Just send the description of all commands. bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?; } Ok(Command::Start) => { // Create a list of buttons and send them. let keyboard = make_keyboard(); bot.send_message(msg.chat.id, "Debian versions:").reply_markup(keyboard).await?; } Err(_) => { bot.send_message(msg.chat.id, "Command not found!").await?; } } } Ok(()) } async fn inline_query_handler( bot: Bot, q: InlineQuery, ) -> Result<(), Box> { let choose_debian_version = InlineQueryResultArticle::new( "0", "Chose debian version", InputMessageContent::Text(InputMessageContentText::new("Debian versions:")), ) .reply_markup(make_keyboard()); bot.answer_inline_query(q.id, vec![choose_debian_version.into()]).await?; Ok(()) } /// When it receives a callback from a button it edits the message with all /// those buttons writing a text with the selected Debian version. /// /// **IMPORTANT**: do not send privacy-sensitive data this way!!! /// Anyone can read data stored in the callback button. async fn callback_handler(bot: Bot, q: CallbackQuery) -> Result<(), Box> { if let Some(version) = q.data { let text = format!("You chose: {version}"); // Tell telegram that we've seen this query, to remove 🕑 icons from the // clients. You could also use `answer_callback_query`'s optional // parameters to tweak what happens on the client side. bot.answer_callback_query(q.id).await?; // Edit text of the message to which the buttons were attached if let Some(message) = q.message { bot.edit_message_text(message.chat().id, message.id(), text).await?; } else if let Some(id) = q.inline_message_id { bot.edit_message_text_inline(id, text).await?; } log::info!("You chose: {}", version); } Ok(()) }