// This example demonstrates how to deal with messages and callback queries // within a single dialogue. // // # Example // ``` // - /start // - Let's start! What's your full name? // - John Doe // - Select a product: // [Apple, Banana, Orange, Potato] // - // - John Doe, product 'Banana' has been purchased successfully! // ``` use teloxide::{ dispatching::{dialogue, dialogue::InMemStorage, UpdateHandler}, prelude::*, types::{InlineKeyboardButton, InlineKeyboardMarkup}, utils::command::BotCommands, }; type MyDialogue = Dialogue>; type HandlerResult = Result<(), Box>; #[derive(Clone, Default)] pub enum State { #[default] Start, ReceiveFullName, ReceiveProductChoice { full_name: String, }, } #[derive(BotCommands, Clone)] #[command(rename_rule = "lowercase", description = "These commands are supported:")] enum Command { #[command(description = "display this text.")] Help, #[command(description = "start the purchase procedure.")] Start, #[command(description = "cancel the purchase procedure.")] Cancel, } #[tokio::main] async fn main() { pretty_env_logger::init(); log::info!("Starting purchase bot..."); let bot = Bot::from_env(); Dispatcher::builder(bot, schema()) .dependencies(dptree::deps![InMemStorage::::new()]) .enable_ctrlc_handler() .build() .dispatch() .await; } fn schema() -> UpdateHandler> { use dptree::case; let command_handler = teloxide::filter_command::() .branch( case![State::Start] .branch(case![Command::Help].endpoint(help)) .branch(case![Command::Start].endpoint(start)), ) .branch(case![Command::Cancel].endpoint(cancel)); let message_handler = Update::filter_message() .branch(command_handler) .branch(case![State::ReceiveFullName].endpoint(receive_full_name)) .branch(dptree::endpoint(invalid_state)); let callback_query_handler = Update::filter_callback_query().branch( case![State::ReceiveProductChoice { full_name }].endpoint(receive_product_selection), ); dialogue::enter::, State, _>() .branch(message_handler) .branch(callback_query_handler) } async fn start(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Let's start! What's your full name?").await?; dialogue.update(State::ReceiveFullName).await?; Ok(()) } async fn help(bot: Bot, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?; Ok(()) } async fn cancel(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Cancelling the dialogue.").await?; dialogue.exit().await?; Ok(()) } async fn invalid_state(bot: Bot, msg: Message) -> HandlerResult { bot.send_message(msg.chat.id, "Unable to handle the message. Type /help to see the usage.") .await?; Ok(()) } async fn receive_full_name(bot: Bot, dialogue: MyDialogue, msg: Message) -> HandlerResult { match msg.text().map(ToOwned::to_owned) { Some(full_name) => { let products = ["Apple", "Banana", "Orange", "Potato"] .map(|product| InlineKeyboardButton::callback(product, product)); bot.send_message(msg.chat.id, "Select a product:") .reply_markup(InlineKeyboardMarkup::new([products])) .await?; dialogue.update(State::ReceiveProductChoice { full_name }).await?; } None => { bot.send_message(msg.chat.id, "Please, send me your full name.").await?; } } Ok(()) } async fn receive_product_selection( bot: Bot, dialogue: MyDialogue, full_name: String, // Available from `State::ReceiveProductChoice`. q: CallbackQuery, ) -> HandlerResult { if let Some(product) = &q.data { bot.send_message( dialogue.chat_id(), format!("{full_name}, product '{product}' has been purchased successfully!"), ) .await?; dialogue.exit().await?; } Ok(()) }