From 1aef86ad01eda0a738ee768dcfcc5a588f0dc0c1 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 18 Feb 2025 21:14:05 +0000 Subject: [PATCH] feat: now properly sets and removes roles for committee members --- db/migrations/8_committee-mk-ii.sql | 2 + doc/Committee.md | 4 -- src/bin/update_data.rs | 5 +- src/commands/add_server.rs | 51 ++---------------- src/common/database.rs | 3 -- src/common/set_roles.rs | 84 ++++++++++++++++++++++------- src/common/wolves.rs | 35 +++++++++++- src/lib.rs | 2 - src/main.rs | 40 ++++++++++---- 9 files changed, 137 insertions(+), 89 deletions(-) diff --git a/db/migrations/8_committee-mk-ii.sql b/db/migrations/8_committee-mk-ii.sql index ef6d185..3c942cd 100644 --- a/db/migrations/8_committee-mk-ii.sql +++ b/db/migrations/8_committee-mk-ii.sql @@ -10,3 +10,5 @@ CREATE TABLE IF NOT EXISTS committees ( link text not null, committee text not null ); + +ALTER TABLE servers DROP COLUMN wolves_link; \ No newline at end of file diff --git a/doc/Committee.md b/doc/Committee.md index d53be13..cfe718e 100644 --- a/doc/Committee.md +++ b/doc/Committee.md @@ -46,10 +46,6 @@ You (personally) will need a role with ``Administrator`` permission to be able t 4. ``role_past`` (optional) is the role for all current and past members. 5. ``bot_channel`` is a channel that folks are recommended to use the bot. * You can have it so folks cannot see message history -6. ``server_name`` For example ``UL Computer Society`` - * Will be removed in the future -7. ``wolves_link`` for example - * Will be removed in the future At this point the bot is set up and no further action is required. diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 4385a1c..2d36892 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -16,7 +16,10 @@ async fn main() { let config = get_config(); let db = match db_init(&config).await { Ok(x) => x, - Err(_) => return, + Err(e) => { + dbg!(e); + return; + } }; // Intents are a bitflag, bitwise operations can be used to dictate which intents to use diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 52284ac..8f06527 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -67,34 +67,6 @@ pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> Stri return "Please provide a valid channel for ``Bot Channel``".to_string(); }; - let server_name = if let CommandDataOptionValue::String(name) = command - .data - .options - .get(3) - .expect("Expected Server Name option") - .resolved - .as_ref() - .expect("Expected Server Name object") - { - name - } else { - &"UL Computer Society".to_string() - }; - - let wolves_link = if let CommandDataOptionValue::String(wolves) = command - .data - .options - .get(4) - .expect("Expected Wolves Link option") - .resolved - .as_ref() - .expect("Expected Server Name object") - { - wolves - } else { - &"https://ulwolves.ie/society/computer".to_string() - }; - let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() @@ -109,8 +81,7 @@ pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> Stri member_past: 0, member_current: 0, bot_channel_id, - server_name: server_name.to_owned(), - wolves_link: wolves_link.to_string(), + server_name: "".to_string(), }; match add_server(&db, ctx, &server_data).await { @@ -149,20 +120,6 @@ pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicatio .kind(CommandOptionType::Channel) .required(true) }) - .create_option(|option| { - option - .name("server_name") - .description("Name of the Discord Server.") - .kind(CommandOptionType::String) - .required(true) - }) - .create_option(|option| { - option - .name("wolves_link") - .description("Link to the Club/Society on UL Wolves.") - .kind(CommandOptionType::String) - .required(true) - }) .create_option(|option| { option .name("role_past") @@ -178,8 +135,8 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul let insert = sqlx::query_as::<_, Servers>( " - INSERT OR REPLACE INTO servers (server, wolves_api, role_past, role_current, bot_channel_id, server_name, wolves_link) - VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) + INSERT OR REPLACE INTO servers (server, wolves_api, role_past, role_current, bot_channel_id) + VALUES (?1, ?2, ?3, ?4, ?5) ", ) .bind(*server.server.as_u64() as i64) @@ -187,8 +144,6 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul .bind(role_past) .bind(*server.role_current.as_u64() as i64) .bind(*server.bot_channel_id.as_u64() as i64) - .bind(&server.server_name) - .bind(&server.wolves_link) .fetch_optional(db) .await; diff --git a/src/common/database.rs b/src/common/database.rs index 5139cb5..ace94ec 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -135,9 +135,7 @@ pub struct Servers { pub member_past: i64, pub member_current: i64, pub bot_channel_id: ChannelId, - // TODO: these can be removed in teh future with an API update pub server_name: String, - pub wolves_link: String, } impl<'r> FromRow<'r, SqliteRow> for Servers { @@ -175,7 +173,6 @@ impl<'r> FromRow<'r, SqliteRow> for Servers { member_current: row.try_get("member_current")?, bot_channel_id, server_name: row.try_get("server_name")?, - wolves_link: row.try_get("wolves_link")?, }) } } diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 912d661..f0a02cb 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -137,6 +137,8 @@ pub mod committee { use serenity::client::Context; use serenity::model::channel::ChannelType; use serenity::model::guild::Member; + use serenity::model::id::ChannelId; + use serenity::model::prelude::RoleId; use sqlx::{Pool, Sqlite}; use std::collections::HashMap; use std::sync::Arc; @@ -170,6 +172,7 @@ pub mod committee { let server = config.committee_server; let committee_member = config.committee_role; let committees = get_committees(db).await; + let categories = vec![config.committee_category, ChannelId(1341457244973305927), ChannelId(1341457509717639279)]; // information about the server let roles = server.roles(&ctx).await.unwrap_or_default(); @@ -185,8 +188,10 @@ pub mod committee { for channel in channels.values() { // we only care about teh channels in teh category if let Some(x) = channel.parent_id { - if x.eq(&config.committee_category) { - channels_name.insert(channel.name.to_owned(), channel.to_owned()); + for category in &categories { + if x.eq(category) { + channels_name.insert(channel.name.to_owned(), channel.to_owned()); + } } } } @@ -197,13 +202,21 @@ pub mod committee { // a list of all the roles that can be removed from folks who should have them let mut committee_roles = vec![committee_member]; - for committee in &committees { + let mut category_index = 0; + + let mut i = 0; + loop { + if i >= committees.len() { + break; + } + let committee = &committees[i]; + // get the role for this committee/club/soc - let role = match roles_name.get(&committee.name_profile) { + let role = match roles_name.get(&committee.name_full) { Some(x) => Some(x.to_owned()), None => { // create teh role if it does not exist - match server.create_role(&ctx, |r| r.hoist(false).mentionable(true).name(&committee.name_profile)).await { + match server.create_role(&ctx, |r| r.hoist(false).mentionable(true).name(&committee.name_full)).await { Ok(x) => Some(x), Err(_) => None, } @@ -213,7 +226,7 @@ pub mod committee { // create teh channel if it does nto exist if !channels_name.contains_key(&committee.name_profile) { match server - .create_channel(&ctx, |c| c.name(&committee.name_profile).kind(ChannelType::Text).category(config.committee_category)) + .create_channel(&ctx, |c| c.name(&committee.name_profile).kind(ChannelType::Text).category(categories[category_index])) .await { Ok(x) => { @@ -223,7 +236,14 @@ pub mod committee { println!("Created channel: {}", &committee.name_profile); } Err(x) => { - dbg!("Unable to create channel: ", x); + let tmp = x.to_string(); + + if x.to_string().contains("Maximum number of channels in category reached (50)") { + category_index += 1; + + continue; + } + dbg!("Unable to create channel: ", &tmp, &tmp.contains("Maximum number of channels in category reached (50)")); } } }; @@ -236,42 +256,60 @@ pub mod committee { // ID in this is the wolves ID, so we need to get a matching discord ID (if one exists) if let Some(x) = get_server_member_discord(db, id_wolves).await { if let Some(member_tmp) = x.discord { - let values = users_roles.entry(member_tmp).or_insert(vec![]); + let values = users_roles.entry(member_tmp).or_insert(vec![committee_member]); values.push(r.id); } } } } + + i += 1; } // now we have a map of all users that should get roles time to go through all the folks on teh server for member in members { + let roles_current = member.roles(ctx).unwrap_or_default(); + let roles_required = match users_roles.get(&member.user.id) { None => { vec![] } Some(x) => { - let mut combined = x.to_owned(); - // this is the main role, since it provides access to everything. - combined.push(committee_member); - combined + let mut tmp = x.to_owned(); + if !tmp.is_empty() { + tmp.push(RoleId(1226602779968274573)); + } + tmp } }; - // get a list of all the roles to remove from someone let mut roles_rem = vec![]; - for role in &committee_roles { - if !roles_required.contains(role) { - roles_rem.push(role.to_owned()); + let mut roles_add = vec![]; + // get a list of all the roles to remove from someone + + let mut roles_current_id = vec![]; + for role in &roles_current { + roles_current_id.push(role.id.to_owned()); + if !roles_required.contains(&role.id) { + roles_rem.push(role.id.to_owned()); } } + + for role in &roles_required { + if !roles_current_id.contains(role) { + roles_add.push(role.to_owned()); + } + } + if !roles_rem.is_empty() { member.remove_roles(&ctx, &roles_rem).await.unwrap_or_default(); } - if !roles_required.is_empty() { + if !roles_add.is_empty() { // these roles are flavor roles, only there to make folks mentionable - member.add_roles(&ctx, &roles_required).await.unwrap_or_default(); + member.add_roles(&ctx, &roles_add).await.unwrap_or_default(); + } else { + member.remove_roles(&ctx, &[RoleId(1226602779968274573)]).await.unwrap_or_default(); } } @@ -303,7 +341,7 @@ pub mod committee { } async fn get_committees(db: &Pool) -> Vec { - sqlx::query_as::<_, Committees>( + match sqlx::query_as::<_, Committees>( r#" SELECT * FROM committees @@ -311,7 +349,13 @@ pub mod committee { ) .fetch_all(db) .await - .unwrap_or_default() + { + Ok(a) => a, + Err(e) => { + dbg!(e); + vec![] + } + } } async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { diff --git a/src/common/wolves.rs b/src/common/wolves.rs index ba0ff93..9141983 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -87,15 +87,22 @@ pub mod cns { server, // this is the unique api key for each club/soc wolves_api, + server_name, .. } = &server_config; + // dbg!(&server_config); let existing_tmp = get_server_member(&db, server).await; let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::>(); // list of users that need to be updated for this server let mut user_to_update = vec![]; + let mut server_name_tmp = None; for user in wolves.get_members(wolves_api).await { + // dbg!(&user.committee); + if server_name_tmp.is_none() { + server_name_tmp = Some(user.committee.to_owned()); + } let id = user.member_id.parse::().unwrap_or_default(); match existing.get(&(id as i64)) { None => { @@ -117,12 +124,38 @@ pub mod cns { } } + if let Some(name) = server_name_tmp { + if &name != server_name { + set_server_member(&db, server, &name).await; + } + } if !user_to_update.is_empty() { update_server(ctx, &server_config, &[], &user_to_update).await; } } } + async fn set_server_member(db: &Pool, server: &GuildId, name: &str) { + match sqlx::query_as::<_, Servers>( + " + UPDATE servers + SET server_name = ? + WHERE server = ? + ", + ) + .bind(name) + .bind(*server.as_u64() as i64) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + println!("Failure to set server name {}", server.as_u64()); + println!("{:?}", e); + } + } + } + async fn get_server_member(db: &Pool, server: &GuildId) -> Vec { sqlx::query_as::<_, ServerMembersWolves>( r#" @@ -223,7 +256,7 @@ pub mod committees { " INSERT INTO committees (id, name_profile, name_full, name_plain, link, committee) VALUES ($1, $2, $3, $4, $5, $6) - ON CONFLICT(id) DO UPDATE SET committee = $4 + ON CONFLICT(id) DO UPDATE SET committee = $6 ", ) .bind(committee.id) diff --git a/src/lib.rs b/src/lib.rs index 5b8d073..1e11ada 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -262,7 +262,6 @@ pub struct Servers { pub bot_channel_id: ChannelId, // these can be removed in teh future with an API update pub server_name: String, - pub wolves_link: String, } impl<'r> FromRow<'r, SqliteRow> for Servers { fn from_row(row: &'r SqliteRow) -> Result { @@ -299,7 +298,6 @@ impl<'r> FromRow<'r, SqliteRow> for Servers { member_current: row.try_get("member_current")?, bot_channel_id, server_name: row.try_get("server_name")?, - wolves_link: row.try_get("wolves_link")?, }) } } diff --git a/src/main.rs b/src/main.rs index 62c0350..e1fe422 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,9 @@ use serenity::{ }; use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_member, DataBase}; use skynet_discord_bot::common::set_roles::committee::update_committees; +use skynet_discord_bot::common::wolves::committees::Committees; use skynet_discord_bot::{get_config, Config}; +use sqlx::{Pool, Sqlite}; use std::sync::Arc; use tokio::sync::RwLock; @@ -67,20 +69,24 @@ impl EventHandler for Handler { println!("{:?}", e); } } else { - let msg = format!( - r#" + let tmp = get_committee(&db, &config_server.server_name).await; + if !tmp.is_empty() { + let committee = &tmp[0]; + let msg = format!( + r#" Welcome {} to the {} server! Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use ``/link_wolves`` to get full access. "#, - new_member.display_name(), - &config_server.server_name, - &config_server.wolves_link, - &config_server.server, - &config_server.bot_channel_id - ); + new_member.display_name(), + committee.name_full, + committee.link, + &config_server.server, + &config_server.bot_channel_id + ); - if let Err(err) = new_member.user.direct_message(&ctx, |m| m.content(&msg)).await { - dbg!(err); + if let Err(err) = new_member.user.direct_message(&ctx, |m| m.content(&msg)).await { + dbg!(err); + } } } } @@ -149,6 +155,20 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } } +async fn get_committee(db: &Pool, committee: &str) -> Vec { + sqlx::query_as::<_, Committees>( + r#" + SELECT * + FROM committees + WHERE name_plain = ? + "#, + ) + .bind(committee) + .fetch_all(db) + .await + .unwrap_or_default() +} + #[tokio::main] async fn main() { let config = get_config();