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
-	}
-}