From 3779222a38174b5641c7e9e4c768f2c91fed61ea Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 25 Nov 2023 21:15:07 +0000 Subject: [PATCH] feat: updating teh data from wolves should now also update roles for whoever changed. This should lead to faster activations of folks who have previously linked, join a server, then get membership. Closes #6 --- src/bin/update_data.rs | 44 ++++++++++++++++-- src/bin/update_users.rs | 2 +- src/commands/add_server.rs | 12 ++--- src/lib.rs | 92 +++++++++++++++++++++++++++++++++----- 4 files changed, 127 insertions(+), 23 deletions(-) diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 08bb7a0..22bd5bd 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -1,4 +1,12 @@ -use skynet_discord_bot::{db_init, get_config, get_data::get_wolves}; +use serenity::{ + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, +}; +use skynet_discord_bot::{db_init, get_config, get_data::get_wolves, Config, DataBase}; +use std::{process, sync::Arc}; +use tokio::sync::RwLock; #[tokio::main] async fn main() { @@ -8,6 +16,36 @@ async fn main() { Err(_) => return, }; - // handle wolves api here - get_wolves(&db, &config).await; + // Intents are a bitflag, bitwise operations can be used to dictate which intents to use + let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; + // Build our client. + let mut client = Client::builder(&config.discord_token, intents) + .event_handler(Handler {}) + .await + .expect("Error creating client"); + + { + let mut data = client.data.write().await; + + data.insert::(Arc::new(RwLock::new(config))); + data.insert::(Arc::new(RwLock::new(db))); + } + + if let Err(why) = client.start().await { + println!("Client error: {:?}", why); + } +} + +struct Handler; +#[async_trait] +impl EventHandler for Handler { + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); + + get_wolves(&ctx).await; + + // finish up + process::exit(0); + } } diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index f9396e7..a5ca7e0 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -59,6 +59,6 @@ async fn bulk_check(ctx: Arc) { let db = db_lock.read().await; for server_config in get_server_config_bulk(&db).await { - update_server(&db, &ctx, &server_config, &[]).await; + update_server(&ctx, &server_config, &[], &vec![]).await; } } diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 598c320..c576b16 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -7,7 +7,7 @@ use serenity::{ }, }; use skynet_discord_bot::get_data::get_wolves; -use skynet_discord_bot::{get_server_config, set_roles::update_server, Config, DataBase, Servers}; +use skynet_discord_bot::{get_server_config, set_roles::update_server, DataBase, Servers}; use sqlx::{Error, Pool, Sqlite}; pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String { @@ -173,14 +173,8 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul // update all users if update { - 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; - // handle wolves api here - get_wolves(db, &config).await; + get_wolves(ctx).await; let mut roles_remove = vec![]; if current_remove { @@ -189,7 +183,7 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul if past_remove { roles_remove.push(past_role) } - update_server(db, ctx, server, &roles_remove).await; + update_server(ctx, server, &roles_remove, &vec![]).await; } insert diff --git a/src/lib.rs b/src/lib.rs index c85200c..c8ca3f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -381,7 +381,14 @@ pub fn random_string(len: usize) -> String { pub mod set_roles { use super::*; - pub async fn update_server(db: &Pool, ctx: &Context, server: &Servers, remove_roles: &[Option]) { + pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &Vec) { + 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, @@ -392,7 +399,7 @@ pub mod set_roles { let mut roles_set = [0, 0, 0]; let mut members = vec![]; - for member in get_server_member_bulk(db, server).await { + for member in get_server_member_bulk(&db, server).await { if let Some(x) = member.discord { members.push(x); } @@ -401,6 +408,11 @@ pub mod set_roles { 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![]; @@ -448,7 +460,7 @@ pub mod set_roles { } } - set_server_numbers(db, server, members_all as i64, members.len() as i64).await; + 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]); @@ -499,6 +511,8 @@ 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 { @@ -528,20 +542,77 @@ pub mod get_data { pub email: String, pub expiry: String, } - pub async fn get_wolves(db: &Pool, config: &Config) { - for server_config in get_server_config_bulk(db).await { + 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; + + for server_config in get_server_config_bulk(&db).await { let Servers { server, wolves_api, .. - } = server_config; + } = &server_config; - for user in get_wolves_sub(config, &wolves_api).await { - add_users_wolves(db, &server, &user).await; + 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_sub(&config, wolves_api).await { + let id = user.wolves_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_sub(config: &Config, wolves_api: &str) -> Vec { if config.wolves_url.is_empty() { return vec![]; @@ -565,7 +636,7 @@ pub mod get_data { vec![] } - async fn add_users_wolves(db: &Pool, server: &GuildId, user: &WolvesResultUser) { + async fn add_users_wolves(db: &Pool, user: &WolvesResultUser) { // expiry match sqlx::query_as::<_, Wolves>( " @@ -585,7 +656,8 @@ pub mod get_data { 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)