From e6874778775c616cf06412e537e01918abf41294 Mon Sep 17 00:00:00 2001 From: trivernis Date: Thu, 22 Apr 2021 12:07:05 +0200 Subject: [PATCH] Add custom raw event handler with callbacks Signed-off-by: trivernis --- Cargo.lock | 2 +- Cargo.toml | 2 +- .../event_callbacks.rs} | 17 +- src/events/handler.rs | 153 ++++++++++++++++++ src/events/mod.rs | 3 + src/lib.rs | 2 +- src/menu/container.rs | 12 ++ src/menu/controls.rs | 3 +- src/menu/menu.rs | 3 +- 9 files changed, 178 insertions(+), 19 deletions(-) rename src/{event_handlers.rs => events/event_callbacks.rs} (91%) create mode 100644 src/events/handler.rs create mode 100644 src/events/mod.rs diff --git a/Cargo.lock b/Cargo.lock index eaa10a6..c4c0772 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,7 +1033,7 @@ dependencies = [ [[package]] name = "serenity-rich-interaction" -version = "0.1.0" +version = "0.2.0" dependencies = [ "futures", "log", diff --git a/Cargo.toml b/Cargo.toml index 058571b..a5875be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serenity-rich-interaction" -version = "0.1.0" +version = "0.2.0" authors = ["trivernis "] edition = "2018" description = "Menus and self deleting messages for the serenity discord framework" diff --git a/src/event_handlers.rs b/src/events/event_callbacks.rs similarity index 91% rename from src/event_handlers.rs rename to src/events/event_callbacks.rs index dadc40c..360a4c9 100644 --- a/src/event_handlers.rs +++ b/src/events/event_callbacks.rs @@ -1,6 +1,6 @@ use crate::core::MessageHandle; -use crate::menu::{EventDrivenMessageContainer, EventDrivenMessagesRef, MessageRef}; -use crate::{Error, Result}; +use crate::menu::{get_listeners_from_context, MessageRef}; +use crate::Result; use serenity::client::Context; use serenity::model::channel::Reaction; use serenity::model::id::{ChannelId, MessageId}; @@ -10,7 +10,7 @@ use tokio::time::Duration; static UPDATE_INTERVAL_SECS: u64 = 5; /// Starts the loop to handle message updates -pub async fn start_update_loop(ctx: &Context) { +pub async fn start_update_loop(ctx: &Context) -> Result<()> { let event_messages = get_listeners_from_context(ctx) .await .expect("Failed to get event message container"); @@ -51,6 +51,8 @@ pub async fn start_update_loop(ctx: &Context) { tokio::time::sleep(Duration::from_secs(UPDATE_INTERVAL_SECS)).await; } }); + + Ok(()) } /// To be fired from the serenity handler when a message was deleted @@ -158,12 +160,3 @@ pub async fn handle_reaction_remove(ctx: &Context, reaction: &Reaction) -> Resul Ok(()) } - -pub async fn get_listeners_from_context(ctx: &Context) -> Result { - let data = ctx.data.read().await; - let listeners = data - .get::() - .ok_or(Error::Uninitialized)?; - log::trace!("Returning listener"); - Ok(listeners.clone()) -} diff --git a/src/events/handler.rs b/src/events/handler.rs new file mode 100644 index 0000000..b941412 --- /dev/null +++ b/src/events/handler.rs @@ -0,0 +1,153 @@ +use crate::events::event_callbacks; +use crate::Result; +use serenity::async_trait; +use serenity::client::{Context, RawEventHandler}; +use serenity::model::event; +use serenity::model::event::Event; +use std::any::{Any, TypeId}; +use std::collections::HashMap; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; + +pub struct EventCallback { + inner: Arc< + dyn for<'a> Fn( + &'a Context, + &'a T, + ) -> Pin> + Send + 'a)>> + + Send + + Sync, + >, +} + +impl EventCallback { + pub async fn run(&self, ctx: &Context, arg: &T) -> Result<()> { + self.inner.clone()(ctx, arg).await?; + Ok(()) + } +} + +/// A handler for raw serenity events +pub struct RichEventHandler { + callbacks: HashMap>>, +} + +impl RichEventHandler { + /// Handles a generic event + async fn handle_event(&self, ctx: &Context, value: T) { + if let Some(callbacks) = self.callbacks.get(&TypeId::of::()) { + for callback in callbacks { + if let Some(cb) = callback.downcast_ref::>() { + if let Err(e) = cb.run(ctx, &value).await { + log::error!("Error in event callback: {:?}", e); + } + } + } + } + } + + pub fn add_event(&mut self, cb: F) -> &mut Self + where + F: for<'a> Fn( + &'a Context, + &'a T, + ) -> Pin> + Send + 'a)>> + + Send + + Sync, + { + let type_id = TypeId::of::(); + let callbacks = if let Some(cbs) = self.callbacks.get_mut(&type_id) { + cbs + } else { + self.callbacks.insert(type_id, Vec::new()); + self.callbacks.get_mut(&type_id).unwrap() + }; + callbacks.push(Box::new(EventCallback { + inner: Arc::new(cb), + })); + + self + } +} + +impl Default for RichEventHandler { + fn default() -> Self { + let mut handler = Self { + callbacks: Default::default(), + }; + handler + .add_event(|ctx, _: &event::ReadyEvent| { + Box::pin(event_callbacks::start_update_loop(ctx)) + }) + .add_event(|ctx, e: &event::ReactionAddEvent| { + Box::pin(event_callbacks::handle_reaction_add(ctx, &e.reaction)) + }) + .add_event(|ctx, e: &event::ReactionRemoveEvent| { + Box::pin(event_callbacks::handle_reaction_remove(ctx, &e.reaction)) + }) + .add_event(|ctx, e: &event::MessageDeleteEvent| { + Box::pin(event_callbacks::handle_message_delete( + ctx, + e.channel_id, + e.message_id, + )) + }) + .add_event(|ctx, e: &event::MessageDeleteBulkEvent| { + Box::pin(event_callbacks::handle_message_delete_bulk( + ctx, + e.channel_id, + &e.ids, + )) + }); + + handler + } +} + +#[async_trait] +impl RawEventHandler for RichEventHandler { + async fn raw_event(&self, ctx: Context, event: Event) { + match event { + Event::ChannelCreate(e) => self.handle_event(&ctx, e).await, + Event::ChannelDelete(e) => self.handle_event(&ctx, e).await, + Event::ChannelPinsUpdate(e) => self.handle_event(&ctx, e).await, + Event::ChannelUpdate(e) => self.handle_event(&ctx, e).await, + Event::GuildBanAdd(e) => self.handle_event(&ctx, e).await, + Event::GuildBanRemove(e) => self.handle_event(&ctx, e).await, + Event::GuildCreate(e) => self.handle_event(&ctx, e).await, + Event::GuildDelete(e) => self.handle_event(&ctx, e).await, + Event::GuildEmojisUpdate(e) => self.handle_event(&ctx, e).await, + Event::GuildIntegrationsUpdate(e) => self.handle_event(&ctx, e).await, + Event::GuildMemberAdd(e) => self.handle_event(&ctx, e).await, + Event::GuildMemberRemove(e) => self.handle_event(&ctx, e).await, + Event::GuildMemberUpdate(e) => self.handle_event(&ctx, e).await, + Event::GuildMembersChunk(e) => self.handle_event(&ctx, e).await, + Event::GuildRoleCreate(e) => self.handle_event(&ctx, e).await, + Event::GuildRoleDelete(e) => self.handle_event(&ctx, e).await, + Event::GuildRoleUpdate(e) => self.handle_event(&ctx, e).await, + Event::GuildUnavailable(e) => self.handle_event(&ctx, e).await, + Event::GuildUpdate(e) => self.handle_event(&ctx, e).await, + Event::InviteCreate(e) => self.handle_event(&ctx, e).await, + Event::InviteDelete(e) => self.handle_event(&ctx, e).await, + Event::MessageCreate(e) => self.handle_event(&ctx, e).await, + Event::MessageDelete(e) => self.handle_event(&ctx, e).await, + Event::MessageDeleteBulk(e) => self.handle_event(&ctx, e).await, + Event::MessageUpdate(e) => self.handle_event(&ctx, e).await, + Event::PresenceUpdate(e) => self.handle_event(&ctx, e).await, + Event::PresencesReplace(e) => self.handle_event(&ctx, e).await, + Event::ReactionAdd(e) => self.handle_event(&ctx, e).await, + Event::ReactionRemove(e) => self.handle_event(&ctx, e).await, + Event::ReactionRemoveAll(e) => self.handle_event(&ctx, e).await, + Event::Ready(e) => self.handle_event(&ctx, e).await, + Event::Resumed(e) => self.handle_event(&ctx, e).await, + Event::TypingStart(e) => self.handle_event(&ctx, e).await, + Event::UserUpdate(e) => self.handle_event(&ctx, e).await, + Event::VoiceStateUpdate(e) => self.handle_event(&ctx, e).await, + Event::VoiceServerUpdate(e) => self.handle_event(&ctx, e).await, + Event::WebhookUpdate(e) => self.handle_event(&ctx, e).await, + Event::Unknown(e) => self.handle_event(&ctx, e).await, + _ => {} + } + } +} diff --git a/src/events/mod.rs b/src/events/mod.rs new file mode 100644 index 0000000..e300a19 --- /dev/null +++ b/src/events/mod.rs @@ -0,0 +1,3 @@ +pub mod event_callbacks; +mod handler; +pub use handler::*; diff --git a/src/lib.rs b/src/lib.rs index 0617f35..5f345fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ pub mod core; pub mod ephemeral_message; mod error; -pub mod event_handlers; +pub mod events; pub mod menu; pub static VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/src/menu/container.rs b/src/menu/container.rs index 53c3854..bf1957c 100644 --- a/src/menu/container.rs +++ b/src/menu/container.rs @@ -1,4 +1,7 @@ use crate::core::{BoxedEventDrivenMessage, MessageHandle}; +use crate::Error; +use crate::Result; +use serenity::client::Context; use serenity::prelude::TypeMapKey; use std::collections::HashMap; use std::sync::Arc; @@ -12,3 +15,12 @@ pub type EventDrivenMessagesRef = Arc>> impl TypeMapKey for EventDrivenMessageContainer { type Value = EventDrivenMessagesRef; } + +pub async fn get_listeners_from_context(ctx: &Context) -> Result { + let data = ctx.data.read().await; + let listeners = data + .get::() + .ok_or(Error::Uninitialized)?; + log::trace!("Returning listener"); + Ok(listeners.clone()) +} diff --git a/src/menu/controls.rs b/src/menu/controls.rs index f82eced..2d36146 100644 --- a/src/menu/controls.rs +++ b/src/menu/controls.rs @@ -1,8 +1,7 @@ use crate::error::{Error, Result}; -use crate::event_handlers::get_listeners_from_context; use crate::menu::menu::Menu; use crate::menu::typedata::HelpActiveContainer; -use crate::menu::ActionContainer; +use crate::menu::{get_listeners_from_context, ActionContainer}; use serde_json::json; use serde_json::Value; use serenity::client::Context; diff --git a/src/menu/menu.rs b/src/menu/menu.rs index 736c833..3147f15 100644 --- a/src/menu/menu.rs +++ b/src/menu/menu.rs @@ -1,10 +1,9 @@ use crate::core::MessageHandle; use crate::error::{Error, Result}; -use crate::event_handlers::get_listeners_from_context; use crate::menu::controls::{close_menu, next_page, previous_page, toggle_help}; use crate::menu::traits::EventDrivenMessage; use crate::menu::typedata::HelpActiveContainer; -use crate::menu::{EventDrivenMessagesRef, Page}; +use crate::menu::{get_listeners_from_context, EventDrivenMessagesRef, Page}; use futures::FutureExt; use serenity::async_trait; use serenity::client::Context;