Add DispatcherHandlerRxExt

This commit is contained in:
Temirkhan Myrzamadi 2020-02-19 15:53:54 +06:00
parent 9d78b7e0a6
commit 46b20f676e
7 changed files with 101 additions and 55 deletions

View file

@ -63,7 +63,6 @@ $ rustup update stable
[dependencies]
teloxide = "0.1.0"
log = "0.4.8"
futures = "0.3.4"
tokio = "0.2.11"
pretty_env_logger = "0.4.0"
```
@ -147,13 +146,12 @@ async fn answer(
}
async fn handle_command(rx: DispatcherHandlerRx<Message>) {
rx.filter_map(|cx| {
future::ready(cx.update.text_owned().map(|text| (cx, text)))
})
.filter_map(|(cx, text)| {
future::ready(Command::parse(&text).map(|(command, _)| (cx, command)))
})
.for_each_concurrent(None, |(cx, command)| async move {
// Only iterate through text messages:
rx.text_messages()
// Only iterate through commands in a proper format:
.commands::<Command>()
// Execute all incoming commands concurrently:
.for_each_concurrent(None, |(cx, command, _)| async move {
answer(cx, command).await.log_on_error().await;
})
.await;
@ -184,7 +182,7 @@ TELOXIDE_TOKEN=<Your token here> cargo run
</div>
See? The dispatcher gives us a stream of messages, so we can handle it as we want! Here we use [`.filter_map()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.filter_map) and [`.for_each_concurrent()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.for_each_concurrent), but others are also available:
See? The dispatcher gives us a stream of messages, so we can handle it as we want! Here we use our `.text_messages()`, `.commands()`, and [`.for_each_concurrent()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.for_each_concurrent), but others are also available:
- [`.flatten()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.flatten)
- [`.left_stream()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.left_stream)
- [`.scan()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.scan)

View file

@ -176,21 +176,13 @@ async fn action(
// Handle all messages.
async fn handle_commands(rx: DispatcherHandlerRx<Message>) {
rx.filter(|cx| future::ready(!cx.update.chat.is_group()))
.filter_map(|cx| {
future::ready(cx.update.text_owned().map(|text| (cx, text)))
})
.filter_map(|(cx, text)| {
future::ready(Command::parse(&text).map(|(command, args)| {
(
cx,
command,
args.into_iter()
.map(ToOwned::to_owned)
.collect::<Vec<String>>(),
)
}))
})
// Only iterate through messages from groups:
rx.filter(|cx| future::ready(cx.update.chat.is_group()))
// Only iterate through text messages:
.text_messages()
// Only iterate through commands in a proper format:
.commands::<Command>()
// Execute all incoming commands concurrently:
.for_each_concurrent(None, |(cx, command, args)| async move {
action(cx, command, &args).await.log_on_error().await;
})

View file

@ -1,6 +1,5 @@
use teloxide::{prelude::*, utils::command::BotCommand};
use futures::future;
use rand::{thread_rng, Rng};
#[derive(BotCommand)]
@ -32,13 +31,12 @@ async fn answer(
}
async fn handle_command(rx: DispatcherHandlerRx<Message>) {
rx.filter_map(|cx| {
future::ready(cx.update.text_owned().map(|text| (cx, text)))
})
.filter_map(|(cx, text)| {
future::ready(Command::parse(&text).map(|(command, _)| (cx, command)))
})
.for_each_concurrent(None, |(cx, command)| async move {
// Only iterate through text messages:
rx.text_messages()
// Only iterate through commands in a proper format:
.commands::<Command>()
// Execute all incoming commands concurrently:
.for_each_concurrent(None, |(cx, command, _)| async move {
answer(cx, command).await.log_on_error().await;
})
.await;

View file

@ -0,0 +1,61 @@
use crate::{
prelude::DispatcherHandlerCx, types::Message, utils::command::BotCommand,
};
use futures::{stream::BoxStream, Stream, StreamExt};
/// An extension trait to be used with [`DispatcherHandlerRx`].
///
/// [`DispatcherHandlerRx`]: crate:dispatching::DispatcherHandlerRx
pub trait DispatcherHandlerRxExt {
/// Extracts only text messages from this stream.
fn text_messages(
self,
) -> BoxStream<'static, (DispatcherHandlerCx<Message>, String)>
where
Self: Stream<Item = DispatcherHandlerCx<Message>>;
/// Extracts only commands with their arguments from this stream of text
/// messages.
fn commands<C>(
self,
) -> BoxStream<'static, (DispatcherHandlerCx<Message>, C, Vec<String>)>
where
Self: Stream<Item = (DispatcherHandlerCx<Message>, String)>,
C: BotCommand;
}
impl<T> DispatcherHandlerRxExt for T
where
T: Send + 'static,
{
fn text_messages(
self,
) -> BoxStream<'static, (DispatcherHandlerCx<Message>, String)>
where
Self: Stream<Item = DispatcherHandlerCx<Message>>,
{
Box::pin(self.filter_map(|cx| async move {
cx.update.text_owned().map(|text| (cx, text))
}))
}
fn commands<C>(
self,
) -> BoxStream<'static, (DispatcherHandlerCx<Message>, C, Vec<String>)>
where
Self: Stream<Item = (DispatcherHandlerCx<Message>, String)>,
C: BotCommand,
{
Box::pin(self.filter_map(|(cx, text)| async move {
C::parse(&text).map(|(command, args)| {
(
cx,
command,
args.into_iter()
.map(ToOwned::to_owned)
.collect::<Vec<String>>(),
)
})
}))
}
}

View file

@ -81,11 +81,13 @@ pub mod dialogue;
mod dispatcher;
mod dispatcher_handler;
mod dispatcher_handler_cx;
mod dispatcher_handler_rx_ext;
pub mod update_listeners;
pub use dispatcher::Dispatcher;
pub use dispatcher_handler::DispatcherHandler;
pub use dispatcher_handler_cx::DispatcherHandlerCx;
pub use dispatcher_handler_rx_ext::DispatcherHandlerRxExt;
use tokio::sync::mpsc::UnboundedReceiver;
/// A type of a stream, consumed by [`Dispatcher`]'s handlers.

View file

@ -42,7 +42,6 @@
//! [dependencies]
//! teloxide = "0.1.0"
//! log = "0.4.8"
//! futures = "0.3.4"
//! tokio = "0.2.11"
//! pretty_env_logger = "0.4.0"
//! ```
@ -99,7 +98,6 @@
//! ```no_run
//! // Imports are omitted...
//! # use teloxide::{prelude::*, utils::command::BotCommand};
//! # use futures::future;
//! # use rand::{thread_rng, Rng};
//!
//! #[derive(BotCommand)]
@ -134,15 +132,12 @@
//! }
//!
//! async fn handle_command(rx: DispatcherHandlerRx<Message>) {
//! rx.filter_map(|cx| {
//! future::ready(cx.update.text_owned().map(|text| (cx, text)))
//! })
//! .filter_map(|(cx, text)| {
//! future::ready(
//! Command::parse(&text).map(|(command, _)| (cx, command)),
//! )
//! })
//! .for_each_concurrent(None, |(cx, command)| async move {
//! // Only iterate through text messages:
//! rx.text_messages()
//! // Only iterate through commands in a proper format:
//! .commands::<Command>()
//! // Execute all incoming commands concurrently:
//! .for_each_concurrent(None, |(cx, command, _)| async move {
//! answer(cx, command).await.log_on_error().await;
//! })
//! .await;
@ -178,8 +173,8 @@
//!
//!
//! See? The dispatcher gives us a stream of messages, so we can handle it as we
//! want! Here we use [`.filter_map()`] and [`.for_each_concurrent()`], but
//! others are also available:
//! want! Here we use our `.text_messages()`, `.commands()`, and
//! [`.for_each_concurrent()`], but others are also available:
//! - [`.flatten()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.flatten)
//! - [`.left_stream()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.left_stream)
//! - [`.scan()`](https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.scan)
@ -349,7 +344,6 @@
//! [@Botfather]: https://t.me/botfather
//! [streams]: https://docs.rs/futures/0.3.4/futures/stream/index.html
//! [all 30+ patterns]: https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html
//! [`.filter_map()`]: https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.filter_map
//! [`.for_each_concurrent()`]: https://docs.rs/futures/0.3.4/futures/stream/trait.StreamExt.html#method.for_each_concurrent
//! [See more examples]: https://github.com/teloxide/teloxide/tree/master/examples
//! [category theory]: https://en.wikipedia.org/wiki/Category_theory

View file

@ -7,6 +7,7 @@ pub use crate::{
DialogueStage, GetChatId,
},
Dispatcher, DispatcherHandlerCx, DispatcherHandlerRx,
DispatcherHandlerRxExt,
},
error_handlers::{LoggingErrorHandler, OnError},
requests::{Request, ResponseResult},