mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +01:00
5922984f6c
In reordering the parameters, I stick the following principle: place parameters from least changing to most changing. Thus, we have config and bot right from the beginning, next a dialogue with a possible payload, and next updates such as messages, inline queries, etc. This principle is used in languages with a native support for currying, although in Rust people appear to order parameters arbitrarily, so this commit is mostly for the sake of consistency.
144 lines
4.3 KiB
Rust
144 lines
4.3 KiB
Rust
// 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]
|
|
// - <A user selects "Banana">
|
|
// - 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<State, InMemStorage<State>>;
|
|
type HandlerResult = Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
|
|
|
#[derive(Clone, Default)]
|
|
pub enum State {
|
|
#[default]
|
|
Start,
|
|
ReceiveFullName,
|
|
ReceiveProductChoice {
|
|
full_name: String,
|
|
},
|
|
}
|
|
|
|
#[derive(BotCommands, Clone)]
|
|
#[command(rename = "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::<State>::new()])
|
|
.enable_ctrlc_handler()
|
|
.build()
|
|
.dispatch()
|
|
.await;
|
|
}
|
|
|
|
fn schema() -> UpdateHandler<Box<dyn std::error::Error + Send + Sync + 'static>> {
|
|
use dptree::case;
|
|
|
|
let command_handler = teloxide::filter_command::<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::<Update, InMemStorage<State>, 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(())
|
|
}
|