mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-31 16:40:37 +01:00
Merge pull request #587 from teloxide/allowed_updates_dp2
implement allowed updates in dispatcher
This commit is contained in:
commit
7186eddb75
11 changed files with 180 additions and 97 deletions
|
@ -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"
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
61
src/dispatching/handler_description.rs
Normal file
61
src/dispatching/handler_description.rs
Normal 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) }
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
{
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(())
|
||||
// }
|
||||
// }
|
||||
|
|
Loading…
Reference in a new issue