Add custom raw event handler with callbacks

Signed-off-by: trivernis <trivernis@protonmail.com>
main
trivernis 3 years ago
parent 7fc99c574f
commit e687477877
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

2
Cargo.lock generated

@ -1033,7 +1033,7 @@ dependencies = [
[[package]]
name = "serenity-rich-interaction"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"futures",
"log",

@ -1,6 +1,6 @@
[package]
name = "serenity-rich-interaction"
version = "0.1.0"
version = "0.2.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
description = "Menus and self deleting messages for the serenity discord framework"

@ -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<EventDrivenMessagesRef> {
let data = ctx.data.read().await;
let listeners = data
.get::<EventDrivenMessageContainer>()
.ok_or(Error::Uninitialized)?;
log::trace!("Returning listener");
Ok(listeners.clone())
}

@ -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<T> {
inner: Arc<
dyn for<'a> Fn(
&'a Context,
&'a T,
) -> Pin<Box<(dyn Future<Output = Result<()>> + Send + 'a)>>
+ Send
+ Sync,
>,
}
impl<T> EventCallback<T> {
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<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
}
impl RichEventHandler {
/// Handles a generic event
async fn handle_event<T: 'static>(&self, ctx: &Context, value: T) {
if let Some(callbacks) = self.callbacks.get(&TypeId::of::<T>()) {
for callback in callbacks {
if let Some(cb) = callback.downcast_ref::<EventCallback<T>>() {
if let Err(e) = cb.run(ctx, &value).await {
log::error!("Error in event callback: {:?}", e);
}
}
}
}
}
pub fn add_event<T: 'static, F: 'static>(&mut self, cb: F) -> &mut Self
where
F: for<'a> Fn(
&'a Context,
&'a T,
) -> Pin<Box<(dyn Future<Output = Result<()>> + Send + 'a)>>
+ Send
+ Sync,
{
let type_id = TypeId::of::<T>();
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,
_ => {}
}
}
}

@ -0,0 +1,3 @@
pub mod event_callbacks;
mod handler;
pub use handler::*;

@ -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");

@ -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<Mutex<HashMap<MessageHandle, MessageRef>>>
impl TypeMapKey for EventDrivenMessageContainer {
type Value = EventDrivenMessagesRef;
}
pub async fn get_listeners_from_context(ctx: &Context) -> Result<EventDrivenMessagesRef> {
let data = ctx.data.read().await;
let listeners = data
.get::<EventDrivenMessageContainer>()
.ok_or(Error::Uninitialized)?;
log::trace!("Returning listener");
Ok(listeners.clone())
}

@ -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;

@ -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;

Loading…
Cancel
Save