Prelude/Main: rework hook system

This commit is contained in:
Natsu Kagami 2020-09-07 19:41:59 -04:00
parent b819509244
commit 238e44a64c
Signed by: nki
GPG key ID: 73376E117CD20735
5 changed files with 96 additions and 47 deletions

View file

@ -25,7 +25,11 @@ lazy_static! {
).unwrap(); ).unwrap();
} }
pub async fn hook(ctx: &Context, msg: &Message) -> Result<()> { pub fn hook<'a>(
ctx: &'a Context,
msg: &'a Message,
) -> std::pin::Pin<Box<dyn future::Future<Output = Result<()>> + Send + 'a>> {
Box::pin(async move {
if msg.author.bot { if msg.author.bot {
return Ok(()); return Ok(());
} }
@ -68,6 +72,7 @@ pub async fn hook(ctx: &Context, msg: &Message) -> Result<()> {
super::cache::save_beatmap(&*ctx.data.read().await, msg.channel_id, &t)?; super::cache::save_beatmap(&*ctx.data.read().await, msg.channel_id, &t)?;
} }
Ok(()) Ok(())
})
} }
enum EmbedType { enum EmbedType {

View file

@ -49,7 +49,7 @@ impl TypeMapKey for OsuClient {
/// - Commands on the "osu" prefix /// - Commands on the "osu" prefix
/// - Hooks. Hooks are completely opt-in. /// - Hooks. Hooks are completely opt-in.
/// ///
pub async fn setup( pub fn setup(
path: &std::path::Path, path: &std::path::Path,
data: &mut TypeMap, data: &mut TypeMap,
announcers: &mut AnnouncerHandler, announcers: &mut AnnouncerHandler,

View file

@ -0,0 +1,24 @@
use crate::{async_trait, future, Context, Result};
use serenity::model::channel::Message;
/// Hook represents the asynchronous hook that is run on every message.
#[async_trait]
pub trait Hook: Send + Sync {
async fn call(&mut self, ctx: &Context, message: &Message) -> Result<()>;
}
#[async_trait]
impl<T> Hook for T
where
T: for<'a> FnMut(
&'a Context,
&'a Message,
)
-> std::pin::Pin<Box<dyn future::Future<Output = Result<()>> + 'a + Send>>
+ Send
+ Sync,
{
async fn call(&mut self, ctx: &Context, message: &Message) -> Result<()> {
self(ctx, message).await
}
}

View file

@ -5,11 +5,13 @@ use std::sync::Arc;
pub mod announcer; pub mod announcer;
pub mod args; pub mod args;
pub mod hook;
pub mod pagination; pub mod pagination;
pub mod setup; pub mod setup;
pub use announcer::{Announcer, AnnouncerHandler}; pub use announcer::{Announcer, AnnouncerHandler};
pub use args::{Duration, UsernameArg}; pub use args::{Duration, UsernameArg};
pub use hook::Hook;
pub use pagination::paginate; pub use pagination::paginate;
/// Re-exporting async_trait helps with implementing Announcer. /// Re-exporting async_trait helps with implementing Announcer.

View file

@ -13,13 +13,17 @@ use serenity::{
use youmubot_prelude::*; use youmubot_prelude::*;
struct Handler { struct Handler {
hooks: Vec<fn(&mut Context, &Message) -> ()>, hooks: Vec<RwLock<Box<dyn Hook>>>,
} }
impl Handler { impl Handler {
fn new() -> Handler { fn new() -> Handler {
Handler { hooks: vec![] } Handler { hooks: vec![] }
} }
fn push_hook<T: Hook + 'static>(&mut self, f: T) {
self.hooks.push(RwLock::new(Box::new(f)));
}
} }
#[async_trait] #[async_trait]
@ -28,8 +32,22 @@ impl EventHandler for Handler {
println!("{} is connected!", ready.user.name); println!("{} is connected!", ready.user.name);
} }
async fn message(&self, mut ctx: Context, message: Message) { async fn message(&self, ctx: Context, message: Message) {
self.hooks.iter().for_each(|f| f(&mut ctx, &message)); self.hooks
.iter()
.map(|hook| {
let ctx = ctx.clone();
let message = message.clone();
hook.write()
.then(|mut h| async move { h.call(&ctx, &message).await })
})
.collect::<stream::FuturesUnordered<_>>()
.for_each(|v| async move {
if let Err(e) = v {
eprintln!("{}", e)
}
})
.await;
} }
} }
@ -53,12 +71,12 @@ async fn main() {
println!("Loaded dotenv from {:?}", path); println!("Loaded dotenv from {:?}", path);
} }
let handler = Handler::new(); let mut handler = Handler::new();
// Set up hooks // Set up hooks
#[cfg(feature = "osu")] #[cfg(feature = "osu")]
handler.hooks.push(youmubot_osu::discord::hook); handler.push_hook(youmubot_osu::discord::hook);
#[cfg(feature = "codeforces")] #[cfg(feature = "codeforces")]
handler.hooks.push(youmubot_cf::codeforces_info_hook); handler.push_hook(youmubot_cf::codeforces_info_hook);
// Collect the token // Collect the token
let token = var("TOKEN").expect("Please set TOKEN as the Discord Bot's token to be used."); let token = var("TOKEN").expect("Please set TOKEN as the Discord Bot's token to be used.");