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 */ // #[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 { // 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 */ pub mod cns { 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::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 { 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: 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; 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_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, &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, &WolvesResultUserMin::from(&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; } } } 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![]; } 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) { 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); } } } } /** Get and store the data on C&S committees */ 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; #[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 } }