diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 5b42ac1..4d49490 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -1,27 +1,16 @@ -use chrono::{Datelike, Utc}; -use rand::{rngs::SmallRng, seq::IndexedRandom, SeedableRng}; -use serde::Deserialize; use serenity::{ - all::GuildId, async_trait, - builder::{CreateAttachment, EditGuild}, client::{Context, EventHandler}, model::gateway::{GatewayIntents, Ready}, Client, }; +use skynet_discord_bot::common::server_icon; use skynet_discord_bot::{ - common::{ - database::{db_init, DataBase}, - renderer::{Args, Renderer}, - }, - get_config, get_now_iso, Config, + common::database::{db_init, DataBase}, + get_config, Config, }; -use sqlx::{Pool, Sqlite}; use std::{ - ffi::OsString, - fs, - path::PathBuf, - process::{self, Command}, + process::self, sync::Arc, }; use tokio::sync::RwLock; @@ -61,267 +50,23 @@ impl EventHandler for Handler { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); - // pull in the open governance repo - // u[date committee server - update_icon_main(Arc::clone(&ctx)).await; + 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 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 config_toml = server_icon::get_config_icons(); + + server_icon::update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; // finish up process::exit(0); } } - -async fn update_icon_main(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 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 config_toml = get_config_icons(); - let server = GuildId::new(689189992417067052); - - // clone repo into local folder - clone_repo(&config_global, &config_toml); - - // see if there is a current festival - let festival_data = get_festival(&config_toml); - - // get a list of all the graphics files - let logos = get_logos(&config_global, &config_toml); - - // filter them so only the current season (if any) are active - let logos_filtered = logos_filter(&festival_data, logos); - - let mut rng = SmallRng::from_os_rng(); - let logo_selected = logos_filtered.choose(&mut rng).unwrap(); - - logo_set(&ctx, &db, &server, logo_selected).await; -} - -#[derive(Debug)] -struct FestivalData { - current: Vec, - exclusions: Vec, -} - -fn get_festival(config_toml: &ConfigToml) -> FestivalData { - let today = Utc::now(); - let day = today.day(); - let month = today.month(); - let year = today.year(); - - let mut result = FestivalData { - current: vec![], - exclusions: vec![], - }; - - for festival in &config_toml.festivals { - if (day >= festival.start.day && day <= festival.end.day) && (month >= festival.start.month && month <= festival.end.month) { - if festival.start.year == 0 || festival.end.year == 0 || (year >= festival.start.year && year <= festival.end.year) { - result.current.push(festival.name.to_owned()); - } - } else if !festival.all_year { - result.exclusions.push(festival.name.to_owned()); - } - } - - result -} - -#[derive(Deserialize)] -struct ConfigToml { - source: ConfigTomlSource, - festivals: Vec, -} -#[derive(Deserialize, Debug)] -struct ConfigTomlSource { - repo: String, - directory: String, -} - -#[derive(Deserialize, Debug)] -struct ConfigTomlFestivals { - name: String, - all_year: bool, - start: ConfigTomlFestivalsTime, - end: ConfigTomlFestivalsTime, -} -#[derive(Deserialize, Debug)] -struct ConfigTomlFestivalsTime { - day: u32, - month: u32, - year: i32, -} - -fn get_config_icons() -> ConfigToml { - let toml_raw = include_str!("../../.server-icons.toml"); - let config: ConfigToml = toml::from_str(toml_raw).unwrap(); - config -} -fn clone_repo(config: &Config, config_toml: &ConfigToml) { - let url = &config_toml.source.repo; - let folder = format!("{}/open-governance", &config.home); - - Command::new("git").arg("clone").arg(url).arg(&folder).output().expect("failed to execute process"); - - Command::new("git") - .arg("pull") - .arg("origin") - .arg("main") - .current_dir(&folder) - .output() - .expect("failed to execute process"); -} - -#[derive(Debug)] -struct LogoData { - name: OsString, - path: PathBuf, -} -fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { - let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); - let folder_path = PathBuf::from(&folder); - let mut folder_output = folder_path.clone(); - folder_output.push("converted"); - let paths = fs::read_dir(folder).unwrap(); - - let args = Args { - input: folder_path.clone(), - output: folder_output, - colors: String::from(""), - width: 1024, - height: 1024, - }; - let mut r = Renderer::new(&args).unwrap(); - - let mut logos = vec![]; - - for tmp in paths.flatten() { - let path_local = tmp.path().to_owned(); - let path_local2 = tmp.path().to_owned(); - let name = path_local2.file_name().unwrap().to_owned(); - let mut path = tmp.path(); - - if path.is_dir() { - continue; - } - - match tmp.path().extension() { - None => {} - Some(ext) => { - if ext == "svg" { - let mut path_new = path_local.clone(); - path_new.set_extension("png"); - let filename_tmp = path_new.clone(); - let filename = filename_tmp.file_name().unwrap_or_default(); - path_new.pop(); - path_new.push("converted"); - path_new.push(filename); - - // check if exists - if !path_new.exists() { - // convert if it hasnt been converted already - match r.render(&path_local, &args) { - Ok(_) => {} - Err(_e) => { - dbg!("Failed to render {path_local:?}: {}"); - } - } - } - path = path_new; - } - } - }; - - logos.push(LogoData { - name, - path, - }); - - // println!("Name: {}", &tmp.path().display()); - } - - logos -} - -fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec { - let mut filtered: Vec = vec![]; - - 'outer: for logo in existing { - let name_lowercase0 = logo.name.to_ascii_lowercase(); - let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); - - if !festival_data.current.is_empty() { - // if its a current festival filter based on it - for festival in &festival_data.current { - if name_lowercase.contains(festival) { - filtered.push(logo); - continue 'outer; - } - } - } else { - // else filter using the excluded ones - let mut excluded = false; - for festival in &festival_data.exclusions { - if name_lowercase.contains(festival) { - excluded = true; - } - } - - if !excluded { - filtered.push(logo); - } - } - } - - filtered -} - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct ServerIcons { - pub id: i64, - pub name: String, - pub date: String, -} - -async fn logo_set(ctx: &Arc, db: &Pool, server: &GuildId, logo_selected: &LogoData) { - // add to teh database - logo_set_db(db, logo_selected).await; - - let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); - - // assuming a `guild` has already been bound - let builder = EditGuild::new().icon(Some(&icon)); - server.edit(ctx, builder).await.unwrap(); -} - -async fn logo_set_db(db: &Pool, logo_selected: &LogoData) { - let name = logo_selected.name.to_str().unwrap_or_default(); - - match sqlx::query_as::<_, ServerIcons>( - " - INSERT OR REPLACE INTO server_icons (name, date) - VALUES (?1, ?2) - ", - ) - .bind(name) - .bind(get_now_iso(false)) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - dbg!(e); - } - } -} - -// fn command(config_toml: &ConfigToml, logo_selected: &LogoData){ -// let web_url = format!("{}/src/branch/main/{}/{}", &config_toml.source.repo, &config_toml.source.directory, &logo_selected.name.to_str().unwrap_or_default()); -// } diff --git a/src/common/mod.rs b/src/common/mod.rs index b82b7bb..9e1745e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -4,3 +4,4 @@ pub mod set_roles; pub mod wolves; pub mod renderer; +pub mod server_icon; diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs new file mode 100644 index 0000000..4a489be --- /dev/null +++ b/src/common/server_icon.rs @@ -0,0 +1,263 @@ +use serde::Deserialize; +use std::{ffi::OsString, path::PathBuf}; + +#[derive(Deserialize)] +pub struct ConfigToml { + pub source: ConfigTomlSource, + pub festivals: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct ConfigTomlSource { + pub repo: String, + pub directory: String, +} + +#[derive(Deserialize, Debug)] +pub struct ConfigTomlFestivals { + pub name: String, + pub all_year: bool, + pub start: ConfigTomlFestivalsTime, + pub end: ConfigTomlFestivalsTime, +} + +#[derive(Deserialize, Debug)] +pub struct ConfigTomlFestivalsTime { + pub day: u32, + pub month: u32, + pub year: i32, +} + +pub fn get_config_icons() -> ConfigToml { + let toml_raw = include_str!("../../.server-icons.toml"); + let config: ConfigToml = toml::from_str(toml_raw).unwrap(); + config +} + +#[derive(Debug)] +pub struct LogoData { + pub name: OsString, + pub path: PathBuf, +} + +#[derive(Debug, Clone, sqlx::FromRow)] +pub struct ServerIcons { + pub id: i64, + pub name: String, + pub date: String, +} + +pub mod update_icon { + use super::*; + use crate::{ + common::renderer::{Args, Renderer}, + get_now_iso, Config, + }; + use chrono::{Datelike, Utc}; + use rand::{rngs::SmallRng, seq::IndexedRandom, SeedableRng}; + use serenity::{ + all::GuildId, + builder::{CreateAttachment, EditGuild}, + client::Context, + }; + use sqlx::{Pool, Sqlite}; + use std::{fs, process::Command}; + + /// Update the server icon, pulling from open governance. + pub async fn update_icon_main(ctx: &Context, db: &Pool, config_global: &Config, config_toml: &ConfigToml) { + let server = GuildId::new(689189992417067052); + + // clone repo into local folder + clone_repo(config_global, config_toml); + + // see if there is a current festival + let festival_data = get_festival(config_toml); + + // get a list of all the graphics files + let logos = get_logos(config_global, config_toml); + + // filter them so only the current season (if any) are active + let logos_filtered = logos_filter(&festival_data, logos); + + let mut rng = SmallRng::from_os_rng(); + let logo_selected = logos_filtered.choose(&mut rng).unwrap(); + + logo_set(ctx, db, &server, logo_selected).await; + } + + #[derive(Debug)] + struct FestivalData { + current: Vec, + exclusions: Vec, + } + + fn get_festival(config_toml: &ConfigToml) -> FestivalData { + let today = Utc::now(); + let day = today.day(); + let month = today.month(); + let year = today.year(); + + let mut result = FestivalData { + current: vec![], + exclusions: vec![], + }; + + for festival in &config_toml.festivals { + if (day >= festival.start.day && day <= festival.end.day) && (month >= festival.start.month && month <= festival.end.month) { + if festival.start.year == 0 || festival.end.year == 0 || (year >= festival.start.year && year <= festival.end.year) { + result.current.push(festival.name.to_owned()); + } + } else if !festival.all_year { + result.exclusions.push(festival.name.to_owned()); + } + } + + result + } + + fn clone_repo(config: &Config, config_toml: &ConfigToml) { + let url = &config_toml.source.repo; + let folder = format!("{}/open-governance", &config.home); + + Command::new("git").arg("clone").arg(url).arg(&folder).output().expect("failed to execute process"); + + Command::new("git") + .arg("pull") + .arg("origin") + .arg("main") + .current_dir(&folder) + .output() + .expect("failed to execute process"); + } + + fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { + let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); + let folder_path = PathBuf::from(&folder); + let mut folder_output = folder_path.clone(); + folder_output.push("converted"); + let paths = fs::read_dir(folder).unwrap(); + + let args = Args { + input: folder_path.clone(), + output: folder_output, + colors: String::from(""), + width: 1024, + height: 1024, + }; + let mut r = Renderer::new(&args).unwrap(); + + let mut logos = vec![]; + + for tmp in paths.flatten() { + let path_local = tmp.path().to_owned(); + let path_local2 = tmp.path().to_owned(); + let name = path_local2.file_name().unwrap().to_owned(); + let mut path = tmp.path(); + + if path.is_dir() { + continue; + } + + match tmp.path().extension() { + None => {} + Some(ext) => { + if ext == "svg" { + let mut path_new = path_local.clone(); + path_new.set_extension("png"); + let filename_tmp = path_new.clone(); + let filename = filename_tmp.file_name().unwrap_or_default(); + path_new.pop(); + path_new.push("converted"); + path_new.push(filename); + + // check if exists + if !path_new.exists() { + // convert if it hasnt been converted already + match r.render(&path_local, &args) { + Ok(_) => {} + Err(_e) => { + dbg!("Failed to render {path_local:?}: {}"); + } + } + } + path = path_new; + } + } + }; + + logos.push(LogoData { + name, + path, + }); + + // println!("Name: {}", &tmp.path().display()); + } + + logos + } + + fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec { + let mut filtered: Vec = vec![]; + + 'outer: for logo in existing { + let name_lowercase0 = logo.name.to_ascii_lowercase(); + let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); + + if !festival_data.current.is_empty() { + // if its a current festival filter based on it + for festival in &festival_data.current { + if name_lowercase.contains(festival) { + filtered.push(logo); + continue 'outer; + } + } + } else { + // else filter using the excluded ones + let mut excluded = false; + for festival in &festival_data.exclusions { + if name_lowercase.contains(festival) { + excluded = true; + } + } + + if !excluded { + filtered.push(logo); + } + } + } + + filtered + } + + async fn logo_set(ctx: &Context, db: &Pool, server: &GuildId, logo_selected: &LogoData) { + // add to teh database + logo_set_db(db, logo_selected).await; + + let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); + + // assuming a `guild` has already been bound + let builder = EditGuild::new().icon(Some(&icon)); + server.edit(ctx, builder).await.unwrap(); + } + + async fn logo_set_db(db: &Pool, logo_selected: &LogoData) { + let name = logo_selected.name.to_str().unwrap_or_default(); + + match sqlx::query_as::<_, ServerIcons>( + " + INSERT OR REPLACE INTO server_icons (name, date) + VALUES (?1, ?2) + ", + ) + .bind(name) + .bind(get_now_iso(false)) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + dbg!(e); + } + } + } +}