diff --git a/src/config.rs b/src/config.rs
index c8a173b31f4a2b9fac7e304783818078a8d8b3b4..d1dcff5bac1bc530ed3f9c404601733ba8160e73 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,3 +1,4 @@
+use std::collections::HashMap;
 use serde::Deserialize;
 use serenity::model::id;
 use std::fs;
@@ -27,6 +28,7 @@ pub struct UccbotConfig {
     pub approve_react: String,
     pub disapprove_react: String,
     pub unsure_react: String,
+    pub react_role_messages: Vec<ReactionMapping>,
 }
 
 impl UccbotConfig {
@@ -41,3 +43,9 @@ impl UccbotConfig {
         ]
     }
 }
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct ReactionMapping {
+    pub message: serenity::model::id::MessageId,
+    pub mapping: HashMap<String, id::RoleId>,
+}
diff --git a/src/config.test.yml b/src/config.test.yml
index f0d8295e567e75a10ff5e59aca6e2658ede37cae..6f8a814c7ca5d6b600aab7368e47601cbb7aa04f 100644
--- a/src/config.test.yml
+++ b/src/config.test.yml
@@ -18,3 +18,10 @@ abstain_vote: "🙊"
 approve_react: "⬆"
 disapprove_react: "⬇"
 unsure_react: "❔"
+
+react_role_messages:
+  - message: 673400351277187072
+    mapping:
+      🐊: 609708723006472198 # Autonomous Alligators
+      🐃: 609708839243087892 # Bipedal Bison
+      🦆: 609708889763479562 # Omnipresent Ostriches
diff --git a/src/main.rs b/src/main.rs
index aff885222f0f3952685e82e071dbee0511a969b4..44d328f9d8653dfbe0b222baee09fb3fe70aa79c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -18,9 +18,11 @@ mod config;
 mod user_management;
 mod voting;
 mod util;
+mod reaction_roles;
 
 use config::CONFIG;
 use util::get_string_from_react;
+use reaction_roles::{add_role_by_reaction, remove_role_by_reaction};
 
 macro_rules! e {
     ($error: literal, $x:expr) => {
@@ -102,10 +104,15 @@ impl EventHandler for Handler {
     fn reaction_add(&self, ctx: Context, add_reaction: channel::Reaction) {
         match add_reaction.message(&ctx.http) {
             Ok(message) => {
+                let message_type = get_message_type(&message);
+                if message_type == MessageType::RoleReactMessage {
+                    add_role_by_reaction(ctx, message, add_reaction);
+                    return;
+                }
                 if message.author.id.0 != CONFIG.bot_id || add_reaction.user_id == CONFIG.bot_id {
                     return;
                 }
-                match get_message_type(&message) {
+                match message_type {
                     MessageType::Motion => {
                         voting::reaction_add(ctx, add_reaction);
                     }
@@ -146,11 +153,16 @@ impl EventHandler for Handler {
     fn reaction_remove(&self, ctx: Context, removed_reaction: channel::Reaction) {
         match removed_reaction.message(&ctx.http) {
             Ok(message) => {
+                let message_type = get_message_type(&message);
+                if message_type == MessageType::RoleReactMessage {
+                    remove_role_by_reaction(ctx, message, removed_reaction);
+                    return;
+                }
                 if message.author.id.0 != CONFIG.bot_id || removed_reaction.user_id == CONFIG.bot_id
                 {
                     return;
                 }
-                match get_message_type(&message) {
+                match message_type {
                     MessageType::Motion => {
                         voting::reaction_remove(ctx, removed_reaction);
                     }
@@ -213,12 +225,16 @@ fn main() {
 enum MessageType {
     Motion,
     Role,
+    RoleReactMessage,
     LogReact,
     Poll,
     Misc
 }
 
 fn get_message_type(message: &Message) -> MessageType {
+    if CONFIG.react_role_messages.iter().any(|rrm| rrm.message == message.id) {
+        return MessageType::RoleReactMessage;
+    }
     if message.embeds.len() <= 0 {
         // Get first word of message
         return match message.content.splitn(2, ' ').next().unwrap() {
diff --git a/src/reaction_roles.rs b/src/reaction_roles.rs
index e27ff0f5dddb797d28578be86839aea8670e35b8..cd69711fa717bbea65bed68e3ce2f8374965c073 100644
--- a/src/reaction_roles.rs
+++ b/src/reaction_roles.rs
@@ -1,10 +1,24 @@
+use serenity::{
+    model::{channel::Message, channel::Reaction},
+    client::Context
+};
+use crate::util::get_string_from_react;
+use crate::config::CONFIG;
 
-#[derive(Debug, Clone)]
-struct ReactionMapping {
-    mapping: HashMap<serenity::model::id::EmojiId, serenity::model::id::RoleId>,
+pub fn add_role_by_reaction(ctx: Context, msg: Message, added_reaction: Reaction) {
+    CONFIG.react_role_messages.iter().find(|rrm| rrm.message == msg.id).and_then(|reaction_mapping| {
+        let react_as_string = get_string_from_react(added_reaction.emoji);
+        return reaction_mapping.mapping.get(&react_as_string);
+    }).and_then(|role_id|{
+        return ctx.http.add_member_role(CONFIG.server_id, *msg.author.id.as_u64(), *role_id.as_u64()).ok();
+    });
 }
 
-lazy_static! {
-    static ref REACTIONS_CACHE: Mutex<HashMap<serenity::model::id::MessageId, ReactionMapping>> =
-        Mutex::new(HashMap::new());
+pub fn remove_role_by_reaction(ctx: Context, msg: Message, removed_reaction: Reaction) {
+    CONFIG.react_role_messages.iter().find(|rrm| rrm.message == msg.id).and_then(|reaction_mapping| {
+        let react_as_string = get_string_from_react(removed_reaction.emoji);
+        return reaction_mapping.mapping.get(&react_as_string);
+    }).and_then(|role_id|{
+        return ctx.http.remove_member_role(CONFIG.server_id, *msg.author.id.as_u64(), *role_id.as_u64()).ok();
+    });
 }