From 04a487cd8fc693af777dc7a69a808548fb9a36a6 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Sep 2024 15:07:29 +0100 Subject: [PATCH 01/40] fix: rename ``get_wolves`` to be just for clubs/socs --- src/bin/update_data.rs | 5 +++-- src/commands/add_server.rs | 4 ++-- src/lib.rs | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 22bd5bd..ec3713a 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -4,7 +4,7 @@ use serenity::{ model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::{db_init, get_config, get_data::get_wolves, Config, DataBase}; +use skynet_discord_bot::{db_init, get_config, get_data::get_wolves_cns, Config, DataBase}; use std::{process, sync::Arc}; use tokio::sync::RwLock; @@ -43,7 +43,8 @@ impl EventHandler for Handler { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); - get_wolves(&ctx).await; + // get the data for each individual club/soc + get_wolves_cns(&ctx).await; // finish up process::exit(0); diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index e1e7ca4..817d61a 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -6,7 +6,7 @@ use serenity::{ prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, }, }; -use skynet_discord_bot::get_data::get_wolves; +use skynet_discord_bot::get_data::get_wolves_cns; use skynet_discord_bot::{get_server_config, is_admin, set_roles::update_server, DataBase, Servers}; use sqlx::{Error, Pool, Sqlite}; @@ -147,7 +147,7 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul // update all users if update { // handle wolves api here - get_wolves(ctx).await; + get_wolves_cns(ctx).await; let mut roles_remove = vec![]; if current_remove { diff --git a/src/lib.rs b/src/lib.rs index c0d980a..a247d33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -523,7 +523,7 @@ pub mod get_data { pub email: String, pub expiry: String, } - pub async fn get_wolves(ctx: &Context) { + pub async fn get_wolves_cns(ctx: &Context) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() @@ -548,7 +548,7 @@ pub mod get_data { // list of users that need to be updated for this server let mut user_to_update = vec![]; - for user in get_wolves_sub(&config, wolves_api).await { + for user in get_wolves_cns_sub(&config, wolves_api).await { let id = user.member_id.parse::().unwrap_or_default(); match existing.get(&(id as i64)) { None => { @@ -594,7 +594,7 @@ pub mod get_data { .unwrap_or_default() } - async fn get_wolves_sub(config: &Config, wolves_api: &str) -> Vec { + async fn get_wolves_cns_sub(config: &Config, wolves_api: &str) -> Vec { if config.wolves_url.is_empty() { return vec![]; } -- 2.47.2 From fd32adb138d505954dcdb6421522dad2a3942e5b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Sep 2024 17:02:37 +0100 Subject: [PATCH 02/40] db: delete teh old table and recreate a new one with teh right fields --- db/migrations/4_committee-mk-ii.sql | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 db/migrations/4_committee-mk-ii.sql diff --git a/db/migrations/4_committee-mk-ii.sql b/db/migrations/4_committee-mk-ii.sql new file mode 100644 index 0000000..be59f61 --- /dev/null +++ b/db/migrations/4_committee-mk-ii.sql @@ -0,0 +1,9 @@ +-- No longer using the previous committee table +DROP TABLE committee; + +-- new table pulling from teh api +CREATE TABLE IF NOT EXISTS committee ( + id integer PRIMARY KEY, + name text not null, + members text not null +); -- 2.47.2 From 0e1a7d56b6c86a4346253eed310ddbab23674b30 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Sep 2024 17:07:03 +0100 Subject: [PATCH 03/40] feat: remove teh temp setup --- src/commands/committee.rs | 311 -------------------------------------- src/commands/mod.rs | 1 - src/lib.rs | 22 +-- src/main.rs | 6 - 4 files changed, 4 insertions(+), 336 deletions(-) delete mode 100644 src/commands/committee.rs diff --git a/src/commands/committee.rs b/src/commands/committee.rs deleted file mode 100644 index 3de0ec9..0000000 --- a/src/commands/committee.rs +++ /dev/null @@ -1,311 +0,0 @@ -use lettre::{ - message::{header, MultiPart, SinglePart}, - transport::smtp::{self, authentication::Credentials}, - Message, SmtpTransport, Transport, -}; -use maud::html; -use serenity::{ - builder::CreateApplicationCommand, - client::Context, - model::{ - application::interaction::application_command::ApplicationCommandInteraction, - id::UserId, - prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, - }, -}; -use skynet_discord_bot::{random_string, Config, DataBase}; -use sqlx::{Pool, Sqlite}; - -pub mod link { - use super::*; - use serenity::model::id::GuildId; - use skynet_discord_bot::Committee; - - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { - let committee_server = GuildId(1220150752656363520); - match command.guild_id { - None => { - return "Not in correct discord server.".to_string(); - } - Some(x) => { - if x != committee_server { - return "Not in correct discord server.".to_string(); - } - } - } - - let option = command - .data - .options - .first() - .expect("Expected email option") - .resolved - .as_ref() - .expect("Expected email object"); - - let email = if let CommandDataOptionValue::String(email) = option { - email.trim() - } else { - return "Please provide a valid committee email.".to_string(); - }; - - // fail early - if !email.ends_with("@ulwolves.ie") { - return "Please use a @ulwolves.ie address you have access to.".to_string(); - } - - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Databse in TypeMap.").clone() - }; - let db = db_lock.read().await; - - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let config = config_lock.read().await; - - if get_server_member_discord(&db, &command.user.id).await.is_some() { - return "Already linked".to_string(); - } - - if get_verify_from_db(&db, &command.user.id).await.is_some() { - return "Linking already in process, please check email.".to_string(); - } - - // generate a auth key - let auth = random_string(20); - match send_mail(&config, email, &auth, &command.user.name) { - Ok(_) => match save_to_db(&db, email, &auth, &command.user.id).await { - Ok(_) => {} - Err(e) => { - return format!("Unable to save to db {} {e:?}", email); - } - }, - Err(e) => { - return format!("Unable to send mail to {} {e:?}", email); - } - } - - format!("Verification email sent to {}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.", email) - } - - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command - .name("link_committee") - .description("Verify you are a committee member") - .create_option(|option| { - option - .name("email") - .description("UL Wolves Committee Email") - .kind(CommandOptionType::String) - .required(true) - }) - } - - pub async fn get_server_member_discord(db: &Pool, user: &UserId) -> Option { - sqlx::query_as::<_, Committee>( - r#" - SELECT * - FROM committee - WHERE discord = ? - "#, - ) - .bind(*user.as_u64() as i64) - .fetch_one(db) - .await - .ok() - } - - fn send_mail(config: &Config, mail: &str, auth: &str, user: &str) -> Result { - let sender = format!("UL Computer Society <{}>", &config.mail_user); - - // Create the html we want to send. - let html = html! { - head { - title { "Hello from Skynet!" } - style type="text/css" { - "h2, h4 { font-family: Arial, Helvetica, sans-serif; }" - } - } - div style="display: flex; flex-direction: column; align-items: center;" { - h2 { "Hello from Skynet!" } - // Substitute in the name of our recipient. - p { "Hi " (user) "," } - p { - "Please use " pre { "/verify_committee code: " (auth)} " to verify your discord account." - } - p { - "Skynet Team" - br; - "UL Computer Society" - } - } - }; - - let body_text = format!( - r#" - Hi {user} - - Please use "/verify_committee code: {auth}" to verify your discord account. - - Skynet Team - UL Computer Society - "# - ); - - // Build the message. - let email = Message::builder() - .from(sender.parse().unwrap()) - .to(mail.parse().unwrap()) - .subject("Skynet-Discord: Link Committee.") - .multipart( - // This is composed of two parts. - // also helps not trip spam settings (uneven number of url's - MultiPart::alternative() - .singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text)) - .singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())), - ) - .expect("failed to build email"); - - let creds = Credentials::new(config.mail_user.clone(), config.mail_pass.clone()); - - // Open a remote connection to gmail using STARTTLS - let mailer = SmtpTransport::starttls_relay(&config.mail_smtp).unwrap().credentials(creds).build(); - - // Send the email - mailer.send(&email) - } - - pub async fn get_verify_from_db(db: &Pool, user: &UserId) -> Option { - sqlx::query_as::<_, Committee>( - r#" - SELECT * - FROM committee - WHERE discord = ? - "#, - ) - .bind(*user.as_u64() as i64) - .fetch_one(db) - .await - .ok() - } - - async fn save_to_db(db: &Pool, email: &str, auth: &str, user: &UserId) -> Result, sqlx::Error> { - sqlx::query_as::<_, Committee>( - " - INSERT INTO committee (email, discord, auth_code) - VALUES (?1, ?2, ?3) - ", - ) - .bind(email.to_owned()) - .bind(*user.as_u64() as i64) - .bind(auth.to_owned()) - .fetch_optional(db) - .await - } -} - -pub mod verify { - use super::*; - use crate::commands::committee::link::get_verify_from_db; - use serenity::model::id::{GuildId, RoleId}; - use serenity::model::user::User; - use skynet_discord_bot::Committee; - use sqlx::Error; - - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { - let committee_server = GuildId(1220150752656363520); - match command.guild_id { - None => { - return "Not in correct discord server.".to_string(); - } - Some(x) => { - if x != committee_server { - return "Not in correct discord server.".to_string(); - } - } - } - - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Database in TypeMap.").clone() - }; - let db = db_lock.read().await; - - // check if user has used /link_committee - let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await { - x - } else { - return "Please use /link_committee first".to_string(); - }; - - let option = command - .data - .options - .first() - .expect("Expected code option") - .resolved - .as_ref() - .expect("Expected code object"); - - let code = if let CommandDataOptionValue::String(code) = option { - code - } else { - return "Please provide a verification code".to_string(); - }; - - if &details.auth_code != code { - return "Invalid verification code".to_string(); - } - - match set_discord(&db, &command.user.id).await { - Ok(_) => { - // get teh right roles for the user - set_server_roles(&command.user, ctx).await; - "Discord username linked to Wolves for committee".to_string() - } - Err(e) => { - println!("{:?}", e); - "Failed to save, please try /link_committee again".to_string() - } - } - } - - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command - .name("verify_committee") - .description("Verify Wolves Committee Email") - .create_option(|option| { - option - .name("code") - .description("Code from verification email") - .kind(CommandOptionType::String) - .required(true) - }) - } - - async fn set_discord(db: &Pool, discord: &UserId) -> Result, Error> { - sqlx::query_as::<_, Committee>( - " - UPDATE committee - SET committee = 1 - WHERE discord = ? - ", - ) - .bind(*discord.as_u64() as i64) - .fetch_optional(db) - .await - } - - async fn set_server_roles(discord: &User, ctx: &Context) { - let committee_server = GuildId(1220150752656363520); - if let Ok(mut member) = committee_server.member(&ctx.http, &discord.id).await { - let committee_member = RoleId(1226602779968274573); - if let Err(e) = member.add_role(&ctx, committee_member).await { - println!("{:?}", e); - } - } - } -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 3acfe81..9d3bea5 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,4 +1,3 @@ pub mod add_server; -pub mod committee; pub mod link_email; pub mod minecraft; diff --git a/src/lib.rs b/src/lib.rs index a247d33..c6c8d68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -202,25 +202,11 @@ impl<'r> FromRow<'r, SqliteRow> for WolvesVerify { } } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, sqlx::FromRow)] pub struct Committee { - pub email: String, - pub discord: UserId, - pub auth_code: String, - pub committee: i64, -} -impl<'r> FromRow<'r, SqliteRow> for Committee { - fn from_row(row: &'r SqliteRow) -> Result { - let user_tmp: i64 = row.try_get("discord")?; - let discord = UserId::from(user_tmp as u64); - - Ok(Self { - email: row.try_get("email")?, - discord, - auth_code: row.try_get("auth_code")?, - committee: row.try_get("committee")?, - }) - } + pub id: i64, + pub name: UserId, + pub members: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/src/main.rs b/src/main.rs index cbe8d5d..d1ebf2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,9 +64,6 @@ impl EventHandler for Handler { .create_application_command(|command| commands::minecraft::server::list::register(command)) .create_application_command(|command| commands::minecraft::server::delete::register(command)) .create_application_command(|command| commands::minecraft::user::add::register(command)) - // for committee server, temp - .create_application_command(|command| commands::committee::link::register(command)) - .create_application_command(|command| commands::committee::verify::register(command)) }) .await { @@ -92,9 +89,6 @@ impl EventHandler for Handler { "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, - // for teh committee server, temporary - "link_committee" => commands::committee::link::run(&command, &ctx).await, - "verify_committee" => commands::committee::verify::run(&command, &ctx).await, _ => "not implemented :(".to_string(), }; -- 2.47.2 From a7e8f5698ee9ab852f3048c49382b6677f32fdc2 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 27 Oct 2024 23:21:30 +0000 Subject: [PATCH 04/40] git: expand out the .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 2405fba..8b68dcb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,13 @@ /.idea .env +*.env result /result *.db +*.db* tmp/ tmp.* -- 2.47.2 From ceade9b97261f6f7d7f0892918996dfeb4b9caeb Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 00:03:52 +0000 Subject: [PATCH 05/40] sql: slight reordering of the migrations --- db/migrations/{4_committee-mk-ii.sql => 7_committee-mk-ii.sql} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename db/migrations/{4_committee-mk-ii.sql => 7_committee-mk-ii.sql} (74%) diff --git a/db/migrations/4_committee-mk-ii.sql b/db/migrations/7_committee-mk-ii.sql similarity index 74% rename from db/migrations/4_committee-mk-ii.sql rename to db/migrations/7_committee-mk-ii.sql index be59f61..2ddf316 100644 --- a/db/migrations/4_committee-mk-ii.sql +++ b/db/migrations/7_committee-mk-ii.sql @@ -2,8 +2,9 @@ DROP TABLE committee; -- new table pulling from teh api -CREATE TABLE IF NOT EXISTS committee ( +CREATE TABLE IF NOT EXISTS committees ( id integer PRIMARY KEY, name text not null, + link text not null, members text not null ); -- 2.47.2 From 79f880daeae6ce9002bb74ea99a29e52780f3aaa Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 00:51:39 +0000 Subject: [PATCH 06/40] feat: splitting up lib.rs into subfiles, starting with anythign taht interacts with teh api --- src/bin/update_data.rs | 3 +- src/commands/add_server.rs | 2 +- src/common/database.rs | 0 src/common/mod.rs | 1 + src/common/wolves.rs | 187 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 182 +----------------------------------- 6 files changed, 194 insertions(+), 181 deletions(-) create mode 100644 src/common/database.rs create mode 100644 src/common/mod.rs create mode 100644 src/common/wolves.rs diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index ec3713a..b5d89a5 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -4,9 +4,10 @@ use serenity::{ model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::{db_init, get_config, get_data::get_wolves_cns, Config, DataBase}; +use skynet_discord_bot::{db_init, get_config, Config, DataBase}; use std::{process, sync::Arc}; use tokio::sync::RwLock; +use skynet_discord_bot::common::wolves::get_data::get_wolves_cns; #[tokio::main] async fn main() { diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 1889257..8c7e9e5 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -6,7 +6,7 @@ use serenity::{ prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, }, }; -use skynet_discord_bot::get_data::get_wolves_cns; +use skynet_discord_bot::common::wolves::get_data::get_wolves_cns; use skynet_discord_bot::{get_server_config, is_admin, set_roles::update_server, DataBase, Servers}; use sqlx::{Error, Pool, Sqlite}; diff --git a/src/common/database.rs b/src/common/database.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 0000000..5d73638 --- /dev/null +++ b/src/common/mod.rs @@ -0,0 +1 @@ +pub mod wolves; \ No newline at end of file diff --git a/src/common/wolves.rs b/src/common/wolves.rs new file mode 100644 index 0000000..d92c34d --- /dev/null +++ b/src/common/wolves.rs @@ -0,0 +1,187 @@ +/** + This file relates to anything that directly interacts with teh wolves API + */ + + +// #[derive(Debug, Clone, sqlx::FromRow)] +// pub struct Committees { +// pub id: i64, +// pub name: String, +// pub link: String, +// pub members: Vec, +// } + +pub mod get_data { + use crate::set_roles::update_server; + use std::collections::BTreeMap; + use serde::{Deserialize, Serialize}; + use serenity::client::Context; + use serenity::model::id::GuildId; + use sqlx::{Pool, Sqlite}; + use crate::{get_server_config_bulk, Config, DataBase, ServerMembers, ServerMembersWolves, Servers, Wolves}; + + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResultUser { + committee: String, + member_id: String, + first_name: String, + last_name: String, + contact_email: String, + opt_in_email: String, + student_id: Option, + note: Option, + expiry: String, + requested: String, + approved: String, + sitename: String, + domain: String, + } + + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResult { + success: i8, + result: Vec, + } + + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResultLocal { + pub id_wolves: String, + pub email: String, + pub expiry: String, + } + pub async fn get_wolves_cns(ctx: &Context) { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Database in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let config = config_lock.read().await; + + for server_config in get_server_config_bulk(&db).await { + let Servers { + server, + wolves_api, + .. + } = &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![]; + for user in get_wolves_cns_sub(&config, wolves_api).await { + let id = user.member_id.parse::().unwrap_or_default(); + match existing.get(&(id as i64)) { + None => { + // user does not exist already, add everything + add_users_wolves(&db, &user).await; + add_users_server_members(&db, server, &user).await; + } + Some(old) => { + // always update wolves table, in case data has changed + add_users_wolves(&db, &user).await; + if old.expiry != user.expiry { + add_users_server_members(&db, server, &user).await; + + if let Some(discord_id) = old.discord { + user_to_update.push(discord_id); + } + } + } + } + } + + if !user_to_update.is_empty() { + update_server(ctx, &server_config, &[], &user_to_update).await; + } + } + } + + pub async fn get_server_member(db: &Pool, server: &GuildId) -> Vec { + sqlx::query_as::<_, ServerMembersWolves>( + r#" + SELECT * + FROM server_members + JOIN wolves USING (id_wolves) + WHERE ( + server = ? + AND discord IS NOT NULL + ) + "#, + ) + .bind(*server.as_u64() as i64) + .fetch_all(db) + .await + .unwrap_or_default() + } + + async fn get_wolves_cns_sub(config: &Config, wolves_api: &str) -> Vec { + if config.wolves_url.is_empty() { + return vec![]; + } + + // get wolves data + if let Ok(mut res) = surf::post(&config.wolves_url).header("X-AM-Identity", wolves_api).await { + if let Ok(WolvesResult { + success, + result, + }) = res.body_json().await + { + if success != 1 { + return vec![]; + } + + return result; + } + } + + vec![] + } + + async fn add_users_wolves(db: &Pool, user: &WolvesResultUser) { + // expiry + match sqlx::query_as::<_, Wolves>( + " + INSERT INTO wolves (id_wolves, email) + VALUES ($1, $2) + ON CONFLICT(id_wolves) DO UPDATE SET email = $2 + ", + ) + .bind(&user.member_id) + .bind(&user.contact_email) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + println!("Failure to insert into Wolves {:?}", user); + println!("{:?}", e); + } + } + } + async fn add_users_server_members(db: &Pool, server: &GuildId, user: &WolvesResultUser) { + match sqlx::query_as::<_, ServerMembers>( + " + INSERT OR REPLACE INTO server_members (server, id_wolves, expiry) + VALUES (?1, ?2, ?3) + ", + ) + .bind(*server.as_u64() as i64) + .bind(&user.member_id) + .bind(&user.expiry) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + println!("Failure to insert into ServerMembers {} {:?}", server.as_u64(), user); + println!("{:?}", e); + } + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 6c6ded9..9e1c599 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +pub mod common; + use dotenvy::dotenv; use serde::{Deserialize, Serialize}; use serenity::{ @@ -202,13 +204,6 @@ impl<'r> FromRow<'r, SqliteRow> for WolvesVerify { } } -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct Committee { - pub id: i64, - pub name: UserId, - pub members: Vec, -} - #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Servers { pub server: GuildId, @@ -218,7 +213,7 @@ pub struct Servers { pub member_past: i64, pub member_current: i64, pub bot_channel_id: ChannelId, - // these can be removed in teh future with an API update + // TODO: these can be removed in teh future with an API update pub server_name: String, pub wolves_link: String, } @@ -508,177 +503,6 @@ pub mod set_roles { } } -pub mod get_data { - use super::*; - use crate::set_roles::update_server; - use std::collections::BTreeMap; - - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResultUser { - committee: String, - member_id: String, - first_name: String, - last_name: String, - contact_email: String, - opt_in_email: String, - student_id: Option, - note: Option, - expiry: String, - requested: String, - approved: String, - sitename: String, - domain: String, - } - - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResult { - success: i8, - result: Vec, - } - - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResultLocal { - pub id_wolves: String, - pub email: String, - pub expiry: String, - } - pub async fn get_wolves_cns(ctx: &Context) { - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Database in TypeMap.").clone() - }; - let db = db_lock.read().await; - - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let config = config_lock.read().await; - - for server_config in get_server_config_bulk(&db).await { - let Servers { - server, - wolves_api, - .. - } = &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![]; - for user in get_wolves_cns_sub(&config, wolves_api).await { - let id = user.member_id.parse::().unwrap_or_default(); - match existing.get(&(id as i64)) { - None => { - // user does not exist already, add everything - add_users_wolves(&db, &user).await; - add_users_server_members(&db, server, &user).await; - } - Some(old) => { - // always update wolves table, in case data has changed - add_users_wolves(&db, &user).await; - if old.expiry != user.expiry { - add_users_server_members(&db, server, &user).await; - - if let Some(discord_id) = old.discord { - user_to_update.push(discord_id); - } - } - } - } - } - - if !user_to_update.is_empty() { - update_server(ctx, &server_config, &[], &user_to_update).await; - } - } - } - - pub async fn get_server_member(db: &Pool, server: &GuildId) -> Vec { - sqlx::query_as::<_, ServerMembersWolves>( - r#" - SELECT * - FROM server_members - JOIN wolves USING (id_wolves) - WHERE ( - server = ? - AND discord IS NOT NULL - ) - "#, - ) - .bind(*server.as_u64() as i64) - .fetch_all(db) - .await - .unwrap_or_default() - } - - async fn get_wolves_cns_sub(config: &Config, wolves_api: &str) -> Vec { - if config.wolves_url.is_empty() { - return vec![]; - } - - // get wolves data - if let Ok(mut res) = surf::post(&config.wolves_url).header("X-AM-Identity", wolves_api).await { - if let Ok(WolvesResult { - success, - result, - }) = res.body_json().await - { - if success != 1 { - return vec![]; - } - - return result; - } - } - - vec![] - } - - async fn add_users_wolves(db: &Pool, user: &WolvesResultUser) { - // expiry - match sqlx::query_as::<_, Wolves>( - " - INSERT INTO wolves (id_wolves, email) - VALUES ($1, $2) - ON CONFLICT(id_wolves) DO UPDATE SET email = $2 - ", - ) - .bind(&user.member_id) - .bind(&user.contact_email) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - println!("Failure to insert into Wolves {:?}", user); - println!("{:?}", e); - } - } - } - async fn add_users_server_members(db: &Pool, server: &GuildId, user: &WolvesResultUser) { - match sqlx::query_as::<_, ServerMembers>( - " - INSERT OR REPLACE INTO server_members (server, id_wolves, expiry) - VALUES (?1, ?2, ?3) - ", - ) - .bind(*server.as_u64() as i64) - .bind(&user.member_id) - .bind(&user.expiry) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - println!("Failure to insert into ServerMembers {} {:?}", server.as_u64(), user); - println!("{:?}", e); - } - } - } -} - /** For any time ye need to check if a user who calls a command has admin privlages */ -- 2.47.2 From 41407ecefb68e56338c1c593d02a75eb72b73f8a Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 00:59:04 +0000 Subject: [PATCH 07/40] feat: split out all the databse interactions into their own file --- src/bin/update_data.rs | 11 +- src/bin/update_minecraft.rs | 3 +- src/bin/update_users.rs | 11 +- src/commands/add_server.rs | 15 +- src/commands/link_email.rs | 7 +- src/commands/minecraft.rs | 14 +- src/commands/role_adder.rs | 5 +- src/common/database.rs | 288 ++++++++++++++++++++++++++++++++++++ src/common/mod.rs | 3 +- src/common/wolves.rs | 3 +- src/lib.rs | 285 +---------------------------------- src/main.rs | 21 +-- 12 files changed, 348 insertions(+), 318 deletions(-) diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index b5d89a5..e4b662d 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -1,12 +1,13 @@ use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, - Client, + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, }; -use skynet_discord_bot::{db_init, get_config, Config, DataBase}; +use skynet_discord_bot::{get_config, Config}; use std::{process, sync::Arc}; use tokio::sync::RwLock; +use skynet_discord_bot::common::database::{db_init, DataBase}; use skynet_discord_bot::common::wolves::get_data::get_wolves_cns; #[tokio::main] diff --git a/src/bin/update_minecraft.rs b/src/bin/update_minecraft.rs index 72aad1c..f959d99 100644 --- a/src/bin/update_minecraft.rs +++ b/src/bin/update_minecraft.rs @@ -1,5 +1,6 @@ -use skynet_discord_bot::{db_init, get_config, get_minecraft_config, update_server, whitelist_wipe}; +use skynet_discord_bot::{get_config, get_minecraft_config, update_server, whitelist_wipe}; use std::collections::HashSet; +use skynet_discord_bot::common::database::db_init; #[tokio::main] async fn main() { diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index d46c0e5..cf48650 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -1,12 +1,13 @@ use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, - Client, + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, }; -use skynet_discord_bot::{db_init, get_config, get_server_config_bulk, set_roles, Config, DataBase}; +use skynet_discord_bot::{get_config, set_roles, Config}; use std::{process, sync::Arc}; use tokio::sync::RwLock; +use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; #[tokio::main] async fn main() { diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 8c7e9e5..dace04e 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -1,14 +1,15 @@ use serenity::{ - builder::CreateApplicationCommand, - client::Context, - model::{ - application::interaction::application_command::ApplicationCommandInteraction, - prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, - }, + builder::CreateApplicationCommand, + client::Context, + model::{ + application::interaction::application_command::ApplicationCommandInteraction, + prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, + }, }; use skynet_discord_bot::common::wolves::get_data::get_wolves_cns; -use skynet_discord_bot::{get_server_config, is_admin, set_roles::update_server, DataBase, Servers}; +use skynet_discord_bot::{is_admin, set_roles::update_server}; use sqlx::{Error, Pool, Sqlite}; +use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { // check if user has high enough permisssions diff --git a/src/commands/link_email.rs b/src/commands/link_email.rs index 4961dbb..224c6da 100644 --- a/src/commands/link_email.rs +++ b/src/commands/link_email.rs @@ -13,8 +13,10 @@ use serenity::{ prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, }, }; -use skynet_discord_bot::{get_now_iso, random_string, Config, DataBase, Wolves, WolvesVerify}; +use skynet_discord_bot::{get_now_iso, random_string, Config}; use sqlx::{Pool, Sqlite}; +use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify}; + pub mod link { use super::*; @@ -238,8 +240,9 @@ pub mod verify { use super::*; use crate::commands::link_email::link::{db_pending_clear_expired, get_verify_from_db}; use serenity::model::user::User; - use skynet_discord_bot::{get_server_config, ServerMembersWolves, Servers}; + use skynet_discord_bot::common::database::get_server_config; use sqlx::Error; + use skynet_discord_bot::common::database::{ServerMembersWolves, Servers}; pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { let db_lock = { diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index b6b6dd5..fb87326 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -7,7 +7,7 @@ use serenity::{ }, }; -use skynet_discord_bot::DataBase; +use skynet_discord_bot::common::database::DataBase; use sqlx::{Pool, Sqlite}; pub(crate) mod user { @@ -16,8 +16,9 @@ pub(crate) mod user { use super::*; use crate::commands::link_email::link::get_server_member_discord; use serenity::model::id::UserId; - use skynet_discord_bot::{whitelist_update, Config, Minecraft, Wolves}; + use skynet_discord_bot::{whitelist_update, Config}; use sqlx::Error; + use skynet_discord_bot::common::database::{Minecraft, Wolves}; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("link_minecraft").description("Link your minecraft account").create_option(|option| { @@ -122,7 +123,8 @@ pub(crate) mod server { use sqlx::Error; // this is to managfe the server side of commands related to minecraft use super::*; - use skynet_discord_bot::{is_admin, update_server, Config, Minecraft}; + use skynet_discord_bot::{is_admin, update_server, Config}; + use skynet_discord_bot::common::database::Minecraft; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_add").description("Add a minecraft server").create_option(|option| { @@ -201,7 +203,8 @@ pub(crate) mod server { use serenity::builder::CreateApplicationCommand; use serenity::client::Context; use serenity::model::prelude::application_command::ApplicationCommandInteraction; - use skynet_discord_bot::{get_minecraft_config_server, is_admin, server_information, Config, DataBase}; + use skynet_discord_bot::{get_minecraft_config_server, is_admin, server_information, Config}; + use skynet_discord_bot::common::database::DataBase; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_list").description("List your minecraft servers") @@ -262,8 +265,9 @@ pub(crate) mod server { use serenity::model::application::command::CommandOptionType; use serenity::model::id::GuildId; use serenity::model::prelude::application_command::{ApplicationCommandInteraction, CommandDataOptionValue}; - use skynet_discord_bot::{is_admin, DataBase, Minecraft}; + use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; + use skynet_discord_bot::common::database::{DataBase, Minecraft}; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_delete").description("Delete a minecraft server").create_option(|option| { diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index 949bf32..89f9441 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -7,8 +7,9 @@ use serenity::{ }, }; -use skynet_discord_bot::{is_admin, DataBase, RoleAdder}; +use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; +use skynet_discord_bot::common::database::{DataBase, RoleAdder}; pub mod edit { use super::*; @@ -188,7 +189,7 @@ pub mod list {} pub mod tools { use serenity::client::Context; use serenity::model::guild::Member; - use skynet_discord_bot::RoleAdder; + use skynet_discord_bot::common::database::RoleAdder; use sqlx::{Pool, Sqlite}; pub async fn on_role_change(db: &Pool, ctx: &Context, mut new_data: Member) { diff --git a/src/common/database.rs b/src/common/database.rs index e69de29..6eb1734 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -0,0 +1,288 @@ +use serenity::prelude::TypeMapKey; +use std::sync::Arc; +use tokio::sync::RwLock; +use sqlx::{Error, FromRow, Pool, Row, Sqlite}; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}; +use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; +use serde::{Deserialize, Serialize}; +use serenity::model::guild; +use std::str::FromStr; +use crate::Config; + +pub struct DataBase; +impl TypeMapKey for DataBase { + type Value = Arc>>; +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ServerMembers { + pub server: GuildId, + pub id_wolves: i64, + pub expiry: String, +} + +impl<'r> FromRow<'r, SqliteRow> for ServerMembers { + fn from_row(row: &'r SqliteRow) -> Result { + let server_tmp: i64 = row.try_get("server")?; + let server = GuildId::from(server_tmp as u64); + + Ok(Self { + server, + id_wolves: row.try_get("id_wolves")?, + expiry: row.try_get("expiry")?, + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ServerMembersWolves { + pub server: GuildId, + pub id_wolves: i64, + pub expiry: String, + pub email: String, + pub discord: Option, + pub minecraft: Option, +} + +impl<'r> FromRow<'r, SqliteRow> for ServerMembersWolves { + fn from_row(row: &'r SqliteRow) -> Result { + let server_tmp: i64 = row.try_get("server")?; + let server = GuildId::from(server_tmp as u64); + let discord = match row.try_get("discord") { + Ok(x) => { + let tmp: i64 = x; + if tmp == 0 { + None + } else { + Some(UserId::from(tmp as u64)) + } + } + _ => None, + }; + + Ok(Self { + server, + id_wolves: row.try_get("id_wolves")?, + expiry: row.try_get("expiry")?, + email: row.try_get("email")?, + discord, + minecraft: row.try_get("minecraft")?, + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Wolves { + pub id_wolves: i64, + pub email: String, + pub discord: Option, + pub minecraft: Option, +} + +impl<'r> FromRow<'r, SqliteRow> for Wolves { + fn from_row(row: &'r SqliteRow) -> Result { + let discord = match row.try_get("discord") { + Ok(x) => { + let tmp: i64 = x; + if tmp == 0 { + None + } else { + Some(UserId::from(tmp as u64)) + } + } + _ => None, + }; + + Ok(Self { + id_wolves: row.try_get("id_wolves")?, + email: row.try_get("email")?, + discord, + minecraft: row.try_get("minecraft")?, + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct WolvesVerify { + pub email: String, + pub discord: UserId, + pub auth_code: String, + pub date_expiry: String, +} + +impl<'r> FromRow<'r, SqliteRow> for WolvesVerify { + fn from_row(row: &'r SqliteRow) -> Result { + let user_tmp: i64 = row.try_get("discord")?; + let discord = UserId::from(user_tmp as u64); + + Ok(Self { + email: row.try_get("email")?, + discord, + auth_code: row.try_get("auth_code")?, + date_expiry: row.try_get("date_expiry")?, + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Servers { + pub server: GuildId, + pub wolves_api: String, + pub role_past: Option, + pub role_current: RoleId, + 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 { + fn from_row(row: &'r SqliteRow) -> Result { + let server_tmp: i64 = row.try_get("server")?; + let server = GuildId::from(server_tmp as u64); + let role_past = match row.try_get("role_past") { + Ok(x) => { + let tmp: i64 = x; + if tmp == 0 { + None + } else { + Some(RoleId::from(tmp as u64)) + } + } + _ => None, + }; + let role_current = match row.try_get("role_current") { + Ok(x) => { + let tmp: i64 = x; + RoleId::from(tmp as u64) + } + _ => RoleId::from(0u64), + }; + + let bot_channel_tmp: i64 = row.try_get("bot_channel_id")?; + let bot_channel_id = ChannelId::from(bot_channel_tmp as u64); + + Ok(Self { + server, + wolves_api: row.try_get("wolves_api")?, + role_past, + role_current, + member_past: row.try_get("member_past")?, + member_current: row.try_get("member_current")?, + bot_channel_id, + server_name: row.try_get("server_name")?, + wolves_link: row.try_get("wolves_link")?, + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Minecraft { + pub discord: GuildId, + pub minecraft: String, +} + +impl<'r> FromRow<'r, SqliteRow> for Minecraft { + fn from_row(row: &'r SqliteRow) -> Result { + let server_tmp: i64 = row.try_get("server_discord")?; + let discord = GuildId::from(server_tmp as u64); + + Ok(Self { + discord, + minecraft: row.try_get("server_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 { + let server_tmp: i64 = row.try_get("server")?; + 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, Error> { + let database = format!("{}/{}", &config.home, &config.database); + + let pool = SqlitePoolOptions::new() + .max_connections(5) + .connect_with( + SqliteConnectOptions::from_str(&format!("sqlite://{}", database))? + .foreign_keys(true) + .create_if_missing(true), + ) + .await?; + + // migrations are amazing! + sqlx::migrate!("./db/migrations").run(&pool).await?; + + Ok(pool) +} + +pub async fn get_server_config(db: &Pool, server: &GuildId) -> Option { + sqlx::query_as::<_, Servers>( + r#" + SELECT * + FROM servers + WHERE server = ? + "#, + ) + .bind(*server.as_u64() as i64) + .fetch_one(db) + .await + .ok() +} + +pub async fn get_server_member(db: &Pool, server: &GuildId, member: &guild::Member) -> Result { + sqlx::query_as::<_, ServerMembersWolves>( + r#" + SELECT * + FROM server_members + JOIN wolves USING (id_wolves) + WHERE server = ? AND discord = ? + "#, + ) + .bind(*server.as_u64() as i64) + .bind(*member.user.id.as_u64() as i64) + .fetch_one(db) + .await +} + +pub async fn get_server_config_bulk(db: &Pool) -> Vec { + sqlx::query_as::<_, Servers>( + r#" + SELECT * + FROM servers + "#, + ) + .fetch_all(db) + .await + .unwrap_or_default() +} \ No newline at end of file diff --git a/src/common/mod.rs b/src/common/mod.rs index 5d73638..a992f8b 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1 +1,2 @@ -pub mod wolves; \ No newline at end of file +pub mod wolves; +pub mod database; \ No newline at end of file diff --git a/src/common/wolves.rs b/src/common/wolves.rs index d92c34d..99e7818 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -18,7 +18,8 @@ pub mod get_data { use serenity::client::Context; use serenity::model::id::GuildId; use sqlx::{Pool, Sqlite}; - use crate::{get_server_config_bulk, Config, DataBase, ServerMembers, ServerMembersWolves, Servers, Wolves}; + use crate::Config; + use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers, Wolves}; #[derive(Deserialize, Serialize, Debug)] struct WolvesResultUser { diff --git a/src/lib.rs b/src/lib.rs index 9e1c599..dfc7c4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,11 +3,8 @@ pub mod common; use dotenvy::dotenv; use serde::{Deserialize, Serialize}; use serenity::{ - model::{ - guild, - id::{ChannelId, GuildId, RoleId}, - }, - prelude::TypeMapKey, + model::id::{GuildId, RoleId}, + prelude::TypeMapKey, }; use crate::set_roles::get_server_member_bulk; @@ -18,11 +15,11 @@ use serenity::client::Context; use serenity::model::id::UserId; use serenity::model::prelude::application_command::ApplicationCommandInteraction; use sqlx::{ - sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}, - Error, FromRow, Pool, Row, Sqlite, + Pool, Sqlite, }; -use std::{env, str::FromStr, sync::Arc}; +use std::{env, sync::Arc}; use tokio::sync::RwLock; +use common::database::{Minecraft, ServerMembersWolves, Servers}; pub struct Config { // manages where teh database is stored @@ -45,11 +42,6 @@ impl TypeMapKey for Config { type Value = Arc>; } -pub struct DataBase; -impl TypeMapKey for DataBase { - type Value = Arc>>; -} - pub fn get_config() -> Config { dotenv().ok(); @@ -98,272 +90,6 @@ pub fn get_config() -> Config { config } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct ServerMembers { - pub server: GuildId, - pub id_wolves: i64, - pub expiry: String, -} -impl<'r> FromRow<'r, SqliteRow> for ServerMembers { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server")?; - let server = GuildId::from(server_tmp as u64); - - Ok(Self { - server, - id_wolves: row.try_get("id_wolves")?, - expiry: row.try_get("expiry")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct ServerMembersWolves { - pub server: GuildId, - pub id_wolves: i64, - pub expiry: String, - pub email: String, - pub discord: Option, - pub minecraft: Option, -} -impl<'r> FromRow<'r, SqliteRow> for ServerMembersWolves { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server")?; - let server = GuildId::from(server_tmp as u64); - let discord = match row.try_get("discord") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(UserId::from(tmp as u64)) - } - } - _ => None, - }; - - Ok(Self { - server, - id_wolves: row.try_get("id_wolves")?, - expiry: row.try_get("expiry")?, - email: row.try_get("email")?, - discord, - minecraft: row.try_get("minecraft")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Wolves { - pub id_wolves: i64, - pub email: String, - pub discord: Option, - pub minecraft: Option, -} -impl<'r> FromRow<'r, SqliteRow> for Wolves { - fn from_row(row: &'r SqliteRow) -> Result { - let discord = match row.try_get("discord") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(UserId::from(tmp as u64)) - } - } - _ => None, - }; - - Ok(Self { - id_wolves: row.try_get("id_wolves")?, - email: row.try_get("email")?, - discord, - minecraft: row.try_get("minecraft")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct WolvesVerify { - pub email: String, - pub discord: UserId, - pub auth_code: String, - pub date_expiry: String, -} -impl<'r> FromRow<'r, SqliteRow> for WolvesVerify { - fn from_row(row: &'r SqliteRow) -> Result { - let user_tmp: i64 = row.try_get("discord")?; - let discord = UserId::from(user_tmp as u64); - - Ok(Self { - email: row.try_get("email")?, - discord, - auth_code: row.try_get("auth_code")?, - date_expiry: row.try_get("date_expiry")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Servers { - pub server: GuildId, - pub wolves_api: String, - pub role_past: Option, - pub role_current: RoleId, - 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 { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server")?; - let server = GuildId::from(server_tmp as u64); - let role_past = match row.try_get("role_past") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(RoleId::from(tmp as u64)) - } - } - _ => None, - }; - let role_current = match row.try_get("role_current") { - Ok(x) => { - let tmp: i64 = x; - RoleId::from(tmp as u64) - } - _ => RoleId::from(0u64), - }; - - let bot_channel_tmp: i64 = row.try_get("bot_channel_id")?; - let bot_channel_id = ChannelId::from(bot_channel_tmp as u64); - - Ok(Self { - server, - wolves_api: row.try_get("wolves_api")?, - role_past, - role_current, - member_past: row.try_get("member_past")?, - member_current: row.try_get("member_current")?, - bot_channel_id, - server_name: row.try_get("server_name")?, - wolves_link: row.try_get("wolves_link")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Minecraft { - pub discord: GuildId, - pub minecraft: String, -} -impl<'r> FromRow<'r, SqliteRow> for Minecraft { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server_discord")?; - let discord = GuildId::from(server_tmp as u64); - - Ok(Self { - discord, - minecraft: row.try_get("server_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 { - let server_tmp: i64 = row.try_get("server")?; - 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, Error> { - let database = format!("{}/{}", &config.home, &config.database); - - let pool = SqlitePoolOptions::new() - .max_connections(5) - .connect_with( - SqliteConnectOptions::from_str(&format!("sqlite://{}", database))? - .foreign_keys(true) - .create_if_missing(true), - ) - .await?; - - // migrations are amazing! - sqlx::migrate!("./db/migrations").run(&pool).await?; - - Ok(pool) -} - -pub async fn get_server_config(db: &Pool, server: &GuildId) -> Option { - sqlx::query_as::<_, Servers>( - r#" - SELECT * - FROM servers - WHERE server = ? - "#, - ) - .bind(*server.as_u64() as i64) - .fetch_one(db) - .await - .ok() -} - -pub async fn get_server_member(db: &Pool, server: &GuildId, member: &guild::Member) -> Result { - sqlx::query_as::<_, ServerMembersWolves>( - r#" - SELECT * - FROM server_members - JOIN wolves USING (id_wolves) - WHERE server = ? AND discord = ? - "#, - ) - .bind(*server.as_u64() as i64) - .bind(*member.user.id.as_u64() as i64) - .fetch_one(db) - .await -} - -pub async fn get_server_config_bulk(db: &Pool) -> Vec { - sqlx::query_as::<_, Servers>( - r#" - SELECT * - FROM servers - "#, - ) - .fetch_all(db) - .await - .unwrap_or_default() -} - pub fn get_now_iso(short: bool) -> String { let now = Utc::now(); if short { @@ -378,6 +104,7 @@ pub fn random_string(len: usize) -> String { } pub mod set_roles { + use crate::common::database::{DataBase, Wolves}; use super::*; pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { let db_lock = { diff --git a/src/main.rs b/src/main.rs index 1b80a6d..6f86f86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,19 +3,20 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; use serenity::model::guild::Member; use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::{ - application::{command::Command, interaction::Interaction}, - gateway::{GatewayIntents, Ready}, - prelude::Activity, - user::OnlineStatus, - }, - Client, + async_trait, + client::{Context, EventHandler}, + model::{ + application::{command::Command, interaction::Interaction}, + gateway::{GatewayIntents, Ready}, + prelude::Activity, + user::OnlineStatus, + }, + Client, }; -use skynet_discord_bot::{db_init, get_config, get_server_config, get_server_member, Config, DataBase}; +use skynet_discord_bot::{get_config, Config}; use std::sync::Arc; use tokio::sync::RwLock; +use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_member, DataBase}; struct Handler; -- 2.47.2 From 39277340839c85efb67ed6d7494aefa2da201ab6 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 01:06:21 +0000 Subject: [PATCH 08/40] feat: split out minecraft --- src/bin/update_minecraft.rs | 3 +- src/commands/minecraft.rs | 16 ++-- src/common/database.rs | 18 ---- src/common/minecraft.rs | 167 ++++++++++++++++++++++++++++++++++++ src/common/mod.rs | 3 +- src/lib.rs | 140 +----------------------------- 6 files changed, 182 insertions(+), 165 deletions(-) create mode 100644 src/common/minecraft.rs diff --git a/src/bin/update_minecraft.rs b/src/bin/update_minecraft.rs index f959d99..cae6b62 100644 --- a/src/bin/update_minecraft.rs +++ b/src/bin/update_minecraft.rs @@ -1,6 +1,7 @@ -use skynet_discord_bot::{get_config, get_minecraft_config, update_server, whitelist_wipe}; +use skynet_discord_bot::get_config; use std::collections::HashSet; use skynet_discord_bot::common::database::db_init; +use skynet_discord_bot::common::minecraft::{get_minecraft_config, update_server, whitelist_wipe}; #[tokio::main] async fn main() { diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index fb87326..962fe1f 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -16,9 +16,10 @@ pub(crate) mod user { use super::*; use crate::commands::link_email::link::get_server_member_discord; use serenity::model::id::UserId; - use skynet_discord_bot::{whitelist_update, Config}; + use skynet_discord_bot::Config; use sqlx::Error; - use skynet_discord_bot::common::database::{Minecraft, Wolves}; + use skynet_discord_bot::common::database::Wolves; + use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft}; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("link_minecraft").description("Link your minecraft account").create_option(|option| { @@ -123,8 +124,9 @@ pub(crate) mod server { use sqlx::Error; // this is to managfe the server side of commands related to minecraft use super::*; - use skynet_discord_bot::{is_admin, update_server, Config}; - use skynet_discord_bot::common::database::Minecraft; + use skynet_discord_bot::{is_admin, Config}; + use skynet_discord_bot::common::minecraft::Minecraft; + use skynet_discord_bot::common::minecraft::update_server; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_add").description("Add a minecraft server").create_option(|option| { @@ -203,8 +205,9 @@ pub(crate) mod server { use serenity::builder::CreateApplicationCommand; use serenity::client::Context; use serenity::model::prelude::application_command::ApplicationCommandInteraction; - use skynet_discord_bot::{get_minecraft_config_server, is_admin, server_information, Config}; + use skynet_discord_bot::{is_admin, Config}; use skynet_discord_bot::common::database::DataBase; + use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information}; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_list").description("List your minecraft servers") @@ -267,7 +270,8 @@ pub(crate) mod server { use serenity::model::prelude::application_command::{ApplicationCommandInteraction, CommandDataOptionValue}; use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; - use skynet_discord_bot::common::database::{DataBase, Minecraft}; + use skynet_discord_bot::common::database::DataBase; + use skynet_discord_bot::common::minecraft::Minecraft; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_delete").description("Delete a minecraft server").create_option(|option| { diff --git a/src/common/database.rs b/src/common/database.rs index 6eb1734..9674499 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -178,24 +178,6 @@ impl<'r> FromRow<'r, SqliteRow> for Servers { } } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Minecraft { - pub discord: GuildId, - pub minecraft: String, -} - -impl<'r> FromRow<'r, SqliteRow> for Minecraft { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server_discord")?; - let discord = GuildId::from(server_tmp as u64); - - Ok(Self { - discord, - minecraft: row.try_get("server_minecraft")?, - }) - } -} - #[derive(Debug, Clone, Deserialize, Serialize)] pub struct RoleAdder { pub server: GuildId, diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs new file mode 100644 index 0000000..55ce53b --- /dev/null +++ b/src/common/minecraft.rs @@ -0,0 +1,167 @@ +use serde::{Deserialize, Serialize}; +use serde::de::DeserializeOwned; +use sqlx::{Error, FromRow, Pool, Row, Sqlite}; +use serenity::model::id::GuildId; +use sqlx::sqlite::SqliteRow; +use crate::Config; +use crate::set_roles::get_server_member_bulk; + + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Minecraft { + pub discord: GuildId, + pub minecraft: String, +} + +impl<'r> FromRow<'r, SqliteRow> for Minecraft { + fn from_row(row: &'r SqliteRow) -> Result { + let server_tmp: i64 = row.try_get("server_discord")?; + let discord = GuildId::from(server_tmp as u64); + + Ok(Self { + discord, + minecraft: row.try_get("server_minecraft")?, + }) + } +} + + +/** +loop through all members of server +get a list of folks with mc accounts that are members +and a list that arent members + */ +pub async fn update_server(server_id: &str, db: &Pool, g_id: &GuildId, config: &Config) { + let mut usernames = vec![]; + for member in get_server_member_bulk(db, g_id).await { + if let Some(x) = member.minecraft { + usernames.push(x); + } + } + if !usernames.is_empty() { + whitelist_update(&usernames, server_id, &config.discord_token_minecraft).await; + } +} + +async fn post(url: &str, bearer: &str, data: &T) { + match surf::post(url) + .header("Authorization", bearer) + .header("Content-Type", "application/json") + .header("Accept", "Application/vnd.pterodactyl.v1+json") + .body_json(&data) + { + Ok(req) => { + req.await.ok(); + } + Err(e) => { + dbg!(e); + } + } +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct ServerDetailsResSub { + pub identifier: String, + pub name: String, + pub description: String, + pub is_suspended: bool, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct ServerDetailsRes { + pub attributes: ServerDetailsResSub, +} + +async fn get(url: &str, bearer: &str) -> Option { + match surf::get(url) + .header("Authorization", bearer) + .header("Content-Type", "application/json") + .header("Accept", "Application/vnd.pterodactyl.v1+json") + .recv_json() + .await + { + Ok(res) => Some(res), + Err(e) => { + dbg!(e); + + None + } + } +} + +#[derive(Deserialize, Serialize, Debug)] +struct BodyCommand { + command: String, +} + +#[derive(Deserialize, Serialize, Debug)] +struct BodyDelete { + root: String, + files: Vec, +} + +pub async fn whitelist_update(add: &Vec, server: &str, token: &str) { + let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); + let bearer = format!("Bearer {token}"); + + for name in add { + let data = BodyCommand { + command: format!("whitelist add {name}"), + }; + post(&format!("{url_base}/command"), &bearer, &data).await; + } +} + +pub async fn whitelist_wipe(server: &str, token: &str) { + let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); + let bearer = format!("Bearer {token}"); + + // delete whitelist + let deletion = BodyDelete { + root: "/".to_string(), + files: vec!["whitelist.json".to_string()], + }; + post(&format!("{url_base}/files/delete"), &bearer, &deletion).await; + + // recreate teh file, passing in the type here so the compiler knows what type of vec it is + post::>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await; + + // reload the whitelist + let data = BodyCommand { + command: "whitelist reload".to_string(), + }; + post(&format!("{url_base}/command"), &bearer, &data).await; +} + +pub async fn server_information(server: &str, token: &str) -> Option { + let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); + let bearer = format!("Bearer {token}"); + get::(&format!("{url_base}/"), &bearer).await +} + +pub async fn get_minecraft_config(db: &Pool) -> Vec { + sqlx::query_as::<_, Minecraft>( + r#" + SELECT * + FROM minecraft + "#, + ) + .fetch_all(db) + .await + .unwrap_or_default() +} + +pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Vec { + sqlx::query_as::<_, Minecraft>( + r#" + SELECT * + FROM minecraft + WHERE server_discord = ?1 + "#, + ) + .bind(*g_id.as_u64() as i64) + .fetch_all(db) + .await + .unwrap_or_default() +} + diff --git a/src/common/mod.rs b/src/common/mod.rs index a992f8b..3302619 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,2 +1,3 @@ pub mod wolves; -pub mod database; \ No newline at end of file +pub mod database; +pub mod minecraft; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index dfc7c4f..5dbbe0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ use sqlx::{ }; use std::{env, sync::Arc}; use tokio::sync::RwLock; -use common::database::{Minecraft, ServerMembersWolves, Servers}; +use common::database::{ServerMembersWolves, Servers}; pub struct Config { // manages where teh database is stored @@ -267,141 +267,3 @@ pub async fn is_admin(command: &ApplicationCommandInteraction, ctx: &Context) -> None } } - -/** -loop through all members of server -get a list of folks with mc accounts that are members -and a list that arent members - */ -pub async fn update_server(server_id: &str, db: &Pool, g_id: &GuildId, config: &Config) { - let mut usernames = vec![]; - for member in get_server_member_bulk(db, g_id).await { - if let Some(x) = member.minecraft { - usernames.push(x); - } - } - if !usernames.is_empty() { - whitelist_update(&usernames, server_id, &config.discord_token_minecraft).await; - } -} - -async fn post(url: &str, bearer: &str, data: &T) { - match surf::post(url) - .header("Authorization", bearer) - .header("Content-Type", "application/json") - .header("Accept", "Application/vnd.pterodactyl.v1+json") - .body_json(&data) - { - Ok(req) => { - req.await.ok(); - } - Err(e) => { - dbg!(e); - } - } -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct ServerDetailsResSub { - pub identifier: String, - pub name: String, - pub description: String, - pub is_suspended: bool, -} -#[derive(Deserialize, Serialize, Debug)] -pub struct ServerDetailsRes { - pub attributes: ServerDetailsResSub, -} - -async fn get(url: &str, bearer: &str) -> Option { - match surf::get(url) - .header("Authorization", bearer) - .header("Content-Type", "application/json") - .header("Accept", "Application/vnd.pterodactyl.v1+json") - .recv_json() - .await - { - Ok(res) => Some(res), - Err(e) => { - dbg!(e); - - None - } - } -} - -#[derive(Deserialize, Serialize, Debug)] -struct BodyCommand { - command: String, -} - -#[derive(Deserialize, Serialize, Debug)] -struct BodyDelete { - root: String, - files: Vec, -} - -pub async fn whitelist_update(add: &Vec, server: &str, token: &str) { - let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); - let bearer = format!("Bearer {token}"); - - for name in add { - let data = BodyCommand { - command: format!("whitelist add {name}"), - }; - post(&format!("{url_base}/command"), &bearer, &data).await; - } -} - -pub async fn whitelist_wipe(server: &str, token: &str) { - let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); - let bearer = format!("Bearer {token}"); - - // delete whitelist - let deletion = BodyDelete { - root: "/".to_string(), - files: vec!["whitelist.json".to_string()], - }; - post(&format!("{url_base}/files/delete"), &bearer, &deletion).await; - - // recreate teh file, passing in the type here so the compiler knows what type of vec it is - post::>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await; - - // reload the whitelist - let data = BodyCommand { - command: "whitelist reload".to_string(), - }; - post(&format!("{url_base}/command"), &bearer, &data).await; -} - -pub async fn server_information(server: &str, token: &str) -> Option { - let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); - let bearer = format!("Bearer {token}"); - get::(&format!("{url_base}/"), &bearer).await -} - -pub async fn get_minecraft_config(db: &Pool) -> Vec { - sqlx::query_as::<_, Minecraft>( - r#" - SELECT * - FROM minecraft - "#, - ) - .fetch_all(db) - .await - .unwrap_or_default() -} - -pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Vec { - sqlx::query_as::<_, Minecraft>( - r#" - SELECT * - FROM minecraft - WHERE server_discord = ?1 - "#, - ) - .bind(*g_id.as_u64() as i64) - .fetch_all(db) - .await - .unwrap_or_default() -} -- 2.47.2 From 273c58d035bbbf1669d30547565c17caf721dae1 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 01:29:01 +0000 Subject: [PATCH 09/40] fmt: re-organise the regular data request --- src/bin/update_data.rs | 4 ++-- src/commands/add_server.rs | 4 ++-- src/common/wolves.rs | 13 ++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index e4b662d..310b97a 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -8,7 +8,7 @@ use skynet_discord_bot::{get_config, Config}; use std::{process, sync::Arc}; use tokio::sync::RwLock; use skynet_discord_bot::common::database::{db_init, DataBase}; -use skynet_discord_bot::common::wolves::get_data::get_wolves_cns; +use skynet_discord_bot::common::wolves::cns::get_wolves; #[tokio::main] async fn main() { @@ -46,7 +46,7 @@ impl EventHandler for Handler { println!("{} is connected!", ready.user.name); // get the data for each individual club/soc - get_wolves_cns(&ctx).await; + get_wolves(&ctx).await; // finish up process::exit(0); diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index dace04e..1886e56 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -6,7 +6,7 @@ use serenity::{ prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, }, }; -use skynet_discord_bot::common::wolves::get_data::get_wolves_cns; +use skynet_discord_bot::common::wolves::cns::get_wolves; use skynet_discord_bot::{is_admin, set_roles::update_server}; use sqlx::{Error, Pool, Sqlite}; use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; @@ -216,7 +216,7 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul // update all users if update { // handle wolves api here - get_wolves_cns(ctx).await; + get_wolves(ctx).await; let mut roles_remove = vec![]; if current_remove { diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 99e7818..71f30c0 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -11,7 +11,10 @@ // pub members: Vec, // } -pub mod get_data { +/** + This is getting data for Clubs and Socs +*/ +pub mod cns { use crate::set_roles::update_server; use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; @@ -50,7 +53,7 @@ pub mod get_data { pub email: String, pub expiry: String, } - pub async fn get_wolves_cns(ctx: &Context) { + pub async fn get_wolves(ctx: &Context) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() @@ -75,7 +78,7 @@ pub mod get_data { // list of users that need to be updated for this server let mut user_to_update = vec![]; - for user in get_wolves_cns_sub(&config, wolves_api).await { + for user in get_wolves_sub(&config, wolves_api).await { let id = user.member_id.parse::().unwrap_or_default(); match existing.get(&(id as i64)) { None => { @@ -103,7 +106,7 @@ pub mod get_data { } } - pub async fn get_server_member(db: &Pool, server: &GuildId) -> Vec { + async fn get_server_member(db: &Pool, server: &GuildId) -> Vec { sqlx::query_as::<_, ServerMembersWolves>( r#" SELECT * @@ -121,7 +124,7 @@ pub mod get_data { .unwrap_or_default() } - async fn get_wolves_cns_sub(config: &Config, wolves_api: &str) -> Vec { + async fn get_wolves_sub(config: &Config, wolves_api: &str) -> Vec { if config.wolves_url.is_empty() { return vec![]; } -- 2.47.2 From fe5aa5b2526f719e8940b9c5ba0eb8c229160eed Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 01:34:23 +0000 Subject: [PATCH 10/40] prep: rough format for requesting data for an indivual and committee --- src/common/wolves.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 71f30c0..766ee92 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -188,4 +188,14 @@ pub mod cns { } } } -} \ No newline at end of file +} + +/** + Get and store the data on C&S committees +*/ +pub mod committees {} + +/** +get the data for an individual user +*/ +pub mod individual {} \ No newline at end of file -- 2.47.2 From bd80bda22fcf3adeb3787531d124dc6ef1e0736e Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 02:06:24 +0000 Subject: [PATCH 11/40] feat: added rough code to get an individuals member_id --- src/common/wolves.rs | 176 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 151 insertions(+), 25 deletions(-) diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 766ee92..c13b163 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -1,3 +1,7 @@ +use serde::{Deserialize, Serialize}; +use sqlx::{Pool, Sqlite}; +use crate::common::database::Wolves; + /** This file relates to anything that directly interacts with teh wolves API */ @@ -11,6 +15,45 @@ // pub members: Vec, // } +#[derive(Deserialize, Serialize, Debug)] +struct WolvesResultUserMin { + // committee: String, + member_id: String, + // first_name: String, + // last_name: String, + contact_email: String, + // opt_in_email: String, + // student_id: Option, + // note: Option, + // expiry: String, + // requested: String, + // approved: String, + // sitename: String, + // domain: String, +} +async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { + // expiry + match sqlx::query_as::<_, Wolves>( + " + INSERT INTO wolves (id_wolves, email) + VALUES ($1, $2) + ON CONFLICT(id_wolves) DO UPDATE SET email = $2 + ", + ) + .bind(&user.member_id) + .bind(&user.contact_email) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + println!("Failure to insert into Wolves {:?}", user); + println!("{:?}", e); + } + } +} + + /** This is getting data for Clubs and Socs */ @@ -23,6 +66,7 @@ pub mod cns { use sqlx::{Pool, Sqlite}; use crate::Config; use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers, Wolves}; + use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; #[derive(Deserialize, Serialize, Debug)] struct WolvesResultUser { @@ -41,6 +85,15 @@ pub mod cns { domain: String, } + impl From<&WolvesResultUser> for WolvesResultUserMin { + fn from(value: &WolvesResultUser) -> Self { + Self { + member_id: value.member_id.to_owned(), + contact_email: value.contact_email.to_owned(), + } + } + } + #[derive(Deserialize, Serialize, Debug)] struct WolvesResult { success: i8, @@ -83,12 +136,12 @@ pub mod cns { match existing.get(&(id as i64)) { None => { // user does not exist already, add everything - add_users_wolves(&db, &user).await; + add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; add_users_server_members(&db, server, &user).await; } Some(old) => { // always update wolves table, in case data has changed - add_users_wolves(&db, &user).await; + add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; if old.expiry != user.expiry { add_users_server_members(&db, server, &user).await; @@ -129,8 +182,10 @@ pub mod cns { return vec![]; } + let url = format!("{}/get_members", &config.wolves_url); + // get wolves data - if let Ok(mut res) = surf::post(&config.wolves_url).header("X-AM-Identity", wolves_api).await { + if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { if let Ok(WolvesResult { success, result, @@ -147,27 +202,6 @@ pub mod cns { vec![] } - async fn add_users_wolves(db: &Pool, user: &WolvesResultUser) { - // expiry - match sqlx::query_as::<_, Wolves>( - " - INSERT INTO wolves (id_wolves, email) - VALUES ($1, $2) - ON CONFLICT(id_wolves) DO UPDATE SET email = $2 - ", - ) - .bind(&user.member_id) - .bind(&user.contact_email) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - println!("Failure to insert into Wolves {:?}", user); - println!("{:?}", e); - } - } - } async fn add_users_server_members(db: &Pool, server: &GuildId, user: &WolvesResultUser) { match sqlx::query_as::<_, ServerMembers>( " @@ -198,4 +232,96 @@ pub mod committees {} /** get the data for an individual user */ -pub mod individual {} \ No newline at end of file +pub mod individual { + use serde::{Deserialize, Serialize}; + use serenity::client::Context; + use crate::common::database::DataBase; + use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; + use crate::Config; + + + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResultUser { + // committee: String, + member_id: String, + // first_name: String, + // last_name: String, + contact_email: String, + // opt_in_email: String, + // student_id: Option, + // note: Option, + // expiry: String, + // requested: String, + // approved: String, + // sitename: String, + // domain: String, + } + + impl From<&WolvesResultUser> for WolvesResultUserMin { + fn from(value: &WolvesResultUser) -> Self { + Self { + member_id: value.member_id.to_owned(), + contact_email: value.contact_email.to_owned(), + } + } + } + + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResult { + success: i8, + result: WolvesResultUser, + } + + + pub async fn get_user(ctx: &Context, email: &str) -> bool{ + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Database in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let config = config_lock.read().await; + + // TODO: proper api key management + let api_key = ""; + // request data from wolves + match get_user_sub(&config, api_key, email).await { + None => { + false + } + // if exists save it and return true + Some(user) => { + // add to db + add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; + + true + } + } + } + + async fn get_user_sub(config: &Config, wolves_api: &str, email: &str) -> Option { + if config.wolves_url.is_empty() { + return None; + } + + // TODO: Change teh stored env value to teh base domain + let url = format!("{}/get_member", &config.wolves_url); + + // get wolves data + if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { + if let Ok(WolvesResult { success, result, }) = res.body_json().await { + if success != 1 { + return None; + } + + return Some(result); + } + } + + None + } +} \ No newline at end of file -- 2.47.2 From aff6299ac7ecea32367b7d08476be9c5fb53f635 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 12:49:35 +0000 Subject: [PATCH 12/40] feat: added the committee request from wolves --- Cargo.lock | 1 + Cargo.toml | 1 + db/migrations/7_committee-mk-ii.sql | 8 +- src/common/wolves.rs | 121 ++++++++++++++++++++++++++-- 4 files changed, 119 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8c9113..af01bd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2419,6 +2419,7 @@ dependencies = [ "maud", "rand 0.8.5", "serde", + "serde_json", "serenity", "sqlx", "surf", diff --git a/Cargo.toml b/Cargo.toml index a26023a..2128303 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ dotenvy = "0.15.7" # For sqlite sqlx = { version = "0.7.1", features = [ "runtime-tokio", "sqlite", "migrate" ] } +serde_json = { version = "1.0", features = ["raw_value"] } # create random strings rand = "0.8.5" diff --git a/db/migrations/7_committee-mk-ii.sql b/db/migrations/7_committee-mk-ii.sql index 2ddf316..c086f3d 100644 --- a/db/migrations/7_committee-mk-ii.sql +++ b/db/migrations/7_committee-mk-ii.sql @@ -3,8 +3,8 @@ DROP TABLE committee; -- new table pulling from teh api CREATE TABLE IF NOT EXISTS committees ( - id integer PRIMARY KEY, - name text not null, - link text not null, - members text not null + id integer PRIMARY KEY, + name text not null, + link text not null, + committee text not null ); diff --git a/src/common/wolves.rs b/src/common/wolves.rs index c13b163..380d25a 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -7,13 +7,6 @@ use crate::common::database::Wolves; */ -// #[derive(Debug, Clone, sqlx::FromRow)] -// pub struct Committees { -// pub id: i64, -// pub name: String, -// pub link: String, -// pub members: Vec, -// } #[derive(Deserialize, Serialize, Debug)] struct WolvesResultUserMin { @@ -227,7 +220,119 @@ pub mod cns { /** Get and store the data on C&S committees */ -pub mod committees {} +pub mod committees { + use serde::{Deserialize, Serialize}; + use serenity::client::Context; + use sqlx::{ Pool, Sqlite}; + use crate::common::database::{DataBase}; + use crate::Config; + + // This is what Wolves returns to us + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResult { + success: i8, + result: Vec, + } + + // this is teh actual data we care about + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResultCNS { + id: String, + name: String, + // Link to their page such as https://ulwolves.ie/society/computer + link: String, + // array of Committee members member_id's + committee: Vec + } + + // Database entry for it + #[derive(Debug, Clone, sqlx::FromRow)] + pub struct Committees { + pub id: i64, + pub name: String, + pub link: String, + #[sqlx(json)] + pub committee: Vec, + } + + impl From for Committees { + fn from(value: WolvesResultCNS) -> Self { + Self{ + id: value.id.parse().unwrap_or(0), + name: value.name, + link: value.link, + committee: value.committee.iter().map(|x| x.parse::().unwrap_or(0)).collect(), + } + } + } + + pub async fn get_cns(ctx: &Context){ + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Database in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let config = config_lock.read().await; + + // TODO: proper api key management + let api_key = ""; + // request data from wolves + for committee in get_committees(&config, api_key).await { + let tmp = Committees::from(committee); + add_committee(&db, &tmp).await; + } + } + + async fn get_committees(config: &Config, wolves_api: &str) -> Vec { + if config.wolves_url.is_empty() { + return vec![]; + } + + // TODO: Change teh stored env value to teh base domain + let url = format!("{}/get_cns", &config.wolves_url); + + // get wolves data + if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { + if let Ok(WolvesResult { success, result, }) = res.body_json().await { + if success != 1 { + return vec![]; + } + + return result; + } + } + + vec![] + } + + async fn add_committee(db: &Pool, committee: &Committees) { + match sqlx::query_as::<_, Committees>( + " + INSERT INTO committees (id, name, link, committee) + VALUES ($1, $2, $3, $4) + ON CONFLICT(id) DO UPDATE SET committee = $4 + ", + ) + .bind(committee.id) + .bind(&committee.name) + .bind(&committee.link) + .bind(serde_json::to_string(&committee.committee).unwrap_or_default()) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + println!("Failure to insert into Committees {:?}", committee); + println!("{:?}", e); + } + } + } +} /** get the data for an individual user -- 2.47.2 From 3e6dc9d56028178b4107267ab4162b349b4fcaf1 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 14:20:36 +0000 Subject: [PATCH 13/40] feat: actually get the data for teh committees and pop it in the database --- src/bin/update_data.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 310b97a..3151080 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -9,6 +9,7 @@ use std::{process, sync::Arc}; use tokio::sync::RwLock; use skynet_discord_bot::common::database::{db_init, DataBase}; use skynet_discord_bot::common::wolves::cns::get_wolves; +use skynet_discord_bot::common::wolves::committees::get_cns; #[tokio::main] async fn main() { @@ -47,6 +48,9 @@ impl EventHandler for Handler { // get the data for each individual club/soc get_wolves(&ctx).await; + + // get teh data for the clubs/socs committees + get_cns(&ctx).await; // finish up process::exit(0); -- 2.47.2 From 61e76db8ddebbd6efbdd20bdf96af4621074d5d1 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 21:17:44 +0000 Subject: [PATCH 14/40] feat: this should be able to update teh roles of commettee members on teh discord server --- src/bin/update_users.rs | 145 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 3 deletions(-) diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index cf48650..133dd13 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -6,8 +6,14 @@ use serenity::{ }; use skynet_discord_bot::{get_config, set_roles, Config}; use std::{process, sync::Arc}; +use std::collections::HashMap; +use serenity::builder::EditRole; +use serenity::model::guild::{Member, Role}; +use serenity::model::id::{GuildId, RoleId, UserId}; +use sqlx::{Pool, Sqlite}; use tokio::sync::RwLock; -use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; +use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase, Servers, Wolves}; +use skynet_discord_bot::common::wolves::committees::Committees; #[tokio::main] async fn main() { @@ -44,14 +50,18 @@ impl EventHandler for Handler { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); - bulk_check(Arc::clone(&ctx)).await; + // this goes into each server and sets roles for each wolves member + check_bulk(Arc::clone(&ctx)).await; + + // u[date committee server + committee::check_committee(Arc::clone(&ctx)).await; // finish up process::exit(0); } } -async fn bulk_check(ctx: Arc) { +async fn check_bulk(ctx: Arc) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() @@ -63,3 +73,132 @@ async fn bulk_check(ctx: Arc) { set_roles::update_server(&ctx, &server_config, &[], &[]).await; } } + +// for updating committee members +pub mod committee { + use super::*; + + pub(crate) async fn check_committee(ctx: Arc) { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + + let db = db_lock.read().await; + + let server = GuildId(1220150752656363520); + let mut members = server.members(&ctx, None, None).await.unwrap_or_default(); + + update_committees(&db, &ctx, &mut members).await; + } + + pub async fn update_committees(db: &Pool, ctx: &Context, members: &mut Vec){ + let server = GuildId(1220150752656363520); + let committee_member = RoleId(1226602779968274573); + let committees = get_committees(db).await; + + // information about the server + let roles = server.roles(&ctx).await.unwrap_or_default(); + + // make a hashmap of the nameof roles to quickly get them out again + let mut roles_name = HashMap::new(); + for role in roles.values() { + roles_name.insert(role.name.to_owned(), role.to_owned()); + } + + + // a map of users and the roles they are goign to be getting + let mut users_roles = HashMap::new(); + + // 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 { + // get the role for this committee/club/soc + let role = match roles_name.get(&committee.name) { + 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)).await{ + Ok(x) => { Some(x) } + Err(_) => {None} + } + } + }; + + // so if the role exists + if let Some(r) = role { + committee_roles.push(r.id); + + for id_wolves in &committee.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![]); + values.push(r.id); + } + } + } + } + } + + // 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_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 + } + }; + + // 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()); + } + } + if !roles_rem.is_empty() { + member.remove_roles(&ctx, &roles_rem).await.unwrap_or_default(); + } + + if !roles_required.is_empty() { + // these roles are flavor roles, only there to make folks mentionable + member.add_roles(&ctx, &roles_required).await.unwrap_or_default(); + } + } + } + + async fn get_committees(db: &Pool) -> Vec { + sqlx::query_as::<_, Committees>( + r#" + SELECT * + FROM committees + "#, + ) + .fetch_all(db) + .await + .unwrap_or_default() + } + + async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { + sqlx::query_as::<_, Wolves>( + r#" + SELECT * + FROM wolves + WHERE id_wolves = ? + "#, + ) + .bind(user) + .fetch_one(db) + .await + .ok() + } +} \ No newline at end of file -- 2.47.2 From f1138a3c81a8095053f31c1269f63c4af0eda403 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 21:34:21 +0000 Subject: [PATCH 15/40] fix: moved the methods that changes role into their own module folder --- src/bin/update_users.rs | 142 +------------------- src/commands/add_server.rs | 15 ++- src/common/minecraft.rs | 2 +- src/common/mod.rs | 3 +- src/common/set_roles.rs | 266 +++++++++++++++++++++++++++++++++++++ src/common/wolves.rs | 2 +- src/lib.rs | 139 +------------------ 7 files changed, 283 insertions(+), 286 deletions(-) create mode 100644 src/common/set_roles.rs diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 133dd13..9b98c79 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -4,16 +4,11 @@ use serenity::{ model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::{get_config, set_roles, Config}; +use skynet_discord_bot::{get_config, Config}; use std::{process, sync::Arc}; -use std::collections::HashMap; -use serenity::builder::EditRole; -use serenity::model::guild::{Member, Role}; -use serenity::model::id::{GuildId, RoleId, UserId}; -use sqlx::{Pool, Sqlite}; use tokio::sync::RwLock; -use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase, Servers, Wolves}; -use skynet_discord_bot::common::wolves::committees::Committees; +use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; +use skynet_discord_bot::common::set_roles::{committee, normal}; #[tokio::main] async fn main() { @@ -70,135 +65,6 @@ async fn check_bulk(ctx: Arc) { let db = db_lock.read().await; for server_config in get_server_config_bulk(&db).await { - set_roles::update_server(&ctx, &server_config, &[], &[]).await; - } -} - -// for updating committee members -pub mod committee { - use super::*; - - pub(crate) async fn check_committee(ctx: Arc) { - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - - let db = db_lock.read().await; - - let server = GuildId(1220150752656363520); - let mut members = server.members(&ctx, None, None).await.unwrap_or_default(); - - update_committees(&db, &ctx, &mut members).await; - } - - pub async fn update_committees(db: &Pool, ctx: &Context, members: &mut Vec){ - let server = GuildId(1220150752656363520); - let committee_member = RoleId(1226602779968274573); - let committees = get_committees(db).await; - - // information about the server - let roles = server.roles(&ctx).await.unwrap_or_default(); - - // make a hashmap of the nameof roles to quickly get them out again - let mut roles_name = HashMap::new(); - for role in roles.values() { - roles_name.insert(role.name.to_owned(), role.to_owned()); - } - - - // a map of users and the roles they are goign to be getting - let mut users_roles = HashMap::new(); - - // 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 { - // get the role for this committee/club/soc - let role = match roles_name.get(&committee.name) { - 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)).await{ - Ok(x) => { Some(x) } - Err(_) => {None} - } - } - }; - - // so if the role exists - if let Some(r) = role { - committee_roles.push(r.id); - - for id_wolves in &committee.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![]); - values.push(r.id); - } - } - } - } - } - - // 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_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 - } - }; - - // 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()); - } - } - if !roles_rem.is_empty() { - member.remove_roles(&ctx, &roles_rem).await.unwrap_or_default(); - } - - if !roles_required.is_empty() { - // these roles are flavor roles, only there to make folks mentionable - member.add_roles(&ctx, &roles_required).await.unwrap_or_default(); - } - } - } - - async fn get_committees(db: &Pool) -> Vec { - sqlx::query_as::<_, Committees>( - r#" - SELECT * - FROM committees - "#, - ) - .fetch_all(db) - .await - .unwrap_or_default() - } - - async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { - sqlx::query_as::<_, Wolves>( - r#" - SELECT * - FROM wolves - WHERE id_wolves = ? - "#, - ) - .bind(user) - .fetch_one(db) - .await - .ok() + normal::update_server(&ctx, &server_config, &[], &[]).await; } } \ No newline at end of file diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 1886e56..f049028 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -1,15 +1,16 @@ use serenity::{ - builder::CreateApplicationCommand, - client::Context, - model::{ - application::interaction::application_command::ApplicationCommandInteraction, - prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, - }, + builder::CreateApplicationCommand, + client::Context, + model::{ + application::interaction::application_command::ApplicationCommandInteraction, + prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, + }, }; use skynet_discord_bot::common::wolves::cns::get_wolves; -use skynet_discord_bot::{is_admin, set_roles::update_server}; +use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; +use skynet_discord_bot::common::set_roles::normal::update_server; pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { // check if user has high enough permisssions diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 55ce53b..40fbb4f 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -4,7 +4,7 @@ use sqlx::{Error, FromRow, Pool, Row, Sqlite}; use serenity::model::id::GuildId; use sqlx::sqlite::SqliteRow; use crate::Config; -use crate::set_roles::get_server_member_bulk; +use crate::common::set_roles::normal::get_server_member_bulk; #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/src/common/mod.rs b/src/common/mod.rs index 3302619..6b1adc9 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,3 +1,4 @@ pub mod wolves; pub mod database; -pub mod minecraft; \ No newline at end of file +pub mod minecraft; +pub mod set_roles; \ No newline at end of file diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs new file mode 100644 index 0000000..20c8e6d --- /dev/null +++ b/src/common/set_roles.rs @@ -0,0 +1,266 @@ +pub mod normal { + use serenity::client::Context; + use serenity::model::id::{GuildId, RoleId, UserId}; + use sqlx::{Pool, Sqlite}; + use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves}; + use crate::get_now_iso; + + pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Database in TypeMap.").clone() + }; + + let db = db_lock.read().await; + + let Servers { + server, + role_past, + role_current, + .. + } = server; + + let mut roles_set = [0, 0, 0]; + let mut members = vec![]; + + for member in get_server_member_bulk(&db, server).await { + if let Some(x) = member.discord { + members.push(x); + } + } + let mut members_all = members.len(); + + if let Ok(x) = server.members(ctx, None, None).await { + for mut member in x { + // members_changed acts as an override to only deal with teh users in it + if !members_changed.is_empty() && !members_changed.contains(&member.user.id) { + continue; + } + + if members.contains(&member.user.id) { + let mut roles = vec![]; + + if let Some(role) = &role_past { + if !member.roles.contains(role) { + roles_set[0] += 1; + roles.push(role.to_owned()); + } + } + + if !member.roles.contains(role_current) { + roles_set[1] += 1; + roles.push(role_current.to_owned()); + } + + if let Err(e) = member.add_roles(ctx, &roles).await { + println!("{:?}", e); + } + } else { + // old and never + + if let Some(role) = &role_past { + if member.roles.contains(role) { + members_all += 1; + } + } + + if member.roles.contains(role_current) { + roles_set[2] += 1; + // if theya re not a current member and have the role then remove it + if let Err(e) = member.remove_role(ctx, role_current).await { + println!("{:?}", e); + } + } + } + for role in remove_roles.iter().flatten() { + if let Err(e) = member.remove_role(ctx, role).await { + println!("{:?}", e); + } + } + } + } + + set_server_numbers(&db, server, members_all as i64, members.len() as i64).await; + + // small bit of logging to note changes over time + println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.as_u64(), roles_set[0], roles_set[1], roles_set[2]); + } + + pub async fn get_server_member_bulk(db: &Pool, server: &GuildId) -> Vec { + sqlx::query_as::<_, ServerMembersWolves>( + r#" + SELECT * + FROM server_members + JOIN wolves USING (id_wolves) + WHERE ( + server = ? + AND discord IS NOT NULL + AND expiry > ? + ) + "#, + ) + .bind(*server.as_u64() as i64) + .bind(get_now_iso(true)) + .fetch_all(db) + .await + .unwrap_or_default() + } + + async fn set_server_numbers(db: &Pool, server: &GuildId, past: i64, current: i64) { + match sqlx::query_as::<_, Wolves>( + " + UPDATE servers + SET member_past = ?, member_current = ? + WHERE server = ? + ", + ) + .bind(past) + .bind(current) + .bind(*server.as_u64() as i64) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + println!("Failure to insert into {}", server.as_u64()); + println!("{:?}", e); + } + } + } +} + +// for updating committee members +pub mod committee { + use std::collections::HashMap; + use std::sync::Arc; + use serenity::client::Context; + use serenity::model::guild::Member; + use serenity::model::id::{GuildId, RoleId}; + use sqlx::{Pool, Sqlite}; + use crate::common::database::{DataBase, Wolves}; + use crate::common::wolves::committees::Committees; + + pub async fn check_committee(ctx: Arc) { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + + let db = db_lock.read().await; + + let server = GuildId(1220150752656363520); + let mut members = server.members(&ctx, None, None).await.unwrap_or_default(); + + update_committees(&db, &ctx, &mut members).await; + } + + pub async fn update_committees(db: &Pool, ctx: &Context, members: &mut Vec){ + let server = GuildId(1220150752656363520); + let committee_member = RoleId(1226602779968274573); + let committees = get_committees(db).await; + + // information about the server + let roles = server.roles(&ctx).await.unwrap_or_default(); + + // make a hashmap of the nameof roles to quickly get them out again + let mut roles_name = HashMap::new(); + for role in roles.values() { + roles_name.insert(role.name.to_owned(), role.to_owned()); + } + + + // a map of users and the roles they are goign to be getting + let mut users_roles = HashMap::new(); + + // 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 { + // get the role for this committee/club/soc + let role = match roles_name.get(&committee.name) { + 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)).await{ + Ok(x) => { Some(x) } + Err(_) => {None} + } + } + }; + + // so if the role exists + if let Some(r) = role { + committee_roles.push(r.id); + + for id_wolves in &committee.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![]); + values.push(r.id); + } + } + } + } + } + + // 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_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 + } + }; + + // 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()); + } + } + if !roles_rem.is_empty() { + member.remove_roles(&ctx, &roles_rem).await.unwrap_or_default(); + } + + if !roles_required.is_empty() { + // these roles are flavor roles, only there to make folks mentionable + member.add_roles(&ctx, &roles_required).await.unwrap_or_default(); + } + } + } + + async fn get_committees(db: &Pool) -> Vec { + sqlx::query_as::<_, Committees>( + r#" + SELECT * + FROM committees + "#, + ) + .fetch_all(db) + .await + .unwrap_or_default() + } + + async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { + sqlx::query_as::<_, Wolves>( + r#" + SELECT * + FROM wolves + WHERE id_wolves = ? + "#, + ) + .bind(user) + .fetch_one(db) + .await + .ok() + } +} \ No newline at end of file diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 380d25a..0560b27 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -51,7 +51,7 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { This is getting data for Clubs and Socs */ pub mod cns { - use crate::set_roles::update_server; + use crate::common::set_roles::normal::update_server; use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use serenity::client::Context; diff --git a/src/lib.rs b/src/lib.rs index 5dbbe0d..335f51e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,25 +2,15 @@ pub mod common; use dotenvy::dotenv; use serde::{Deserialize, Serialize}; -use serenity::{ - model::id::{GuildId, RoleId}, - prelude::TypeMapKey, -}; +use serenity::prelude::TypeMapKey; -use crate::set_roles::get_server_member_bulk; use chrono::{Datelike, SecondsFormat, Utc}; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use serde::de::DeserializeOwned; use serenity::client::Context; -use serenity::model::id::UserId; use serenity::model::prelude::application_command::ApplicationCommandInteraction; -use sqlx::{ - Pool, Sqlite, -}; use std::{env, sync::Arc}; use tokio::sync::RwLock; -use common::database::{ServerMembersWolves, Servers}; - pub struct Config { // manages where teh database is stored pub home: String, @@ -103,133 +93,6 @@ pub fn random_string(len: usize) -> String { thread_rng().sample_iter(&Alphanumeric).take(len).map(char::from).collect() } -pub mod set_roles { - use crate::common::database::{DataBase, Wolves}; - use super::*; - pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Database in TypeMap.").clone() - }; - - let db = db_lock.read().await; - - let Servers { - server, - role_past, - role_current, - .. - } = server; - - let mut roles_set = [0, 0, 0]; - let mut members = vec![]; - - for member in get_server_member_bulk(&db, server).await { - if let Some(x) = member.discord { - members.push(x); - } - } - let mut members_all = members.len(); - - if let Ok(x) = server.members(ctx, None, None).await { - for mut member in x { - // members_changed acts as an override to only deal with teh users in it - if !members_changed.is_empty() && !members_changed.contains(&member.user.id) { - continue; - } - - if members.contains(&member.user.id) { - let mut roles = vec![]; - - if let Some(role) = &role_past { - if !member.roles.contains(role) { - roles_set[0] += 1; - roles.push(role.to_owned()); - } - } - - if !member.roles.contains(role_current) { - roles_set[1] += 1; - roles.push(role_current.to_owned()); - } - - if let Err(e) = member.add_roles(ctx, &roles).await { - println!("{:?}", e); - } - } else { - // old and never - - if let Some(role) = &role_past { - if member.roles.contains(role) { - members_all += 1; - } - } - - if member.roles.contains(role_current) { - roles_set[2] += 1; - // if theya re not a current member and have the role then remove it - if let Err(e) = member.remove_role(ctx, role_current).await { - println!("{:?}", e); - } - } - } - for role in remove_roles.iter().flatten() { - if let Err(e) = member.remove_role(ctx, role).await { - println!("{:?}", e); - } - } - } - } - - set_server_numbers(&db, server, members_all as i64, members.len() as i64).await; - - // small bit of logging to note changes over time - println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.as_u64(), roles_set[0], roles_set[1], roles_set[2]); - } - - pub async fn get_server_member_bulk(db: &Pool, server: &GuildId) -> Vec { - sqlx::query_as::<_, ServerMembersWolves>( - r#" - SELECT * - FROM server_members - JOIN wolves USING (id_wolves) - WHERE ( - server = ? - AND discord IS NOT NULL - AND expiry > ? - ) - "#, - ) - .bind(*server.as_u64() as i64) - .bind(get_now_iso(true)) - .fetch_all(db) - .await - .unwrap_or_default() - } - - async fn set_server_numbers(db: &Pool, server: &GuildId, past: i64, current: i64) { - match sqlx::query_as::<_, Wolves>( - " - UPDATE servers - SET member_past = ?, member_current = ? - WHERE server = ? - ", - ) - .bind(past) - .bind(current) - .bind(*server.as_u64() as i64) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - println!("Failure to insert into {}", server.as_u64()); - println!("{:?}", e); - } - } - } -} - /** For any time ye need to check if a user who calls a command has admin privlages */ -- 2.47.2 From 32249364ff4250c7d528f1bf7131566f06a015f2 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 21:40:48 +0000 Subject: [PATCH 16/40] feat: new committee member joins the committee server they automagically get roles --- src/main.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main.rs b/src/main.rs index 6f86f86..a274a8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,8 +15,10 @@ use serenity::{ }; use skynet_discord_bot::{get_config, Config}; use std::sync::Arc; +use serenity::model::id::GuildId; use tokio::sync::RwLock; 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; struct Handler; @@ -35,6 +37,13 @@ impl EventHandler for Handler { Some(x) => x, }; + // committee server takes priority + if new_member.guild_id.eq(&GuildId(1220150752656363520)) { + let mut member = vec![new_member.clone()]; + update_committees(&db, &ctx, &mut member).await; + return; + } + if get_server_member(&db, &new_member.guild_id, &new_member).await.is_ok() { let mut roles = vec![]; -- 2.47.2 From b7161e26143a29c8693c5c2544f83d8bb7c480f9 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 21:51:14 +0000 Subject: [PATCH 17/40] todo: added note --- src/common/wolves.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 0560b27..5759104 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -63,6 +63,7 @@ pub mod cns { #[derive(Deserialize, Serialize, Debug)] struct WolvesResultUser { + // TODO: Might be worth trying to get this replaced with the club/soc ID? committee: String, member_id: String, first_name: String, -- 2.47.2 From 344d6d3585bd0be34369086e428c7ee17c093419 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 28 Oct 2024 21:53:04 +0000 Subject: [PATCH 18/40] fmt: formatting and clippy --- src/bin/update_data.rs | 16 +++---- src/bin/update_minecraft.rs | 4 +- src/bin/update_users.rs | 14 +++--- src/commands/add_server.rs | 4 +- src/commands/link_email.rs | 4 +- src/commands/minecraft.rs | 14 +++--- src/commands/role_adder.rs | 2 +- src/common/database.rs | 16 +++---- src/common/minecraft.rs | 11 ++--- src/common/mod.rs | 4 +- src/common/set_roles.rs | 53 +++++++++++----------- src/common/wolves.rs | 89 +++++++++++++++++++------------------ src/lib.rs | 7 +-- src/main.rs | 30 ++++++------- 14 files changed, 130 insertions(+), 138 deletions(-) diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 3151080..4385a1c 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -1,15 +1,15 @@ use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, - Client, + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, }; -use skynet_discord_bot::{get_config, Config}; -use std::{process, sync::Arc}; -use tokio::sync::RwLock; use skynet_discord_bot::common::database::{db_init, DataBase}; use skynet_discord_bot::common::wolves::cns::get_wolves; use skynet_discord_bot::common::wolves::committees::get_cns; +use skynet_discord_bot::{get_config, Config}; +use std::{process, sync::Arc}; +use tokio::sync::RwLock; #[tokio::main] async fn main() { @@ -48,7 +48,7 @@ impl EventHandler for Handler { // get the data for each individual club/soc get_wolves(&ctx).await; - + // get teh data for the clubs/socs committees get_cns(&ctx).await; diff --git a/src/bin/update_minecraft.rs b/src/bin/update_minecraft.rs index cae6b62..f5d3634 100644 --- a/src/bin/update_minecraft.rs +++ b/src/bin/update_minecraft.rs @@ -1,7 +1,7 @@ -use skynet_discord_bot::get_config; -use std::collections::HashSet; use skynet_discord_bot::common::database::db_init; use skynet_discord_bot::common::minecraft::{get_minecraft_config, update_server, whitelist_wipe}; +use skynet_discord_bot::get_config; +use std::collections::HashSet; #[tokio::main] async fn main() { diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 9b98c79..e3170eb 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -1,14 +1,14 @@ use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, - Client, + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, }; +use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; +use skynet_discord_bot::common::set_roles::{committee, normal}; use skynet_discord_bot::{get_config, Config}; use std::{process, sync::Arc}; use tokio::sync::RwLock; -use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; -use skynet_discord_bot::common::set_roles::{committee, normal}; #[tokio::main] async fn main() { @@ -67,4 +67,4 @@ async fn check_bulk(ctx: Arc) { for server_config in get_server_config_bulk(&db).await { normal::update_server(&ctx, &server_config, &[], &[]).await; } -} \ No newline at end of file +} diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index f049028..52284ac 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -6,11 +6,11 @@ use serenity::{ prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, }, }; +use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; +use skynet_discord_bot::common::set_roles::normal::update_server; use skynet_discord_bot::common::wolves::cns::get_wolves; use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; -use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; -use skynet_discord_bot::common::set_roles::normal::update_server; pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { // check if user has high enough permisssions diff --git a/src/commands/link_email.rs b/src/commands/link_email.rs index 224c6da..25e0262 100644 --- a/src/commands/link_email.rs +++ b/src/commands/link_email.rs @@ -13,9 +13,9 @@ use serenity::{ prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, }, }; +use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify}; use skynet_discord_bot::{get_now_iso, random_string, Config}; use sqlx::{Pool, Sqlite}; -use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify}; pub mod link { use super::*; @@ -241,8 +241,8 @@ pub mod verify { use crate::commands::link_email::link::{db_pending_clear_expired, get_verify_from_db}; use serenity::model::user::User; use skynet_discord_bot::common::database::get_server_config; - use sqlx::Error; use skynet_discord_bot::common::database::{ServerMembersWolves, Servers}; + use sqlx::Error; pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { let db_lock = { diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index 962fe1f..61b304d 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -16,10 +16,10 @@ pub(crate) mod user { use super::*; use crate::commands::link_email::link::get_server_member_discord; use serenity::model::id::UserId; - use skynet_discord_bot::Config; - use sqlx::Error; use skynet_discord_bot::common::database::Wolves; use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft}; + use skynet_discord_bot::Config; + use sqlx::Error; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("link_minecraft").description("Link your minecraft account").create_option(|option| { @@ -124,9 +124,9 @@ pub(crate) mod server { use sqlx::Error; // this is to managfe the server side of commands related to minecraft use super::*; - use skynet_discord_bot::{is_admin, Config}; - use skynet_discord_bot::common::minecraft::Minecraft; use skynet_discord_bot::common::minecraft::update_server; + use skynet_discord_bot::common::minecraft::Minecraft; + use skynet_discord_bot::{is_admin, Config}; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_add").description("Add a minecraft server").create_option(|option| { @@ -205,9 +205,9 @@ pub(crate) mod server { use serenity::builder::CreateApplicationCommand; use serenity::client::Context; use serenity::model::prelude::application_command::ApplicationCommandInteraction; - use skynet_discord_bot::{is_admin, Config}; use skynet_discord_bot::common::database::DataBase; use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information}; + use skynet_discord_bot::{is_admin, Config}; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_list").description("List your minecraft servers") @@ -268,10 +268,10 @@ pub(crate) mod server { use serenity::model::application::command::CommandOptionType; use serenity::model::id::GuildId; use serenity::model::prelude::application_command::{ApplicationCommandInteraction, CommandDataOptionValue}; - use skynet_discord_bot::is_admin; - use sqlx::{Error, Pool, Sqlite}; use skynet_discord_bot::common::database::DataBase; use skynet_discord_bot::common::minecraft::Minecraft; + use skynet_discord_bot::is_admin; + use sqlx::{Error, Pool, Sqlite}; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("minecraft_delete").description("Delete a minecraft server").create_option(|option| { diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index 89f9441..d45d445 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -7,9 +7,9 @@ use serenity::{ }, }; +use skynet_discord_bot::common::database::{DataBase, RoleAdder}; use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; -use skynet_discord_bot::common::database::{DataBase, RoleAdder}; pub mod edit { use super::*; diff --git a/src/common/database.rs b/src/common/database.rs index 9674499..b3edc18 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -1,13 +1,13 @@ -use serenity::prelude::TypeMapKey; -use std::sync::Arc; -use tokio::sync::RwLock; -use sqlx::{Error, FromRow, Pool, Row, Sqlite}; -use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}; -use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; +use crate::Config; use serde::{Deserialize, Serialize}; use serenity::model::guild; +use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; +use serenity::prelude::TypeMapKey; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}; +use sqlx::{Error, FromRow, Pool, Row, Sqlite}; use std::str::FromStr; -use crate::Config; +use std::sync::Arc; +use tokio::sync::RwLock; pub struct DataBase; impl TypeMapKey for DataBase { @@ -267,4 +267,4 @@ pub async fn get_server_config_bulk(db: &Pool) -> Vec { .fetch_all(db) .await .unwrap_or_default() -} \ No newline at end of file +} diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 40fbb4f..6ee11c8 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -1,11 +1,10 @@ -use serde::{Deserialize, Serialize}; +use crate::common::set_roles::normal::get_server_member_bulk; +use crate::Config; use serde::de::DeserializeOwned; -use sqlx::{Error, FromRow, Pool, Row, Sqlite}; +use serde::{Deserialize, Serialize}; use serenity::model::id::GuildId; use sqlx::sqlite::SqliteRow; -use crate::Config; -use crate::common::set_roles::normal::get_server_member_bulk; - +use sqlx::{Error, FromRow, Pool, Row, Sqlite}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Minecraft { @@ -25,7 +24,6 @@ impl<'r> FromRow<'r, SqliteRow> for Minecraft { } } - /** loop through all members of server get a list of folks with mc accounts that are members @@ -164,4 +162,3 @@ pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Ve .await .unwrap_or_default() } - diff --git a/src/common/mod.rs b/src/common/mod.rs index 6b1adc9..38f457a 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,4 +1,4 @@ -pub mod wolves; pub mod database; pub mod minecraft; -pub mod set_roles; \ No newline at end of file +pub mod set_roles; +pub mod wolves; diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 20c8e6d..c3c80cb 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -1,11 +1,11 @@ pub mod normal { - use serenity::client::Context; - use serenity::model::id::{GuildId, RoleId, UserId}; - use sqlx::{Pool, Sqlite}; - use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves}; - use crate::get_now_iso; + use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves}; + use crate::get_now_iso; + use serenity::client::Context; + use serenity::model::id::{GuildId, RoleId, UserId}; + use sqlx::{Pool, Sqlite}; - pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { + pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() @@ -131,14 +131,14 @@ pub mod normal { // for updating committee members pub mod committee { - use std::collections::HashMap; - use std::sync::Arc; + use crate::common::database::{DataBase, Wolves}; + use crate::common::wolves::committees::Committees; use serenity::client::Context; use serenity::model::guild::Member; use serenity::model::id::{GuildId, RoleId}; use sqlx::{Pool, Sqlite}; - use crate::common::database::{DataBase, Wolves}; - use crate::common::wolves::committees::Committees; + use std::collections::HashMap; + use std::sync::Arc; pub async fn check_committee(ctx: Arc) { let db_lock = { @@ -154,7 +154,7 @@ pub mod committee { update_committees(&db, &ctx, &mut members).await; } - pub async fn update_committees(db: &Pool, ctx: &Context, members: &mut Vec){ + pub async fn update_committees(db: &Pool, ctx: &Context, members: &mut Vec) { let server = GuildId(1220150752656363520); let committee_member = RoleId(1226602779968274573); let committees = get_committees(db).await; @@ -168,24 +168,21 @@ pub mod committee { roles_name.insert(role.name.to_owned(), role.to_owned()); } - // a map of users and the roles they are goign to be getting let mut users_roles = HashMap::new(); // a list of all the roles that can be removed from folks who should have them - let mut committee_roles = vec![ - committee_member - ]; + let mut committee_roles = vec![committee_member]; for committee in &committees { // get the role for this committee/club/soc let role = match roles_name.get(&committee.name) { - Some(x) => {Some(x.to_owned())} + 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)).await{ - Ok(x) => { Some(x) } - Err(_) => {None} + match server.create_role(&ctx, |r| r.hoist(false).mentionable(true).name(&committee.name)).await { + Ok(x) => Some(x), + Err(_) => None, } } }; @@ -196,7 +193,7 @@ pub mod committee { for id_wolves in &committee.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(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![]); values.push(r.id); @@ -245,9 +242,9 @@ pub mod committee { FROM committees "#, ) - .fetch_all(db) - .await - .unwrap_or_default() + .fetch_all(db) + .await + .unwrap_or_default() } async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { @@ -258,9 +255,9 @@ pub mod committee { WHERE id_wolves = ? "#, ) - .bind(user) - .fetch_one(db) - .await - .ok() + .bind(user) + .fetch_one(db) + .await + .ok() } -} \ No newline at end of file +} diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 5759104..7e29f1c 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -1,12 +1,10 @@ +use crate::common::database::Wolves; use serde::{Deserialize, Serialize}; use sqlx::{Pool, Sqlite}; -use crate::common::database::Wolves; /** - This file relates to anything that directly interacts with teh wolves API - */ - - + This file relates to anything that directly interacts with teh wolves API +*/ #[derive(Deserialize, Serialize, Debug)] struct WolvesResultUserMin { @@ -33,10 +31,10 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { ON CONFLICT(id_wolves) DO UPDATE SET email = $2 ", ) - .bind(&user.member_id) - .bind(&user.contact_email) - .fetch_optional(db) - .await + .bind(&user.member_id) + .bind(&user.contact_email) + .fetch_optional(db) + .await { Ok(_) => {} Err(e) => { @@ -46,20 +44,19 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { } } - /** This is getting data for Clubs and Socs */ pub mod cns { + use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}; use crate::common::set_roles::normal::update_server; - use std::collections::BTreeMap; + use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; + use crate::Config; use serde::{Deserialize, Serialize}; use serenity::client::Context; use serenity::model::id::GuildId; use sqlx::{Pool, Sqlite}; - use crate::Config; - use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers, Wolves}; - use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; + use std::collections::BTreeMap; #[derive(Deserialize, Serialize, Debug)] struct WolvesResultUser { @@ -222,11 +219,11 @@ pub mod cns { Get and store the data on C&S committees */ pub mod committees { + use crate::common::database::DataBase; + use crate::Config; use serde::{Deserialize, Serialize}; use serenity::client::Context; - use sqlx::{ Pool, Sqlite}; - use crate::common::database::{DataBase}; - use crate::Config; + use sqlx::{Pool, Sqlite}; // This is what Wolves returns to us #[derive(Deserialize, Serialize, Debug)] @@ -243,22 +240,22 @@ pub mod committees { // Link to their page such as https://ulwolves.ie/society/computer link: String, // array of Committee members member_id's - committee: Vec + committee: Vec, } // Database entry for it #[derive(Debug, Clone, sqlx::FromRow)] pub struct Committees { - pub id: i64, - pub name: String, - pub link: String, - #[sqlx(json)] - pub committee: Vec, + pub id: i64, + pub name: String, + pub link: String, + #[sqlx(json)] + pub committee: Vec, } impl From for Committees { fn from(value: WolvesResultCNS) -> Self { - Self{ + Self { id: value.id.parse().unwrap_or(0), name: value.name, link: value.link, @@ -267,7 +264,7 @@ pub mod committees { } } - pub async fn get_cns(ctx: &Context){ + pub async fn get_cns(ctx: &Context) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() @@ -283,7 +280,7 @@ pub mod committees { // TODO: proper api key management let api_key = ""; // request data from wolves - for committee in get_committees(&config, api_key).await { + for committee in get_committees(&config, api_key).await { let tmp = Committees::from(committee); add_committee(&db, &tmp).await; } @@ -299,7 +296,11 @@ pub mod committees { // get wolves data if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { - if let Ok(WolvesResult { success, result, }) = res.body_json().await { + if let Ok(WolvesResult { + success, + result, + }) = res.body_json().await + { if success != 1 { return vec![]; } @@ -319,12 +320,12 @@ pub mod committees { ON CONFLICT(id) DO UPDATE SET committee = $4 ", ) - .bind(committee.id) - .bind(&committee.name) - .bind(&committee.link) - .bind(serde_json::to_string(&committee.committee).unwrap_or_default()) - .fetch_optional(db) - .await + .bind(committee.id) + .bind(&committee.name) + .bind(&committee.link) + .bind(serde_json::to_string(&committee.committee).unwrap_or_default()) + .fetch_optional(db) + .await { Ok(_) => {} Err(e) => { @@ -339,12 +340,11 @@ pub mod committees { get the data for an individual user */ pub mod individual { - use serde::{Deserialize, Serialize}; - use serenity::client::Context; use crate::common::database::DataBase; use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; use crate::Config; - + use serde::{Deserialize, Serialize}; + use serenity::client::Context; #[derive(Deserialize, Serialize, Debug)] struct WolvesResultUser { @@ -378,8 +378,7 @@ pub mod individual { result: WolvesResultUser, } - - pub async fn get_user(ctx: &Context, email: &str) -> bool{ + pub async fn get_user(ctx: &Context, email: &str) -> bool { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() @@ -396,9 +395,7 @@ pub mod individual { let api_key = ""; // request data from wolves match get_user_sub(&config, api_key, email).await { - None => { - false - } + None => false, // if exists save it and return true Some(user) => { // add to db @@ -409,7 +406,7 @@ pub mod individual { } } - async fn get_user_sub(config: &Config, wolves_api: &str, email: &str) -> Option { + async fn get_user_sub(config: &Config, wolves_api: &str, _email: &str) -> Option { if config.wolves_url.is_empty() { return None; } @@ -419,7 +416,11 @@ pub mod individual { // get wolves data if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { - if let Ok(WolvesResult { success, result, }) = res.body_json().await { + if let Ok(WolvesResult { + success, + result, + }) = res.body_json().await + { if success != 1 { return None; } @@ -430,4 +431,4 @@ pub mod individual { None } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 335f51e..6abc48a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,11 @@ pub mod common; -use dotenvy::dotenv; -use serde::{Deserialize, Serialize}; -use serenity::prelude::TypeMapKey; - use chrono::{Datelike, SecondsFormat, Utc}; +use dotenvy::dotenv; use rand::{distributions::Alphanumeric, thread_rng, Rng}; -use serde::de::DeserializeOwned; use serenity::client::Context; use serenity::model::prelude::application_command::ApplicationCommandInteraction; +use serenity::prelude::TypeMapKey; use std::{env, sync::Arc}; use tokio::sync::RwLock; pub struct Config { diff --git a/src/main.rs b/src/main.rs index a274a8b..17d6615 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,23 +2,23 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; use serenity::model::guild::Member; -use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::{ - application::{command::Command, interaction::Interaction}, - gateway::{GatewayIntents, Ready}, - prelude::Activity, - user::OnlineStatus, - }, - Client, -}; -use skynet_discord_bot::{get_config, Config}; -use std::sync::Arc; use serenity::model::id::GuildId; -use tokio::sync::RwLock; +use serenity::{ + async_trait, + client::{Context, EventHandler}, + model::{ + application::{command::Command, interaction::Interaction}, + gateway::{GatewayIntents, Ready}, + prelude::Activity, + user::OnlineStatus, + }, + Client, +}; 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::{get_config, Config}; +use std::sync::Arc; +use tokio::sync::RwLock; struct Handler; @@ -43,7 +43,7 @@ impl EventHandler for Handler { update_committees(&db, &ctx, &mut member).await; return; } - + if get_server_member(&db, &new_member.guild_id, &new_member).await.is_ok() { let mut roles = vec![]; -- 2.47.2 From 733827c3e6175fe64f0a74cd4dcb439172f1ec88 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 01:17:43 +0000 Subject: [PATCH 19/40] feat: added support for teh new api key --- src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 6abc48a..a3b4edf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,8 @@ pub struct Config { // wolves API base for clubs/socs pub wolves_url: String, + // API key for accessing more general resources + pub wolves_api: String, } impl TypeMapKey for Config { type Value = Arc>; @@ -44,6 +46,7 @@ pub fn get_config() -> Config { mail_user: "".to_string(), mail_pass: "".to_string(), wolves_url: "".to_string(), + wolves_api: "".to_string(), }; if let Ok(x) = env::var("DATABASE_HOME") { @@ -74,6 +77,10 @@ pub fn get_config() -> Config { config.wolves_url = x.trim().to_string(); } + if let Ok(x) = env::var("WOLVES_API") { + config.wolves_api = x.trim().to_string(); + } + config } -- 2.47.2 From 7a6421469ca98c753fa7057464d794f71a4ba78d Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 02:23:46 +0000 Subject: [PATCH 20/40] feat: now able to get the memebr_id from just email --- Cargo.lock | 340 ++++++++++++++++++++++++++++++++++--- Cargo.toml | 3 +- src/commands/link_email.rs | 81 ++++++++- 3 files changed, 402 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af01bd3..cc8ef0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1024,7 +1024,26 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes 1.7.1", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", "indexmap", "slab", "tokio", @@ -1147,6 +1166,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes 1.7.1", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1154,7 +1184,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes 1.7.1", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes 1.7.1", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes 1.7.1", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1216,9 +1269,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1230,6 +1283,26 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +dependencies = [ + "bytes 1.7.1", + "futures-channel", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -1237,13 +1310,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", - "hyper", + "http 0.2.12", + "hyper 0.14.30", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.5.0", + "hyper-util", + "rustls 0.23.16", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes 1.7.1", + "http-body-util", + "hyper 1.5.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes 1.7.1", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.5.0", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -1330,7 +1455,7 @@ dependencies = [ "curl-sys", "flume 0.9.2", "futures-lite 1.13.0", - "http", + "http 0.2.12", "log", "once_cell", "slab", @@ -2014,11 +2139,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", @@ -2028,12 +2153,12 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", "tokio", "tokio-rustls 0.24.1", "tokio-util", @@ -2047,6 +2172,49 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64 0.22.1", + "bytes 1.7.1", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.0", + "hyper-rustls 0.27.3", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration 0.6.1", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "ring" version = "0.16.20" @@ -2145,10 +2313,23 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2158,6 +2339,21 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2168,6 +2364,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "ryu" version = "1.0.18" @@ -2321,7 +2528,7 @@ dependencies = [ "mime_guess", "parking_lot", "percent-encoding", - "reqwest", + "reqwest 0.11.27", "serde", "serde-value", "serde_json", @@ -2399,6 +2606,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -2418,6 +2634,7 @@ dependencies = [ "lettre", "maud", "rand 0.8.5", + "reqwest 0.12.9", "serde", "serde_json", "serenity", @@ -2835,6 +3052,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2843,7 +3069,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys 0.6.0", ] [[package]] @@ -2856,6 +3093,16 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.12.0" @@ -2983,7 +3230,9 @@ dependencies = [ "bytes 1.7.1", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.7", "tokio-macros", "windows-sys 0.52.0", @@ -3000,6 +3249,16 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.23.4" @@ -3021,6 +3280,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.16", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.16" @@ -3108,7 +3378,7 @@ dependencies = [ "base64 0.13.1", "byteorder", "bytes 1.7.1", - "http", + "http 0.2.12", "httparse", "log", "rand 0.8.5", @@ -3433,6 +3703,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 2128303..c94d0a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,8 @@ name = "update_minecraft" [dependencies] # discord library serenity = { version = "0.11.6", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } -tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "full"] } +reqwest = { version = "0.12", features = ["json"] } # to make the http requests surf = "2.3.2" diff --git a/src/commands/link_email.rs b/src/commands/link_email.rs index 25e0262..daa847c 100644 --- a/src/commands/link_email.rs +++ b/src/commands/link_email.rs @@ -19,6 +19,7 @@ use sqlx::{Pool, Sqlite}; pub mod link { use super::*; + use serde::{Deserialize, Serialize}; pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { let db_lock = { @@ -61,7 +62,32 @@ pub mod link { // check if email exists let details = match get_server_member_email(&db, email).await { None => { - return "Please check it matches (including case) your preferred contact on https://ulwolves.ie/memberships/profile and that you are fully paid up.".to_string() + let invalid_user = "Please check it matches (including case) your preferred contact on https://ulwolves.ie/memberships/profile and that you are fully paid up.".to_string(); + + // see if the user actually exists + let id = match get_user(&config, email).await { + None => { + return invalid_user; + } + Some(x) => x, + }; + + // save teh user id and email to teh db + match save_to_db_user(&db, id, email).await { + Ok(x) => x, + Err(x) => { + dbg!(x); + return "Error: unable to save user to teh database, contact Computer Society".to_string(); + } + }; + + // pull it back out (technically could do it in previous step but more explicit) + match get_server_member_email(&db, email).await { + None => { + return "Error: failed to read user from database.".to_string(); + } + Some(x) => x, + } } Some(x) => x, }; @@ -234,6 +260,59 @@ pub mod link { .fetch_optional(db) .await } + + #[derive(Serialize, Deserialize, Debug)] + #[serde(untagged)] + pub enum WolvesResultUserResult { + B(bool), + S(String), + } + + #[derive(Deserialize, Serialize, Debug)] + struct WolvesResultUser { + success: i64, + result: WolvesResultUserResult, + } + + async fn get_user(config: &Config, email: &str) -> Option { + let url = format!("{}/get_id_from_email", &config.wolves_url); + match reqwest::Client::new() + .post(&url) + .form(&[("email", email)]) + .header("X-AM-Identity", &config.wolves_api) + .send() + .await + { + Ok(x) => { + if let Ok(y) = x.json::().await { + // this is the only time we will get a positive response, the None at the end catches everything else + if let WolvesResultUserResult::S(z) = y.result { + if let Ok(id) = z.parse::() { + return Some(id); + } + } + } + } + Err(e) => { + dbg!(e); + } + } + + None + } + + async fn save_to_db_user(db: &Pool, id_wolves: i64, email: &str) -> Result, sqlx::Error> { + sqlx::query_as::<_, Wolves>( + " + INSERT INTO wolves (id_wolves, email) + VALUES (?1, ?2) + ", + ) + .bind(id_wolves) + .bind(email) + .fetch_optional(db) + .await + } } pub mod verify { -- 2.47.2 From 015f23b9223014c942f3a951c1e9dca037932a2b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 12:51:41 +0000 Subject: [PATCH 21/40] feat: no longer using teh "hardcoded" api key --- src/common/wolves.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 7e29f1c..5a1e1b7 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -277,25 +277,24 @@ pub mod committees { }; let config = config_lock.read().await; - // TODO: proper api key management - let api_key = ""; // request data from wolves - for committee in get_committees(&config, api_key).await { - let tmp = Committees::from(committee); - add_committee(&db, &tmp).await; + for committee_wolves in get_committees(&config).await { + let committee = Committees::from(committee_wolves); + add_committee(&db, &committee).await; } } - async fn get_committees(config: &Config, wolves_api: &str) -> Vec { + async fn get_committees(config: &Config) -> Vec { if config.wolves_url.is_empty() { return vec![]; } // TODO: Change teh stored env value to teh base domain + // TODO: this address may change let url = format!("{}/get_cns", &config.wolves_url); // get wolves data - if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { + if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", &config.wolves_api).await { if let Ok(WolvesResult { success, result, -- 2.47.2 From d673dce6fa10e9d579bb18cec5fe571143b5523d Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 12:53:53 +0000 Subject: [PATCH 22/40] fix: handle the just in case the user alrady exists as a different person --- src/commands/link_email.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commands/link_email.rs b/src/commands/link_email.rs index daa847c..ace4691 100644 --- a/src/commands/link_email.rs +++ b/src/commands/link_email.rs @@ -305,7 +305,8 @@ pub mod link { sqlx::query_as::<_, Wolves>( " INSERT INTO wolves (id_wolves, email) - VALUES (?1, ?2) + VALUES ($1, $2) + ON CONFLICT(id_wolves) DO UPDATE SET email = $2 ", ) .bind(id_wolves) -- 2.47.2 From 6739c7e06860a0f50781a07563867dcceca0a0f6 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 14:55:26 +0000 Subject: [PATCH 23/40] feat: now use env vars to get teh server and roles for committee --- src/common/set_roles.rs | 23 +++++++++++++++++------ src/lib.rs | 18 ++++++++++++++++++ src/main.rs | 11 ++++++++--- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index c3c80cb..a3cd2ba 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -133,9 +133,9 @@ pub mod normal { pub mod committee { use crate::common::database::{DataBase, Wolves}; use crate::common::wolves::committees::Committees; + use crate::Config; use serenity::client::Context; use serenity::model::guild::Member; - use serenity::model::id::{GuildId, RoleId}; use sqlx::{Pool, Sqlite}; use std::collections::HashMap; use std::sync::Arc; @@ -148,15 +148,26 @@ pub mod committee { let db = db_lock.read().await; - let server = GuildId(1220150752656363520); + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let config_global = config_lock.read().await; + + let server = config_global.committee_server; + + // because to use it to update a single user we need to pre-get the members of teh server let mut members = server.members(&ctx, None, None).await.unwrap_or_default(); - update_committees(&db, &ctx, &mut members).await; + update_committees(&db, &ctx, &config_global, &mut members).await; } - pub async fn update_committees(db: &Pool, ctx: &Context, members: &mut Vec) { - let server = GuildId(1220150752656363520); - let committee_member = RoleId(1226602779968274573); + /** + This function can take a vec of members (or just one) and gives tehm the appropiate roles on teh committee server + */ + pub async fn update_committees(db: &Pool, ctx: &Context, config: &Config, members: &mut Vec) { + let server = config.committee_server; + let committee_member = config.committee_role; let committees = get_committees(db).await; // information about the server diff --git a/src/lib.rs b/src/lib.rs index a3b4edf..5a9d43d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use serenity::client::Context; +use serenity::model::id::{GuildId, RoleId}; use serenity::model::prelude::application_command::ApplicationCommandInteraction; use serenity::prelude::TypeMapKey; use std::{env, sync::Arc}; @@ -26,6 +27,10 @@ pub struct Config { pub wolves_url: String, // API key for accessing more general resources pub wolves_api: String, + + // discord server for committee + pub committee_server: GuildId, + pub committee_role: RoleId, } impl TypeMapKey for Config { type Value = Arc>; @@ -47,6 +52,8 @@ pub fn get_config() -> Config { mail_pass: "".to_string(), wolves_url: "".to_string(), wolves_api: "".to_string(), + committee_server: GuildId(0), + committee_role: RoleId(0), }; if let Ok(x) = env::var("DATABASE_HOME") { @@ -81,6 +88,17 @@ pub fn get_config() -> Config { config.wolves_api = x.trim().to_string(); } + if let Ok(x) = env::var("COMMITTEE_DISCORD") { + if let Ok(x) = x.trim().parse::() { + config.committee_server = GuildId(x); + } + } + if let Ok(x) = env::var("COMMITTEE_DISCORD") { + if let Ok(x) = x.trim().parse::() { + config.committee_role = RoleId(x); + } + } + config } diff --git a/src/main.rs b/src/main.rs index 17d6615..be4eebb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; use serenity::model::guild::Member; -use serenity::model::id::GuildId; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -37,10 +36,16 @@ impl EventHandler for Handler { Some(x) => x, }; + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let config_global = config_lock.read().await; + // committee server takes priority - if new_member.guild_id.eq(&GuildId(1220150752656363520)) { + if new_member.guild_id.eq(&config_global.committee_server) { let mut member = vec![new_member.clone()]; - update_committees(&db, &ctx, &mut member).await; + update_committees(&db, &ctx, &config_global, &mut member).await; return; } -- 2.47.2 From 5b22f699d687fd41880c91a10938123fafccf85a Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 14:59:05 +0000 Subject: [PATCH 24/40] fix: getting teh server config needs to happen after checking for committee --- src/main.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index be4eebb..62c0350 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,10 +31,6 @@ impl EventHandler for Handler { }; let db = db_lock.read().await; - let config = match get_server_config(&db, &new_member.guild_id).await { - None => return, - Some(x) => x, - }; let config_lock = { let data_read = ctx.data.read().await; @@ -49,17 +45,22 @@ impl EventHandler for Handler { return; } + let config_server = match get_server_config(&db, &new_member.guild_id).await { + None => return, + Some(x) => x, + }; + if get_server_member(&db, &new_member.guild_id, &new_member).await.is_ok() { let mut roles = vec![]; - if let Some(role) = &config.role_past { + if let Some(role) = &config_server.role_past { if !new_member.roles.contains(role) { roles.push(role.to_owned()); } } - if !new_member.roles.contains(&config.role_current) { - roles.push(config.role_current.to_owned()); + if !new_member.roles.contains(&config_server.role_current) { + roles.push(config_server.role_current.to_owned()); } if let Err(e) = new_member.add_roles(&ctx, &roles).await { @@ -72,10 +73,10 @@ 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_name, - &config.wolves_link, - &config.server, - &config.bot_channel_id + &config_server.server_name, + &config_server.wolves_link, + &config_server.server, + &config_server.bot_channel_id ); if let Err(err) = new_member.user.direct_message(&ctx, |m| m.content(&msg)).await { -- 2.47.2 From e4a8cce725266ede78c523584c9628a567032d44 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 16:17:43 +0000 Subject: [PATCH 25/40] feat: new env var for teh specific channel that the general chat stuff will be under --- src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5a9d43d..144b5e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use serenity::client::Context; -use serenity::model::id::{GuildId, RoleId}; +use serenity::model::id::{ChannelId, GuildId, RoleId}; use serenity::model::prelude::application_command::ApplicationCommandInteraction; use serenity::prelude::TypeMapKey; use std::{env, sync::Arc}; @@ -31,6 +31,7 @@ pub struct Config { // discord server for committee pub committee_server: GuildId, pub committee_role: RoleId, + pub committee_category: ChannelId, } impl TypeMapKey for Config { type Value = Arc>; @@ -54,6 +55,7 @@ pub fn get_config() -> Config { wolves_api: "".to_string(), committee_server: GuildId(0), committee_role: RoleId(0), + committee_category: ChannelId(0), }; if let Ok(x) = env::var("DATABASE_HOME") { @@ -98,6 +100,11 @@ pub fn get_config() -> Config { config.committee_role = RoleId(x); } } + if let Ok(x) = env::var("COMMITTEE_CATEGORY") { + if let Ok(x) = x.trim().parse::() { + config.committee_category = ChannelId(x); + } + } config } -- 2.47.2 From c98baa9d729b2090572f9031f878ac5ffb082bc4 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 16:23:03 +0000 Subject: [PATCH 26/40] feat: will now create a channel for any new club/soc --- src/common/set_roles.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index a3cd2ba..ced3ad5 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -135,6 +135,7 @@ pub mod committee { use crate::common::wolves::committees::Committees; use crate::Config; use serenity::client::Context; + use serenity::model::channel::ChannelType; use serenity::model::guild::Member; use sqlx::{Pool, Sqlite}; use std::collections::HashMap; @@ -172,6 +173,7 @@ pub mod committee { // information about the server let roles = server.roles(&ctx).await.unwrap_or_default(); + let channels = server.channels(&ctx).await.unwrap_or_default(); // make a hashmap of the nameof roles to quickly get them out again let mut roles_name = HashMap::new(); @@ -179,6 +181,11 @@ pub mod committee { roles_name.insert(role.name.to_owned(), role.to_owned()); } + let mut channels_name = HashMap::new(); + for channel in channels.values() { + channels_name.insert(channel.name.to_owned(), channel.to_owned()); + } + // a map of users and the roles they are goign to be getting let mut users_roles = HashMap::new(); @@ -198,6 +205,24 @@ pub mod committee { } }; + // create teh channel if it does nto exist + if !channels_name.contains_key(&committee.name) { + match server + .create_channel(&ctx, |c| c.name(&committee.name).kind(ChannelType::Text).category(config.committee_category)) + .await + { + Ok(x) => { + // update teh channels name list + channels_name.insert(x.name.to_owned(), x.to_owned()); + + println!("Created channel: {}", &committee.name); + } + Err(x) => { + dbg!("Unable to create channel: ", x); + } + } + }; + // so if the role exists if let Some(r) = role { committee_roles.push(r.id); -- 2.47.2 From 2f75dc41c87d12bb04cde777b533bd6887302f5d Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 16:47:48 +0000 Subject: [PATCH 27/40] feat: will properly re-order the channels created Also focuses on anything in teh right category --- src/common/set_roles.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index ced3ad5..9b26117 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -183,7 +183,12 @@ pub mod committee { let mut channels_name = HashMap::new(); for channel in channels.values() { - channels_name.insert(channel.name.to_owned(), channel.to_owned()); + // 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()); + } + } } // a map of users and the roles they are goign to be getting @@ -269,6 +274,32 @@ pub mod committee { member.add_roles(&ctx, &roles_required).await.unwrap_or_default(); } } + + // finally re-order teh channels to make them visually apealing + let mut channel_names = channels_name.clone().into_keys().collect::>(); + channel_names.sort(); + + // get a list of all teh new positions + let mut new_positions = vec![]; + for (i, name) in channel_names.iter().enumerate() { + if let Some(channel) = channels_name.get_mut(name) { + let position_new = i as u64; + if position_new != channel.position as u64 { + new_positions.push((channel.id.to_owned(), position_new)); + } + } + } + + if !new_positions.is_empty() { + match server.reorder_channels(&ctx, new_positions).await { + Ok(_) => { + println!("Successfully re-orderd the committee category"); + } + Err(e) => { + dbg!("Failed to re-order ", e); + } + } + } } async fn get_committees(db: &Pool) -> Vec { -- 2.47.2 From 77a7b7b81d3b74c20fe214d8f1fa4b51ae0a08b5 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 9 Nov 2024 16:53:26 +0000 Subject: [PATCH 28/40] fix: slight change in env var used to get teh base URL --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 144b5e1..09f60bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,7 @@ pub fn get_config() -> Config { config.mail_pass = x.trim().to_string(); } - if let Ok(x) = env::var("WOLVES_URL") { + if let Ok(x) = env::var("WOLVES_URL_BASE") { config.wolves_url = x.trim().to_string(); } -- 2.47.2 From ca55a78447089a2b04446d917777e25f1fd5f7f3 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 23 Nov 2024 21:53:30 +0000 Subject: [PATCH 29/40] feat: switched over to using a library to interact with teh wolves API --- Cargo.lock | 86 +++++++++++---- Cargo.toml | 5 +- src/commands/link_email.rs | 31 +----- src/common/wolves.rs | 218 ++----------------------------------- 4 files changed, 85 insertions(+), 255 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc8ef0d..af719e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,28 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "async-task" version = "4.7.1" @@ -232,7 +254,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -920,7 +942,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1803,7 +1825,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1900,7 +1922,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -2026,9 +2048,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2446,9 +2468,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -2465,13 +2487,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -2634,13 +2656,13 @@ dependencies = [ "lettre", "maud", "rand 0.8.5", - "reqwest 0.12.9", "serde", "serde_json", "serenity", "sqlx", "surf", "tokio", + "wolves_oxidised", ] [[package]] @@ -3037,9 +3059,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -3133,7 +3155,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3246,7 +3268,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3302,6 +3324,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-test" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +dependencies = [ + "async-stream", + "bytes 1.7.1", + "futures-core", + "tokio", + "tokio-stream", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -3341,7 +3376,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3569,7 +3604,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "wasm-bindgen-shared", ] @@ -3603,7 +3638,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3891,6 +3926,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wolves_oxidised" +version = "0.1.0" +source = "git+https://forgejo.skynet.ie/Skynet/wolves-oxidised.git#eee6a76c695a36f1fe220fdeafd5a43757e50527" +dependencies = [ + "reqwest 0.12.9", + "serde", + "serde_json", + "tokio-test", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -3909,7 +3955,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c94d0a0..92fa6c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,10 @@ name = "update_minecraft" # discord library serenity = { version = "0.11.6", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "full"] } -reqwest = { version = "0.12", features = ["json"] } + +# wolves api +# TODO: move off of unstable +wolves_oxidised = { git = "https://forgejo.skynet.ie/Skynet/wolves-oxidised.git", features = ["unstable"]} # to make the http requests surf = "2.3.2" diff --git a/src/commands/link_email.rs b/src/commands/link_email.rs index ace4691..9d6328f 100644 --- a/src/commands/link_email.rs +++ b/src/commands/link_email.rs @@ -64,8 +64,10 @@ pub mod link { None => { let invalid_user = "Please check it matches (including case) your preferred contact on https://ulwolves.ie/memberships/profile and that you are fully paid up.".to_string(); + let wolves = wolves_oxidised::Client::new(&config.wolves_url, Some(&config.wolves_api)); + // see if the user actually exists - let id = match get_user(&config, email).await { + let id = match wolves.get_member(email).await { None => { return invalid_user; } @@ -274,33 +276,6 @@ pub mod link { result: WolvesResultUserResult, } - async fn get_user(config: &Config, email: &str) -> Option { - let url = format!("{}/get_id_from_email", &config.wolves_url); - match reqwest::Client::new() - .post(&url) - .form(&[("email", email)]) - .header("X-AM-Identity", &config.wolves_api) - .send() - .await - { - Ok(x) => { - if let Ok(y) = x.json::().await { - // this is the only time we will get a positive response, the None at the end catches everything else - if let WolvesResultUserResult::S(z) = y.result { - if let Ok(id) = z.parse::() { - return Some(id); - } - } - } - } - Err(e) => { - dbg!(e); - } - } - - None - } - async fn save_to_db_user(db: &Pool, id_wolves: i64, email: &str) -> Result, sqlx::Error> { sqlx::query_as::<_, Wolves>( " diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 5a1e1b7..0f975e3 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -52,32 +52,13 @@ pub mod cns { use crate::common::set_roles::normal::update_server; use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; use crate::Config; - use serde::{Deserialize, Serialize}; use serenity::client::Context; use serenity::model::id::GuildId; use sqlx::{Pool, Sqlite}; use std::collections::BTreeMap; - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResultUser { - // TODO: Might be worth trying to get this replaced with the club/soc ID? - committee: String, - member_id: String, - first_name: String, - last_name: String, - contact_email: String, - opt_in_email: String, - student_id: Option, - note: Option, - expiry: String, - requested: String, - approved: String, - sitename: String, - domain: String, - } - - impl From<&WolvesResultUser> for WolvesResultUserMin { - fn from(value: &WolvesResultUser) -> Self { + impl From<&wolves_oxidised::WolvesUser> for WolvesResultUserMin { + fn from(value: &wolves_oxidised::WolvesUser) -> Self { Self { member_id: value.member_id.to_owned(), contact_email: value.contact_email.to_owned(), @@ -85,18 +66,6 @@ pub mod cns { } } - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResult { - success: i8, - result: Vec, - } - - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResultLocal { - pub id_wolves: String, - pub email: String, - pub expiry: String, - } pub async fn get_wolves(ctx: &Context) { let db_lock = { let data_read = ctx.data.read().await; @@ -110,9 +79,13 @@ pub mod cns { }; let config = config_lock.read().await; + // set up teh client + let wolves = wolves_oxidised::Client::new(&config.wolves_url, Some(&config.wolves_api)); + for server_config in get_server_config_bulk(&db).await { let Servers { server, + // this is the unique api key for each club/soc wolves_api, .. } = &server_config; @@ -122,7 +95,7 @@ pub mod cns { // list of users that need to be updated for this server let mut user_to_update = vec![]; - for user in get_wolves_sub(&config, wolves_api).await { + for user in wolves.get_members(wolves_api).await { let id = user.member_id.parse::().unwrap_or_default(); match existing.get(&(id as i64)) { None => { @@ -168,32 +141,7 @@ pub mod cns { .unwrap_or_default() } - async fn get_wolves_sub(config: &Config, wolves_api: &str) -> Vec { - if config.wolves_url.is_empty() { - return vec![]; - } - - let url = format!("{}/get_members", &config.wolves_url); - - // get wolves data - if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { - if let Ok(WolvesResult { - success, - result, - }) = res.body_json().await - { - if success != 1 { - return vec![]; - } - - return result; - } - } - - vec![] - } - - async fn add_users_server_members(db: &Pool, server: &GuildId, user: &WolvesResultUser) { + async fn add_users_server_members(db: &Pool, server: &GuildId, user: &wolves_oxidised::WolvesUser) { match sqlx::query_as::<_, ServerMembers>( " INSERT OR REPLACE INTO server_members (server, id_wolves, expiry) @@ -221,28 +169,9 @@ pub mod cns { pub mod committees { use crate::common::database::DataBase; use crate::Config; - use serde::{Deserialize, Serialize}; use serenity::client::Context; use sqlx::{Pool, Sqlite}; - // This is what Wolves returns to us - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResult { - success: i8, - result: Vec, - } - - // this is teh actual data we care about - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResultCNS { - id: String, - name: String, - // Link to their page such as https://ulwolves.ie/society/computer - link: String, - // array of Committee members member_id's - committee: Vec, - } - // Database entry for it #[derive(Debug, Clone, sqlx::FromRow)] pub struct Committees { @@ -253,8 +182,8 @@ pub mod committees { pub committee: Vec, } - impl From for Committees { - fn from(value: WolvesResultCNS) -> Self { + impl From for Committees { + fn from(value: wolves_oxidised::WolvesCNS) -> Self { Self { id: value.id.parse().unwrap_or(0), name: value.name, @@ -277,40 +206,14 @@ pub mod committees { }; let config = config_lock.read().await; + let wolves = wolves_oxidised::Client::new(&config.wolves_url, Some(&config.wolves_api)); // request data from wolves - for committee_wolves in get_committees(&config).await { + for committee_wolves in wolves.get_committees().await { let committee = Committees::from(committee_wolves); add_committee(&db, &committee).await; } } - async fn get_committees(config: &Config) -> Vec { - if config.wolves_url.is_empty() { - return vec![]; - } - - // TODO: Change teh stored env value to teh base domain - // TODO: this address may change - let url = format!("{}/get_cns", &config.wolves_url); - - // get wolves data - if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", &config.wolves_api).await { - if let Ok(WolvesResult { - success, - result, - }) = res.body_json().await - { - if success != 1 { - return vec![]; - } - - return result; - } - } - - vec![] - } - async fn add_committee(db: &Pool, committee: &Committees) { match sqlx::query_as::<_, Committees>( " @@ -334,100 +237,3 @@ pub mod committees { } } } - -/** -get the data for an individual user -*/ -pub mod individual { - use crate::common::database::DataBase; - use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; - use crate::Config; - use serde::{Deserialize, Serialize}; - use serenity::client::Context; - - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResultUser { - // committee: String, - member_id: String, - // first_name: String, - // last_name: String, - contact_email: String, - // opt_in_email: String, - // student_id: Option, - // note: Option, - // expiry: String, - // requested: String, - // approved: String, - // sitename: String, - // domain: String, - } - - impl From<&WolvesResultUser> for WolvesResultUserMin { - fn from(value: &WolvesResultUser) -> Self { - Self { - member_id: value.member_id.to_owned(), - contact_email: value.contact_email.to_owned(), - } - } - } - - #[derive(Deserialize, Serialize, Debug)] - struct WolvesResult { - success: i8, - result: WolvesResultUser, - } - - pub async fn get_user(ctx: &Context, email: &str) -> bool { - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Database in TypeMap.").clone() - }; - let db = db_lock.read().await; - - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let config = config_lock.read().await; - - // TODO: proper api key management - let api_key = ""; - // request data from wolves - match get_user_sub(&config, api_key, email).await { - None => false, - // if exists save it and return true - Some(user) => { - // add to db - add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; - - true - } - } - } - - async fn get_user_sub(config: &Config, wolves_api: &str, _email: &str) -> Option { - if config.wolves_url.is_empty() { - return None; - } - - // TODO: Change teh stored env value to teh base domain - let url = format!("{}/get_member", &config.wolves_url); - - // get wolves data - if let Ok(mut res) = surf::post(&url).header("X-AM-Identity", wolves_api).await { - if let Ok(WolvesResult { - success, - result, - }) = res.body_json().await - { - if success != 1 { - return None; - } - - return Some(result); - } - } - - None - } -} -- 2.47.2 From cab04a068f158e9c31659fad452a48afd82698d2 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 18 Feb 2025 13:36:08 +0000 Subject: [PATCH 30/40] feat: fixed up the changes --- Cargo.lock | 16 ++++++++++++++-- Cargo.toml | 7 ++----- db/migrations/7_committee-mk-ii.sql | 10 ---------- db/migrations/8_committee-mk-ii.sql | 11 +++++++++++ src/commands/link_email.rs | 1 - src/commands/minecraft.rs | 4 ++-- src/common/database.rs | 2 ++ src/common/minecraft.rs | 19 +++++-------------- src/common/set_roles.rs | 10 +++++----- src/common/wolves.rs | 17 ++++++++++------- src/lib.rs | 12 +++++++++++- 11 files changed, 62 insertions(+), 47 deletions(-) delete mode 100644 db/migrations/7_committee-mk-ii.sql create mode 100644 db/migrations/8_committee-mk-ii.sql diff --git a/Cargo.lock b/Cargo.lock index 23bef46..a1c92c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1298,7 +1298,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -2628,6 +2628,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -2648,6 +2657,7 @@ dependencies = [ "maud", "rand 0.8.5", "serde", + "serde_json", "serenity", "sqlx", "surf", @@ -3242,7 +3252,9 @@ dependencies = [ "bytes 1.7.1", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.7", "tokio-macros", "windows-sys 0.52.0", @@ -3917,7 +3929,7 @@ dependencies = [ [[package]] name = "wolves_oxidised" version = "0.1.0" -source = "git+https://forgejo.skynet.ie/Skynet/wolves-oxidised.git#867778128c1ef580ebfded7808bbd4e86f22903b" +source = "git+https://forgejo.skynet.ie/Skynet/wolves-oxidised.git#6101104c794c2dcf7100b057fe37fdf165b8b381" dependencies = [ "reqwest 0.12.9", "serde", diff --git a/Cargo.toml b/Cargo.toml index 19cb9ae..fa0ffdc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,11 +20,8 @@ serenity = { version = "0.11.6", default-features = false, features = ["client", tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "full"] } # wolves api -# TODO: move off of unstable -wolves_oxidised = { git = "https://forgejo.skynet.ie/Skynet/wolves-oxidised.git", features = ["unstable"]} - -# wolves api -wolves_oxidised = { git = "https://forgejo.skynet.ie/Skynet/wolves-oxidised.git" } +wolves_oxidised = { git = "https://forgejo.skynet.ie/Skynet/wolves-oxidised.git", features = ["unstable"] } +# wolves_oxidised = { path = "../wolves-oxidised", features = ["unstable"] } # to make the http requests surf = "2.3.2" diff --git a/db/migrations/7_committee-mk-ii.sql b/db/migrations/7_committee-mk-ii.sql deleted file mode 100644 index c086f3d..0000000 --- a/db/migrations/7_committee-mk-ii.sql +++ /dev/null @@ -1,10 +0,0 @@ --- No longer using the previous committee table -DROP TABLE committee; - --- new table pulling from teh api -CREATE TABLE IF NOT EXISTS committees ( - id integer PRIMARY KEY, - name text not null, - link text not null, - committee text not null -); diff --git a/db/migrations/8_committee-mk-ii.sql b/db/migrations/8_committee-mk-ii.sql new file mode 100644 index 0000000..d2e1d7c --- /dev/null +++ b/db/migrations/8_committee-mk-ii.sql @@ -0,0 +1,11 @@ +-- No longer using the previous committee table +DROP TABLE committee; + +-- new table pulling from teh api +CREATE TABLE IF NOT EXISTS committees ( + id integer PRIMARY KEY, + name_profile text not null, + name_full text not null, + link text not null, + committee text not null +); diff --git a/src/commands/link_email.rs b/src/commands/link_email.rs index fdf82d4..a76769f 100644 --- a/src/commands/link_email.rs +++ b/src/commands/link_email.rs @@ -263,7 +263,6 @@ pub mod link { .await } - #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum WolvesResultUserResult { diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index 45c4643..10ee284 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -18,8 +18,8 @@ pub(crate) mod user { use serde::{Deserialize, Serialize}; use serenity::model::id::UserId; use skynet_discord_bot::common::database::Wolves; - use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft}; - use skynet_discord_bot::Config; + use skynet_discord_bot::common::minecraft::Minecraft; + use skynet_discord_bot::{whitelist_update, Config}; use sqlx::Error; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { diff --git a/src/common/database.rs b/src/common/database.rs index b3edc18..5139cb5 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -42,6 +42,7 @@ pub struct ServerMembersWolves { pub email: String, pub discord: Option, pub minecraft: Option, + pub minecraft_uid: Option, } impl<'r> FromRow<'r, SqliteRow> for ServerMembersWolves { @@ -67,6 +68,7 @@ impl<'r> FromRow<'r, SqliteRow> for ServerMembersWolves { email: row.try_get("email")?, discord, minecraft: row.try_get("minecraft")?, + minecraft_uid: row.try_get("minecraft_uid")?, }) } } diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 6ee11c8..000aa46 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -1,5 +1,5 @@ use crate::common::set_roles::normal::get_server_member_bulk; -use crate::Config; +use crate::{whitelist_update, Config}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serenity::model::id::GuildId; @@ -33,7 +33,10 @@ pub async fn update_server(server_id: &str, db: &Pool, g_id: &GuildId, c let mut usernames = vec![]; for member in get_server_member_bulk(db, g_id).await { if let Some(x) = member.minecraft { - usernames.push(x); + usernames.push((x, true)); + } + if let Some(x) = member.minecraft_uid { + usernames.push((x, false)); } } if !usernames.is_empty() { @@ -98,18 +101,6 @@ struct BodyDelete { files: Vec, } -pub async fn whitelist_update(add: &Vec, server: &str, token: &str) { - let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); - let bearer = format!("Bearer {token}"); - - for name in add { - let data = BodyCommand { - command: format!("whitelist add {name}"), - }; - post(&format!("{url_base}/command"), &bearer, &data).await; - } -} - pub async fn whitelist_wipe(server: &str, token: &str) { let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); let bearer = format!("Bearer {token}"); diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 9b26117..912d661 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -199,11 +199,11 @@ pub mod committee { for committee in &committees { // get the role for this committee/club/soc - let role = match roles_name.get(&committee.name) { + let role = match roles_name.get(&committee.name_profile) { 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)).await { + match server.create_role(&ctx, |r| r.hoist(false).mentionable(true).name(&committee.name_profile)).await { Ok(x) => Some(x), Err(_) => None, } @@ -211,16 +211,16 @@ pub mod committee { }; // create teh channel if it does nto exist - if !channels_name.contains_key(&committee.name) { + if !channels_name.contains_key(&committee.name_profile) { match server - .create_channel(&ctx, |c| c.name(&committee.name).kind(ChannelType::Text).category(config.committee_category)) + .create_channel(&ctx, |c| c.name(&committee.name_profile).kind(ChannelType::Text).category(config.committee_category)) .await { Ok(x) => { // update teh channels name list channels_name.insert(x.name.to_owned(), x.to_owned()); - println!("Created channel: {}", &committee.name); + println!("Created channel: {}", &committee.name_profile); } Err(x) => { dbg!("Unable to create channel: ", x); diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 0f975e3..b2c6af1 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -176,7 +176,8 @@ pub mod committees { #[derive(Debug, Clone, sqlx::FromRow)] pub struct Committees { pub id: i64, - pub name: String, + pub name_full: String, + pub name_profile: String, pub link: String, #[sqlx(json)] pub committee: Vec, @@ -185,10 +186,11 @@ pub mod committees { impl From for Committees { fn from(value: wolves_oxidised::WolvesCNS) -> Self { Self { - id: value.id.parse().unwrap_or(0), - name: value.name, + id: value.id, + name_full: value.name_full, + name_profile: value.name_profile, link: value.link, - committee: value.committee.iter().map(|x| x.parse::().unwrap_or(0)).collect(), + committee: value.committee, } } } @@ -217,13 +219,14 @@ pub mod committees { async fn add_committee(db: &Pool, committee: &Committees) { match sqlx::query_as::<_, Committees>( " - INSERT INTO committees (id, name, link, committee) - VALUES ($1, $2, $3, $4) + INSERT INTO committees (id, name_profile, name_full, link, committee) + VALUES ($1, $2, $3, $4, $5) ON CONFLICT(id) DO UPDATE SET committee = $4 ", ) .bind(committee.id) - .bind(&committee.name) + .bind(&committee.name_profile) + .bind(&committee.name_full) .bind(&committee.link) .bind(serde_json::to_string(&committee.committee).unwrap_or_default()) .fetch_optional(db) diff --git a/src/lib.rs b/src/lib.rs index 1b5000d..5b8d073 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,19 @@ pub mod common; +use crate::common::set_roles::normal::get_server_member_bulk; use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; use serenity::client::Context; -use serenity::model::id::{ChannelId, GuildId, RoleId}; +use serenity::model::guild; +use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; use serenity::model::prelude::application_command::ApplicationCommandInteraction; use serenity::prelude::TypeMapKey; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}; +use sqlx::{Error, FromRow, Pool, Row, Sqlite}; +use std::str::FromStr; use std::{env, sync::Arc}; use tokio::sync::RwLock; @@ -56,6 +63,7 @@ pub fn get_config() -> Config { mail_user: "".to_string(), mail_pass: "".to_string(), wolves_url: "".to_string(), + wolves_api: "".to_string(), committee_server: GuildId(0), committee_role: RoleId(0), committee_category: ChannelId(0), @@ -418,6 +426,7 @@ pub fn random_string(len: usize) -> String { pub mod set_roles { use super::*; + use crate::common::database::DataBase; pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { let db_lock = { let data_read = ctx.data.read().await; @@ -544,6 +553,7 @@ pub mod set_roles { pub mod get_data { use super::*; + use crate::common::database::DataBase; use crate::set_roles::update_server; use std::collections::BTreeMap; use wolves_oxidised::WolvesUser; -- 2.47.2 From 4eeb7f213584478838996190d5190155d448e456 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 18 Feb 2025 14:00:21 +0000 Subject: [PATCH 31/40] fix: grab the committee name since we can use that to match up against teh suers --- db/migrations/8_committee-mk-ii.sql | 1 + src/common/wolves.rs | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/db/migrations/8_committee-mk-ii.sql b/db/migrations/8_committee-mk-ii.sql index d2e1d7c..ef6d185 100644 --- a/db/migrations/8_committee-mk-ii.sql +++ b/db/migrations/8_committee-mk-ii.sql @@ -5,6 +5,7 @@ DROP TABLE committee; CREATE TABLE IF NOT EXISTS committees ( id integer PRIMARY KEY, name_profile text not null, + name_plain text not null, name_full text not null, link text not null, committee text not null diff --git a/src/common/wolves.rs b/src/common/wolves.rs index b2c6af1..ba0ff93 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -178,6 +178,7 @@ pub mod committees { pub id: i64, pub name_full: String, pub name_profile: String, + pub name_plain: String, pub link: String, #[sqlx(json)] pub committee: Vec, @@ -189,6 +190,7 @@ pub mod committees { id: value.id, name_full: value.name_full, name_profile: value.name_profile, + name_plain: value.name_plain, link: value.link, committee: value.committee, } @@ -219,14 +221,15 @@ pub mod committees { async fn add_committee(db: &Pool, committee: &Committees) { match sqlx::query_as::<_, Committees>( " - INSERT INTO committees (id, name_profile, name_full, link, committee) - VALUES ($1, $2, $3, $4, $5) + 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 ", ) .bind(committee.id) .bind(&committee.name_profile) .bind(&committee.name_full) + .bind(&committee.name_plain) .bind(&committee.link) .bind(serde_json::to_string(&committee.committee).unwrap_or_default()) .fetch_optional(db) -- 2.47.2 From 1aef86ad01eda0a738ee768dcfcc5a588f0dc0c1 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 18 Feb 2025 21:14:05 +0000 Subject: [PATCH 32/40] 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(); -- 2.47.2 From b6cffd8a226da98c9b8e77f53cec79c98598d01c Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 18 Feb 2025 21:41:28 +0000 Subject: [PATCH 33/40] fix: trimmed down teh duplicates --- src/commands/minecraft.rs | 4 +- src/common/minecraft.rs | 23 +- src/lib.rs | 697 +------------------------------------- 3 files changed, 24 insertions(+), 700 deletions(-) diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index 10ee284..45c4643 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -18,8 +18,8 @@ pub(crate) mod user { use serde::{Deserialize, Serialize}; use serenity::model::id::UserId; use skynet_discord_bot::common::database::Wolves; - use skynet_discord_bot::common::minecraft::Minecraft; - use skynet_discord_bot::{whitelist_update, Config}; + use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft}; + use skynet_discord_bot::Config; use sqlx::Error; pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 000aa46..25bde46 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -1,5 +1,5 @@ use crate::common::set_roles::normal::get_server_member_bulk; -use crate::{whitelist_update, Config}; +use crate::Config; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serenity::model::id::GuildId; @@ -44,7 +44,7 @@ pub async fn update_server(server_id: &str, db: &Pool, g_id: &GuildId, c } } -async fn post(url: &str, bearer: &str, data: &T) { +pub async fn post(url: &str, bearer: &str, data: &T) { match surf::post(url) .header("Authorization", bearer) .header("Content-Type", "application/json") @@ -153,3 +153,22 @@ pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Ve .await .unwrap_or_default() } + +pub async fn whitelist_update(add: &Vec<(String, bool)>, server: &str, token: &str) { + println!("Update whitelist for {}", server); + let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); + let bearer = format!("Bearer {token}"); + + for (name, java) in add { + let data = if *java { + BodyCommand { + command: format!("whitelist add {name}"), + } + } else { + BodyCommand { + command: format!("fwhitelist add {name}"), + } + }; + post(&format!("{url_base}/command"), &bearer, &data).await; + } +} diff --git a/src/lib.rs b/src/lib.rs index 1e11ada..5611a21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,12 @@ pub mod common; -use crate::common::set_roles::normal::get_server_member_bulk; use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; use rand::{distributions::Alphanumeric, thread_rng, Rng}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; use serenity::client::Context; -use serenity::model::guild; -use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; +use serenity::model::id::{ChannelId, GuildId, RoleId}; use serenity::model::prelude::application_command::ApplicationCommandInteraction; use serenity::prelude::TypeMapKey; -use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}; -use sqlx::{Error, FromRow, Pool, Row, Sqlite}; -use std::str::FromStr; use std::{env, sync::Arc}; use tokio::sync::RwLock; @@ -122,293 +115,6 @@ pub fn get_config() -> Config { config } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct ServerMembers { - pub server: GuildId, - pub id_wolves: i64, - pub expiry: String, -} -impl<'r> FromRow<'r, SqliteRow> for ServerMembers { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server")?; - let server = GuildId::from(server_tmp as u64); - - Ok(Self { - server, - id_wolves: row.try_get("id_wolves")?, - expiry: row.try_get("expiry")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct ServerMembersWolves { - pub server: GuildId, - pub id_wolves: i64, - pub expiry: String, - pub email: String, - pub discord: Option, - pub minecraft: Option, - pub minecraft_uid: Option, -} -impl<'r> FromRow<'r, SqliteRow> for ServerMembersWolves { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server")?; - let server = GuildId::from(server_tmp as u64); - let discord = match row.try_get("discord") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(UserId::from(tmp as u64)) - } - } - _ => None, - }; - - Ok(Self { - server, - id_wolves: row.try_get("id_wolves")?, - expiry: row.try_get("expiry")?, - email: row.try_get("email")?, - discord, - minecraft: row.try_get("minecraft")?, - minecraft_uid: row.try_get("minecraft_uid")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Wolves { - pub id_wolves: i64, - pub email: String, - pub discord: Option, - pub minecraft: Option, -} -impl<'r> FromRow<'r, SqliteRow> for Wolves { - fn from_row(row: &'r SqliteRow) -> Result { - let discord = match row.try_get("discord") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(UserId::from(tmp as u64)) - } - } - _ => None, - }; - - Ok(Self { - id_wolves: row.try_get("id_wolves")?, - email: row.try_get("email")?, - discord, - minecraft: row.try_get("minecraft")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct WolvesVerify { - pub email: String, - pub discord: UserId, - pub auth_code: String, - pub date_expiry: String, -} -impl<'r> FromRow<'r, SqliteRow> for WolvesVerify { - fn from_row(row: &'r SqliteRow) -> Result { - let user_tmp: i64 = row.try_get("discord")?; - let discord = UserId::from(user_tmp as u64); - - Ok(Self { - email: row.try_get("email")?, - discord, - auth_code: row.try_get("auth_code")?, - date_expiry: row.try_get("date_expiry")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Committee { - pub email: String, - pub discord: UserId, - pub auth_code: String, - pub committee: i64, -} -impl<'r> FromRow<'r, SqliteRow> for Committee { - fn from_row(row: &'r SqliteRow) -> Result { - let user_tmp: i64 = row.try_get("discord")?; - let discord = UserId::from(user_tmp as u64); - - Ok(Self { - email: row.try_get("email")?, - discord, - auth_code: row.try_get("auth_code")?, - committee: row.try_get("committee")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Servers { - pub server: GuildId, - pub wolves_api: String, - pub role_past: Option, - pub role_current: RoleId, - pub member_past: i64, - pub member_current: i64, - pub bot_channel_id: ChannelId, - // these can be removed in teh future with an API update - pub server_name: String, -} -impl<'r> FromRow<'r, SqliteRow> for Servers { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server")?; - let server = GuildId::from(server_tmp as u64); - let role_past = match row.try_get("role_past") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(RoleId::from(tmp as u64)) - } - } - _ => None, - }; - let role_current = match row.try_get("role_current") { - Ok(x) => { - let tmp: i64 = x; - RoleId::from(tmp as u64) - } - _ => RoleId::from(0u64), - }; - - let bot_channel_tmp: i64 = row.try_get("bot_channel_id")?; - let bot_channel_id = ChannelId::from(bot_channel_tmp as u64); - - Ok(Self { - server, - wolves_api: row.try_get("wolves_api")?, - role_past, - role_current, - member_past: row.try_get("member_past")?, - member_current: row.try_get("member_current")?, - bot_channel_id, - server_name: row.try_get("server_name")?, - }) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Minecraft { - pub discord: GuildId, - pub minecraft: String, -} -impl<'r> FromRow<'r, SqliteRow> for Minecraft { - fn from_row(row: &'r SqliteRow) -> Result { - let server_tmp: i64 = row.try_get("server_discord")?; - let discord = GuildId::from(server_tmp as u64); - - Ok(Self { - discord, - minecraft: row.try_get("server_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 { - let server_tmp: i64 = row.try_get("server")?; - 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, Error> { - let database = format!("{}/{}", &config.home, &config.database); - - let pool = SqlitePoolOptions::new() - .max_connections(5) - .connect_with( - SqliteConnectOptions::from_str(&format!("sqlite://{}", database))? - .foreign_keys(true) - .create_if_missing(true), - ) - .await?; - - // migrations are amazing! - sqlx::migrate!("./db/migrations").run(&pool).await?; - - Ok(pool) -} - -pub async fn get_server_config(db: &Pool, server: &GuildId) -> Option { - sqlx::query_as::<_, Servers>( - r#" - SELECT * - FROM servers - WHERE server = ? - "#, - ) - .bind(*server.as_u64() as i64) - .fetch_one(db) - .await - .ok() -} - -pub async fn get_server_member(db: &Pool, server: &GuildId, member: &guild::Member) -> Result { - sqlx::query_as::<_, ServerMembersWolves>( - r#" - SELECT * - FROM server_members - JOIN wolves USING (id_wolves) - WHERE server = ? AND discord = ? - "#, - ) - .bind(*server.as_u64() as i64) - .bind(*member.user.id.as_u64() as i64) - .fetch_one(db) - .await -} - -pub async fn get_server_config_bulk(db: &Pool) -> Vec { - sqlx::query_as::<_, Servers>( - r#" - SELECT * - FROM servers - "#, - ) - .fetch_all(db) - .await - .unwrap_or_default() -} - pub fn get_now_iso(short: bool) -> String { let now = Utc::now(); if short { @@ -422,257 +128,6 @@ pub fn random_string(len: usize) -> String { thread_rng().sample_iter(&Alphanumeric).take(len).map(char::from).collect() } -pub mod set_roles { - use super::*; - use crate::common::database::DataBase; - pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Database in TypeMap.").clone() - }; - - let db = db_lock.read().await; - - let Servers { - server, - role_past, - role_current, - .. - } = server; - - let mut roles_set = [0, 0, 0]; - let mut members = vec![]; - - for member in get_server_member_bulk(&db, server).await { - if let Some(x) = member.discord { - members.push(x); - } - } - let mut members_all = members.len(); - - if let Ok(x) = server.members(ctx, None, None).await { - for mut member in x { - // members_changed acts as an override to only deal with teh users in it - if !members_changed.is_empty() && !members_changed.contains(&member.user.id) { - continue; - } - - if members.contains(&member.user.id) { - let mut roles = vec![]; - - if let Some(role) = &role_past { - if !member.roles.contains(role) { - roles_set[0] += 1; - roles.push(role.to_owned()); - } - } - - if !member.roles.contains(role_current) { - roles_set[1] += 1; - roles.push(role_current.to_owned()); - } - - if let Err(e) = member.add_roles(ctx, &roles).await { - println!("{:?}", e); - } - } else { - // old and never - - if let Some(role) = &role_past { - if member.roles.contains(role) { - members_all += 1; - } - } - - if member.roles.contains(role_current) { - roles_set[2] += 1; - // if theya re not a current member and have the role then remove it - if let Err(e) = member.remove_role(ctx, role_current).await { - println!("{:?}", e); - } - } - } - for role in remove_roles.iter().flatten() { - if let Err(e) = member.remove_role(ctx, role).await { - println!("{:?}", e); - } - } - } - } - - set_server_numbers(&db, server, members_all as i64, members.len() as i64).await; - - // small bit of logging to note changes over time - println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.as_u64(), roles_set[0], roles_set[1], roles_set[2]); - } - - pub async fn get_server_member_bulk(db: &Pool, server: &GuildId) -> Vec { - sqlx::query_as::<_, ServerMembersWolves>( - r#" - SELECT * - FROM server_members - JOIN wolves USING (id_wolves) - WHERE ( - server = ? - AND discord IS NOT NULL - AND expiry > ? - ) - "#, - ) - .bind(*server.as_u64() as i64) - .bind(get_now_iso(true)) - .fetch_all(db) - .await - .unwrap_or_default() - } - - async fn set_server_numbers(db: &Pool, server: &GuildId, past: i64, current: i64) { - match sqlx::query_as::<_, Wolves>( - " - UPDATE servers - SET member_past = ?, member_current = ? - WHERE server = ? - ", - ) - .bind(past) - .bind(current) - .bind(*server.as_u64() as i64) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - println!("Failure to insert into {}", server.as_u64()); - println!("{:?}", e); - } - } - } -} - -pub mod get_data { - use super::*; - use crate::common::database::DataBase; - use crate::set_roles::update_server; - use std::collections::BTreeMap; - use wolves_oxidised::WolvesUser; - - pub async fn get_wolves(ctx: &Context) { - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Database in TypeMap.").clone() - }; - let db = db_lock.read().await; - - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let config = config_lock.read().await; - - // set up teh client - let wolves = wolves_oxidised::Client::new(&config.wolves_url, Some(&config.wolves_api)); - - for server_config in get_server_config_bulk(&db).await { - let Servers { - server, - wolves_api, - .. - } = &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![]; - for user in wolves.get_members(wolves_api).await { - let id = user.member_id.parse::().unwrap_or_default(); - match existing.get(&(id as i64)) { - None => { - // user does not exist already, add everything - add_users_wolves(&db, &user).await; - add_users_server_members(&db, server, &user).await; - } - Some(old) => { - // always update wolves table, in case data has changed - add_users_wolves(&db, &user).await; - if old.expiry != user.expiry { - add_users_server_members(&db, server, &user).await; - - if let Some(discord_id) = old.discord { - user_to_update.push(discord_id); - } - } - } - } - } - - if !user_to_update.is_empty() { - update_server(ctx, &server_config, &[], &user_to_update).await; - } - } - } - - pub async fn get_server_member(db: &Pool, server: &GuildId) -> Vec { - sqlx::query_as::<_, ServerMembersWolves>( - r#" - SELECT * - FROM server_members - JOIN wolves USING (id_wolves) - WHERE ( - server = ? - AND discord IS NOT NULL - ) - "#, - ) - .bind(*server.as_u64() as i64) - .fetch_all(db) - .await - .unwrap_or_default() - } - - async fn add_users_wolves(db: &Pool, user: &WolvesUser) { - // expiry - match sqlx::query_as::<_, Wolves>( - " - INSERT INTO wolves (id_wolves, email) - VALUES ($1, $2) - ON CONFLICT(id_wolves) DO UPDATE SET email = $2 - ", - ) - .bind(&user.member_id) - .bind(&user.contact_email) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - println!("Failure to insert into Wolves {:?}", user); - println!("{:?}", e); - } - } - } - async fn add_users_server_members(db: &Pool, server: &GuildId, user: &WolvesUser) { - match sqlx::query_as::<_, ServerMembers>( - " - INSERT OR REPLACE INTO server_members (server, id_wolves, expiry) - VALUES (?1, ?2, ?3) - ", - ) - .bind(*server.as_u64() as i64) - .bind(&user.member_id) - .bind(&user.expiry) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - println!("Failure to insert into ServerMembers {} {:?}", server.as_u64(), user); - println!("{:?}", e); - } - } - } -} - /** For any time ye need to check if a user who calls a command has admin privlages */ @@ -710,153 +165,3 @@ pub async fn is_admin(command: &ApplicationCommandInteraction, ctx: &Context) -> None } } - -/** -loop through all members of server -get a list of folks with mc accounts that are members -and a list that arent members - */ -pub async fn update_server(server_id: &str, db: &Pool, g_id: &GuildId, config: &Config) { - let mut usernames = vec![]; - for member in get_server_member_bulk(db, g_id).await { - if let Some(x) = member.minecraft { - usernames.push((x, true)); - } - if let Some(x) = member.minecraft_uid { - usernames.push((x, false)); - } - } - if !usernames.is_empty() { - whitelist_update(&usernames, server_id, &config.discord_token_minecraft).await; - } -} - -async fn post(url: &str, bearer: &str, data: &T) { - match surf::post(url) - .header("Authorization", bearer) - .header("Content-Type", "application/json") - .header("Accept", "Application/vnd.pterodactyl.v1+json") - .body_json(&data) - { - Ok(req) => { - req.await.ok(); - } - Err(e) => { - dbg!(e); - } - } -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct ServerDetailsResSub { - pub identifier: String, - pub name: String, - pub description: String, - pub is_suspended: bool, -} -#[derive(Deserialize, Serialize, Debug)] -pub struct ServerDetailsRes { - pub attributes: ServerDetailsResSub, -} - -async fn get(url: &str, bearer: &str) -> Option { - match surf::get(url) - .header("Authorization", bearer) - .header("Content-Type", "application/json") - .header("Accept", "Application/vnd.pterodactyl.v1+json") - .recv_json() - .await - { - Ok(res) => Some(res), - Err(e) => { - dbg!(e); - - None - } - } -} - -#[derive(Deserialize, Serialize, Debug)] -struct BodyCommand { - command: String, -} - -#[derive(Deserialize, Serialize, Debug)] -struct BodyDelete { - root: String, - files: Vec, -} - -pub async fn whitelist_update(add: &Vec<(String, bool)>, server: &str, token: &str) { - println!("Update whitelist for {}", server); - let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); - let bearer = format!("Bearer {token}"); - - for (name, java) in add { - let data = if *java { - BodyCommand { - command: format!("whitelist add {name}"), - } - } else { - BodyCommand { - command: format!("fwhitelist add {name}"), - } - }; - post(&format!("{url_base}/command"), &bearer, &data).await; - } -} - -pub async fn whitelist_wipe(server: &str, token: &str) { - println!("Wiping whitelist for {}", server); - let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); - let bearer = format!("Bearer {token}"); - - // delete whitelist - let deletion = BodyDelete { - root: "/".to_string(), - files: vec!["whitelist.json".to_string()], - }; - post(&format!("{url_base}/files/delete"), &bearer, &deletion).await; - - // recreate teh file, passing in the type here so the compiler knows what type of vec it is - post::>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await; - - // reload the whitelist - let data = BodyCommand { - command: "whitelist reload".to_string(), - }; - post(&format!("{url_base}/command"), &bearer, &data).await; -} - -pub async fn server_information(server: &str, token: &str) -> Option { - println!("Get server information for {}", server); - let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); - let bearer = format!("Bearer {token}"); - get::(&format!("{url_base}/"), &bearer).await -} - -pub async fn get_minecraft_config(db: &Pool) -> Vec { - sqlx::query_as::<_, Minecraft>( - r#" - SELECT * - FROM minecraft - "#, - ) - .fetch_all(db) - .await - .unwrap_or_default() -} - -pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Vec { - sqlx::query_as::<_, Minecraft>( - r#" - SELECT * - FROM minecraft - WHERE server_discord = ?1 - "#, - ) - .bind(*g_id.as_u64() as i64) - .fetch_all(db) - .await - .unwrap_or_default() -} -- 2.47.2 From 8c81fb864a24bf468b421765a639ebeb6d68a6cb Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 18 Feb 2025 22:44:32 +0000 Subject: [PATCH 34/40] fix: slight duplicate removal --- src/common/database.rs | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/common/database.rs b/src/common/database.rs index ace94ec..fb413e2 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -49,30 +49,33 @@ impl<'r> FromRow<'r, SqliteRow> for ServerMembersWolves { fn from_row(row: &'r SqliteRow) -> Result { let server_tmp: i64 = row.try_get("server")?; let server = GuildId::from(server_tmp as u64); - let discord = match row.try_get("discord") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(UserId::from(tmp as u64)) - } - } - _ => None, - }; Ok(Self { server, id_wolves: row.try_get("id_wolves")?, expiry: row.try_get("expiry")?, email: row.try_get("email")?, - discord, + discord: get_discord_from_row(row), minecraft: row.try_get("minecraft")?, minecraft_uid: row.try_get("minecraft_uid")?, }) } } +fn get_discord_from_row(row: &SqliteRow) -> Option { + match row.try_get("discord") { + Ok(x) => { + let tmp: i64 = x; + if tmp == 0 { + None + } else { + Some(UserId::from(tmp as u64)) + } + } + _ => None, + } +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Wolves { pub id_wolves: i64, @@ -83,22 +86,10 @@ pub struct Wolves { impl<'r> FromRow<'r, SqliteRow> for Wolves { fn from_row(row: &'r SqliteRow) -> Result { - let discord = match row.try_get("discord") { - Ok(x) => { - let tmp: i64 = x; - if tmp == 0 { - None - } else { - Some(UserId::from(tmp as u64)) - } - } - _ => None, - }; - Ok(Self { id_wolves: row.try_get("id_wolves")?, email: row.try_get("email")?, - discord, + discord: get_discord_from_row(row), minecraft: row.try_get("minecraft")?, }) } -- 2.47.2 From a8c1cc9cf1c3004f33420cbabad211fed5f6f2cf Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 18 Feb 2025 22:46:58 +0000 Subject: [PATCH 35/40] fix: slight duplicate removal --- src/common/minecraft.rs | 4 ++-- src/common/set_roles.rs | 16 ++++++---------- src/main.rs | 28 ++++++++++++++-------------- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 25bde46..7b27d36 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -102,7 +102,7 @@ struct BodyDelete { } pub async fn whitelist_wipe(server: &str, token: &str) { - let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); + let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); let bearer = format!("Bearer {token}"); // delete whitelist @@ -123,7 +123,7 @@ pub async fn whitelist_wipe(server: &str, token: &str) { } pub async fn server_information(server: &str, token: &str) -> Option { - let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}"); + let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); let bearer = format!("Bearer {token}"); get::(&format!("{url_base}/"), &bearer).await } diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index f0a02cb..9ebd39e 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -341,21 +341,17 @@ pub mod committee { } async fn get_committees(db: &Pool) -> Vec { - match sqlx::query_as::<_, Committees>( + sqlx::query_as::<_, Committees>( r#" SELECT * FROM committees "#, ) - .fetch_all(db) - .await - { - Ok(a) => a, - Err(e) => { - dbg!(e); - vec![] - } - } + .fetch_all(db) + .await.unwrap_or_else(|e| { + dbg!(e); + vec![] + }) } async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { diff --git a/src/main.rs b/src/main.rs index e1fe422..8b1b20a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,6 +91,20 @@ 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, new_data: Member) { + // get config/db + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().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 + on_role_change(&db, &ctx, new_data).await; + } + async fn ready(&self, ctx: Context, ready: Ready) { println!("[Main] {} is connected!", ready.user.name); ctx.set_presence(Some(Activity::playing("with humanity's fate")), OnlineStatus::Online).await; @@ -139,20 +153,6 @@ 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, new_data: Member) { - // get config/db - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().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 - on_role_change(&db, &ctx, new_data).await; - } } async fn get_committee(db: &Pool, committee: &str) -> Vec { -- 2.47.2 From 6b84f33d2e0e2bec6dd32d8dfa8d66deefdc618c Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Wed, 19 Feb 2025 00:17:02 +0000 Subject: [PATCH 36/40] feat: bumped serenity to the latest version Lots of changes to how it runs --- Cargo.lock | 255 +++++++++++++++++-------------------- Cargo.toml | 2 +- src/commands/add_server.rs | 114 ++++++----------- src/commands/link_email.rs | 79 +++++------- src/commands/minecraft.rs | 142 ++++++++------------- src/commands/role_adder.rs | 122 +++++++----------- src/common/database.rs | 8 +- src/common/minecraft.rs | 2 +- src/common/set_roles.rs | 44 +++++-- src/common/wolves.rs | 10 +- src/lib.rs | 16 +-- src/main.rs | 43 ++++--- 12 files changed, 352 insertions(+), 485 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1c92c1..d0b769f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,15 @@ version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -257,22 +266,6 @@ dependencies = [ "syn 2.0.89", ] -[[package]] -name = "async-tungstenite" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb" -dependencies = [ - "futures-io", - "futures-util", - "log", - "pin-project-lite", - "tokio", - "tokio-rustls 0.23.4", - "tungstenite", - "webpki-roots 0.22.6", -] - [[package]] name = "atoi" version = "2.0.0" @@ -629,6 +622,12 @@ dependencies = [ "serde", ] +[[package]] +name = "data-encoding" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" + [[package]] name = "der" version = "0.7.9" @@ -815,7 +814,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -975,6 +974,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1298,7 +1306,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -1518,7 +1526,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -1846,15 +1854,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - [[package]] name = "parking" version = "2.2.1" @@ -2237,21 +2236,6 @@ dependencies = [ "windows-registry", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.8" @@ -2262,8 +2246,8 @@ dependencies = [ "cfg-if", "getrandom 0.2.15", "libc", - "spin 0.9.8", - "untrusted 0.9.0", + "spin", + "untrusted", "windows-sys 0.52.0", ] @@ -2315,18 +2299,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" -dependencies = [ - "log", - "ring 0.16.20", - "sct", - "webpki", -] - [[package]] name = "rustls" version = "0.21.12" @@ -2334,11 +2306,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.8", + "ring", "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + [[package]] name = "rustls" version = "0.23.18" @@ -2382,8 +2368,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -2392,9 +2378,9 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.8", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -2424,8 +2410,18 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", ] [[package]] @@ -2476,12 +2472,11 @@ dependencies = [ ] [[package]] -name = "serde-value" -version = "0.7.0" +name = "serde_cow" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +checksum = "1e7bbbec7196bfde255ab54b65e34087c0849629280028238e67ee25d6a4b7da" dependencies = [ - "ordered-float", "serde", ] @@ -2533,45 +2528,35 @@ dependencies = [ [[package]] name = "serenity" -version = "0.11.7" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a7a89cef23483fc9d4caf2df41e6d3928e18aada84c56abd237439d929622c6" +checksum = "3d72ec4323681bf9a3cabe40fd080abc2435859b502a1b5aa9bf693f125bfa76" dependencies = [ + "arrayvec", "async-trait", - "async-tungstenite", - "base64 0.21.7", - "bitflags 1.3.2", + "base64 0.22.1", + "bitflags 2.6.0", "bytes 1.7.1", - "cfg-if", "dashmap", "flate2", "futures", - "mime", + "fxhash", "mime_guess", "parking_lot", "percent-encoding", "reqwest 0.11.27", + "secrecy", "serde", - "serde-value", + "serde_cow", "serde_json", "time 0.3.36", "tokio", + "tokio-tungstenite", "tracing", "typemap_rev", "url", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - [[package]] name = "sha1" version = "0.6.1" @@ -2711,12 +2696,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -3281,17 +3260,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.9", - "tokio", - "webpki", -] - [[package]] name = "tokio-rustls" version = "0.24.1" @@ -3302,6 +3270,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.0" @@ -3337,6 +3316,22 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "rustls 0.22.4", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tungstenite", + "webpki-roots 0.26.8", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -3406,30 +3401,30 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ - "base64 0.13.1", "byteorder", "bytes 1.7.1", - "http 0.2.12", + "data-encoding", + "http 1.1.0", "httparse", "log", "rand 0.8.5", - "rustls 0.20.9", - "sha-1", + "rustls 0.22.4", + "rustls-pki-types", + "sha1 0.10.6", "thiserror", "url", "utf-8", - "webpki", ] [[package]] name = "typemap_rev" -version = "0.1.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5b74f0a24b5454580a79abb6994393b09adf0ab8070f15827cb666255de155" +checksum = "74b08b0c1257381af16a5c3605254d529d3e7e109f3c62befc5d168968192998" [[package]] name = "typenum" @@ -3495,12 +3490,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -3672,31 +3661,21 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - [[package]] name = "webpki-roots" version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "whoami" version = "1.5.2" diff --git a/Cargo.toml b/Cargo.toml index fa0ffdc..d637ae8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ name = "update_minecraft" [dependencies] # discord library -serenity = { version = "0.11.6", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } +serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "full"] } # wolves api diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 8f06527..fdc0f23 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -1,68 +1,53 @@ -use serenity::{ - builder::CreateApplicationCommand, - client::Context, - model::{ - application::interaction::application_command::ApplicationCommandInteraction, - prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, - }, -}; +use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}; +use serenity::{builder::CreateCommand, client::Context}; use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; use skynet_discord_bot::common::set_roles::normal::update_server; use skynet_discord_bot::common::wolves::cns::get_wolves; use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; -pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { +pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { // check if user has high enough permisssions if let Some(msg) = is_admin(command, ctx).await { return msg; } - let api_key = if let CommandDataOptionValue::String(key) = command - .data - .options - .first() - .expect("Expected user option") - .resolved - .as_ref() - .expect("Expected user object") + let wolves_api = if let Some(CommandDataOption { + value: CommandDataOptionValue::String(key), + .. + }) = command.data.options.first() { - key + key.to_string() } else { return "Please provide a wolves API key".to_string(); }; - let role_current = if let CommandDataOptionValue::Role(role) = command - .data - .options - .get(1) - .expect("Expected role option") - .resolved - .as_ref() - .expect("Expected role object") + let role_current = if let Some(CommandDataOption { + value: CommandDataOptionValue::Role(role), + .. + }) = command.data.options.get(1) { - role.id.to_owned() + role.to_owned() } else { return "Please provide a valid role for ``Role Current``".to_string(); }; - let mut role_past = None; - if let Some(x) = command.data.options.get(5) { - if let Some(CommandDataOptionValue::Role(role)) = &x.resolved { - role_past = Some(role.id.to_owned()); - } + let role_past = if let Some(CommandDataOption { + value: CommandDataOptionValue::Role(role), + .. + }) = command.data.options.get(5) + { + Some(role.to_owned()) + } else { + None }; - let bot_channel_id = if let CommandDataOptionValue::Channel(channel) = command - .data - .options - .get(2) - .expect("Expected channel option") - .resolved - .as_ref() - .expect("Expected channel object") + let bot_channel_id = if let Some(CommandDataOption { + value: CommandDataOptionValue::Channel(channel), + .. + }) = command.data.options.get(2) { - channel.id.to_owned() + channel.to_owned() } else { return "Please provide a valid channel for ``Bot Channel``".to_string(); }; @@ -75,7 +60,7 @@ pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> Stri let server_data = Servers { server: command.guild_id.unwrap_or_default(), - wolves_api: api_key.to_owned(), + wolves_api, role_past, role_current, member_past: 0, @@ -95,43 +80,18 @@ pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> Stri "Added/Updated server info".to_string() } -pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command - .name("add") +pub fn register() -> CreateCommand { + CreateCommand::new("add") .description("Enable the bot for this discord") - .create_option(|option| { - option - .name("api_key") - .description("UL Wolves API Key") - .kind(CommandOptionType::String) - .required(true) - }) - .create_option(|option| { - option - .name("role_current") - .description("Role for Current members") - .kind(CommandOptionType::Role) - .required(true) - }) - .create_option(|option| { - option - .name("bot_channel") - .description("Safe space for folks to use the bot commands.") - .kind(CommandOptionType::Channel) - .required(true) - }) - .create_option(|option| { - option - .name("role_past") - .description("Role for Past members") - .kind(CommandOptionType::Role) - .required(false) - }) + .add_option(CreateCommandOption::new(CommandOptionType::String, "api_key", "UL Wolves API Key").required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::Role, "role_current", "Role for Current members").required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::Channel, "bot_channel", "Safe space for folks to use the bot commands.").required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::Role, "role_past", "Role for Past members").required(false)) } async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Result, Error> { let existing = get_server_config(db, &server.server).await; - let role_past = server.role_past.map(|x| *x.as_u64() as i64); + let role_past = server.role_past.map(|x| x.get() as i64); let insert = sqlx::query_as::<_, Servers>( " @@ -139,11 +99,11 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul VALUES (?1, ?2, ?3, ?4, ?5) ", ) - .bind(*server.server.as_u64() as i64) + .bind(server.server.get() as i64) .bind(&server.wolves_api) .bind(role_past) - .bind(*server.role_current.as_u64() as i64) - .bind(*server.bot_channel_id.as_u64() as i64) + .bind(server.role_current.get() as i64) + .bind(server.bot_channel_id.get() as i64) .fetch_optional(db) .await; diff --git a/src/commands/link_email.rs b/src/commands/link_email.rs index a76769f..750e0ee 100644 --- a/src/commands/link_email.rs +++ b/src/commands/link_email.rs @@ -4,15 +4,7 @@ use lettre::{ Message, SmtpTransport, Transport, }; use maud::html; -use serenity::{ - builder::CreateApplicationCommand, - client::Context, - model::{ - application::interaction::application_command::ApplicationCommandInteraction, - id::UserId, - prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, - }, -}; +use serenity::{builder::CreateCommand, client::Context, model::id::UserId}; use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify}; use skynet_discord_bot::{get_now_iso, random_string, Config}; use sqlx::{Pool, Sqlite}; @@ -20,8 +12,9 @@ use sqlx::{Pool, Sqlite}; pub mod link { use super::*; use serde::{Deserialize, Serialize}; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}; - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() @@ -44,16 +37,11 @@ pub mod link { return "Linking already in process, please check email.".to_string(); } - let option = command - .data - .options - .first() - .expect("Expected email option") - .resolved - .as_ref() - .expect("Expected email object"); - - let email = if let CommandDataOptionValue::String(email) = option { + let email = if let Some(CommandDataOption { + value: CommandDataOptionValue::String(email), + .. + }) = command.data.options.first() + { email.trim() } else { return "Please provide a valid user".to_string(); @@ -115,11 +103,10 @@ pub mod link { format!("Verification email sent to {}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.", email) } - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command - .name("link_wolves") + pub fn register() -> CreateCommand { + CreateCommand::new("link_wolves") .description("Set Wolves Email") - .create_option(|option| option.name("email").description("UL Wolves Email").kind(CommandOptionType::String).required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::String, "email", "UL Wolves Email").required(true)) } pub async fn get_server_member_discord(db: &Pool, user: &UserId) -> Option { @@ -130,7 +117,7 @@ pub mod link { WHERE discord = ? "#, ) - .bind(*user.as_u64() as i64) + .bind(user.get() as i64) .fetch_one(db) .await .ok() @@ -242,7 +229,7 @@ pub mod link { WHERE discord = ? "#, ) - .bind(*user.as_u64() as i64) + .bind(user.get() as i64) .fetch_one(db) .await .ok() @@ -256,7 +243,7 @@ pub mod link { ", ) .bind(record.email.to_owned()) - .bind(*user.as_u64() as i64) + .bind(user.get() as i64) .bind(auth.to_owned()) .bind(get_now_iso(false)) .fetch_optional(db) @@ -294,12 +281,13 @@ pub mod link { pub mod verify { use super::*; use crate::commands::link_email::link::{db_pending_clear_expired, get_verify_from_db}; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}; use serenity::model::user::User; use skynet_discord_bot::common::database::get_server_config; use skynet_discord_bot::common::database::{ServerMembersWolves, Servers}; use sqlx::Error; - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() @@ -313,16 +301,11 @@ pub mod verify { return "Please use /link_wolves first".to_string(); }; - let option = command - .data - .options - .first() - .expect("Expected code option") - .resolved - .as_ref() - .expect("Expected code object"); - - let code = if let CommandDataOptionValue::String(code) = option { + let code = if let Some(CommandDataOption { + value: CommandDataOptionValue::String(code), + .. + }) = command.data.options.first() + { code } else { return "Please provide a verification code".to_string(); @@ -354,14 +337,10 @@ pub mod verify { "Failed to verify".to_string() } - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command.name("verify").description("Verify Wolves Email").create_option(|option| { - option - .name("code") - .description("Code from verification email") - .kind(CommandOptionType::String) - .required(true) - }) + pub fn register() -> CreateCommand { + CreateCommand::new("verify") + .description("Verify Wolves Email") + .add_option(CreateCommandOption::new(CommandOptionType::String, "code", "Code from verification email").required(true)) } async fn db_pending_clear_successful(pool: &Pool, user: &UserId) -> Result, Error> { @@ -372,7 +351,7 @@ pub mod verify { WHERE discord = ? "#, ) - .bind(*user.as_u64() as i64) + .bind(user.get() as i64) .fetch_optional(pool) .await } @@ -385,7 +364,7 @@ pub mod verify { WHERE email = ? ", ) - .bind(*discord.as_u64() as i64) + .bind(discord.get() as i64) .bind(email) .fetch_optional(db) .await @@ -394,7 +373,7 @@ pub mod verify { async fn set_server_roles(db: &Pool, discord: &User, ctx: &Context) { if let Ok(servers) = get_servers(db, &discord.id).await { for server in servers { - if let Ok(mut member) = server.server.member(&ctx.http, &discord.id).await { + if let Ok(member) = server.server.member(&ctx.http, &discord.id).await { if let Some(config) = get_server_config(db, &server.server).await { let Servers { role_past, @@ -432,7 +411,7 @@ pub mod verify { WHERE discord = ? ", ) - .bind(*discord.as_u64() as i64) + .bind(discord.get() as i64) .fetch_all(db) .await } diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index 45c4643..54bf35c 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -1,11 +1,4 @@ -use serenity::{ - builder::CreateApplicationCommand, - client::Context, - model::{ - application::interaction::application_command::ApplicationCommandInteraction, - prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, - }, -}; +use serenity::{builder::CreateCommand, client::Context}; use skynet_discord_bot::common::database::DataBase; use sqlx::{Pool, Sqlite}; @@ -16,33 +9,21 @@ pub(crate) mod user { use super::*; use crate::commands::link_email::link::get_server_member_discord; use serde::{Deserialize, Serialize}; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}; use serenity::model::id::UserId; use skynet_discord_bot::common::database::Wolves; use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft}; use skynet_discord_bot::Config; use sqlx::Error; - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command - .name("link_minecraft") + pub fn register() -> CreateCommand { + CreateCommand::new("link_minecraft") .description("Link your minecraft account") - .create_option(|option| { - option - .name("minecraft_username") - .description("Your Minecraft username") - .kind(CommandOptionType::String) - .required(true) - }) - .create_option(|option| { - option - .name("bedrock_account") - .description("Is this a Bedrock account?") - .kind(CommandOptionType::Boolean) - .required(false) - }) + .add_option(CreateCommandOption::new(CommandOptionType::String, "minecraft_username", "Your Minecraft username").required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::Boolean, "bedrock_account", "Is this a Bedrock account?").required(false)) } - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() @@ -60,14 +41,10 @@ pub(crate) mod user { return "Not linked with wolves, please use ``/link_wolves`` with your wolves email.".to_string(); } - let username = if let CommandDataOptionValue::String(username) = command - .data - .options - .first() - .expect("Expected username option") - .resolved - .as_ref() - .expect("Expected username object") + let username = if let Some(CommandDataOption { + value: CommandDataOptionValue::String(username), + .. + }) = command.data.options.first() { username.trim() } else { @@ -75,12 +52,15 @@ pub(crate) mod user { }; // this is always true unless they state its not - let mut java = true; - if let Some(x) = command.data.options.get(1) { - if let Some(CommandDataOptionValue::Boolean(z)) = x.to_owned().resolved { - java = !z; - } - } + let java = if let Some(CommandDataOption { + value: CommandDataOptionValue::Boolean(z), + .. + }) = command.data.options.get(1) + { + !z + } else { + true + }; let username_mc; if java { @@ -130,7 +110,7 @@ pub(crate) mod user { WHERE discord = ?1; ", ) - .bind(*user.as_u64() as i64) + .bind(user.get() as i64) .bind(minecraft) .fetch_optional(db) .await @@ -175,7 +155,7 @@ pub(crate) mod user { WHERE discord = ?1; ", ) - .bind(*user.as_u64() as i64) + .bind(user.get() as i64) .bind(minecraft) .fetch_optional(db) .await @@ -194,7 +174,7 @@ pub(crate) mod user { ) sub on minecraft.server_discord = sub.server ", ) - .bind(*discord.as_u64() as i64) + .bind(discord.get() as i64) .fetch_all(db) .await } @@ -205,6 +185,7 @@ pub(crate) mod server { use super::*; pub(crate) mod add { + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}; use serenity::model::id::GuildId; use sqlx::Error; // this is to managfe the server side of commands related to minecraft @@ -213,17 +194,13 @@ pub(crate) mod server { use skynet_discord_bot::common::minecraft::Minecraft; use skynet_discord_bot::{is_admin, Config}; - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command.name("minecraft_add").description("Add a minecraft server").create_option(|option| { - option - .name("server_id") - .description("ID of the Minecraft server hosted by the Computer Society") - .kind(CommandOptionType::String) - .required(true) - }) + pub fn register() -> CreateCommand { + CreateCommand::new("minecraft_add").description("Add a minecraft server").add_option( + CreateCommandOption::new(CommandOptionType::String, "server_id", "ID of the Minecraft server hosted by the Computer Society").required(true), + ) } - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { // check if user has high enough permisssions if let Some(msg) = is_admin(command, ctx).await { return msg; @@ -233,16 +210,12 @@ pub(crate) mod server { Some(x) => x, }; - let server_minecraft = if let CommandDataOptionValue::String(id) = command - .data - .options - .first() - .expect("Expected server_id option") - .resolved - .as_ref() - .expect("Expected server_id object") + let server_minecraft = if let Some(CommandDataOption { + value: CommandDataOptionValue::String(id), + .. + }) = command.data.options.first() { - id.to_owned() + id.to_string() } else { return String::from("Expected Server ID"); }; @@ -279,7 +252,7 @@ pub(crate) mod server { VALUES (?1, ?2) ", ) - .bind(*discord.as_u64() as i64) + .bind(discord.get() as i64) .bind(minecraft) .fetch_optional(db) .await @@ -287,18 +260,18 @@ pub(crate) mod server { } pub(crate) mod list { - use serenity::builder::CreateApplicationCommand; + use serenity::all::CommandInteraction; + use serenity::builder::CreateCommand; use serenity::client::Context; - use serenity::model::prelude::application_command::ApplicationCommandInteraction; use skynet_discord_bot::common::database::DataBase; use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information}; use skynet_discord_bot::{is_admin, Config}; - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command.name("minecraft_list").description("List your minecraft servers") + pub fn register() -> CreateCommand { + CreateCommand::new("minecraft_list").description("List your minecraft servers") } - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { if let Some(msg) = is_admin(command, ctx).await { return msg; } @@ -348,27 +321,22 @@ pub(crate) mod server { } pub(crate) mod delete { - use serenity::builder::CreateApplicationCommand; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}; + use serenity::builder::CreateCommand; use serenity::client::Context; - use serenity::model::application::command::CommandOptionType; use serenity::model::id::GuildId; - use serenity::model::prelude::application_command::{ApplicationCommandInteraction, CommandDataOptionValue}; use skynet_discord_bot::common::database::DataBase; use skynet_discord_bot::common::minecraft::Minecraft; use skynet_discord_bot::is_admin; use sqlx::{Error, Pool, Sqlite}; - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command.name("minecraft_delete").description("Delete a minecraft server").create_option(|option| { - option - .name("server_id") - .description("ID of the Minecraft server hosted by the Computer Society") - .kind(CommandOptionType::String) - .required(true) - }) + pub fn register() -> CreateCommand { + CreateCommand::new("minecraft_delete").description("Delete a minecraft server").add_option( + CreateCommandOption::new(CommandOptionType::String, "server_id", "ID of the Minecraft server hosted by the Computer Society").required(true), + ) } - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { // check if user has high enough permisssions if let Some(msg) = is_admin(command, ctx).await { return msg; @@ -378,16 +346,12 @@ pub(crate) mod server { Some(x) => x, }; - let server_minecraft = if let CommandDataOptionValue::String(id) = command - .data - .options - .first() - .expect("Expected server_id option") - .resolved - .as_ref() - .expect("Expected server_id object") + let server_minecraft = if let Some(CommandDataOption { + value: CommandDataOptionValue::String(id), + .. + }) = command.data.options.first() { - id.to_owned() + id.to_string() } else { return String::from("Expected Server ID"); }; @@ -418,7 +382,7 @@ pub(crate) mod server { WHERE server_discord = ?1 AND server_minecraft = ?2 ", ) - .bind(*discord.as_u64() as i64) + .bind(discord.get() as i64) .bind(minecraft) .fetch_optional(db) .await diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index d45d445..182eeb9 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -1,11 +1,4 @@ -use serenity::{ - builder::CreateApplicationCommand, - client::Context, - model::{ - application::interaction::application_command::ApplicationCommandInteraction, - prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue}, - }, -}; +use serenity::client::Context; use skynet_discord_bot::common::database::{DataBase, RoleAdder}; use skynet_discord_bot::is_admin; @@ -13,51 +6,40 @@ use sqlx::{Error, Pool, Sqlite}; pub mod edit { use super::*; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}; - pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { + pub async fn run(command: &CommandInteraction, 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 - .first() - .expect("Expected role option") - .resolved - .as_ref() - .expect("Expected role object") + let role_a = if let Some(CommandDataOption { + value: CommandDataOptionValue::Role(role), + .. + }) = command.data.options.first() { - role.id.to_owned() + role.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") + let role_b = if let Some(CommandDataOption { + value: CommandDataOptionValue::Role(role), + .. + }) = command.data.options.get(1) { - role.id.to_owned() + role.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") + let role_c = if let Some(CommandDataOption { + value: CommandDataOptionValue::Role(role), + .. + }) = command.data.options.get(2) { - role.id.to_owned() + role.to_owned() } else { return "Please provide a valid role for ``Role Current``".to_string(); }; @@ -70,14 +52,15 @@ pub mod edit { 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) { - let tmp = x.to_owned(); - if let Some(CommandDataOptionValue::Boolean(z)) = tmp.resolved { - delete = z; - } - } + let delete = if let Some(CommandDataOption { + value: CommandDataOptionValue::Boolean(z), + .. + }) = command.data.options.get(3) + { + *z + } else { + false + }; let db_lock = { let data_read = ctx.data.read().await; @@ -124,32 +107,13 @@ pub mod edit { } } - pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { - command - .name("roles_adder") + pub fn register() -> CreateCommand { + CreateCommand::new("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) - }) + .add_option(CreateCommandOption::new(CommandOptionType::Role, "role_a", "A role you want to add to Role B").required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::Role, "role_b", "A role you want to add to Role A").required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::Role, "role_c", "Sum of A and B").required(true)) + .add_option(CreateCommandOption::new(CommandOptionType::Boolean, "delete", "Delete this entry.").required(false)) } async fn add_server(db: &Pool, server: &RoleAdder, delete: bool) -> Result, Error> { @@ -160,10 +124,10 @@ 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) + .bind(server.server.get() as i64) + .bind(server.role_a.get() as i64) + .bind(server.role_b.get() as i64) + .bind(server.role_c.get() as i64) .fetch_optional(db) .await } else { @@ -173,10 +137,10 @@ 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) + .bind(server.server.get() as i64) + .bind(server.role_a.get() as i64) + .bind(server.role_b.get() as i64) + .bind(server.role_c.get() as i64) .fetch_optional(db) .await } @@ -192,7 +156,7 @@ pub mod tools { use skynet_discord_bot::common::database::RoleAdder; use sqlx::{Pool, Sqlite}; - pub async fn on_role_change(db: &Pool, ctx: &Context, mut new_data: Member) { + pub async fn on_role_change(db: &Pool, ctx: &Context, new_data: Member) { // check if the role changed is part of the oens for this server if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>( r#" @@ -201,7 +165,7 @@ pub mod tools { WHERE server = ? "#, ) - .bind(*new_data.guild_id.as_u64() as i64) + .bind(new_data.guild_id.get() as i64) .fetch_all(db) .await { diff --git a/src/common/database.rs b/src/common/database.rs index fb413e2..596b411 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -194,7 +194,7 @@ 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::new(tmp as u64) } _ => RoleId::from(0u64), } @@ -226,7 +226,7 @@ pub async fn get_server_config(db: &Pool, server: &GuildId) -> Option, server: &GuildId, member: &gui WHERE server = ? AND discord = ? "#, ) - .bind(*server.as_u64() as i64) - .bind(*member.user.id.as_u64() as i64) + .bind(server.get() as i64) + .bind(member.user.id.get() as i64) .fetch_one(db) .await } diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 7b27d36..4faa8ed 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -148,7 +148,7 @@ pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Ve WHERE server_discord = ?1 "#, ) - .bind(*g_id.as_u64() as i64) + .bind(g_id.get() as i64) .fetch_all(db) .await .unwrap_or_default() diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 9ebd39e..d47313d 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -31,7 +31,7 @@ pub mod normal { let mut members_all = members.len(); if let Ok(x) = server.members(ctx, None, None).await { - for mut member in x { + for member in x { // members_changed acts as an override to only deal with teh users in it if !members_changed.is_empty() && !members_changed.contains(&member.user.id) { continue; @@ -83,7 +83,7 @@ pub mod normal { set_server_numbers(&db, server, members_all as i64, members.len() as i64).await; // small bit of logging to note changes over time - println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.as_u64(), roles_set[0], roles_set[1], roles_set[2]); + println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.get(), roles_set[0], roles_set[1], roles_set[2]); } pub async fn get_server_member_bulk(db: &Pool, server: &GuildId) -> Vec { @@ -99,7 +99,7 @@ pub mod normal { ) "#, ) - .bind(*server.as_u64() as i64) + .bind(server.get() as i64) .bind(get_now_iso(true)) .fetch_all(db) .await @@ -116,13 +116,13 @@ pub mod normal { ) .bind(past) .bind(current) - .bind(*server.as_u64() as i64) + .bind(server.get() as i64) .fetch_optional(db) .await { Ok(_) => {} Err(e) => { - println!("Failure to insert into {}", server.as_u64()); + println!("Failure to insert into {}", server.get()); println!("{:?}", e); } } @@ -134,6 +134,8 @@ pub mod committee { use crate::common::database::{DataBase, Wolves}; use crate::common::wolves::committees::Committees; use crate::Config; + use serenity::all::EditRole; + use serenity::builder::CreateChannel; use serenity::client::Context; use serenity::model::channel::ChannelType; use serenity::model::guild::Member; @@ -170,9 +172,16 @@ pub mod committee { */ pub async fn update_committees(db: &Pool, ctx: &Context, config: &Config, members: &mut Vec) { let server = config.committee_server; - let committee_member = config.committee_role; + let committee_member = RoleId::new(1226602779968274573); let committees = get_committees(db).await; - let categories = vec![config.committee_category, ChannelId(1341457244973305927), ChannelId(1341457509717639279)]; + let categories = vec![ + // C&S Chats 1 + ChannelId::new(1226606560973815839), + // C&S Chats 2 + ChannelId::new(1341457244973305927), + // C&S Chats 3 + ChannelId::new(1341457509717639279), + ]; // information about the server let roles = server.roles(&ctx).await.unwrap_or_default(); @@ -216,7 +225,10 @@ pub mod committee { 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_full)).await { + match server + .create_role(&ctx, EditRole::new().name(&committee.name_full).hoist(false).mentionable(true)) + .await + { Ok(x) => Some(x), Err(_) => None, } @@ -226,7 +238,12 @@ 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(categories[category_index])) + .create_channel( + &ctx, + CreateChannel::new(&committee.name_profile) + .kind(ChannelType::Text) + .category(categories[category_index]), + ) .await { Ok(x) => { @@ -277,7 +294,7 @@ pub mod committee { Some(x) => { let mut tmp = x.to_owned(); if !tmp.is_empty() { - tmp.push(RoleId(1226602779968274573)); + tmp.push(committee_member); } tmp } @@ -309,7 +326,7 @@ pub mod committee { // these roles are flavor roles, only there to make folks mentionable member.add_roles(&ctx, &roles_add).await.unwrap_or_default(); } else { - member.remove_roles(&ctx, &[RoleId(1226602779968274573)]).await.unwrap_or_default(); + member.remove_roles(&ctx, &[committee_member]).await.unwrap_or_default(); } } @@ -347,8 +364,9 @@ pub mod committee { FROM committees "#, ) - .fetch_all(db) - .await.unwrap_or_else(|e| { + .fetch_all(db) + .await + .unwrap_or_else(|e| { dbg!(e); vec![] }) diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 9141983..ea64699 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -144,13 +144,13 @@ pub mod cns { ", ) .bind(name) - .bind(*server.as_u64() as i64) + .bind(server.get() as i64) .fetch_optional(db) .await { Ok(_) => {} Err(e) => { - println!("Failure to set server name {}", server.as_u64()); + println!("Failure to set server name {}", server.get()); println!("{:?}", e); } } @@ -168,7 +168,7 @@ pub mod cns { ) "#, ) - .bind(*server.as_u64() as i64) + .bind(server.get() as i64) .fetch_all(db) .await .unwrap_or_default() @@ -181,7 +181,7 @@ pub mod cns { VALUES (?1, ?2, ?3) ", ) - .bind(*server.as_u64() as i64) + .bind(server.get() as i64) .bind(&user.member_id) .bind(&user.expiry) .fetch_optional(db) @@ -189,7 +189,7 @@ pub mod cns { { Ok(_) => {} Err(e) => { - println!("Failure to insert into ServerMembers {} {:?}", server.as_u64(), user); + println!("Failure to insert into ServerMembers {} {:?}", server.get(), user); println!("{:?}", e); } } diff --git a/src/lib.rs b/src/lib.rs index 5611a21..ba86483 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,9 +3,9 @@ pub mod common; use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use serenity::all::CommandInteraction; use serenity::client::Context; use serenity::model::id::{ChannelId, GuildId, RoleId}; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; use serenity::prelude::TypeMapKey; use std::{env, sync::Arc}; use tokio::sync::RwLock; @@ -57,9 +57,9 @@ pub fn get_config() -> Config { mail_pass: "".to_string(), wolves_url: "".to_string(), wolves_api: "".to_string(), - committee_server: GuildId(0), - committee_role: RoleId(0), - committee_category: ChannelId(0), + committee_server: GuildId::new(0), + committee_role: RoleId::new(0), + committee_category: ChannelId::new(0), }; if let Ok(x) = env::var("DATABASE_HOME") { @@ -98,17 +98,17 @@ pub fn get_config() -> Config { if let Ok(x) = env::var("COMMITTEE_DISCORD") { if let Ok(x) = x.trim().parse::() { - config.committee_server = GuildId(x); + config.committee_server = GuildId::new(x); } } if let Ok(x) = env::var("COMMITTEE_DISCORD") { if let Ok(x) = x.trim().parse::() { - config.committee_role = RoleId(x); + config.committee_role = RoleId::new(x); } } if let Ok(x) = env::var("COMMITTEE_CATEGORY") { if let Ok(x) = x.trim().parse::() { - config.committee_category = ChannelId(x); + config.committee_category = ChannelId::new(x); } } @@ -131,7 +131,7 @@ pub fn random_string(len: usize) -> String { /** For any time ye need to check if a user who calls a command has admin privlages */ -pub async fn is_admin(command: &ApplicationCommandInteraction, ctx: &Context) -> Option { +pub async fn is_admin(command: &CommandInteraction, ctx: &Context) -> Option { let mut admin = false; let g_id = match command.guild_id { diff --git a/src/main.rs b/src/main.rs index 8b1b20a..36316d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,13 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; +use serenity::all::{ActivityData, Command, CreateMessage, EditInteractionResponse, GuildMemberUpdateEvent, Interaction}; use serenity::model::guild::Member; use serenity::{ async_trait, client::{Context, EventHandler}, model::{ - application::{command::Command, interaction::Interaction}, gateway::{GatewayIntents, Ready}, - prelude::Activity, user::OnlineStatus, }, Client, @@ -26,7 +25,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: Member) { + async fn guild_member_addition(&self, ctx: Context, new_member: Member) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() @@ -84,7 +83,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use &config_server.bot_channel_id ); - if let Err(err) = new_member.user.direct_message(&ctx, |m| m.content(&msg)).await { + if let Err(err) = new_member.user.direct_message(&ctx, CreateMessage::new().content(&msg)).await { dbg!(err); } } @@ -92,7 +91,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, new_data: Member) { + async fn guild_member_update(&self, ctx: Context, _old_data: Option, new_data: Option, _: GuildMemberUpdateEvent) { // get config/db let db_lock = { let data_read = ctx.data.read().await; @@ -102,24 +101,28 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use let db = db_lock.read().await; // check if the role changed is part of the oens for this server - on_role_change(&db, &ctx, new_data).await; + if let Some(x) = new_data { + on_role_change(&db, &ctx, x).await; + } } async fn ready(&self, ctx: Context, ready: Ready) { println!("[Main] {} is connected!", ready.user.name); - ctx.set_presence(Some(Activity::playing("with humanity's fate")), OnlineStatus::Online).await; + ctx.set_presence(Some(ActivityData::playing("with humanity's fate")), OnlineStatus::Online); - 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)) - .create_application_command(|command| commands::minecraft::server::list::register(command)) - .create_application_command(|command| commands::minecraft::server::delete::register(command)) - .create_application_command(|command| commands::minecraft::user::add::register(command)) - }) + match Command::set_global_commands( + &ctx.http, + vec![ + commands::add_server::register(), + commands::role_adder::edit::register(), + commands::link_email::link::register(), + commands::link_email::verify::register(), + commands::minecraft::server::add::register(), + commands::minecraft::server::list::register(), + commands::minecraft::server::delete::register(), + commands::minecraft::user::add::register(), + ], + ) .await { Ok(_) => {} @@ -130,7 +133,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } async fn interaction_create(&self, ctx: Context, interaction: Interaction) { - if let Interaction::ApplicationCommand(command) = interaction { + if let Interaction::Command(command) = interaction { let _ = command.defer_ephemeral(&ctx.http).await; //println!("Received command interaction: {:#?}", command); @@ -148,7 +151,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use _ => "not implemented :(".to_string(), }; - if let Err(why) = command.edit_original_interaction_response(&ctx.http, |response| response.content(content)).await { + if let Err(why) = command.edit_response(&ctx.http, EditInteractionResponse::new().content(content)).await { println!("Cannot respond to slash command: {}", why); } } -- 2.47.2 From 86f71f0fa445bba48b2c3781152c955e0236ef19 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Wed, 19 Feb 2025 00:26:54 +0000 Subject: [PATCH 37/40] feat: updated more dependencies --- Cargo.lock | 689 ++++++++++++++++++++++++++++++++++++++++------------- Cargo.toml | 16 +- src/lib.rs | 4 +- 3 files changed, 533 insertions(+), 176 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0b769f..e4adde8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,10 +78,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -404,9 +403,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.19" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "shlex", ] @@ -419,9 +418,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -431,6 +430,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.5", + "stacker", +] + [[package]] name = "cipher" version = "0.2.5" @@ -588,7 +597,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "socket2 0.5.7", + "socket2", "windows-sys 0.52.0", ] @@ -615,7 +624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -676,6 +685,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -693,9 +713,9 @@ dependencies = [ [[package]] name = "email-encoding" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87260449b06739ee78d6281c68d2a0ff3e3af64a78df63d3a1aeb3c06997c8a" +checksum = "ea3d894bbbab314476b265f9b2d46bf24b123a36dd0e96b06a1b49545b9d9dcc" dependencies = [ "base64 0.22.1", "memchr", @@ -823,6 +843,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1015,6 +1041,18 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "ghash" version = "0.3.1" @@ -1092,22 +1130,30 @@ dependencies = [ ] [[package]] -name = "hashlink" -version = "0.8.4" +name = "hashbrown" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "hashbrown", + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", ] [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -1176,13 +1222,13 @@ dependencies = [ [[package]] name = "hostname" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" dependencies = [ + "cfg-if", "libc", - "match_cfg", - "winapi", + "windows", ] [[package]] @@ -1306,7 +1352,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower-service", "tracing", @@ -1393,7 +1439,7 @@ dependencies = [ "http-body 1.0.1", "hyper 1.5.1", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower-service", "tracing", @@ -1423,13 +1469,121 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.3.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", ] [[package]] @@ -1442,6 +1596,27 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.5.0" @@ -1449,7 +1624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1531,25 +1706,27 @@ dependencies = [ [[package]] name = "lettre" -version = "0.10.4" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bd09637ae3ec7bd605b8e135e757980b3968430ff2b1a4a94fb7769e50166d" +checksum = "504273f23d9f3d2fd09c6e5fa94fafd5177ae6b83ed0df1f3b0e180052c076a9" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", + "chumsky", "email-encoding", "email_address", - "fastrand 1.9.0", + "fastrand 2.1.1", "futures-util", "hostname", "httpdate", - "idna 0.3.0", + "idna 1.0.3", "mime", "native-tls", "nom", - "once_cell", + "percent-encoding", "quoted_printable", - "socket2 0.4.10", + "socket2", "tokio", + "url", ] [[package]] @@ -1576,9 +1753,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.27.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "pkg-config", @@ -1603,6 +1780,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -1622,17 +1805,11 @@ dependencies = [ "value-bag", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "maud" -version = "0.25.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0bab19cef8a7fe1c18a43e881793bfc9d4ea984befec3ae5bd0415abf3ecf00" +checksum = "8156733e27020ea5c684db5beac5d1d611e1272ab17901a49466294b84fc217e" dependencies = [ "itoa", "maud_macros", @@ -1640,14 +1817,14 @@ dependencies = [ [[package]] name = "maud_macros" -version = "0.25.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be95d66c3024ffce639216058e5bae17a83ecaf266ffc6e4d060ad447c9eed2" +checksum = "7261b00f3952f617899bc012e3dbd56e4f0110a038175929fa5d18e5a19913ca" dependencies = [ - "proc-macro-error", "proc-macro2", + "proc-macro2-diagnostics", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] @@ -1883,12 +2060,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2012,31 +2183,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "zerocopy 0.7.35", ] [[package]] @@ -2054,6 +2201,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "version_check", +] + +[[package]] +name = "psm" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +dependencies = [ + "cc", +] + [[package]] name = "quote" version = "1.0.37" @@ -2065,9 +2233,9 @@ dependencies = [ [[package]] name = "quoted_printable" -version = "0.4.8" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49" +checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73" [[package]] name = "rand" @@ -2093,6 +2261,17 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.1", + "zerocopy 0.8.18", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -2113,6 +2292,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.1", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -2131,6 +2320,16 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.18", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -2464,9 +2663,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -2482,9 +2681,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -2493,9 +2692,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -2511,7 +2710,7 @@ checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2640,7 +2839,7 @@ dependencies = [ "dotenvy", "lettre", "maud", - "rand 0.8.5", + "rand 0.9.0", "serde", "serde_json", "serenity", @@ -2675,15 +2874,8 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ - "libc", - "winapi", + "serde", ] [[package]] @@ -2724,21 +2916,11 @@ dependencies = [ "der", ] -[[package]] -name = "sqlformat" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom", - "unicode_categories", -] - [[package]] name = "sqlx" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" dependencies = [ "sqlx-core", "sqlx-macros", @@ -2749,37 +2931,31 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" dependencies = [ - "ahash", - "atoi", - "byteorder", "bytes 1.7.1", "crc", "crossbeam-queue", "either", - "event-listener 2.5.3", - "futures-channel", + "event-listener 5.3.1", "futures-core", "futures-intrusive", "futures-io", "futures-util", + "hashbrown 0.15.2", "hashlink", - "hex", "indexmap", "log", "memchr", "once_cell", - "paste", "percent-encoding", "serde", "serde_json", "sha2 0.10.8", "smallvec", - "sqlformat", - "thiserror", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -2788,22 +2964,22 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] name = "sqlx-macros-core" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ "dotenvy", "either", @@ -2817,8 +2993,9 @@ dependencies = [ "sha2 0.10.8", "sqlx-core", "sqlx-mysql", + "sqlx-postgres", "sqlx-sqlite", - "syn 1.0.109", + "syn 2.0.89", "tempfile", "tokio", "url", @@ -2826,12 +3003,12 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" +checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" dependencies = [ "atoi", - "base64 0.21.7", + "base64 0.22.1", "bitflags 2.6.0", "byteorder", "bytes 1.7.1", @@ -2861,19 +3038,19 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.11", "tracing", "whoami", ] [[package]] name = "sqlx-postgres" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" +checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" dependencies = [ "atoi", - "base64 0.21.7", + "base64 0.22.1", "bitflags 2.6.0", "byteorder", "crc", @@ -2881,7 +3058,6 @@ dependencies = [ "etcetera", "futures-channel", "futures-core", - "futures-io", "futures-util", "hex", "hkdf 0.12.4", @@ -2899,16 +3075,16 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.11", "tracing", "whoami", ] [[package]] name = "sqlx-sqlite" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" dependencies = [ "atoi", "flume 0.11.0", @@ -2921,10 +3097,29 @@ dependencies = [ "log", "percent-encoding", "serde", + "serde_urlencoded", "sqlx-core", "tracing", "url", - "urlencoding", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", ] [[package]] @@ -3062,6 +3257,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -3123,7 +3329,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -3137,6 +3352,17 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "time" version = "0.2.27" @@ -3206,6 +3432,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -3223,9 +3459,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes 1.7.1", @@ -3234,7 +3470,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] @@ -3415,7 +3651,7 @@ dependencies = [ "rustls 0.22.4", "rustls-pki-types", "sha1 0.10.6", - "thiserror", + "thiserror 1.0.63", "url", "utf-8", ] @@ -3468,18 +3704,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - [[package]] name = "universal-hash" version = "0.4.0" @@ -3508,18 +3732,24 @@ dependencies = [ "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "value-bag" version = "1.10.0" @@ -3565,6 +3795,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -3708,6 +3947,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3905,6 +4154,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "wolves_oxidised" version = "0.1.0" @@ -3916,6 +4174,42 @@ dependencies = [ "tokio-test", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -3923,7 +4217,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2" +dependencies = [ + "zerocopy-derive 0.8.18", ] [[package]] @@ -3937,8 +4240,62 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] diff --git a/Cargo.toml b/Cargo.toml index d637ae8..aa0cd6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,29 +17,29 @@ name = "update_minecraft" [dependencies] # discord library serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } -tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "full"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "full"] } # wolves api wolves_oxidised = { git = "https://forgejo.skynet.ie/Skynet/wolves-oxidised.git", features = ["unstable"] } # wolves_oxidised = { path = "../wolves-oxidised", features = ["unstable"] } # to make the http requests -surf = "2.3.2" +surf = "2.3" -dotenvy = "0.15.7" +dotenvy = "0.15" # For sqlite -sqlx = { version = "0.7.1", features = [ "runtime-tokio", "sqlite", "migrate" ] } +sqlx = { version = "0.8", features = [ "runtime-tokio", "sqlite", "migrate" ] } serde_json = { version = "1.0", features = ["raw_value"] } # create random strings -rand = "0.8.5" +rand = "0.9" # fancy time stuff -chrono = "0.4.26" +chrono = "0.4" # for email -lettre = "0.10.4" -maud = "0.25.0" +lettre = "0.11" +maud = "0.27" serde = "1.0" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ba86483..53ae26b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ pub mod common; use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; -use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use rand::{distr::Alphanumeric, rng, Rng}; use serenity::all::CommandInteraction; use serenity::client::Context; use serenity::model::id::{ChannelId, GuildId, RoleId}; @@ -125,7 +125,7 @@ pub fn get_now_iso(short: bool) -> String { } pub fn random_string(len: usize) -> String { - thread_rng().sample_iter(&Alphanumeric).take(len).map(char::from).collect() + rng().sample_iter(&Alphanumeric).take(len).map(char::from).collect() } /** -- 2.47.2 From 30287466cbf5b31ab2b25588eeed9c71e4f67a24 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Wed, 19 Feb 2025 00:42:26 +0000 Subject: [PATCH 38/40] fix: cannot have an ID of 0 for any role, channel or server --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 53ae26b..0152205 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,9 +57,9 @@ pub fn get_config() -> Config { mail_pass: "".to_string(), wolves_url: "".to_string(), wolves_api: "".to_string(), - committee_server: GuildId::new(0), - committee_role: RoleId::new(0), - committee_category: ChannelId::new(0), + committee_server: GuildId::new(1), + committee_role: RoleId::new(1), + committee_category: ChannelId::new(1), }; if let Ok(x) = env::var("DATABASE_HOME") { -- 2.47.2 From 262eb0c991b4050988bfe86f4e27cb8317021f8a Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Wed, 19 Feb 2025 00:51:40 +0000 Subject: [PATCH 39/40] fix: ensure that all committee members get teh right role --- src/common/set_roles.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index d47313d..e47b95c 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -312,6 +312,11 @@ pub mod committee { } } + if !roles_required.is_empty() { + // if there are committee roles then give the general purporse role + roles_add.push(committee_member); + } + for role in &roles_required { if !roles_current_id.contains(role) { roles_add.push(role.to_owned()); -- 2.47.2 From c79944bd6e20bde77f6366bee83b7ff70bb00e4b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Wed, 19 Feb 2025 01:18:28 +0000 Subject: [PATCH 40/40] fix: actually enabled the option to get a persons ID if they arent on one of teh servers with teh bot enabled --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e4adde8..416c3c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4166,7 +4166,7 @@ dependencies = [ [[package]] name = "wolves_oxidised" version = "0.1.0" -source = "git+https://forgejo.skynet.ie/Skynet/wolves-oxidised.git#6101104c794c2dcf7100b057fe37fdf165b8b381" +source = "git+https://forgejo.skynet.ie/Skynet/wolves-oxidised.git#74f00e3dcfd52744583b0ded08efb8bb27fdcec8" dependencies = [ "reqwest 0.12.9", "serde", -- 2.47.2