diff --git a/TODO.org b/TODO.org
index cd92806b3b86295f7af86d28e6d8ade420940a5f..dc26fd1bc0d1762c5c2fac99e75c909ce0b481b1 100644
--- a/TODO.org
+++ b/TODO.org
@@ -7,10 +7,10 @@ _Involves: File r/w + parsing, discord reactions_
 So, for reaction roles, afaict this is what needs to be done
 
  - [X]  Migrate config.rs to something like ~config.yml~
- - [-] Complete ~reaction_roles.rs~
+ - [X] Complete ~reaction_roles.rs~
      - [X] Load from config (roles, and the rr msg, if they exist)
      - [X] Monitor reactions, update user roles etc.
-     - [ ]  On updated to ~config.yml~ (and on bot load, now that I think of it) overwrite the rr msg with content based on roles in config
+     - [X] Only allow reacts which correspond to roles
 
 * LDAP Integration
 
diff --git a/src/config.test.yml b/src/config.test.yml
index 6f8a814c7ca5d6b600aab7368e47601cbb7aa04f..b9bf21de834b9a958a4124bb2dd4c9dfa90d99a6 100644
--- a/src/config.test.yml
+++ b/src/config.test.yml
@@ -20,8 +20,10 @@ disapprove_react: "⬇"
 unsure_react: "❔"
 
 react_role_messages:
-  - message: 673400351277187072
+  - message: 674164298632790026
     mapping:
       🐊: 609708723006472198 # Autonomous Alligators
       🐃: 609708839243087892 # Bipedal Bison
       🦆: 609708889763479562 # Omnipresent Ostriches
