diff --git a/src/discord/commands.rs b/src/discord/commands.rs index 702a672..a5e5a16 100644 --- a/src/discord/commands.rs +++ b/src/discord/commands.rs @@ -167,7 +167,7 @@ async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { let data = ctx.data.read().await; let global_data = data.get::().unwrap(); - let global_data = global_data.lock().await; + let mut global_data = global_data.lock().await; let msg = WoxlfMessage::default() .source(MessageSource::Host) @@ -176,7 +176,7 @@ async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { .content(args.rest()) .clone(); - dispatch_message(ctx, &global_data, msg).await?; + dispatch_message(ctx, &mut global_data, msg).await?; Ok(()) } @@ -188,7 +188,7 @@ async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { let data = ctx.data.read().await; let global_data = data.get::().unwrap(); - let global_data = global_data.lock().await; + let mut global_data = global_data.lock().await; let broadcast = global_data .templates()? @@ -201,7 +201,7 @@ async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { .median(Median::Webhook) .clone(); - dispatch_message(ctx, &global_data, woxlf_msg).await?; + dispatch_message(ctx, &mut global_data, woxlf_msg).await?; Ok(()) } @@ -210,8 +210,8 @@ async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { #[only_in(guilds)] #[allowed_roles("wolfx host")] async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { - let mut data = ctx.data.write().await; - let global_data = data.get_mut::().unwrap(); + let data = ctx.data.read().await; + let global_data = data.get::().unwrap(); let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -237,7 +237,7 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu .content(&broadcast) .clone(); - dispatch_message(ctx, &global_data, woxlf_msg).await?; + dispatch_message(ctx, &mut global_data, woxlf_msg).await?; if global_data.game_state_mut()?.current_phase == Phase::Day { let vote_channel = guild @@ -303,8 +303,8 @@ async fn kill(ctx: &Context, msg: &Message, args: Args) -> CommandResult { #[only_in(guilds)] #[allowed_roles("wolfx host")] async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { - let mut data = ctx.data.write().await; - let global_data = data.get_mut::().unwrap(); + let data = ctx.data.read().await; + let global_data = data.get::().unwrap(); let mut global_data = global_data.lock().await; @@ -335,7 +335,7 @@ async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult .content(&broadcast) .clone(); - dispatch_message(ctx, &global_data, woxlf_msg).await?; + dispatch_message(ctx, &mut global_data, woxlf_msg).await?; msg.reply(&ctx.http, "Phase has been updated") .await @@ -644,7 +644,7 @@ async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult } else { let data = ctx.data.read().await; let global_data = data.get::().unwrap(); - let global_data = global_data.lock().await; + let mut global_data = global_data.lock().await; let target = args.single::()?; let pm = args.rest(); @@ -674,7 +674,7 @@ async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult .content(pm) .clone(); - dispatch_message(ctx, &global_data, woxlf_msg).await?; + dispatch_message(ctx, &mut global_data, woxlf_msg).await?; } else { msg.reply( &ctx.http, diff --git a/src/discord/event_handler.rs b/src/discord/event_handler.rs index 7f294f3..cc38bdf 100644 --- a/src/discord/event_handler.rs +++ b/src/discord/event_handler.rs @@ -26,7 +26,7 @@ impl EventHandler for Handler { let global_data = data.get::().unwrap(); - let global_data = global_data.lock().await; + let mut global_data = global_data.lock().await; if global_data.game_state.is_none() { // no game in progress @@ -75,7 +75,7 @@ impl EventHandler for Handler { .attachments(attachments) .clone(); - dispatch_message(&ctx, &global_data, woxlf_msg) + dispatch_message(&ctx, &mut global_data, woxlf_msg) .await .expect("Unable to send message to players"); } diff --git a/src/game/listener/host_snooper.rs b/src/game/listener/host_snooper.rs new file mode 100644 index 0000000..83d5281 --- /dev/null +++ b/src/game/listener/host_snooper.rs @@ -0,0 +1,29 @@ +use crate::game::listener::{EventStatus, Listener, ListenerContext, Priority}; +use crate::game::message_router::{send_to_host_channel, WoxlfMessage}; +use serenity::async_trait; +use serenity::model::prelude::GuildId; +use std::fmt::Debug; + +#[derive(Debug)] +pub struct HostSnooper {} + +#[async_trait] +impl Listener for HostSnooper { + fn get_priority(&self) -> Priority { + Priority::Logging + } + + async fn on_chat( + &mut self, + ctx: &mut ListenerContext, + msg: &WoxlfMessage, + ) -> crate::error::Result { + let guild_id = ctx.data.cfg.discord_config.guild_id; + let guild = GuildId::from(guild_id) + .to_guild_cached(&ctx.ctx.cache) + .unwrap(); + + send_to_host_channel(&ctx.ctx.http, &guild, ctx.data, msg.clone()).await?; + Ok(EventStatus::Okay) + } +} diff --git a/src/game/listener/mod.rs b/src/game/listener/mod.rs new file mode 100644 index 0000000..7b13338 --- /dev/null +++ b/src/game/listener/mod.rs @@ -0,0 +1,103 @@ +pub mod host_snooper; + +use crate::error::Result; +use crate::game::global_data::GlobalData; +use crate::game::listener::host_snooper::HostSnooper; +use crate::game::message_router::WoxlfMessage; +use serenity::async_trait; +use serenity::client::Context; +use serenity::prelude::TypeMapKey; +use std::fmt::Debug; +use std::sync::Arc; +use tokio::sync::Mutex; + +#[derive(Debug)] +pub struct Listeners { + listeners: Vec>, +} + +impl Listeners { + pub async fn process_event( + &mut self, + ctx: &Context, + data: &mut GlobalData, + event: WoxlfEvent<'_>, + ) -> Result { + let mut listener_ctx = ListenerContext { data, ctx }; + + for listener in &mut self.listeners { + let status = match &event { + WoxlfEvent::Chat(msg) => listener.on_chat(&mut listener_ctx, msg).await?, + }; + + if status == EventStatus::Canceled { + return Ok(status); + } + } + + Ok(EventStatus::Okay) + } + + pub fn add_listener(&mut self, listener: Box) { + self.listeners.push(listener); + + self.listeners.sort_by_key(|l1| l1.get_priority()); + } +} + +impl Default for Listeners { + fn default() -> Self { + let mut listeners = Self { listeners: vec![] }; + + // Add default listeners here + listeners.add_listener(Box::new(HostSnooper {})); + + listeners + } +} + +impl TypeMapKey for Listeners { + type Value = Arc>; +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum EventStatus { + Okay, + Canceled, +} + +#[derive(Debug, Clone)] +pub enum WoxlfEvent<'a> { + Chat(WoxlfMessage<'a>), +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] +#[allow(dead_code)] +pub enum Priority { + Highest, + High, + Medium, + Low, + Lowest, + Logging, +} + +pub struct ListenerContext<'a> { + data: &'a GlobalData, + ctx: &'a Context, +} + +#[async_trait] +pub trait Listener: Debug + Send + Sync { + fn get_priority(&self) -> Priority { + Priority::Medium + } + + async fn on_chat( + &mut self, + _ctx: &mut ListenerContext, + _msg: &WoxlfMessage, + ) -> Result { + return Ok(EventStatus::Okay); + } +} diff --git a/src/game/message_router.rs b/src/game/message_router.rs index 0892f7c..84cec72 100644 --- a/src/game/message_router.rs +++ b/src/game/message_router.rs @@ -1,13 +1,14 @@ use crate::error; use crate::error::WoxlfError; use crate::game::global_data::GlobalData; +use crate::game::listener::{EventStatus, Listeners, WoxlfEvent}; use crate::game::player_data::PlayerData; use bitflags::bitflags; use serenity::client::Context; use serenity::http::{CacheHttp, Http}; use serenity::model::guild::Guild; use serenity::model::id::{ChannelId, UserId}; -use serenity::model::prelude::{AttachmentType, GuildId, WebhookId}; +use serenity::model::prelude::{AttachmentType, WebhookId}; use serenity::utils::MessageBuilder; #[derive(Debug, Clone)] @@ -190,7 +191,7 @@ async fn send_private_message( Ok(()) } -async fn send_to_host_channel( +pub async fn send_to_host_channel( http: &Http, guild: &Guild, global_data: &GlobalData, @@ -284,11 +285,20 @@ pub async fn send_message( pub async fn dispatch_message( ctx: &Context, - global_data: &GlobalData, + global_data: &mut GlobalData, msg: WoxlfMessage<'_>, ) -> error::Result<()> { - let guild_id = global_data.cfg.discord_config.guild_id; - let guild = GuildId::from(guild_id).to_guild_cached(&ctx.cache).unwrap(); + let data = ctx.data.read().await; + let listeners = data.get::().unwrap(); + let mut listeners = listeners.lock().await; + + if listeners + .process_event(ctx, global_data, WoxlfEvent::Chat(msg.clone())) + .await? + == EventStatus::Canceled + { + return Ok(()); + }; let msg_tasks = global_data .game_state()? @@ -309,7 +319,5 @@ pub async fn dispatch_message( results?; - send_to_host_channel(&ctx.http, &guild, global_data, msg).await?; - Ok(()) } diff --git a/src/game/mod.rs b/src/game/mod.rs index 2092255..6107fd7 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; pub mod game_state; pub mod global_data; +pub mod listener; pub mod message_router; pub mod player_data; diff --git a/src/main.rs b/src/main.rs index 0c749b6..eb13fef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use discord::event_handler::Handler; use game::global_data::GlobalData; use crate::config::{Args, BotConfig}; +use crate::game::listener::Listeners; mod config; mod discord; @@ -35,6 +36,7 @@ async fn main() { .event_handler(Handler {}) .framework(command_framework()) .type_map_insert::(Arc::new(Mutex::new(global_data))) + .type_map_insert::(Arc::new(Mutex::new(Listeners::default()))) .await .expect("Err creating client");