diff --git a/src/ldap.rs b/src/ldap.rs
index 7dce6c243482a85daab43152fb355cf691ae3049..0b598f870e4850e13eb9650e33ff88e213973a01 100644
--- a/src/ldap.rs
+++ b/src/ldap.rs
@@ -11,7 +11,7 @@ pub struct LDAPUser {
 
 pub fn ldap_search(username: &str) -> Option<LDAPUser> {
     let settings = LdapConnSettings::new().set_no_tls_verify(true);
-    let ldap =
+    let mut ldap =
         LdapConn::with_settings(settings, &CONFIG.bind_address).expect("Unable to connect to LDAP");
     ldap.simple_bind(
         "cn=ucc-discord-bot,cn=Users,dc=ad,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au",
@@ -31,7 +31,7 @@ pub fn ldap_search(username: &str) -> Option<LDAPUser> {
         .success()
         .expect("LDAP search error");
     if rs.is_empty() {
-        return None
+        return None;
     }
     let result = SearchEntry::construct(rs[0].clone()).attrs;
     Some(LDAPUser {
@@ -44,9 +44,9 @@ pub fn ldap_search(username: &str) -> Option<LDAPUser> {
             .expect("LDAP failed to get 'displayName' field")
             .join(""),
         when_created: "".to_string(), // result
-            // .get("whenCreated")
-            // .expect("LDAP failed to get 'whenCreated' field")
-            // .join(""),
+        // .get("whenCreated")
+        // .expect("LDAP failed to get 'whenCreated' field")
+        // .join(""),
         login_shell: result
             .get("loginShell")
             .expect("LDAP failed to get 'loginShell' field")
diff --git a/src/reaction_roles.rs b/src/reaction_roles.rs
index fd2b5be3cf2dc163900e9fa3dd99530cb8debffe..1a0a50fa4099f7b321cfb807f03adba412ecd985 100644
--- a/src/reaction_roles.rs
+++ b/src/reaction_roles.rs
@@ -1,6 +1,5 @@
 use crate::config::{ReactRoleMap, CONFIG};
 use crate::util::{get_react_from_string, get_string_from_react};
-use rayon::prelude::*;
 use serenity::{
     client::Context,
     model::{channel::Message, channel::Reaction, id::RoleId, id::UserId},
@@ -198,21 +197,17 @@ async fn get_all_role_reaction_message(ctx: &Context) -> Vec<(Message, &'static
     let channels = ctx.http.get_channels(*guild.id.as_u64()).await.unwrap();
     info!("  Find role-react message: channels determined");
     let http = ctx.http.clone();
-    channels
-        .par_iter()
-        .flat_map(|channel| {
-            // since we don't know which channels the messages are in, we check every combination
-            // of message and channel and ignore the bad matches using .ok() and .filter_map()
-            let h = http.clone(); // thread-local copy
-            CONFIG
-                .react_role_messages
-                .par_iter()
-                .filter_map(move |rrm| async {
-                    h.get_message(*channel.id.as_u64(), *rrm.message.as_u64())
-                        .await
-                        .ok()
-                        .map(|m| (m, &rrm.mapping))
-                })
-        })
-        .collect()
+    let mut v = Vec::new();
+    for channel in channels {
+        for reaction in CONFIG.react_role_messages.iter() {
+            if let Some(m) = http
+                .get_message(*channel.id.as_u64(), *reaction.message.as_u64())
+                .await
+                .ok()
+            {
+                v.push((m, &reaction.mapping))
+            }
+        }
+    }
+    v
 }
diff --git a/src/serenity_handler.rs b/src/serenity_handler.rs
index 0cbc512573c22de7f39e61a73e9323422029c86e..7afe70710a59d5ee530f08aaa49b4d0752c44083 100644
--- a/src/serenity_handler.rs
+++ b/src/serenity_handler.rs
@@ -6,7 +6,7 @@ use serenity::{
     utils::MessageBuilder,
 };
 
-use rand::seq::SliceRandom;
+// use rand::seq::SliceRandom;
 
 use crate::config::CONFIG;
 use crate::ldap;
@@ -35,9 +35,8 @@ impl EventHandler for Handler {
                 send_message!(
                     msg.channel_id,
                     &ctx.http,
-                    MENTION_RESPONSES
-                        .choose(&mut rand::thread_rng())
-                        .expect("We couldn't get any sass")
+                    MENTION_RESPONSES[0] //.choose(&mut rand::random())
+                                         //.expect("We couldn't get any sass")
                 );
             }
             return;
@@ -136,16 +135,12 @@ impl EventHandler for Handler {
     async fn reaction_add(&self, ctx: Context, add_reaction: channel::Reaction) {
         match add_reaction.message(&ctx.http).await {
             Ok(message) => match get_message_type(&message) {
-                MessageType::RoleReactMessage if add_reaction.user_id.unwrap() != CONFIG.bot_id => {
-                    add_role_by_reaction(&ctx, message, add_reaction);
-                    return;
+                MessageType::RoleReactMessage if add_reaction.user_id == Some(CONFIG.bot_id) => {
+                    add_role_by_reaction(&ctx, message, add_reaction).await
                 }
                 _ if message.author.id != CONFIG.bot_id
-                    || add_reaction.user_id.unwrap() == CONFIG.bot_id =>
-                {
-                    return
-                }
-                MessageType::Motion => voting::reaction_add(ctx, add_reaction).await,
+                    || add_reaction.user_id == Some(CONFIG.bot_id) => {}
+                MessageType::Motion => voting::reaction_add(&ctx, add_reaction).await,
                 MessageType::LogReact => {
                     let react_user = add_reaction.user(&ctx).await.unwrap();
                     let react_as_string = get_string_from_react(&add_reaction.emoji);
@@ -186,28 +181,24 @@ impl EventHandler for Handler {
                 MessageType::RoleReactMessage
                     if removed_reaction.user_id.unwrap() != CONFIG.bot_id =>
                 {
-                    remove_role_by_reaction(&ctx, message, removed_reaction);
-                    return;
+                    remove_role_by_reaction(&ctx, message, removed_reaction).await
                 }
                 _ if message.author.id != CONFIG.bot_id
-                    || removed_reaction.user_id.unwrap() == CONFIG.bot_id =>
-                {
-                    return
-                }
-                MessageType::Motion => voting::reaction_remove(ctx, removed_reaction).await,
+                    || removed_reaction.user_id.unwrap() == CONFIG.bot_id => {}
+                MessageType::Motion => voting::reaction_remove(&ctx, removed_reaction).await,
                 _ => {}
             },
             Err(why) => error!("Failed to get react message {:?}", why),
         }
     }
 
-    fn guild_member_addition(
+    async fn guild_member_addition(
         &self,
         ctx: Context,
         _guild_id: serenity::model::id::GuildId,
         the_new_member: Member,
     ) {
-        user_management::new_member(&ctx, the_new_member);
+        user_management::new_member(&ctx, the_new_member).await
     }
 
     // Set a handler to be called on the `ready` event. This is called when a
@@ -216,13 +207,13 @@ impl EventHandler for Handler {
     // private channels, and more.
     //
     // In this case, just print what the current user's username is.
-    fn ready(&self, ctx: Context, ready: Ready) {
+    async fn ready(&self, ctx: Context, ready: Ready) {
         info!("{} is connected!", ready.user.name);
-        sync_all_role_reactions(&ctx);
+        sync_all_role_reactions(&ctx).await
     }
 
-    fn resume(&self, ctx: Context, _: serenity::model::event::ResumedEvent) {
-        sync_all_role_reactions(&ctx);
+    async fn resume(&self, ctx: Context, _: serenity::model::event::ResumedEvent) {
+        sync_all_role_reactions(&ctx).await
     }
 }
 
diff --git a/src/token_management.rs b/src/token_management.rs
index badad22ebde7d1c5908fd2e359d22992e046e3e1..5e64cffa56ae17820b928611aa6d9ed5e9105f4f 100644
--- a/src/token_management.rs
+++ b/src/token_management.rs
@@ -17,7 +17,7 @@ lazy_static! {
 }
 
 fn text_encrypt(plaintext: &str) -> String {
-    base64::encode(CIPHER.encrypt_vec(plaintext.as_bytes()))
+    base64::encode(CIPHER.clone().encrypt_vec(plaintext.as_bytes()))
 }
 
 fn text_decrypt(ciphertext: &str) -> Option<String> {
@@ -25,7 +25,7 @@ fn text_decrypt(ciphertext: &str) -> Option<String> {
         warn!("Unable to decode base64 text");
         return None
     });
-    guard!(let Ok(decrypted_vec) = CIPHER.decrypt_vec(&cipher_vec) else {
+    guard!(let Ok(decrypted_vec) = CIPHER.clone().decrypt_vec(&cipher_vec) else {
         warn!("Text decryption failed");
         return None
     });
diff --git a/src/user_management.rs b/src/user_management.rs
index ad3ceb0300b9c11e8a4289fe77755740e331db06..98e91e459db57bfc88aa0f4b72ae74f9d57966df 100644
--- a/src/user_management.rs
+++ b/src/user_management.rs
@@ -290,7 +290,7 @@ impl Commands {
         let user = match ctx.http.get_user(member.discord_id as _).await {
             Ok(u) => u,
             Err(e) => {
-                error!("Couldn't find matching Discord ID for username!");
+                error!("Couldn't find matching Discord ID for username! {:?}", e);
                 return;
             }
         };
@@ -492,8 +492,7 @@ impl Commands {
     pub async fn clear_info(ctx: Context, msg: Message, field: &str) {
         if field.trim().is_empty() {
             // just show the help page from set_info
-            Commands::set_info(ctx, msg, "");
-            return;
+            return Commands::set_info(ctx, msg, "").await;
         }
         match field {
             "bio" => database::set_member_bio(&msg.author.id.0, None),
diff --git a/src/voting.rs b/src/voting.rs
index 84574d2134f22c453dc6805cad5d6ec26b5d7d90..ccbf95192b07991846848b6ef66e9f77fefe04df 100644
--- a/src/voting.rs
+++ b/src/voting.rs
@@ -5,7 +5,7 @@ use serenity::{
 };
 use std::collections::HashMap;
 use std::str::FromStr;
-use std::sync::Mutex;
+use tokio::sync::Mutex;
 
 use crate::config::CONFIG;
 use crate::util::get_string_from_react;
@@ -15,8 +15,7 @@ impl Commands {
     pub async fn move_something(ctx: Context, msg: Message, content: &str) {
         let motion = content;
         if !motion.is_empty() {
-            create_motion(&ctx, &msg, motion);
-            return;
+            return create_motion(&ctx, &msg, motion).await;
         }
         send_message!(
             msg.channel_id,
@@ -34,8 +33,7 @@ impl Commands {
     pub async fn poll(ctx: Context, msg: Message, content: &str) {
         let topic = content;
         if !topic.is_empty() {
-            create_poll(&ctx, &msg, topic);
-            return;
+            return create_poll(&ctx, &msg, topic).await;
         }
         send_message!(
             msg.channel_id,
@@ -167,45 +165,42 @@ lazy_static! {
 }
 
 async fn get_cached_motion(ctx: &Context, msg: &Message) -> MotionInfo {
-    let mut cached_motions = MOTIONS_CACHE.lock().unwrap();
+    let mut cached_motions = MOTIONS_CACHE.lock().await;
     if !cached_motions.contains_key(&msg.id) {
         info!("Initialising representation of motion {:?}", msg.id);
+        let for_votes = msg
+            .reaction_users(
+                ctx,
+                EmojiIdentifier::from_str(&CONFIG.for_vote).unwrap(),
+                Some(100),
+                None,
+            )
+            .await
+            .unwrap();
+        let against_votes = msg
+            .reaction_users(
+                ctx,
+                EmojiIdentifier::from_str(&CONFIG.against_vote).unwrap(),
+                Some(100),
+                None,
+            )
+            .await
+            .unwrap();
+        let abstain_votes = msg
+            .reaction_users(
+                ctx,
+                EmojiIdentifier::from_str(&CONFIG.abstain_vote).unwrap(),
+                Some(100),
+                None,
+            )
+            .await
+            .unwrap();
         let this_motion = MotionInfo {
             votes: {
                 let mut m = HashMap::new();
-                m.insert(
-                    CONFIG.for_vote.to_string(),
-                    msg.reaction_users(
-                        ctx,
-                        EmojiIdentifier::from_str(&CONFIG.for_vote).unwrap(),
-                        Some(100),
-                        None,
-                    )
-                    .await
-                    .unwrap(),
-                );
-                m.insert(
-                    CONFIG.against_vote.to_string(),
-                    msg.reaction_users(
-                        ctx,
-                        EmojiIdentifier::from_str(&CONFIG.against_vote).unwrap(),
-                        Some(100),
-                        None,
-                    )
-                    .await
-                    .unwrap(),
-                );
-                m.insert(
-                    CONFIG.abstain_vote.to_string(),
-                    msg.reaction_users(
-                        ctx,
-                        EmojiIdentifier::from_str(&CONFIG.abstain_vote).unwrap(),
-                        Some(100),
-                        None,
-                    )
-                    .await
-                    .unwrap(),
-                );
+                m.insert(CONFIG.for_vote.to_string(), for_votes);
+                m.insert(CONFIG.against_vote.to_string(), against_votes);
+                m.insert(CONFIG.abstain_vote.to_string(), abstain_votes);
                 m
             },
         };
@@ -213,34 +208,15 @@ async fn get_cached_motion(ctx: &Context, msg: &Message) -> MotionInfo {
     }
     (*cached_motions.get(&msg.id).unwrap()).clone()
 }
-fn set_cached_motion(id: serenity::model::id::MessageId, motion_info: MotionInfo) {
-    if let Some(motion) = MOTIONS_CACHE.lock().unwrap().get_mut(&id) {
+
+async fn set_cached_motion(id: serenity::model::id::MessageId, motion_info: MotionInfo) {
+    if let Some(motion) = MOTIONS_CACHE.lock().await.get_mut(&id) {
         *motion = motion_info;
         return;
     }
     warn!("{}", "Couldn't find motion in cache to set");
 }
 
-macro_rules! tiebreaker {
-    ($ctx: expr, $vote: expr, $motion_info: expr) => {
-        if $motion_info
-            .votes
-            .get($vote)
-            .unwrap()
-            .iter()
-            .any(|u| async {
-                u.has_role($ctx, CONFIG.server_id, CONFIG.tiebreaker_role)
-                    .await
-                    .unwrap()
-            })
-        {
-            0.25
-        } else {
-            0.0
-        }
-    };
-}
-
 async fn update_motion(
     ctx: &Context,
     msg: &mut Message,
@@ -250,15 +226,33 @@ async fn update_motion(
 ) {
     let motion_info: MotionInfo = get_cached_motion(ctx, msg).await;
 
+    async fn tiebreaker(ctx: &Context, motion: &MotionInfo, vote_type: &str) -> f32 {
+        if let Some(votes) = motion.votes.get(vote_type) {
+            for voter in votes {
+                match voter
+                    .has_role(ctx, CONFIG.server_id, CONFIG.tiebreaker_role)
+                    .await
+                {
+                    Ok(true) => return 0.25,
+                    _ => continue,
+                }
+            }
+            0.0
+        } else {
+            warn!("Couldn't find \"{}\" vote for {:?}", vote_type, motion);
+            0.0
+        }
+    }
+
     let for_votes = motion_info.votes.get(&CONFIG.for_vote).unwrap().len() as isize - 1;
     let against_votes = motion_info.votes.get(&CONFIG.against_vote).unwrap().len() as isize - 1;
     let abstain_votes = motion_info.votes.get(&CONFIG.abstain_vote).unwrap().len() as isize - 1;
 
-    let for_strength = for_votes as f32 + tiebreaker!(ctx, &CONFIG.for_vote, motion_info);
+    let for_strength = for_votes as f32 + tiebreaker(ctx, &motion_info, &CONFIG.for_vote).await;
     let against_strength =
-        against_votes as f32 + tiebreaker!(ctx, &CONFIG.against_vote, motion_info);
+        against_votes as f32 + tiebreaker(ctx, &motion_info, &CONFIG.against_vote).await;
     let abstain_strength =
-        abstain_votes as f32 + tiebreaker!(ctx, &CONFIG.abstain_vote, motion_info);
+        abstain_votes as f32 + tiebreaker(ctx, &motion_info, &CONFIG.abstain_vote).await;
 
     let old_embed = msg.embeds[0].clone();
     let topic = old_embed.clone().title.unwrap();
@@ -271,10 +265,13 @@ async fn update_motion(
         topic
     );
 
-    let update_status = |e: &mut serenity::builder::CreateEmbed,
-                         status: &str,
-                         last_status_full: String,
-                         topic: &str| async {
+    fn update_status(
+        ctx: &Context,
+        e: &mut serenity::builder::CreateEmbed,
+        status: &str,
+        last_status_full: String,
+        topic: &str,
+    ) {
         let last_status = last_status_full.lines().next().expect("No previous status");
         if last_status == status {
             e.field("Status", last_status_full, true);
@@ -291,9 +288,15 @@ async fn update_motion(
             message.push(" is now ");
             message.push_bold(status);
             message.push_italic(format!(" (was {})", last_status));
-            send_message!(CONFIG.announcement_channel, &ctx.http, message.build());
+            let ctx = ctx.clone();
+            tokio::spawn(async move {
+                CONFIG
+                    .announcement_channel
+                    .say(ctx.http, message.build())
+                    .await
+            });
         }
-    };
+    }
 
     if let Err(why) = msg
         .edit(ctx, |m| {
@@ -319,14 +322,14 @@ async fn update_motion(
                     .value;
                 if for_strength > (CONFIG.vote_pool_size as f32 / 2.0) {
                     e.colour(serenity::utils::Colour::TEAL);
-                    update_status(e, "Passed", last_status_full, &topic);
+                    update_status(ctx, e, "Passed", last_status_full, &topic);
                 } else if against_strength + abstain_strength > (CONFIG.vote_pool_size as f32 / 2.0)
                 {
                     e.colour(serenity::utils::Colour::RED);
-                    update_status(e, "Failed", last_status_full, &topic);
+                    update_status(ctx, e, "Failed", last_status_full, &topic);
                 } else {
                     e.colour(serenity::utils::Colour::GOLD);
-                    update_status(e, "Under Consideration", last_status_full, &topic);
+                    update_status(ctx, e, "Under Consideration", last_status_full, &topic);
                 }
                 e.field(
                     format!(
@@ -354,7 +357,7 @@ async fn update_motion(
     }
 }
 
-pub async fn reaction_add(ctx: Context, add_reaction: channel::Reaction) {
+pub async fn reaction_add(ctx: &Context, add_reaction: channel::Reaction) {
     let react_as_string = get_string_from_react(&add_reaction.emoji);
     match add_reaction.message(&ctx.http).await {
         Ok(mut message) => {
@@ -407,8 +410,8 @@ pub async fn reaction_add(ctx: Context, add_reaction: channel::Reaction) {
                         vote.retain(|u| u.id != user.id);
                         vote.push(user.clone());
                     }
-                    set_cached_motion(message.id, motion_info);
-                    update_motion(&ctx, &mut message, &user, "add", add_reaction);
+                    set_cached_motion(message.id, motion_info).await;
+                    update_motion(&ctx, &mut message, &user, "add", add_reaction).await
                 }
                 Ok(false) => {
                     if ![
@@ -434,7 +437,7 @@ pub async fn reaction_add(ctx: Context, add_reaction: channel::Reaction) {
     }
 }
 
-pub async fn reaction_remove(ctx: Context, removed_reaction: channel::Reaction) {
+pub async fn reaction_remove(ctx: &Context, removed_reaction: channel::Reaction) {
     match removed_reaction.message(&ctx.http).await {
         Ok(mut message) => {
             if let Ok(user) = removed_reaction.user(&ctx).await {
@@ -445,8 +448,8 @@ pub async fn reaction_remove(ctx: Context, removed_reaction: channel::Reaction)
                 {
                     vote.retain(|u| u.id != user.id);
                 }
-                set_cached_motion(message.id, motion_info);
-                update_motion(&ctx, &mut message, &user, "remove", removed_reaction);
+                set_cached_motion(message.id, motion_info).await;
+                update_motion(&ctx, &mut message, &user, "remove", removed_reaction).await;
             }
         }
         Err(why) => {