diff --git a/Cargo.lock b/Cargo.lock index b6c74ab8e840796d1137e2748c88707c5093efc0..5480247c3b4b6cb8387bede7af1dddb83cdb5230 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,6 +119,7 @@ dependencies = [ "bigdecimal", "diesel", "dotenv", + "itertools", "phf", "png", "rand 0.8.3", @@ -253,6 +254,12 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + [[package]] name = "encoding_rs" version = "0.8.25" @@ -602,6 +609,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.6" diff --git a/Cargo.toml b/Cargo.toml index 4d34e16cc1ed7b66beac39f1750cf9e7a27736ed..ab17f677b2a8f70ee4d6fac812e8a59646834925 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ reqwest = { version = "0.10", features = ["json"] } serde_json = "1.0" rusttype = "0.4.3" rand = "0.8" +itertools = "0.10" diff --git a/src/handlers/react.rs b/src/handlers/react.rs index f65d38fd7e82787eeb1d9880470c0c3a23ddcf75..b83c0244235ec1c7cf5f20d995eb6395cab089dd 100644 --- a/src/handlers/react.rs +++ b/src/handlers/react.rs @@ -1,4 +1,5 @@ use crate::handlers::LineHandler; +use itertools::Itertools; use phf::phf_map; use serenity::async_trait; use serenity::model::channel::{Message, ReactionType}; @@ -14,36 +15,54 @@ static EMOJI_MAP: phf::Map<&'static str, &'static str> = phf_map! { "mango" => "ðŸ¥", "banana" => "ðŸŒ", "bee" => "ðŸ", - "hose" => "ðŸŽ", + "horse" => "ðŸŽ", + "hat" => "🎩" }; +fn map_lookup(msg: &str) -> Option<&'static str> { + // We lose the O(1) benefits of the hashmap + // But whatever. It doesn't need to be fast + for (k, v) in EMOJI_MAP.entries() { + if &msg == k || msg == format!("{}s", k) { + return Some(v); + } + } + + None +} + +#[derive(Default)] pub struct ReactHandler; #[async_trait] impl LineHandler for ReactHandler { async fn message(&self, ctx: &Context, msg: &Message) { - for (key, value) in EMOJI_MAP.entries() { - let msg_lower = format!(" {} ", msg.content.to_lowercase()); - if msg_lower.contains(&format!(" {} ", key)) - || msg_lower.contains(&format!(" {}s ", key)) - { - let reaction_type = match ReactionType::from_str(value) { - Ok(x) => x, - Err(x) => { - println!("Could not react: {}", x); - return; - } - }; - if let Err(e) = msg.react(&ctx, reaction_type).await { - println!("Error reacting: {}", e); + // Kind of convoluted, but allows us to react in the correct order. + let groups = msg + .content + .chars() + .map(|c| match c { + '!' | '?' | ',' | '.' => ' ', + _ => c, + }) + .group_by(|c| *c == ' '); + let reacts: Vec<_> = groups + .into_iter() + .filter_map(|(_, c)| map_lookup(&c.collect::<String>().to_lowercase())) + .unique() + .collect(); + + for r in reacts { + let reaction_type = match ReactionType::from_str(r) { + Ok(x) => x, + Err(x) => { + println!("Could not react: {}", x); + return; } + }; + if let Err(e) = msg.react(&ctx, reaction_type).await { + println!("Error reacting: {}", e); } } } } - -impl Default for ReactHandler { - fn default() -> Self { - Self - } -}