diff --git a/src/passive/desu.rs b/src/passive/desu.rs index b4e2cd8..727e87d 100644 --- a/src/passive/desu.rs +++ b/src/passive/desu.rs @@ -1,3 +1,5 @@ +use super::{PassiveResponder, PassiveResponse}; +use crate::util::debug_say_owned; use futures::future::BoxFuture; use lazy_static::lazy_static; use log::trace; @@ -5,9 +7,8 @@ use rand::seq::SliceRandom; use rand::thread_rng; use rand::Rng; use regex::Regex; -use serenity::async_trait; use serenity::model::channel::Message; -use serenity::prelude::{Context, EventHandler}; +use serenity::prelude::Context; lazy_static! { #[derive(Debug)] @@ -18,34 +19,41 @@ lazy_static! { pub(crate) struct DesuResponder; -#[async_trait] -impl EventHandler for DesuResponder { - async fn message(&self, ctx: Context, message: Message) { - let content = message.content_safe(ctx.clone()).await; - let fufufu_match = FUFUFU_REGEX.is_match(&content); - let desu_match = DESU_REGEX.is_match(&content) && thread_rng().gen::() < 0.1; +impl PassiveResponder for DesuResponder { + fn get_message( + &self, + received_msg: String, + ctx: Context, + message: Message, + ) -> Option> { + let fufufu_match = FUFUFU_REGEX.is_match(&received_msg); + let desu_match = DESU_REGEX.is_match(&received_msg) && thread_rng().gen::() < 0.1; if fufufu_match || desu_match { trace!( "Responding to `{}` because one of the following matched: fufufu={}, desu={}", - content, + received_msg, fufufu_match, desu_match ); - let random_action = - DESU_ACTIONS.choose(&mut thread_rng()).unwrap().clone()(ctx, message); - random_action.await.unwrap(); + + Some(DESU_ACTIONS.choose(&mut thread_rng()).unwrap().clone()( + ctx, message, + )) + } else { + None } } } type DesuAction<'r, T> = &'r (dyn Fn(Context, Message) -> T + Sync); -const DESU_ACTIONS: &[DesuAction>>] = &[ - &|ctx, msg| Box::pin(msg.channel_id.say(ctx, "です。")), - &|ctx, msg| Box::pin(msg.channel_id.say(ctx, "desu~")), - &|ctx, msg| Box::pin(msg.channel_id.say(ctx, "desu.")), +const DESU_ACTIONS: &[DesuAction, serenity::Error>>>] = &[ + &|ctx, msg| Box::pin(debug_say_owned(msg, ctx, "です。")), + &|ctx, msg| Box::pin(debug_say_owned(msg, ctx, "desu~")), + &|ctx, msg| Box::pin(debug_say_owned(msg, ctx, "desu.")), &|ctx, msg| { - Box::pin(msg.channel_id.say( + Box::pin(debug_say_owned( + msg, ctx, r#" ``` @@ -64,17 +72,24 @@ const DESU_ACTIONS: &[DesuAction>>] = &|ctx, msg| { // https://imgur.com/a/yOb5n // One day. - Box::pin( - msg.channel_id - .say(ctx, "https://www.youtube.com/watch?v=60mLvBWOMb4"), - ) - }, - &|ctx, msg| { - Box::pin(msg.channel_id.send_message(ctx, |m| { - m.add_file(serenity::http::AttachmentType::Bytes { - data: std::borrow::Cow::from(&include_bytes!("../../res/desu.jpg")[..]), - filename: "desu.jpg".to_string(), - }) - })) + Box::pin(debug_say_owned( + msg, + ctx, + "https://www.youtube.com/watch?v=60mLvBWOMb4", + )) }, + &|ctx, msg| Box::pin(send_desu_image(ctx, msg)), ]; + +async fn send_desu_image(ctx: Context, msg: Message) -> Result, serenity::Error> { + Ok(Some( + msg.channel_id + .send_message(ctx, |m| { + m.add_file(serenity::http::AttachmentType::Bytes { + data: std::borrow::Cow::from(&include_bytes!("../../res/desu.jpg")[..]), + filename: "desu.jpg".to_string(), + }) + }) + .await?, + )) +} diff --git a/src/passive/mod.rs b/src/passive/mod.rs index 0ca617b..e344a1b 100644 --- a/src/passive/mod.rs +++ b/src/passive/mod.rs @@ -2,6 +2,8 @@ use ahhhh::YellResponder; use all_caps::AllCapsResponder; use best_doctor::BestDoctorResponder; use desu::DesuResponder; +use futures::future::BoxFuture; +use rand::{rngs::OsRng, seq::IteratorRandom}; use reee::ReeeResponder; use serenity::async_trait; use serenity::model::channel::Message; @@ -14,7 +16,7 @@ mod desu; mod reee; pub(crate) struct Handler { - responders: Vec>, + responders: Vec>, } impl Default for Handler { @@ -34,8 +36,15 @@ impl Default for Handler { #[async_trait] impl EventHandler for Handler { async fn message(&self, ctx: Context, message: Message) { - for responder in self.responders.iter() { - responder.message(ctx.clone(), message.clone()).await; + let received_msg = message.content_safe(ctx.clone()).await; + let valid_responses = self.responders.iter().filter_map(|responder| { + responder.get_message(received_msg.clone(), ctx.clone(), message.clone()) + }); + + if let Some(fut) = valid_responses.choose(&mut OsRng) { + if let Err(err) = fut.await { + log::warn!("Got an error while sending a passive response: {}", err); + } } } } @@ -52,19 +61,33 @@ macro_rules! simple_responder { pub(crate) struct $name; - #[serenity::async_trait] - impl serenity::prelude::EventHandler for $name { - async fn message( + impl crate::passive::PassiveResponder for $name { + fn get_message( &self, + received_msg: String, ctx: serenity::prelude::Context, message: serenity::model::channel::Message, - ) { - let msg = message.content_safe(ctx.clone()).await; - if REGEX.is_match(&msg) { - log::trace!("{} matched regex {:?}", msg, REGEX); - message.channel_id.say(ctx, $phrase).await.unwrap(); + ) -> Option> { + if REGEX.is_match(&received_msg) { + log::trace!("{} matched regex {:?}", received_msg, REGEX); + Some(Box::pin(crate::util::debug_say_owned( + message, ctx, $phrase, + ))) + } else { + None } } } }; } + +type PassiveResponse<'a> = BoxFuture<'a, Result, serenity::Error>>; + +trait PassiveResponder { + fn get_message( + &self, + received_msg: String, + ctx: Context, + message: Message, + ) -> Option; +} diff --git a/src/passive/reee.rs b/src/passive/reee.rs index 17fdcf8..5c2d2ec 100644 --- a/src/passive/reee.rs +++ b/src/passive/reee.rs @@ -1,8 +1,9 @@ +use super::{PassiveResponder, PassiveResponse}; +use crate::util::debug_say_owned; use lazy_static::lazy_static; use regex::Regex; -use serenity::async_trait; use serenity::model::channel::Message; -use serenity::prelude::{Context, EventHandler}; +use serenity::prelude::Context; lazy_static! { static ref REGEX: Regex = Regex::new("RE{5,}").unwrap(); @@ -10,19 +11,24 @@ lazy_static! { pub(crate) struct ReeeResponder; -#[async_trait] -impl EventHandler for ReeeResponder { - async fn message(&self, ctx: Context, message: Message) { - if REGEX.is_match(&message.content_safe(ctx.clone()).await) { - if let Some(guild_id) = message.guild_id { - if guild_id == 679888177951277102 { - message - .channel_id - .say(ctx, "<:texaspat:680231475928498217>") - .await - .unwrap(); +impl PassiveResponder for ReeeResponder { + fn get_message( + &self, + received_msg: String, + ctx: Context, + message: Message, + ) -> Option> { + if REGEX.is_match(&received_msg) { + if let Some(guild_id) = &message.guild_id { + if guild_id == &679888177951277102 { + return Some(Box::pin(debug_say_owned( + message, + ctx, + "<:texaspat:680231475928498217>", + ))); } } } + None } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 4a71c21..0970ea6 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,10 +1,9 @@ use lazy_static::lazy_static; use serenity::{ - // builder::CreateMessage, http::Http, model::{channel::Message, id::ChannelId}, }; -use std::env::var; +use std::{borrow::Cow, env::var}; pub mod db; pub mod error; pub mod operators; @@ -28,6 +27,22 @@ pub async fn debug_say( msg: &Message, ctx: impl AsRef, resp: impl std::fmt::Display, +) -> Result, serenity::Error> { + debug_say_cow(Cow::Borrowed(msg), ctx, resp).await +} + +pub async fn debug_say_owned( + msg: Message, + ctx: impl AsRef, + resp: impl std::fmt::Display, +) -> Result, serenity::Error> { + debug_say_cow(Cow::Owned(msg), ctx, resp).await +} + +async fn debug_say_cow( + msg: Cow<'_, Message>, + ctx: impl AsRef, + resp: impl std::fmt::Display, ) -> Result, serenity::Error> { debug_channel_say(&msg.channel_id, *msg.author.id.as_u64(), ctx, resp).await }