From ec74dc0aa71ccae6ded8271fca10481840596bb3 Mon Sep 17 00:00:00 2001
From: Brendan Golden <git@brendan.ie>
Date: Sun, 29 Sep 2024 21:04:08 +0100
Subject: [PATCH 1/7] prep: skeleton to handle roles changing from other means

---
 src/main.rs | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index f1c70f3..413dc6f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,7 +13,7 @@ use serenity::{
   Client,
 };
 use std::sync::Arc;
-
+use serenity::model::guild::Member;
 use skynet_discord_bot::{db_init, get_config, get_server_config, get_server_member, Config, DataBase};
 use tokio::sync::RwLock;
 
@@ -21,6 +21,7 @@ struct Handler;
 
 #[async_trait]
 impl EventHandler for Handler {
+  // handles previously linked accounts joining the server
   async fn guild_member_addition(&self, ctx: Context, mut new_member: guild::Member) {
     let db_lock = {
       let data_read = ctx.data.read().await;
@@ -120,6 +121,25 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
       }
     }
   }
+
+  // handles role updates
+  async fn guild_member_update(&self, _ctx: Context, _old_if_available: Option<Member>, _new: Member){
+    if let Some(x) = _old_if_available {
+      if x.roles.len() != _new.roles.len() {
+        return;
+      }
+      //do we need to do more comparison here?
+    }
+
+
+    // get config/db
+
+    // check if the role changed is part of the oens for this server
+
+    // if so add or remove the resultant one
+
+    // TODO: Finish
+  }
 }
 
 #[tokio::main]

From 2aad895bb3fac3b94798986c19c8217c11474554 Mon Sep 17 00:00:00 2001
From: Brendan Golden <git@brendan.ie>
Date: Sun, 29 Sep 2024 21:25:58 +0100
Subject: [PATCH 2/7] feat: new table for the role adder

---
 db/migrations/6_role-adder.sql | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 db/migrations/6_role-adder.sql

diff --git a/db/migrations/6_role-adder.sql b/db/migrations/6_role-adder.sql
new file mode 100644
index 0000000..7206eca
--- /dev/null
+++ b/db/migrations/6_role-adder.sql
@@ -0,0 +1,11 @@
+CREATE TABLE IF NOT EXISTS roles_adder (
+    server  integer not null,
+    role_a  integer not null,
+    role_b  integer not null,
+    role_c  integer not null,
+    PRIMARY KEY(server,role_a,role_b,role_c)
+);
+CREATE INDEX IF NOT EXISTS index_roles_adder_server ON roles_adder (server);
+CREATE INDEX IF NOT EXISTS index_roles_adder_from ON roles_adder (role_a,role_b);
+CREATE INDEX IF NOT EXISTS index_roles_adder_to ON roles_adder (role_c);
+CREATE INDEX IF NOT EXISTS index_roles_adder_search ON roles_adder (server,role_a,role_b);
\ No newline at end of file

From 7980739627652a6a2c0f3badc2b60cabce79ad5a Mon Sep 17 00:00:00 2001
From: Brendan Golden <git@brendan.ie>
Date: Sun, 29 Sep 2024 21:39:27 +0100
Subject: [PATCH 3/7] feat: new struct to mirror the databse

---
 src/lib.rs | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/src/lib.rs b/src/lib.rs
index 3eda4aa..c6ba064 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -293,6 +293,38 @@ impl<'r> FromRow<'r, SqliteRow> for Minecraft {
   }
 }
 
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct RoleAdder {
+  pub server: GuildId,
+  pub role_a: RoleId,
+  pub role_b: RoleId,
+  pub role_c: RoleId,
+}
+impl<'r> FromRow<'r, SqliteRow> for RoleAdder {
+  fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
+    let server_tmp: i64 = row.try_get("server_discord")?;
+    let server = GuildId::from(server_tmp as u64);
+
+    Ok(Self {
+      server,
+      role_a: get_role_from_row(row, "role_a"),
+      role_b: get_role_from_row(row, "role_b"),
+      role_c: get_role_from_row(row, "role_c"),
+    })
+  }
+}
+
+fn get_role_from_row(row: &SqliteRow, col: &str)-> RoleId{
+  match row.try_get(col) {
+    Ok(x) => {
+      let tmp: i64 = x;
+      RoleId(tmp as u64)
+    }
+    _ => RoleId::from(0u64),
+  }
+}
+
+
 pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
   let database = format!("{}/{}", &config.home, &config.database);
 