+      j5: 607478818038480937 # Vote role
+      👻: 634415546804338688 # Background charachter
diff --git a/src/main.rs b/src/main.rs
index 348aa623c77b549c43f0d7a7dddf690179751ceb..299cfe2cf28ba1bae216f024189d05a6cf4d3fb3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -107,7 +107,9 @@ impl EventHandler for Handler {
         match add_reaction.message(&ctx.http) {
             Ok(message) => {
                 let message_type = get_message_type(&message);
-                if message_type == MessageType::RoleReactMessage {
+                if message_type == MessageType::RoleReactMessage
+                    && add_reaction.user_id.0 != CONFIG.bot_id
+                {
                     add_role_by_reaction(ctx, message, add_reaction);
                     return;
                 }
@@ -120,7 +122,7 @@ impl EventHandler for Handler {
                     }
                     MessageType::LogReact => {
                         let react_user = add_reaction.user(&ctx).unwrap();
-                        let react_as_string = get_string_from_react(add_reaction.emoji.clone());
+                        let react_as_string = get_string_from_react(&add_reaction.emoji);
                         if Utc::now().timestamp() - message.timestamp.timestamp() > 300 {
                             warn!(
                                 "The logreact message {} just tried to use is too old",
@@ -129,8 +131,8 @@ impl EventHandler for Handler {
                             return;
                         }
                         info!(
-                            "The react {} just added is {:?}",
-                            react_user.name, react_as_string
+                            "The react {} just added is {:?}. In full: {:?}",
+                            react_user.name, react_as_string, add_reaction.emoji
                         );
                         let mut msg = MessageBuilder::new();
                         msg.push_italic(react_user.name);
@@ -155,7 +157,9 @@ impl EventHandler for Handler {
         match removed_reaction.message(&ctx.http) {
             Ok(message) => {
                 let message_type = get_message_type(&message);
-                if message_type == MessageType::RoleReactMessage {
+                if message_type == MessageType::RoleReactMessage
+                    && removed_reaction.user_id != CONFIG.bot_id
+                {
                     remove_role_by_reaction(ctx, message, removed_reaction);
                     return;
                 }
diff --git a/src/reaction_roles.rs b/src/reaction_roles.rs
index 6affc31e7c4d2604d53b7aef295e21a86c85c139..35861b9884ce757f059392758e82b17a6684221a 100644
--- a/src/reaction_roles.rs
+++ b/src/reaction_roles.rs
@@ -7,33 +7,48 @@ use serenity::{
 use std::collections::{HashMap, HashSet};
 use std::iter::FromIterator;
 
+macro_rules! e {
+    ($error: literal, $x:expr) => {
+        match $x {
+            Ok(_) => (),
+            Err(why) => error!($error, why),
+        }
+    };
+}
+
 pub fn add_role_by_reaction(ctx: Context, msg: Message, added_reaction: Reaction) {
-    CONFIG
+    let user = added_reaction
+        .user_id
+        .to_user(&ctx)
+        .expect("Unable to get user");
+    if let Some(role_id) = 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.clone().emoji);
+            let react_as_string = get_string_from_react(&added_reaction.emoji);
             reaction_mapping.mapping.get(&react_as_string)
         })
-        .and_then(|role_id| {
-            info!(
-                "{} requested role {}",
-                added_reaction
-                    .user_id
-                    .to_user(&ctx)
-                    .expect("Unable to get user")
-                    .name,
-                role_id
-            );
-            ctx.http
-                .add_member_role(
-                    CONFIG.server_id,
-                    added_reaction.user_id.0,
-                    *role_id.as_u64(),
-                )
-                .ok()
-        });
+    {
+        info!(
+            "{} requested role '{}'",
+            user.name,
+            role_id
+                .to_role_cached(&ctx)
+                .expect("Unable to get role")
+                .name
+        );
+        ctx.http
+            .add_member_role(
+                CONFIG.server_id,
+                added_reaction.user_id.0,
+                *role_id.as_u64(),
+            )
+            .ok();
+    } else {
+        warn!("{} provided invalid react for role", user.name);
+        e!("Unable to delete react: {:?}", added_reaction.delete(&ctx));
+    }
 }
 
 pub fn remove_role_by_reaction(ctx: Context, msg: Message, removed_reaction: Reaction) {
@@ -42,11 +57,18 @@ pub fn remove_role_by_reaction(ctx: Context, msg: Message, removed_reaction: Rea
         .iter()
         .find(|rrm| rrm.message == msg.id)
         .and_then(|reaction_mapping| {
-            let react_as_string = get_string_from_react(removed_reaction.clone().emoji);
+            let react_as_string = get_string_from_react(&removed_reaction.emoji);
             reaction_mapping.mapping.get(&react_as_string)
         })
         .and_then(|role_id| {
-            info!("{} requested removal of role {}", msg.author.name, role_id);
+            info!(
+                "{} requested removal of role '{}'",
+                msg.author.name,
+                role_id
+                    .to_role_cached(&ctx)
+                    .expect("Unable to get role")
+                    .name
+            );
             ctx.http
                 .remove_member_role(
                     CONFIG.server_id,
@@ -81,16 +103,38 @@ pub fn sync_all_role_reactions(ctx: Context) {
     for (message, mapping) in messages_with_role_mappings {
         i += 1;
         info!("  Sync: prossessing message #{}", i);
+        for react in &message.reactions {
+            let react_as_string = get_string_from_react(&react.reaction_type);
+            if mapping.contains_key(&react_as_string) {
+                continue;
+            }
+            info!(
+                "    message #{}: Removing non-role react '{}'",
+                i, react_as_string
+            );
+            for _illegal_react in
+                &message.reaction_users(&ctx, react.reaction_type.clone(), Some(100), None)
+            {
+                warn!("    need to implement react removal");
+            }
+        }
         for (react, role) in mapping {
-            // the docs say this method can't retrieve more than 100 user reactions at a time, but it seems
-            // to work fine when set to 255...
+            info!("    message #{}: processing react '{}'", i, react);
             // TODO: proper pagination for the unlikely scenario that there are more than 100 (255?) reactions?
             let reaction_type = get_react_from_string(react.clone(), guild.clone());
             let reactors = message
-                .reaction_users(ctx.http.clone(), reaction_type, Some(255), None)
+                .reaction_users(ctx.http.clone(), reaction_type.clone(), Some(100), None)
                 .unwrap();
             let reactor_ids: HashSet<UserId> = HashSet::from_iter(reactors.iter().map(|r| r.id));
 
+            // ensure bot has reacted
+            if !reactor_ids.contains(&UserId::from(CONFIG.bot_id)) {
+                e!(
+                    "Unable to add reaction, {:?}",
+                    message.react(&ctx, reaction_type)
+                );
+            }
+
             for member in all_members.clone() {
                 let user_id = &member.user_id();
                 if reactor_ids.contains(&user_id) {
diff --git a/src/util.rs b/src/util.rs
index b168e8ac559409787aebf668a56bb199c61b409a..75aba7ffb49697e3a9d8ad3778fc586bd1816785 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,12 +1,12 @@
 use serenity::model::{channel::ReactionType, guild::PartialGuild};
 
-pub fn get_string_from_react(react: ReactionType) -> String {
+pub fn get_string_from_react(react: &ReactionType) -> String {
     match react {
         ReactionType::Custom {
             name: Some(name), ..
-        } => name,
+        } => name.to_string(),
         ReactionType::Custom { id, name: None, .. } => id.to_string(),
-        ReactionType::Unicode(name) => name,
+        ReactionType::Unicode(name) => name.to_string(),
         _ => format!("Unrecognised reaction type: {:?}", react),
     }
 }
@@ -18,6 +18,6 @@ pub fn get_react_from_string(string: String, guild: PartialGuild) -> ReactionTyp
         .find(|e| e.name == string)
         .map_or_else(
             || ReactionType::from(string), // unicode emoji
-            |custom_emoji| ReactionType::from(custom_emoji.id),
+            |custom_emoji| ReactionType::from(custom_emoji.clone()),
         )
 }
diff --git a/src/voting.rs b/src/voting.rs
index 90b7c6e888b5b13d7021f2072ca77c6e060e0d92..cc26869369323a495aad3f57b461ec2d013a664e 100644
--- a/src/voting.rs
+++ b/src/voting.rs
@@ -179,17 +179,17 @@ fn get_cached_motion(ctx: &Context, msg: &Message) -> MotionInfo {
                 let mut m = HashMap::new();
                 m.insert(
                     CONFIG.for_vote.to_string(),
-                    msg.reaction_users(ctx, CONFIG.for_vote.to_string(), None, None)
+                    msg.reaction_users(ctx, CONFIG.for_vote.to_string(), Some(100), None)
                         .unwrap(),
                 );
                 m.insert(
                     CONFIG.against_vote.to_string(),
-                    msg.reaction_users(ctx, CONFIG.against_vote.to_string(), None, None)
+                    msg.reaction_users(ctx, CONFIG.against_vote.to_string(), Some(100), None)
                         .unwrap(),
                 );
                 m.insert(
                     CONFIG.abstain_vote.to_string(),
-                    msg.reaction_users(ctx, CONFIG.abstain_vote.to_string(), None, None)
+                    msg.reaction_users(ctx, CONFIG.abstain_vote.to_string(), Some(100), None)
                         .unwrap(),
                 );
                 m
@@ -253,7 +253,7 @@ fn update_motion(
         "  {:10} {:6} {} on {}",
         user.name,
         change,
-        get_string_from_react(reaction.emoji),
+        get_string_from_react(&reaction.emoji),
         topic
     );
 
@@ -339,7 +339,7 @@ fn update_motion(
 }
 
 pub fn reaction_add(ctx: Context, add_reaction: channel::Reaction) {
-    let react_as_string = get_string_from_react(add_reaction.emoji.clone());
+    let react_as_string = get_string_from_react(&add_reaction.emoji);
     match add_reaction.message(&ctx.http) {
         Ok(mut message) => {
             if let Ok(user) = add_reaction.user(&ctx) {
@@ -414,7 +414,7 @@ pub fn reaction_remove(ctx: Context, removed_reaction: channel::Reaction) {
                 let mut motion_info = get_cached_motion(&ctx, &message);
                 if let Some(vote) = motion_info
                     .votes
-                    .get_mut(&get_string_from_react(removed_reaction.emoji.clone()))
+                    .get_mut(&get_string_from_react(&removed_reaction.emoji))
                 {
                     vote.retain(|u| u.id != user.id);
                 }