From 24f141c45294fc09bcb6ae46aba19e2b4fdc966f Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 12 Oct 2021 20:10:19 +0200 Subject: [PATCH 1/7] Add example using the InlineKeyboardMarkup --- examples/buttons/Cargo.toml | 12 ++++++ examples/buttons/src/commands.rs | 63 ++++++++++++++++++++++++++++++++ examples/buttons/src/main.rs | 50 +++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 examples/buttons/Cargo.toml create mode 100644 examples/buttons/src/commands.rs create mode 100644 examples/buttons/src/main.rs diff --git a/examples/buttons/Cargo.toml b/examples/buttons/Cargo.toml new file mode 100644 index 00000000..9922e0ae --- /dev/null +++ b/examples/buttons/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "buttons" +version = "0.1.0" +edition = "2018" + +[dependencies] +teloxide = { path = "../../", features = ["macros"] } + +tokio = { version = "1.3", features = ["rt-multi-thread", "macros"] } +log = "0.4.8" +pretty_env_logger = "0.4.0" +tokio-stream = "0.1.6" diff --git a/examples/buttons/src/commands.rs b/examples/buttons/src/commands.rs new file mode 100644 index 00000000..011eab6a --- /dev/null +++ b/examples/buttons/src/commands.rs @@ -0,0 +1,63 @@ +use std::error::Error; +use teloxide::{ + payloads::SendMessageSetters, + prelude::{AutoSend, Bot, GetChatId, Message, UpdateWithCx}, + types::{InlineKeyboardButton, InlineKeyboardMarkup}, + utils::command::BotCommand, +}; + +#[derive(BotCommand)] +#[command(rename = "lowercase", description = "These commands are supported:")] +pub enum Command { + #[command(description = "Display this text")] + Help, + #[command(description = "Start")] + Start, +} + +/// Creates a keyboard made by buttons in a big column. +async fn make_keyboard(chat_id: i64) -> InlineKeyboardMarkup { + let mut keyboard_array: Vec> = vec![]; + // The column is made by the list of Debian versions. + let debian_versions = vec![ + "Buzz", "Rex", "Bo", "Hamm", "Slink", "Potato", "Woody", "Sarge", "Etch", "Lenny", + "Squeeze", "Wheezy", "Jessie", "Stretch", "Buster", "Bullseye", + ]; + + for version in debian_versions { + // Match each button with the chat id and the Debian version. + keyboard_array.push(vec![InlineKeyboardButton::callback( + version.into(), + format!("{}_{}", chat_id, version), + )]); + } + + InlineKeyboardMarkup::new(keyboard_array) +} + +/// 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`. +pub async fn handler( + cx: UpdateWithCx, Message>, +) -> Result<(), Box> { + if let Ok(command) = + BotCommand::parse(cx.update.text().expect("Error with the text"), "buttons") + { + match command { + Command::Help => { + // Just send the description of all commands. + cx.answer(Command::descriptions()).await?; + } + Command::Start => { + let keyboard = make_keyboard(cx.chat_id()).await; + // Create a list of buttons using callbacks to receive the response. + cx.answer("Debian versions:").reply_markup(keyboard).await?; + } + } + } else { + cx.reply_to("Command not found!").await?; + } + + Ok(()) +} diff --git a/examples/buttons/src/main.rs b/examples/buttons/src/main.rs new file mode 100644 index 00000000..6695be43 --- /dev/null +++ b/examples/buttons/src/main.rs @@ -0,0 +1,50 @@ +mod commands; + +use std::error::Error; +use teloxide::prelude::*; + +use tokio_stream::wrappers::UnboundedReceiverStream; + +#[tokio::main] +async fn main() -> Result<(), Box> { + teloxide::enable_logging!(); + log::info!("Starting bot..."); + + let bot = Bot::from_env().auto_send(); + + Dispatcher::new(bot) + .messages_handler(|rx: DispatcherHandlerRx, Message>| { + UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { + commands::handler(cx).await.log_on_error().await; + }) + }) + .callback_queries_handler(|rx: DispatcherHandlerRx, CallbackQuery>| { + UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { + let data = &cx.update.data; + // When it receives a callback from a button it edits the message with all those + // buttons writing a text with the selected Debian version. + if let Some(text) = data { + let callback: Vec<&str> = text.split('_').collect(); + let chat_id = callback[0]; + let version = callback[1]; + + let message_id = cx.update.message.clone().unwrap().id; + let _ = cx + .requester + .edit_message_text( + chat_id.to_string(), + message_id, + format!("You chose: {}", version), + ) + .await; + log::info!("You chose: {}", version); + } + }) + }) + .dispatch() + .await; + + log::info!("Closing bot... Goodbye!"); + + Ok(()) +} From 6f963c91e69f7674e92a5dd6efcdd24aba80d747 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 12 Oct 2021 21:34:19 +0200 Subject: [PATCH 2/7] Remove async on make keyboard function --- examples/buttons/src/commands.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/buttons/src/commands.rs b/examples/buttons/src/commands.rs index 011eab6a..12b1fd1a 100644 --- a/examples/buttons/src/commands.rs +++ b/examples/buttons/src/commands.rs @@ -16,7 +16,7 @@ pub enum Command { } /// Creates a keyboard made by buttons in a big column. -async fn make_keyboard(chat_id: i64) -> InlineKeyboardMarkup { +fn make_keyboard(chat_id: i64) -> InlineKeyboardMarkup { let mut keyboard_array: Vec> = vec![]; // The column is made by the list of Debian versions. let debian_versions = vec![ @@ -50,7 +50,7 @@ pub async fn handler( cx.answer(Command::descriptions()).await?; } Command::Start => { - let keyboard = make_keyboard(cx.chat_id()).await; + let keyboard = make_keyboard(cx.chat_id()); // Create a list of buttons using callbacks to receive the response. cx.answer("Debian versions:").reply_markup(keyboard).await?; } From 52ce224b6c6b40041873dbe09d0b800a4bd44e5d Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 12 Oct 2021 21:50:37 +0200 Subject: [PATCH 3/7] Use callbacks into a self function --- examples/buttons/src/main.rs | 42 ++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/examples/buttons/src/main.rs b/examples/buttons/src/main.rs index 6695be43..f5027032 100644 --- a/examples/buttons/src/main.rs +++ b/examples/buttons/src/main.rs @@ -5,6 +5,28 @@ use teloxide::prelude::*; use tokio_stream::wrappers::UnboundedReceiverStream; +/// When it receives a callback from a button it edits the message with all +/// those buttons writing a text with the selected Debian version. +async fn callback_hander( + cx: UpdateWithCx, CallbackQuery>, +) -> Result<(), Box> { + let data = &cx.update.data; + if let Some(text) = data { + let callback: Vec<&str> = text.split('_').collect(); + let chat_id = callback[0]; + let version = callback[1]; + + let message_id = cx.update.message.clone().unwrap().id; + let _ = cx + .requester + .edit_message_text(chat_id.to_string(), message_id, format!("You chose: {}", version)) + .await; + log::info!("You chose: {}", version); + } + + Ok(()) +} + #[tokio::main] async fn main() -> Result<(), Box> { teloxide::enable_logging!(); @@ -20,25 +42,7 @@ async fn main() -> Result<(), Box> { }) .callback_queries_handler(|rx: DispatcherHandlerRx, CallbackQuery>| { UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { - let data = &cx.update.data; - // When it receives a callback from a button it edits the message with all those - // buttons writing a text with the selected Debian version. - if let Some(text) = data { - let callback: Vec<&str> = text.split('_').collect(); - let chat_id = callback[0]; - let version = callback[1]; - - let message_id = cx.update.message.clone().unwrap().id; - let _ = cx - .requester - .edit_message_text( - chat_id.to_string(), - message_id, - format!("You chose: {}", version), - ) - .await; - log::info!("You chose: {}", version); - } + callback_hander(cx).await.log_on_error().await; }) }) .dispatch() From e8b27ba749e6aef8a12a3d79c5b37825d8a6feb5 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 12 Oct 2021 21:53:41 +0200 Subject: [PATCH 4/7] Merge two source files into one --- examples/buttons/src/commands.rs | 63 ------------------------------ examples/buttons/src/main.rs | 67 ++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 67 deletions(-) delete mode 100644 examples/buttons/src/commands.rs diff --git a/examples/buttons/src/commands.rs b/examples/buttons/src/commands.rs deleted file mode 100644 index 12b1fd1a..00000000 --- a/examples/buttons/src/commands.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::error::Error; -use teloxide::{ - payloads::SendMessageSetters, - prelude::{AutoSend, Bot, GetChatId, Message, UpdateWithCx}, - types::{InlineKeyboardButton, InlineKeyboardMarkup}, - utils::command::BotCommand, -}; - -#[derive(BotCommand)] -#[command(rename = "lowercase", description = "These commands are supported:")] -pub enum Command { - #[command(description = "Display this text")] - Help, - #[command(description = "Start")] - Start, -} - -/// Creates a keyboard made by buttons in a big column. -fn make_keyboard(chat_id: i64) -> InlineKeyboardMarkup { - let mut keyboard_array: Vec> = vec![]; - // The column is made by the list of Debian versions. - let debian_versions = vec![ - "Buzz", "Rex", "Bo", "Hamm", "Slink", "Potato", "Woody", "Sarge", "Etch", "Lenny", - "Squeeze", "Wheezy", "Jessie", "Stretch", "Buster", "Bullseye", - ]; - - for version in debian_versions { - // Match each button with the chat id and the Debian version. - keyboard_array.push(vec![InlineKeyboardButton::callback( - version.into(), - format!("{}_{}", chat_id, version), - )]); - } - - InlineKeyboardMarkup::new(keyboard_array) -} - -/// 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`. -pub async fn handler( - cx: UpdateWithCx, Message>, -) -> Result<(), Box> { - if let Ok(command) = - BotCommand::parse(cx.update.text().expect("Error with the text"), "buttons") - { - match command { - Command::Help => { - // Just send the description of all commands. - cx.answer(Command::descriptions()).await?; - } - Command::Start => { - let keyboard = make_keyboard(cx.chat_id()); - // Create a list of buttons using callbacks to receive the response. - cx.answer("Debian versions:").reply_markup(keyboard).await?; - } - } - } else { - cx.reply_to("Command not found!").await?; - } - - Ok(()) -} diff --git a/examples/buttons/src/main.rs b/examples/buttons/src/main.rs index f5027032..c935d8e9 100644 --- a/examples/buttons/src/main.rs +++ b/examples/buttons/src/main.rs @@ -1,10 +1,69 @@ -mod commands; - use std::error::Error; -use teloxide::prelude::*; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardButton, InlineKeyboardMarkup}, + utils::command::BotCommand, +}; use tokio_stream::wrappers::UnboundedReceiverStream; +#[derive(BotCommand)] +#[command(rename = "lowercase", description = "These commands are supported:")] +pub enum Command { + #[command(description = "Display this text")] + Help, + #[command(description = "Start")] + Start, +} + +/// Creates a keyboard made by buttons in a big column. +fn make_keyboard(chat_id: i64) -> InlineKeyboardMarkup { + let mut keyboard_array: Vec> = vec![]; + // The column is made by the list of Debian versions. + let debian_versions = vec![ + "Buzz", "Rex", "Bo", "Hamm", "Slink", "Potato", "Woody", "Sarge", "Etch", "Lenny", + "Squeeze", "Wheezy", "Jessie", "Stretch", "Buster", "Bullseye", + ]; + + for version in debian_versions { + // Match each button with the chat id and the Debian version. + keyboard_array.push(vec![InlineKeyboardButton::callback( + version.into(), + format!("{}_{}", chat_id, version), + )]); + } + + InlineKeyboardMarkup::new(keyboard_array) +} + +/// 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`. +pub async fn query_handler( + cx: UpdateWithCx, Message>, +) -> Result<(), Box> { + if let Ok(command) = + BotCommand::parse(cx.update.text().expect("Error with the text"), "buttons") + { + match command { + Command::Help => { + // Just send the description of all commands. + cx.answer(Command::descriptions()).await?; + } + Command::Start => { + let keyboard = make_keyboard(cx.chat_id()); + // Create a list of buttons using callbacks to receive the response. + cx.answer("Debian versions:").reply_markup(keyboard).await?; + } + } + } else { + cx.reply_to("Command not found!").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. async fn callback_hander( @@ -37,7 +96,7 @@ async fn main() -> Result<(), Box> { Dispatcher::new(bot) .messages_handler(|rx: DispatcherHandlerRx, Message>| { UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { - commands::handler(cx).await.log_on_error().await; + query_handler(cx).await.log_on_error().await; }) }) .callback_queries_handler(|rx: DispatcherHandlerRx, CallbackQuery>| { From 296c851a119d6497f67699d49268b79b054e2e2d Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 12 Oct 2021 21:55:36 +0200 Subject: [PATCH 5/7] Remove `pub` keywords --- examples/buttons/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/buttons/src/main.rs b/examples/buttons/src/main.rs index c935d8e9..4d0aead2 100644 --- a/examples/buttons/src/main.rs +++ b/examples/buttons/src/main.rs @@ -10,7 +10,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream; #[derive(BotCommand)] #[command(rename = "lowercase", description = "These commands are supported:")] -pub enum Command { +enum Command { #[command(description = "Display this text")] Help, #[command(description = "Start")] @@ -40,7 +40,7 @@ fn make_keyboard(chat_id: i64) -> InlineKeyboardMarkup { /// 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`. -pub async fn query_handler( +async fn query_handler( cx: UpdateWithCx, Message>, ) -> Result<(), Box> { if let Ok(command) = From d501c031ffce68431b60ccc4ff438ae5e22192b6 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 12 Oct 2021 21:56:49 +0200 Subject: [PATCH 6/7] Fix name for message handler --- examples/buttons/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/buttons/src/main.rs b/examples/buttons/src/main.rs index 4d0aead2..d39ac641 100644 --- a/examples/buttons/src/main.rs +++ b/examples/buttons/src/main.rs @@ -40,7 +40,7 @@ fn make_keyboard(chat_id: i64) -> InlineKeyboardMarkup { /// 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 query_handler( +async fn message_handler( cx: UpdateWithCx, Message>, ) -> Result<(), Box> { if let Ok(command) = @@ -96,7 +96,7 @@ async fn main() -> Result<(), Box> { Dispatcher::new(bot) .messages_handler(|rx: DispatcherHandlerRx, Message>| { UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { - query_handler(cx).await.log_on_error().await; + message_handler(cx).await.log_on_error().await; }) }) .callback_queries_handler(|rx: DispatcherHandlerRx, CallbackQuery>| { From 5bacb6d6814f2e24ed97866fbdc4a6e1242bb752 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Wed, 13 Oct 2021 09:19:27 +0200 Subject: [PATCH 7/7] Fix typo for callback_handler --- examples/buttons/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/buttons/src/main.rs b/examples/buttons/src/main.rs index d39ac641..0112a173 100644 --- a/examples/buttons/src/main.rs +++ b/examples/buttons/src/main.rs @@ -66,7 +66,7 @@ async fn message_handler( /// When it receives a callback from a button it edits the message with all /// those buttons writing a text with the selected Debian version. -async fn callback_hander( +async fn callback_handler( cx: UpdateWithCx, CallbackQuery>, ) -> Result<(), Box> { let data = &cx.update.data; @@ -101,7 +101,7 @@ async fn main() -> Result<(), Box> { }) .callback_queries_handler(|rx: DispatcherHandlerRx, CallbackQuery>| { UnboundedReceiverStream::new(rx).for_each_concurrent(None, |cx| async move { - callback_hander(cx).await.log_on_error().await; + callback_handler(cx).await.log_on_error().await; }) }) .dispatch()