diff --git a/src/config.rs b/src/config.rs index a854a7230fcab99ace0047da31f50f35e296e2d1..4812888a9eea9022e4243ab645c1347347fc1bfd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,6 +24,7 @@ pub struct UccbotConfig { pub tiebreaker_role: u64, pub unregistered_member_role: u64, pub registered_member_role: u64, + pub expired_member_role: u64, pub command_prefix: String, pub for_vote: String, pub against_vote: String, diff --git a/src/config.yml b/src/config.yml index 9cbe58b5c615563ef2b94fa9c60cd2a7f2455107..5ea40fba6739d2cbe38266b1592b790d341c45cf 100644 --- a/src/config.yml +++ b/src/config.yml @@ -13,6 +13,7 @@ vote_role: 607478818038480937 tiebreaker_role: 607509283483025409 unregistered_member_role: 608282247350714408 registered_member_role: 608282133118582815 +expired_member_role: 0 command_prefix: "!" for_vote: "ðŸ‘" diff --git a/src/ldap.rs b/src/ldap.rs index ec7d67da46cf5026e96c53956ba1735e6f1f054c..7dce6c243482a85daab43152fb355cf691ae3049 100644 --- a/src/ldap.rs +++ b/src/ldap.rs @@ -6,6 +6,7 @@ pub struct LDAPUser { pub username: String, pub name: String, pub when_created: String, + pub login_shell: String, } pub fn ldap_search(username: &str) -> Option<LDAPUser> { @@ -24,7 +25,7 @@ pub fn ldap_search(username: &str) -> Option<LDAPUser> { "cn=Users,dc=ad,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au", Scope::Subtree, &format!("(cn={})", ldap3::ldap_escape(username)), - vec!["when_created", "displayName", "name"], + vec!["when_created", "displayName", "name", "loginShell"], ) .expect("LDAP error") .success() @@ -42,10 +43,14 @@ pub fn ldap_search(username: &str) -> Option<LDAPUser> { .get("displayName") .expect("LDAP failed to get 'displayName' field") .join(""), - when_created: "".to_string() // result + when_created: "".to_string(), // result // .get("whenCreated") // .expect("LDAP failed to get 'whenCreated' field") // .join(""), + login_shell: result + .get("loginShell") + .expect("LDAP failed to get 'loginShell' field") + .join(""), }) } diff --git a/src/main.rs b/src/main.rs index cc347e2fd0973d97c147c5e3768a59f85edfa4c6..d26cd29d8c97b2e0ef07325c20cf6bd431e63ccb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,7 +122,8 @@ impl EventHandler for Handler { `!poll <text>` to get people's opinions on something", false); embed.field("Account", "`!register <ucc username>` to link your Discord and UCC account\n\ `!profile <user>` to get the profile of a user\n\ - `!set <bio|git|web|photo>` to set that property of _your_ profile", false); + `!set <bio|git|web|photo>` to set that property of _your_ profile\n\ + `!updateroles` to update your registered roles", false); embed.field("Fun", "`!cowsay <text>` to have a cow say your words\n\ with no `<text>` it'll give you a fortune 😉", false); embed @@ -152,6 +153,7 @@ impl EventHandler for Handler { &ctx.http, format!("{:?}", ldap::tla_search(message_content[1])) ), + "updateroles" => user_management::Commands::update_registered_role(ctx, msg), _ => send_message!( msg.channel_id, &ctx.http, diff --git a/src/user_management.rs b/src/user_management.rs index 6791618e412d82d9ae56390788f0420fc979caa7..4c48f050a1b038cefef109e4d30d2ea28037658e 100644 --- a/src/user_management.rs +++ b/src/user_management.rs @@ -1,7 +1,7 @@ use rand::seq::SliceRandom; use regex::Regex; use serenity::{ - model::{channel::Message, guild::Member}, + model::{channel::Message, guild::Member, id::RoleId}, prelude::*, utils::MessageBuilder, }; @@ -10,7 +10,7 @@ use url::Url; use crate::config::CONFIG; use crate::database; -use crate::ldap::ldap_exists; +use crate::ldap::{ldap_exists, ldap_search}; use crate::token_management::*; pub fn new_member(ctx: &Context, mut new_member: Member) { @@ -133,6 +133,55 @@ impl Commands { Err(why) => error!("Unable to send message with mutt {:?}", why), }; } + + pub fn get_registered_role(name: String) -> Option<u64> { + guard!(let Some(result) = ldap_search(&name) else { + return None + }); + if result.login_shell.contains("locked") + && CONFIG.expired_member_role > 0 { + return Some(CONFIG.expired_member_role) + } + Some(CONFIG.registered_member_role) + } + + // TODO: make this return a result + // NOTE: don't make this directly send messages, so it can be used for mass updates + pub fn update_registered_role(ctx: Context, msg: Message) { + guard!(let Ok(member_info) = database::get_member_info(&msg.author.id.0) else { + return // Err() + }); + guard!(let Some(registered_role) = Commands::get_registered_role(member_info.username) else { + return // Err() + }); + guard!(let Ok(mut discord_member) = serenity::model::id::GuildId(CONFIG.server_id) + .member(ctx.http.clone(), msg.author.id) else { + return // Err() + }); + + let roles_to_remove = vec![ + CONFIG.registered_member_role, + CONFIG.unregistered_member_role, + CONFIG.expired_member_role]; + + for role in roles_to_remove { + if role == registered_role { // remove when vec.remove_item is stable + continue + } + if discord_member.roles.contains(&RoleId::from(role)) + && discord_member.remove_role(&ctx.http, role).is_err() { + return // Err() + } + } + + if !discord_member.roles.contains(&RoleId::from(registered_role)) + && discord_member.add_role(&ctx.http, registered_role).is_err() { + return // Err() + } + + // Ok() + } + pub fn verify(ctx: Context, msg: Message, token: &str) { match parse_token(&msg.author, token) { Ok(name) => { @@ -146,9 +195,13 @@ impl Commands { "Unable to remove role: {:?}", member.remove_role(&ctx.http, CONFIG.unregistered_member_role) ); + guard!(let Some(member_role) = Commands::get_registered_role(name) else { + send_message!(msg.channel_id, ctx.http.clone(), "Couldn't find you in LDAP!"); + return + }); e!( "Unable to add role: {:?}", - member.add_role(&ctx.http, CONFIG.registered_member_role) + member.add_role(&ctx.http, member_role) ); e!( "Unable to edit nickname: {:?}",