Merge branch '#6-incrimental-updating' into 'main'

feat: updating teh data from wolves should now also update roles for whoever changed.

See merge request compsoc1/skynet/discord-bot!4
This commit is contained in:
silver 2023-11-25 21:30:43 +00:00
commit c6eaa8ad9a
4 changed files with 127 additions and 23 deletions

View file

@ -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::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(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);
}
}

View file

@ -59,6 +59,6 @@ async fn bulk_check(ctx: Arc<Context>) {
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;
}
}

View file

@ -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<Sqlite>, ctx: &Context, server: &Servers) -> Resul
// update all users
if update {
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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<Sqlite>, 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

View file

@ -381,7 +381,14 @@ pub fn random_string(len: usize) -> String {
pub mod set_roles {
use super::*;
pub async fn update_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers, remove_roles: &[Option<RoleId>]) {
pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option<RoleId>], members_changed: &Vec<UserId>) {
let db_lock = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().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<Sqlite>, 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::<DataBase>().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::<Config>().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::<BTreeMap<_, _>>();
// 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::<u64>().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<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> {
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<WolvesResultUser> {
if config.wolves_url.is_empty() {
return vec![];
@ -565,7 +636,7 @@ pub mod get_data {
vec![]
}
async fn add_users_wolves(db: &Pool<Sqlite>, server: &GuildId, user: &WolvesResultUser) {
async fn add_users_wolves(db: &Pool<Sqlite>, 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<Sqlite>, server: &GuildId, user: &WolvesResultUser) {
match sqlx::query_as::<_, ServerMembers>(
"
INSERT OR REPLACE INTO server_members (server, id_wolves, expiry)