Merge pull request #587 from teloxide/allowed_updates_dp2

implement allowed updates in dispatcher
This commit is contained in:
Hirrolot 2022-04-18 16:23:44 +06:00 committed by GitHub
commit 7186eddb75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 180 additions and 97 deletions

View file

@ -62,7 +62,7 @@ teloxide-macros = { git = "https://github.com/teloxide/teloxide-macros.git", rev
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
dptree = { version = "0.1.0" }
dptree = { version = "0.2.0" }
tokio = { version = "1.8", features = ["fs"] }
tokio-util = "0.6"

View file

@ -257,7 +257,7 @@ mod tests {
#[tokio::test]
async fn handler_empty_variant() {
let input = State::A;
let h = handler![State::A].endpoint(|| async move { 123 });
let h: dptree::Handler<_, _> = handler![State::A].endpoint(|| async move { 123 });
assert_eq!(h.dispatch(dptree::deps![input]).await, ControlFlow::Break(123));
assert!(matches!(h.dispatch(dptree::deps![State::Other]).await, ControlFlow::Continue(_)));
@ -266,7 +266,7 @@ mod tests {
#[tokio::test]
async fn handler_single_fn_variant() {
let input = State::B(42);
let h = handler![State::B(x)].endpoint(|x: i32| async move {
let h: dptree::Handler<_, _> = handler![State::B(x)].endpoint(|x: i32| async move {
assert_eq!(x, 42);
123
});
@ -278,7 +278,7 @@ mod tests {
#[tokio::test]
async fn handler_single_fn_variant_trailing_comma() {
let input = State::B(42);
let h = handler![State::B(x,)].endpoint(|(x,): (i32,)| async move {
let h: dptree::Handler<_, _> = handler![State::B(x,)].endpoint(|(x,): (i32,)| async move {
assert_eq!(x, 42);
123
});
@ -290,11 +290,12 @@ mod tests {
#[tokio::test]
async fn handler_fn_variant() {
let input = State::C(42, "abc");
let h = handler![State::C(x, y)].endpoint(|(x, str): (i32, &'static str)| async move {
assert_eq!(x, 42);
assert_eq!(str, "abc");
123
});
let h: dptree::Handler<_, _> =
handler![State::C(x, y)].endpoint(|(x, str): (i32, &'static str)| async move {
assert_eq!(x, 42);
assert_eq!(str, "abc");
123
});
assert_eq!(h.dispatch(dptree::deps![input]).await, ControlFlow::Break(123));
assert!(matches!(h.dispatch(dptree::deps![State::Other]).await, ControlFlow::Continue(_)));
@ -303,7 +304,7 @@ mod tests {
#[tokio::test]
async fn handler_single_struct_variant() {
let input = State::D { foo: 42 };
let h = handler![State::D { foo }].endpoint(|x: i32| async move {
let h: dptree::Handler<_, _> = handler![State::D { foo }].endpoint(|x: i32| async move {
assert_eq!(x, 42);
123
});
@ -316,7 +317,7 @@ mod tests {
async fn handler_single_struct_variant_trailing_comma() {
let input = State::D { foo: 42 };
#[rustfmt::skip] // rustfmt removes the trailing comma from `State::D { foo, }`, but it plays a vital role in this test.
let h = handler![State::D { foo, }].endpoint(|(x,): (i32,)| async move {
let h: dptree::Handler<_, _> = handler![State::D { foo, }].endpoint(|(x,): (i32,)| async move {
assert_eq!(x, 42);
123
});
@ -328,7 +329,7 @@ mod tests {
#[tokio::test]
async fn handler_struct_variant() {
let input = State::E { foo: 42, bar: "abc" };
let h =
let h: dptree::Handler<_, _> =
handler![State::E { foo, bar }].endpoint(|(x, str): (i32, &'static str)| async move {
assert_eq!(x, 42);
assert_eq!(str, "abc");

View file

@ -1,18 +1,18 @@
use crate::{
dispatching::{
distribution::default_distribution_function, stop_token::StopToken, update_listeners,
update_listeners::UpdateListener, DefaultKey, ShutdownToken,
update_listeners::UpdateListener, DefaultKey, DpHandlerDescription, ShutdownToken,
},
error_handlers::{ErrorHandler, LoggingErrorHandler},
requests::{Request, Requester},
types::{AllowedUpdate, Update, UpdateKind},
types::{Update, UpdateKind},
utils::shutdown_token::shutdown_check_timeout_for,
};
use dptree::di::{DependencyMap, DependencySupplier};
use futures::{future::BoxFuture, stream::FuturesUnordered, StreamExt};
use std::{
collections::{HashMap, HashSet},
collections::HashMap,
fmt::Debug,
hash::Hash,
ops::{ControlFlow, Deref},
@ -132,7 +132,6 @@ where
handler,
default_handler,
error_handler,
allowed_updates: Default::default(),
state: ShutdownToken::new(),
distribution_f,
worker_queue_size,
@ -165,8 +164,6 @@ pub struct Dispatcher<R, Err, Key> {
default_worker: Option<Worker>,
error_handler: Arc<dyn ErrorHandler<Err> + Send + Sync>,
// TODO: respect allowed_udpates
allowed_updates: HashSet<AllowedUpdate>,
state: ShutdownToken,
}
@ -180,7 +177,8 @@ struct Worker {
// webhooks, so we can allow this too. See more there: https://core.telegram.org/bots/api#making-requests-when-getting-updates
/// A handler that processes updates from Telegram.
pub type UpdateHandler<Err> = dptree::Handler<'static, DependencyMap, Result<(), Err>>;
pub type UpdateHandler<Err> =
dptree::Handler<'static, DependencyMap, Result<(), Err>, DpHandlerDescription>;
type DefaultHandler = Arc<dyn Fn(Arc<Update>) -> BoxFuture<'static, ()> + Send + Sync>;
@ -267,7 +265,10 @@ where
self.dependencies.insert(me);
self.dependencies.insert(self.bot.clone());
update_listener.hint_allowed_updates(&mut self.allowed_updates.clone().into_iter());
let description = self.handler.description();
let allowed_updates = description.allowed_updates();
log::debug!("hinting allowed updates: {:?}", allowed_updates);
update_listener.hint_allowed_updates(&mut allowed_updates.into_iter());
let shutdown_check_timeout = shutdown_check_timeout_for(&update_listener);
let mut stop_token = Some(update_listener.stop_token());

View file

@ -1,10 +1,14 @@
#![allow(clippy::redundant_closure_call)]
use dptree::{di::DependencyMap, Handler};
use teloxide_core::types::{Message, Update, UpdateKind};
use crate::{
dispatching::DpHandlerDescription,
types::{AllowedUpdate, Message, Update, UpdateKind},
};
macro_rules! define_ext {
($ext_name:ident, $for_ty:ty => $( ($func:ident, $proj_fn:expr, $fn_doc:expr) ,)*) => {
($ext_name:ident, $for_ty:ty => $( ($func:ident, $proj_fn:expr, $fn_doc:expr $(, $Allowed:ident)? ) ,)*) => {
#[doc = concat!("Filter methods for [`", stringify!($for_ty), "`].")]
pub trait $ext_name<Out>: private::Sealed {
$( define_ext!(@sig $func, $fn_doc); )*
@ -14,17 +18,25 @@ macro_rules! define_ext {
where
Out: Send + Sync + 'static,
{
$( define_ext!(@impl $for_ty, $func, $proj_fn); )*
$( define_ext!(@impl $for_ty, $func, $proj_fn $(, $Allowed )? ); )*
}
};
(@sig $func:ident, $fn_doc:expr) => {
#[doc = $fn_doc]
fn $func() -> Handler<'static, DependencyMap, Out>;
fn $func() -> Handler<'static, DependencyMap, Out, DpHandlerDescription>;
};
(@impl $for_ty:ty, $func:ident, $proj_fn:expr, $Allowed:ident) => {
fn $func() -> Handler<'static, DependencyMap, Out, DpHandlerDescription> {
dptree::filter_map_with_description(DpHandlerDescription::of(AllowedUpdate::$Allowed), move |input: $for_ty| {
$proj_fn(input)
})
}
};
(@impl $for_ty:ty, $func:ident, $proj_fn:expr) => {
fn $func() -> Handler<'static, DependencyMap, Out> {
fn $func() -> Handler<'static, DependencyMap, Out, DpHandlerDescription> {
dptree::filter_map(move |input: $for_ty| {
$proj_fn(input)
})
@ -75,7 +87,7 @@ define_message_ext! {
}
macro_rules! define_update_ext {
($( ($func:ident, $kind:path) ,)*) => {
($( ($func:ident, $kind:path, $Allowed:ident) ,)*) => {
define_ext! {
UpdateFilterExt, crate::types::Update =>
$((
@ -84,7 +96,8 @@ macro_rules! define_update_ext {
$kind(x) => Some(x),
_ => None,
},
concat!("Filters out [`crate::types::", stringify!($kind), "`] objects.")
concat!("Filters out [`crate::types::", stringify!($kind), "`] objects."),
$Allowed
),)*
}
}
@ -92,17 +105,17 @@ macro_rules! define_update_ext {
// May be expanded in the future.
define_update_ext! {
(filter_message, UpdateKind::Message),
(filter_edited_message, UpdateKind::EditedMessage),
(filter_channel_post, UpdateKind::ChannelPost),
(filter_edited_channel_post, UpdateKind::EditedChannelPost),
(filter_inline_query, UpdateKind::InlineQuery),
(filter_chosen_inline_result, UpdateKind::ChosenInlineResult),
(filter_callback_query, UpdateKind::CallbackQuery),
(filter_shipping_query, UpdateKind::ShippingQuery),
(filter_pre_checkout_query, UpdateKind::PreCheckoutQuery),
(filter_poll, UpdateKind::Poll),
(filter_poll_answer, UpdateKind::PollAnswer),
(filter_my_chat_member, UpdateKind::MyChatMember),
(filter_chat_member, UpdateKind::ChatMember),
(filter_message, UpdateKind::Message, Message),
(filter_edited_message, UpdateKind::EditedMessage, EditedMessage),
(filter_channel_post, UpdateKind::ChannelPost, ChannelPost),
(filter_edited_channel_post, UpdateKind::EditedChannelPost, EditedChannelPost),
(filter_inline_query, UpdateKind::InlineQuery, InlineQuery),
(filter_chosen_inline_result, UpdateKind::ChosenInlineResult, ChosenInlineResult),
(filter_callback_query, UpdateKind::CallbackQuery, CallbackQuery),
(filter_shipping_query, UpdateKind::ShippingQuery, ShippingQuery),
(filter_pre_checkout_query, UpdateKind::PreCheckoutQuery, PreCheckoutQuery),
(filter_poll, UpdateKind::Poll, Poll),
(filter_poll_answer, UpdateKind::PollAnswer, PollAnswer),
(filter_my_chat_member, UpdateKind::MyChatMember, MyChatMember),
(filter_chat_member, UpdateKind::ChatMember, ChatMember),
}

View file

@ -0,0 +1,61 @@
use std::collections::HashSet;
use dptree::{description::EventKind, HandlerDescription};
use teloxide_core::types::AllowedUpdate;
/// Handler description that is used by [`Dispatcher`].
///
/// [`Dispatcher`]: crate::dispatching::Dispatcher
pub struct DpHandlerDescription {
allowed: EventKind<AllowedUpdate>,
}
impl DpHandlerDescription {
pub(crate) fn of(allowed: AllowedUpdate) -> Self {
let mut set = HashSet::with_capacity(1);
set.insert(allowed);
Self { allowed: EventKind::InterestList(set) }
}
pub(crate) fn allowed_updates(&self) -> Vec<AllowedUpdate> {
use AllowedUpdate::*;
match &self.allowed {
EventKind::InterestList(set) => set.iter().copied().collect(),
EventKind::Entry => panic!("No updates were allowed"),
EventKind::UserDefined => vec![
Message,
EditedMessage,
ChannelPost,
EditedChannelPost,
InlineQuery,
ChosenInlineResult,
CallbackQuery,
ShippingQuery,
PreCheckoutQuery,
Poll,
PollAnswer,
MyChatMember,
ChatMember,
],
}
}
}
impl HandlerDescription for DpHandlerDescription {
fn entry() -> Self {
Self { allowed: HandlerDescription::entry() }
}
fn user_defined() -> Self {
Self { allowed: HandlerDescription::user_defined() }
}
fn merge_chain(&self, other: &Self) -> Self {
Self { allowed: self.allowed.merge_chain(&other.allowed) }
}
fn merge_branch(&self, other: &Self) -> Self {
Self { allowed: self.allowed.merge_branch(&other.allowed) }
}
}

View file

@ -1,7 +1,10 @@
use std::sync::Arc;
use crate::{
dispatching::dialogue::{Dialogue, GetChatId, Storage},
dispatching::{
dialogue::{Dialogue, GetChatId, Storage},
DpHandlerDescription,
},
types::{Me, Message},
utils::command::BotCommands,
};
@ -58,7 +61,7 @@ pub trait HandlerExt<Output> {
F: HandlerFactory<Out = Output>;
}
impl<Output> HandlerExt<Output> for Handler<'static, DependencyMap, Output>
impl<Output> HandlerExt<Output> for Handler<'static, DependencyMap, Output, DpHandlerDescription>
where
Output: Send + Sync + 'static,
{

View file

@ -1,9 +1,11 @@
use dptree::{di::DependencyMap, Handler};
use crate::dispatching::DpHandlerDescription;
/// Something that can construct a handler.
#[deprecated(note = "Use the teloxide::handler! API")]
pub trait HandlerFactory {
type Out;
fn handler() -> Handler<'static, DependencyMap, Self::Out>;
fn handler() -> Handler<'static, DependencyMap, Self::Out, DpHandlerDescription>;
}

View file

@ -102,6 +102,7 @@ pub mod dialogue;
mod dispatcher;
mod distribution;
mod filter_ext;
mod handler_description;
mod handler_ext;
mod handler_factory;
pub mod stop_token;
@ -111,6 +112,7 @@ pub use crate::utils::shutdown_token::{IdleShutdownError, ShutdownToken};
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;
#[allow(deprecated)]
pub use handler_factory::HandlerFactory;

View file

@ -83,7 +83,7 @@ pub async fn commands_repl_with_listener<'a, R, Cmd, H, L, ListenerE, E, Args>(
Dispatcher::builder(
bot,
Update::filter_message().filter_command::<Cmd>().branch(dptree::endpoint(handler)),
Update::filter_message().filter_command::<Cmd>().chain(dptree::endpoint(handler)),
)
.default_handler(ignore_update)
.build()

View file

@ -59,7 +59,7 @@ where
// messages. See <https://github.com/teloxide/teloxide/issues/557>.
let ignore_update = |_upd| Box::pin(async {});
Dispatcher::builder(bot, Update::filter_message().branch(dptree::endpoint(handler)))
Dispatcher::builder(bot, Update::filter_message().chain(dptree::endpoint(handler)))
.default_handler(ignore_update)
.build()
.setup_ctrlc_handler()

View file

@ -1,62 +1,62 @@
#[cfg(feature = "macros")]
use teloxide::macros::DialogueState;
// We put tests here because macro expand in unit tests in the crate was a
// failure
// #[cfg(feature = "macros")]
// use teloxide::macros::DialogueState;
// // We put tests here because macro expand in unit tests in the crate was a
// // failure
#[test]
#[cfg(feature = "macros")]
fn compile_test() {
#[allow(dead_code)]
#[derive(DialogueState, Clone)]
#[handler_out(Result<(), teloxide::RequestError>)]
enum State {
#[handler(handle_start)]
Start,
// #[test]
// #[cfg(feature = "macros")]
// fn compile_test() {
// #[allow(dead_code)]
// #[derive(DialogueState, Clone)]
// #[handler_out(Result<(), teloxide::RequestError>)]
// enum State {
// #[handler(handle_start)]
// Start,
#[handler(handle_have_data)]
HaveData(String),
}
// #[handler(handle_have_data)]
// HaveData(String),
// }
impl Default for State {
fn default() -> Self {
Self::Start
}
}
// impl Default for State {
// fn default() -> Self {
// Self::Start
// }
// }
async fn handle_start() -> Result<(), teloxide::RequestError> {
Ok(())
}
// async fn handle_start() -> Result<(), teloxide::RequestError> {
// Ok(())
// }
async fn handle_have_data() -> Result<(), teloxide::RequestError> {
Ok(())
}
}
// async fn handle_have_data() -> Result<(), teloxide::RequestError> {
// Ok(())
// }
// }
#[test]
#[cfg(feature = "macros")]
fn compile_test_generics() {
#[allow(dead_code)]
#[derive(DialogueState, Clone)]
#[handler_out(Result<(), teloxide::RequestError>)]
enum State<X: Clone + Send + Sync + 'static> {
#[handler(handle_start)]
Start,
// #[test]
// #[cfg(feature = "macros")]
// fn compile_test_generics() {
// #[allow(dead_code)]
// #[derive(DialogueState, Clone)]
// #[handler_out(Result<(), teloxide::RequestError>)]
// enum State<X: Clone + Send + Sync + 'static> {
// #[handler(handle_start)]
// Start,
#[handler(handle_have_data)]
HaveData(X),
}
// #[handler(handle_have_data)]
// HaveData(X),
// }
impl<X: Clone + Send + Sync + 'static> Default for State<X> {
fn default() -> Self {
Self::Start
}
}
// impl<X: Clone + Send + Sync + 'static> Default for State<X> {
// fn default() -> Self {
// Self::Start
// }
// }
async fn handle_start() -> Result<(), teloxide::RequestError> {
Ok(())
}
// async fn handle_start() -> Result<(), teloxide::RequestError> {
// Ok(())
// }
async fn handle_have_data() -> Result<(), teloxide::RequestError> {
Ok(())
}
}
// async fn handle_have_data() -> Result<(), teloxide::RequestError> {
// Ok(())
// }
// }