From ffce78a10de8c733c5251e5bd4fecdea0139ff81 Mon Sep 17 00:00:00 2001
From: Brendan Golden <git@brendan.ie>
Date: Sun, 29 Sep 2024 22:19:58 +0100
Subject: [PATCH 4/7] feat: command config for setting it up

(lsit command can come later to see the active ones)
---
 src/commands/mod.rs        |   1 +
 src/commands/role_adder.rs | 172 +++++++++++++++++++++++++++++++++++++
 2 files changed, 173 insertions(+)
 create mode 100644 src/commands/role_adder.rs

diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 3acfe81..a11ffd5 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -2,3 +2,4 @@ pub mod add_server;
 pub mod committee;
 pub mod link_email;
 pub mod minecraft;
+pub mod role_adder;
diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs
new file mode 100644
index 0000000..ef7effb
--- /dev/null
+++ b/src/commands/role_adder.rs
@@ -0,0 +1,172 @@
+use serenity::{
+  builder::CreateApplicationCommand,
+  client::Context,
+  model::{
+    application::interaction::application_command::ApplicationCommandInteraction,
+    prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue},
+  },
+};
+
+use skynet_discord_bot::{is_admin, DataBase, RoleAdder};
+use sqlx::{Error, Pool, Sqlite};
+
+pub mod edit {
+  use super::*;
+
+  pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
+    // check if user has high enough permisssions
+    if let Some(msg) = is_admin(command, ctx).await {
+      return msg;
+    }
+
+    let role_a = if let CommandDataOptionValue::Role(role) = command
+        .data
+        .options
+        .get(0)
+        .expect("Expected role option")
+        .resolved
+        .as_ref()
+        .expect("Expected role object")
+    {
+      role.id.to_owned()
+    } else {
+      return "Please provide a valid role for ``Role Current``".to_string();
+    };
+
+    let role_b = if let CommandDataOptionValue::Role(role) = command
+        .data
+        .options
+        .get(1)
+        .expect("Expected role option")
+        .resolved
+        .as_ref()
+        .expect("Expected role object")
+    {
+      role.id.to_owned()
+    } else {
+      return "Please provide a valid role for ``Role Current``".to_string();
+    };
+
+    let role_c = if let CommandDataOptionValue::Role(role) = command
+        .data
+        .options
+        .get(2)
+        .expect("Expected role option")
+        .resolved
+        .as_ref()
+        .expect("Expected role object")
+    {
+      role.id.to_owned()
+    } else {
+      return "Please provide a valid role for ``Role Current``".to_string();
+    };
+
+    let delete = if let CommandDataOptionValue::Boolean(delete) = command
+        .data
+        .options
+        .get(3)
+        .expect("Expected true/false option")
+        .resolved
+        .as_ref()
+        .expect("Expected true/False object")
+    {
+      delete.to_owned()
+    } else {
+      false
+    };
+
+    let db_lock = {
+      let data_read = ctx.data.read().await;
+      data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
+    };
+    let db = db_lock.read().await;
+
+    let server_data = RoleAdder {
+      server: command.guild_id.unwrap_or_default(),
+      role_a,
+      role_b,
+      role_c,
+    };
+
+    match add_server(&db, &server_data, delete).await {
+      Ok(_) => {}
+      Err(e) => {
+        println!("{:?}", e);
+        return format!("Failure to insert into Servers {:?}", server_data);
+      }
+    }
+
+    "Added/Updated server info".to_string()
+  }
+
+  pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
+    command
+        .name("roles_adder")
+        .description("Combine roles together to an new one")
+        .create_option(|option| {
+          option
+              .name("role_a")
+              .description("A role you want to add to Role B")
+              .kind(CommandOptionType::Role)
+              .required(true)
+        })
+        .create_option(|option| {
+          option
+              .name("role_b")
+              .description("A role you want to add to Role A")
+              .kind(CommandOptionType::Role)
+              .required(true)
+        })
+        .create_option(|option| {
+          option
+              .name("role_c")
+              .description("Sum of A and B")
+              .kind(CommandOptionType::Role)
+              .required(true)
+        })
+        .create_option(|option| {
+          option
+              .name("delete")
+              .description("Delete this entry.")
+              .kind(CommandOptionType::Boolean)
+              .required(false)
+        })
+  }
+
+  async fn add_server(db: &Pool<Sqlite>, server: &RoleAdder, delete: bool) -> Result<Option<RoleAdder>, Error> {
+    let action;
+    if delete {
+      action = sqlx::query_as::<_, RoleAdder>(
+        "
+      DELETE FROM roles_adder
+      WHERE server = ?1 AND role_a = ?2 AND role_b = ?3 AND role_c = ?4
+    ",
+      )
+          .bind(*server.server.as_u64() as i64)
+          .bind(*server.role_a.as_u64() as i64)
+          .bind(*server.role_b.as_u64() as i64)
+          .bind(*server.role_c.as_u64() as i64)
+          .fetch_optional(db)
+          .await;
+    } else {
+      action = sqlx::query_as::<_, RoleAdder>(
+        "
+      INSERT OR REPLACE INTO roles_adder (server, role_a, role_b, role_c)
+      VALUES (?1, ?2, ?3, ?4)
+    ",
+      )
+          .bind(*server.server.as_u64() as i64)
+          .bind(*server.role_a.as_u64() as i64)
+          .bind(*server.role_b.as_u64() as i64)
+          .bind(*server.role_c.as_u64() as i64)
+          .fetch_optional(db)
+          .await;
+    }
+
+    action
+  }
+
+}
+
+// TODO
+pub mod list {}
\ No newline at end of file

