Merge branch 'dev' into master-copy

This commit is contained in:
Waffle Maybe 2022-04-26 18:01:38 +04:00 committed by GitHub
commit 6c5bb1932d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 60 deletions

View file

@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## unreleased
### Added
- The `dispatching::filter_command` function (also accessible as `teloxide::filter_command`) as a shortcut for `dptree::entry().filter_command()`.
### Changed
- Update teloxide-core to v0.6.0 with [Bot API 6.0] support [**BC**].
[Bot API 6.0]: https://core.telegram.org/bots/api#april-16-2022
## 0.8.2 - 2022-04-26
### Fixed

View file

@ -56,7 +56,7 @@ full = [
]
[dependencies]
teloxide-core = { version = "0.5.1", default-features = false }
teloxide-core = { version = "0.6.0", default-features = false }
teloxide-macros = { version = "0.6.1", optional = true }
serde_json = "1.0"

View file

@ -363,6 +363,7 @@ Feel free to propose your own bot to our collection!
- [wa7sa34cx/the-black-box-bot](https://github.com/wa7sa34cx/the-black-box-bot) -- This is the Black Box Telegram bot. You can hold any items in it.
- [crapstone/hsctt](https://codeberg.org/crapstones-bots/hsctt) -- A Telegram bot that searches for HTTP status codes in all messages and replies with the text form.
- [alenpaul2001/AurSearchBot](https://gitlab.com/alenpaul2001/aursearchbot) -- Telegram bot for searching AUR in inline mode.
- [studiedlist/EddieBot](https://gitlab.com/studiedlist/eddie-bot) -- Chatting bot with several entertainment features
## Contributing

View file

@ -13,7 +13,7 @@
// ```
use teloxide::{
dispatching::dialogue::{self, GetChatId, InMemStorage},
dispatching::dialogue::{self, InMemStorage},
prelude::*,
types::{InlineKeyboardButton, InlineKeyboardMarkup},
utils::command::BotCommands,
@ -42,30 +42,40 @@ enum Command {
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 dialogue_bot...");
log::info!("Starting purchase bot...");
let bot = Bot::from_env().auto_send();
let command_handler = teloxide::filter_command::<Command, _>()
.branch(
teloxide::handler![State::Start]
.branch(teloxide::handler![Command::Help].endpoint(help))
.branch(teloxide::handler![Command::Start].endpoint(start)),
)
.branch(teloxide::handler![Command::Cancel].endpoint(cancel));
let message_handler = Update::filter_message()
.branch(command_handler)
.branch(teloxide::handler![State::ReceiveFullName].endpoint(receive_full_name))
.branch(dptree::endpoint(invalid_state));
let callback_query_handler = Update::filter_callback_query().chain(
teloxide::handler![State::ReceiveProductChoice { full_name }]
.endpoint(receive_product_selection),
);
Dispatcher::builder(
bot,
dialogue::enter::<Update, InMemStorage<State>, State, _>()
.branch(
Update::filter_message()
.branch(teloxide::handler![State::ReceiveFullName].endpoint(receive_full_name))
.branch(dptree::entry().filter_command::<Command>().endpoint(handle_command))
.branch(dptree::endpoint(invalid_state)),
)
.branch(
Update::filter_callback_query().chain(
teloxide::handler![State::ReceiveProductChoice { full_name }]
.endpoint(receive_product_selection),
),
),
.branch(message_handler)
.branch(callback_query_handler),
)
.dependencies(dptree::deps![InMemStorage::<State>::new()])
.build()
@ -74,22 +84,26 @@ async fn main() {
.await;
}
async fn handle_command(
bot: AutoSend<Bot>,
msg: Message,
cmd: Command,
dialogue: MyDialogue,
) -> HandlerResult {
match cmd {
Command::Help => {
bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?;
}
Command::Start => {
async fn start(bot: AutoSend<Bot>, msg: Message, dialogue: MyDialogue) -> 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: AutoSend<Bot>, msg: Message) -> HandlerResult {
bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?;
Ok(())
}
async fn cancel(bot: AutoSend<Bot>, msg: Message, dialogue: MyDialogue) -> HandlerResult {
bot.send_message(msg.chat.id, "Cancelling the dialogue.").await?;
dialogue.exit().await?;
Ok(())
}
async fn invalid_state(bot: AutoSend<Bot>, msg: Message) -> HandlerResult {
bot.send_message(msg.chat.id, "Unable to handle the message. Type /help to see the usage.")
.await?;
Ok(())
}
@ -100,13 +114,12 @@ async fn receive_full_name(
) -> HandlerResult {
match msg.text().map(ToOwned::to_owned) {
Some(full_name) => {
let products = InlineKeyboardMarkup::default().append_row(
vec!["Apple", "Banana", "Orange", "Potato"].into_iter().map(|product| {
InlineKeyboardButton::callback(product.to_owned(), product.to_owned())
}),
);
let products = ["Apple", "Banana", "Orange", "Potato"]
.map(|product| InlineKeyboardButton::callback(product, product));
bot.send_message(msg.chat.id, "Select a product:").reply_markup(products).await?;
bot.send_message(msg.chat.id, "Select a product:")
.reply_markup(InlineKeyboardMarkup::new([products]))
.await?;
dialogue.update(State::ReceiveProductChoice { full_name }).await?;
}
None => {
@ -124,21 +137,13 @@ async fn receive_product_selection(
full_name: String,
) -> HandlerResult {
if let Some(product) = &q.data {
if let Some(chat_id) = q.chat_id() {
bot.send_message(
chat_id,
dialogue.chat_id(),
format!("{full_name}, product '{product}' has been purchased successfully!"),
)
.await?;
dialogue.exit().await?;
}
}
Ok(())
}
async fn invalid_state(bot: AutoSend<Bot>, msg: Message) -> HandlerResult {
bot.send_message(msg.chat.id, "Unable to handle the message. Type /help to see the usage.")
.await?;
Ok(())
}

View file

@ -189,6 +189,11 @@ where
///
/// See [`HandlerExt::enter_dialogue`].
///
/// ## Dependency requirements
///
/// - `Arc<S>`
/// - `Upd`
///
/// [`HandlerExt::enter_dialogue`]: super::HandlerExt::enter_dialogue
pub fn enter<Upd, S, D, Output>() -> Handler<'static, DependencyMap, Output, DpHandlerDescription>
where
@ -214,11 +219,13 @@ where
}))
}
/// Perform a dialogue FSM transition.
/// Filters an enumeration, passing its payload forwards.
///
/// This macro expands to a [`dptree::Handler`] that filters your dialogue
/// state: if the state enumeration is of a certain variant, the execution
/// continues; otherwise, `dptree` will try the next branch.
/// This macro expands to a [`dptree::Handler`] that acts on your enumeration
/// type: if the enumeration is of a certain variant, the execution continues;
/// otherwise, `dptree` will try the next branch. This is very useful for
/// dialogue FSM transitions and Telegram command filtering; for a complete
/// example, please see [`examples/purchase.rs`].
///
/// Variants can take the following forms:
///
@ -243,6 +250,8 @@ where
/// ## Dependency requirements
///
/// - Your dialogue state enumeration `State`.
///
/// [`examples/purchase.rs`]: https://github.com/teloxide/teloxide/blob/master/examples/purchase.rs
#[macro_export]
macro_rules! handler {
($($variant:ident)::+) => {

View file

@ -42,7 +42,8 @@ pub trait HandlerExt<Output> {
/// - `Arc<S>`
/// - `Upd`
///
/// [`Dialogue<D, S>`]: Dialogue
/// [`Dialogue<D, S>`]: super::dialogue::Dialogue
/// [`Dialogue::get_or_default`]: super::dialogue::Dialogue::get_or_default
#[must_use]
fn enter_dialogue<Upd, S, D>(self) -> Self
where
@ -67,10 +68,7 @@ where
where
C: BotCommands + Send + Sync + 'static,
{
self.chain(dptree::filter_map(move |message: Message, me: Me| {
let bot_name = me.user.username.expect("Bots must have a username");
message.text().and_then(|text| C::parse(text, bot_name).ok())
}))
self.chain(filter_command::<C, Output>())
}
fn enter_dialogue<Upd, S, D>(self) -> Self
@ -91,3 +89,24 @@ where
self.chain(F::handler())
}
}
/// Returns a handler that accepts a parsed command `C`.
///
/// A call to this function is the same as `dptree::entry().filter_command()`.
///
/// See [`HandlerExt::filter_command`].
///
/// ## Dependency requirements
///
/// - [`crate::types::Message`]
/// - [`crate::types::Me`]
pub fn filter_command<C, Output>() -> Handler<'static, DependencyMap, Output, DpHandlerDescription>
where
C: BotCommands + Send + Sync + 'static,
Output: Send + Sync + 'static,
{
dptree::entry().chain(dptree::filter_map(move |message: Message, me: Me| {
let bot_name = me.user.username.expect("Bots must have a username");
message.text().and_then(|text| C::parse(text, bot_name).ok())
}))
}

View file

@ -113,6 +113,6 @@ pub use dispatcher::{Dispatcher, DispatcherBuilder, UpdateHandler};
pub use distribution::DefaultKey;
pub use filter_ext::{MessageFilterExt, UpdateFilterExt};
pub use handler_description::DpHandlerDescription;
pub use handler_ext::HandlerExt;
pub use handler_ext::{filter_command, HandlerExt};
#[allow(deprecated)]
pub use handler_factory::HandlerFactory;

View file

@ -79,6 +79,7 @@ pub use teloxide_core::*;
#[cfg(feature = "macros")]
pub use teloxide_macros as macros;
pub use dispatching::filter_command;
pub use dptree;
#[cfg(all(feature = "nightly", doctest))]