use crate::handlers::LineHandler; use itertools::Itertools; use phf::phf_map; use serenity::async_trait; use serenity::model::channel::{Message, ReactionType}; use serenity::prelude::*; use std::str::FromStr; static EMOJI_MAP: phf::Map<&'static str, &'static str> = phf_map! { "cat" => "🐈", "chicken" => "🐔", "spaghetti" => "🍝", "dog" => "🐕", "bot" => "🤖", "mango" => "🥭", "banana" => "🍌", "bee" => "🐝", "horse" => "🐎", }; 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.contains(k) { return Some(v); } } None } #[derive(Default)] pub struct ReactHandler; #[async_trait] impl LineHandler for ReactHandler { async fn message(&self, ctx: &Context, msg: &Message) { // 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); } } } }