From 32292a3c0b47aaee26396c968ef46d9c231eecdb Mon Sep 17 00:00:00 2001
From: Brendan Golden <git@brendan.ie>
Date: Sun, 29 Sep 2024 23:47:33 +0100
Subject: [PATCH 5/7] feat: tested out command and gt rid of the last few kinks

---
 src/commands/role_adder.rs | 92 +++++++++++++++++++++++++++++++++-----
 src/lib.rs                 |  2 +-
 src/main.rs                | 26 +++++------
 3 files changed, 93 insertions(+), 27 deletions(-)

diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs
index ef7effb..3f224ef 100644
--- a/src/commands/role_adder.rs
+++ b/src/commands/role_adder.rs
@@ -61,19 +61,24 @@ pub mod edit {
       return "Please provide a valid role for ``Role Current``".to_string();
     };
 
-    let delete = if let CommandDataOptionValue::Boolean(delete) = command
+
+    let mut delete = false;
+
+    if let Some(x) = command
         .data
         .options
-        .get(3)
-        .expect("Expected true/false option")
-        .resolved
-        .as_ref()
-        .expect("Expected true/False object")
-    {
-      delete.to_owned()
-    } else {
-      false
-    };
+        .get(3) {
+
+      let tmp = x.to_owned();
+      if let Some(y) = tmp.resolved {
+        match y {
+          CommandDataOptionValue::Boolean(z) => {
+            delete = z;
+          }
+          _ => {}
+        }
+      }
+    }
 
     let db_lock = {
       let data_read = ctx.data.read().await;
@@ -169,4 +174,67 @@ pub mod edit {
 }
 
 // TODO
-pub mod list {}
\ No newline at end of file
+pub mod list {}
+
+
+pub mod tools {
+  use serenity::client::Context;
+  use serenity::model::guild::Member;
+  use sqlx::{Pool, Sqlite};
+  use skynet_discord_bot::RoleAdder;
+
+  pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, mut new_data: Member){
+
+
+    // check if the role changed is part of the oens for this server
+    if let Some(role_adders) = sqlx::query_as::<_, RoleAdder>(
+      r#"
+      SELECT *
+      FROM roles_adder
+      WHERE server = ?
+      "#,
+    )
+        .bind(*new_data.guild_id.as_u64() as i64)
+        .fetch_all(db)
+        .await
+        .ok() {
+
+
+      let mut roles_add = vec![];
+      let mut roles_remove = vec![];
+
+      for role_adder in role_adders {
+        // if the user has both A dnd B give them C
+        if new_data.roles.contains(&role_adder.role_a) &&
+            new_data.roles.contains(&role_adder.role_b) &&
+            !new_data.roles.contains(&role_adder.role_c) {
+          roles_add.push(role_adder.role_c);
+        }
+
+        // If the suer has C but not A or B remove C
+        if new_data.roles.contains(&role_adder.role_c) &&
+            (
+              !new_data.roles.contains(&role_adder.role_a) || !new_data.roles.contains(&role_adder.role_b)
+            ) {
+          roles_remove.push(role_adder.role_c);
+        }
+      }
+
+
+
+      if !roles_add.is_empty(){
+        if let Err(e) = new_data.add_roles(&ctx, &roles_add).await {
+          println!("{:?}", e);
+        }
+      }
+
+      if !roles_remove.is_empty(){
+        if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await{
+          println!("{:?}", e);
+        }
+      }
+    }
+  }
+
+
+}
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index c6ba064..09315f0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -302,7 +302,7 @@ pub struct RoleAdder {
 }
 impl<'r> FromRow<'r, SqliteRow> for RoleAdder {
   fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
-    let server_tmp: i64 = row.try_get("server_discord")?;
+    let server_tmp: i64 = row.try_get("server")?;
     let server = GuildId::from(server_tmp as u64);
 
     Ok(Self {
diff --git a/src/main.rs b/src/main.rs
index 413dc6f..275212d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,7 +6,6 @@ use serenity::{
   model::{
     application::{command::Command, interaction::Interaction},
     gateway::{GatewayIntents, Ready},
-    guild,
     prelude::Activity,
     user::OnlineStatus,
   },
@@ -16,13 +15,14 @@ use std::sync::Arc;
 use serenity::model::guild::Member;
 use skynet_discord_bot::{db_init, get_config, get_server_config, get_server_member, Config, DataBase};
 use tokio::sync::RwLock;
+use crate::commands::role_adder::tools::on_role_change;
 
 struct Handler;
 
 #[async_trait]
 impl EventHandler for Handler {
   // handles previously linked accounts joining the server
-  async fn guild_member_addition(&self, ctx: Context, mut new_member: guild::Member) {
+  async fn guild_member_addition(&self, ctx: Context, mut new_member: Member) {
     let db_lock = {
       let data_read = ctx.data.read().await;
       data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
@@ -76,6 +76,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
     match Command::set_global_application_commands(&ctx.http, |commands| {
       commands
         .create_application_command(|command| commands::add_server::register(command))
+        .create_application_command(|command| commands::role_adder::edit::register(command))
         .create_application_command(|command| commands::link_email::link::register(command))
         .create_application_command(|command| commands::link_email::verify::register(command))
         .create_application_command(|command| commands::minecraft::server::add::register(command))
@@ -107,6 +108,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
         "link_minecraft" => commands::minecraft::user::add::run(&command, &ctx).await,
         // admin commands
         "add" => commands::add_server::run(&command, &ctx).await,
+        "roles_adder" => commands::role_adder::edit::run(&command, &ctx).await,
         "minecraft_add" => commands::minecraft::server::add::run(&command, &ctx).await,
         "minecraft_list" => commands::minecraft::server::list::run(&command, &ctx).await,
         "minecraft_delete" => commands::minecraft::server::delete::run(&command, &ctx).await,
@@ -123,22 +125,18 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
   }
 
   // handles role updates
-  async fn guild_member_update(&self, _ctx: Context, _old_if_available: Option<Member>, _new: Member){
-    if let Some(x) = _old_if_available {
-      if x.roles.len() != _new.roles.len() {
-        return;
-      }
-      //do we need to do more comparison here?
-    }
-
+  async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Member){
 
     // get config/db
+    let db_lock = {
+      let data_read = ctx.data.read().await;
+      data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
+    };
+
+    let db = db_lock.read().await;
 
     // check if the role changed is part of the oens for this server
-
-    // if so add or remove the resultant one
-
-    // TODO: Finish
+    on_role_change(&db, &ctx, new_data).await;
   }
 }
 

From 5e7964ae26b97ba668deb725fc303bda295c3f95 Mon Sep 17 00:00:00 2001
From: Brendan Golden <git@brendan.ie>
Date: Mon, 30 Sep 2024 00:03:03 +0100
Subject: [PATCH 6/7] feat: some cleanup in messages and added some handrails
 so folks wont add stupid combos

---
 src/commands/role_adder.rs | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs
index 3f224ef..08e0286 100644
--- a/src/commands/role_adder.rs
+++ b/src/commands/role_adder.rs
@@ -61,6 +61,13 @@ pub mod edit {
       return "Please provide a valid role for ``Role Current``".to_string();
     };
 
+    if role_a == role_b {
+      return "Roles A and B must be different".to_string();
+    }
+
+    if (role_c == role_a)|| (role_c == role_b) {
+      return "Role C cannot be same as A or B".to_string();
+    }
 
     let mut delete = false;
 
@@ -86,8 +93,9 @@ pub mod edit {
     };
     let db = db_lock.read().await;
 
+    let server = command.guild_id.unwrap_or_default();
     let server_data = RoleAdder {
-      server: command.guild_id.unwrap_or_default(),
+      server,
       role_a,
       role_b,
       role_c,
@@ -101,7 +109,29 @@ pub mod edit {
       }
     }
 
-    "Added/Updated server info".to_string()
+
+    let mut role_a_name = String::new();
+    let mut role_b_name = String::new();
+    let mut role_c_name = String::new();
+
+    if let Ok(x) = server.roles(&ctx).await {
+      if let Some(y) = x.get(&role_a){
+        role_a_name = y.to_owned().name;
+      }
+      if let Some(y) = x.get(&role_b){
+        role_b_name = y.to_owned().name;
+      }
+      if let Some(y) = x.get(&role_b){
+        role_c_name = y.to_owned().name;
+      }
+    }
+
+
+    if delete {
+      format!("Removed {} + {} = {}", role_a_name, role_b_name, role_c_name)
+    } else {
+     format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name)
+    }
   }
 
   pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {

From 0d9ce2de7f61cd7d400087686631f4b5e7bb4e4b Mon Sep 17 00:00:00 2001
From: Brendan Golden <git@brendan.ie>
Date: Mon, 30 Sep 2024 00:09:29 +0100
Subject: [PATCH 7/7] fmt: fmt and clippy

---
 src/commands/role_adder.rs | 181 ++++++++++++++++---------------------
 src/lib.rs                 |   3 +-
 src/main.rs                |   9 +-
 3 files changed, 84 insertions(+), 109 deletions(-)

diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs
index 08e0286..a740194 100644
--- a/src/commands/role_adder.rs
+++ b/src/commands/role_adder.rs
@@ -20,13 +20,13 @@ pub mod edit {
     }
 
     let role_a = if let CommandDataOptionValue::Role(role) = command
-        .data
-        .options
-        .get(0)
-        .expect("Expected role option")
-        .resolved
-        .as_ref()
-        .expect("Expected role object")
+      .data
+      .options
+      .get(0)
+      .expect("Expected role option")
+      .resolved
+      .as_ref()
+      .expect("Expected role object")
     {
       role.id.to_owned()
     } else {
@@ -34,13 +34,13 @@ pub mod edit {
     };
 
     let role_b = if let CommandDataOptionValue::Role(role) = command
-        .data
-        .options
-        .get(1)
-        .expect("Expected role option")
-        .resolved
-        .as_ref()
-        .expect("Expected role object")
+      .data
+      .options
+      .get(1)
+      .expect("Expected role option")
+      .resolved
+      .as_ref()
+      .expect("Expected role object")
     {
       role.id.to_owned()
     } else {
@@ -48,13 +48,13 @@ pub mod edit {
     };
 
     let role_c = if let CommandDataOptionValue::Role(role) = command
-        .data
-        .options
-        .get(2)
-        .expect("Expected role option")
-        .resolved
-        .as_ref()
-        .expect("Expected role object")
+      .data
+      .options
+      .get(2)
+      .expect("Expected role option")
+      .resolved
+      .as_ref()
+      .expect("Expected role object")
     {
       role.id.to_owned()
     } else {
@@ -65,17 +65,13 @@ pub mod edit {
       return "Roles A and B must be different".to_string();
     }
 
-    if (role_c == role_a)|| (role_c == role_b) {
+    if (role_c == role_a) || (role_c == role_b) {
       return "Role C cannot be same as A or B".to_string();
     }
 
     let mut delete = false;
 
-    if let Some(x) = command
-        .data
-        .options
-        .get(3) {
-
+    if let Some(x) = command.data.options.get(3) {
       let tmp = x.to_owned();
       if let Some(y) = tmp.resolved {
         match y {
@@ -109,63 +105,55 @@ pub mod edit {
       }
     }
 
-
     let mut role_a_name = String::new();
     let mut role_b_name = String::new();
     let mut role_c_name = String::new();
 
     if let Ok(x) = server.roles(&ctx).await {
-      if let Some(y) = x.get(&role_a){
+      if let Some(y) = x.get(&role_a) {
         role_a_name = y.to_owned().name;
       }
-      if let Some(y) = x.get(&role_b){
+      if let Some(y) = x.get(&role_b) {
         role_b_name = y.to_owned().name;
       }
-      if let Some(y) = x.get(&role_b){
+      if let Some(y) = x.get(&role_b) {
         role_c_name = y.to_owned().name;
       }
     }
 
-
     if delete {
       format!("Removed {} + {} = {}", role_a_name, role_b_name, role_c_name)
     } else {
-     format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name)
+      format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name)
     }
   }
 
   pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
     command
-        .name("roles_adder")
-        .description("Combine roles together to an new one")
-        .create_option(|option| {
-          option
-              .name("role_a")
-              .description("A role you want to add to Role B")
-              .kind(CommandOptionType::Role)
-              .required(true)
-        })
-        .create_option(|option| {
-          option
-              .name("role_b")
-              .description("A role you want to add to Role A")
-              .kind(CommandOptionType::Role)
-              .required(true)
-        })
-        .create_option(|option| {
-          option
-              .name("role_c")
-              .description("Sum of A and B")
-              .kind(CommandOptionType::Role)
-              .required(true)
-        })
-        .create_option(|option| {
-          option
-              .name("delete")
-              .description("Delete this entry.")
-              .kind(CommandOptionType::Boolean)
-              .required(false)
-        })
+      .name("roles_adder")
+      .description("Combine roles together to an new one")
+      .create_option(|option| {
+        option
+          .name("role_a")
+          .description("A role you want to add to Role B")
+          .kind(CommandOptionType::Role)
+          .required(true)
+      })
+      .create_option(|option| {
+        option
+          .name("role_b")
+          .description("A role you want to add to Role A")
+          .kind(CommandOptionType::Role)
+          .required(true)
+      })
+      .create_option(|option| option.name("role_c").description("Sum of A and B").kind(CommandOptionType::Role).required(true))
+      .create_option(|option| {
+        option
+          .name("delete")
+          .description("Delete this entry.")
+          .kind(CommandOptionType::Boolean)
+          .required(false)
+      })
   }
 
   async fn add_server(db: &Pool<Sqlite>, server: &RoleAdder, delete: bool) -> Result<Option<RoleAdder>, Error> {
@@ -177,12 +165,12 @@ pub mod edit {
       WHERE server = ?1 AND role_a = ?2 AND role_b = ?3 AND role_c = ?4
     ",
       )
-          .bind(*server.server.as_u64() as i64)
-          .bind(*server.role_a.as_u64() as i64)
-          .bind(*server.role_b.as_u64() as i64)
-          .bind(*server.role_c.as_u64() as i64)
-          .fetch_optional(db)
-          .await;
+      .bind(*server.server.as_u64() as i64)
+      .bind(*server.role_a.as_u64() as i64)
+      .bind(*server.role_b.as_u64() as i64)
+      .bind(*server.role_c.as_u64() as i64)
+      .fetch_optional(db)
+      .await;
     } else {
       action = sqlx::query_as::<_, RoleAdder>(
         "
@@ -190,32 +178,28 @@ pub mod edit {
       VALUES (?1, ?2, ?3, ?4)
     ",
       )
-          .bind(*server.server.as_u64() as i64)
-          .bind(*server.role_a.as_u64() as i64)
-          .bind(*server.role_b.as_u64() as i64)
-          .bind(*server.role_c.as_u64() as i64)
-          .fetch_optional(db)
-          .await;
+      .bind(*server.server.as_u64() as i64)
+      .bind(*server.role_a.as_u64() as i64)
+      .bind(*server.role_b.as_u64() as i64)
+      .bind(*server.role_c.as_u64() as i64)
+      .fetch_optional(db)
+      .await;
     }
 
     action
   }
-
 }
 
 // TODO
 pub mod list {}
 
-
 pub mod tools {
   use serenity::client::Context;
   use serenity::model::guild::Member;
-  use sqlx::{Pool, Sqlite};
   use skynet_discord_bot::RoleAdder;
+  use sqlx::{Pool, Sqlite};
 
-  pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, mut new_data: Member){
-
-
+  pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, mut new_data: Member) {
     // check if the role changed is part of the oens for this server
     if let Some(role_adders) = sqlx::query_as::<_, RoleAdder>(
       r#"
@@ -224,47 +208,40 @@ pub mod tools {
       WHERE server = ?
       "#,
     )
-        .bind(*new_data.guild_id.as_u64() as i64)
-        .fetch_all(db)
-        .await
-        .ok() {
-
-
+    .bind(*new_data.guild_id.as_u64() as i64)
+    .fetch_all(db)
+    .await
+    .ok()
+    {
       let mut roles_add = vec![];
       let mut roles_remove = vec![];
 
       for role_adder in role_adders {
         // if the user has both A dnd B give them C
-        if new_data.roles.contains(&role_adder.role_a) &&
-            new_data.roles.contains(&role_adder.role_b) &&
-            !new_data.roles.contains(&role_adder.role_c) {
+        if new_data.roles.contains(&role_adder.role_a) && new_data.roles.contains(&role_adder.role_b) && !new_data.roles.contains(&role_adder.role_c)
+        {
           roles_add.push(role_adder.role_c);
         }
 
         // If the suer has C but not A or B remove C
-        if new_data.roles.contains(&role_adder.role_c) &&
-            (
-              !new_data.roles.contains(&role_adder.role_a) || !new_data.roles.contains(&role_adder.role_b)
-            ) {
+        if new_data.roles.contains(&role_adder.role_c)
+          && (!new_data.roles.contains(&role_adder.role_a) || !new_data.roles.contains(&role_adder.role_b))
+        {
           roles_remove.push(role_adder.role_c);
         }
       }
 
-
-
-      if !roles_add.is_empty(){
+      if !roles_add.is_empty() {
         if let Err(e) = new_data.add_roles(&ctx, &roles_add).await {
           println!("{:?}", e);
         }
       }
 
-      if !roles_remove.is_empty(){
-        if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await{
+      if !roles_remove.is_empty() {
+        if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await {
           println!("{:?}", e);
         }
       }
     }
   }
-
-
-}
\ No newline at end of file
+}
diff --git a/src/lib.rs b/src/lib.rs
index 09315f0..5923151 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -314,7 +314,7 @@ impl<'r> FromRow<'r, SqliteRow> for RoleAdder {
   }
 }
 
-fn get_role_from_row(row: &SqliteRow, col: &str)-> RoleId{
+fn get_role_from_row(row: &SqliteRow, col: &str) -> RoleId {
   match row.try_get(col) {
     Ok(x) => {
       let tmp: i64 = x;
@@ -324,7 +324,6 @@ fn get_role_from_row(row: &SqliteRow, col: &str)-> RoleId{
   }
 }
 
-
 pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
   let database = format!("{}/{}", &config.home, &config.database);
 
diff --git a/src/main.rs b/src/main.rs
index 275212d..a7735e7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,7 @@
 pub mod commands;
 
+use crate::commands::role_adder::tools::on_role_change;
+use serenity::model::guild::Member;
 use serenity::{
   async_trait,
   client::{Context, EventHandler},
@@ -11,11 +13,9 @@ use serenity::{
   },
   Client,
 };
-use std::sync::Arc;
-use serenity::model::guild::Member;
 use skynet_discord_bot::{db_init, get_config, get_server_config, get_server_member, Config, DataBase};
+use std::sync::Arc;
 use tokio::sync::RwLock;
-use crate::commands::role_adder::tools::on_role_change;
 
 struct Handler;
 
@@ -125,8 +125,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
   }
 
   // handles role updates
-  async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Member){
-
+  async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Member) {
     // get config/db
     let db_lock = {
       let data_read = ctx.data.read().await;