2025-06-07 00:10:52 +01:00
|
|
|
use serenity::{
|
|
|
|
async_trait,
|
|
|
|
client::{Context, EventHandler},
|
|
|
|
model::gateway::{GatewayIntents, Ready},
|
|
|
|
Client,
|
|
|
|
};
|
|
|
|
use skynet_discord_bot::common::database::{db_init, DataBase};
|
|
|
|
use skynet_discord_bot::{get_config, Config};
|
|
|
|
use std::{fs, process, sync::Arc};
|
2025-06-07 19:55:36 +01:00
|
|
|
use std::ffi::{OsStr, OsString};
|
2025-06-07 00:10:52 +01:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Read;
|
2025-06-07 01:05:08 +01:00
|
|
|
use std::path::PathBuf;
|
2025-06-07 00:10:52 +01:00
|
|
|
use std::process::Command;
|
|
|
|
use chrono::{Datelike, Utc};
|
2025-06-07 01:05:08 +01:00
|
|
|
use gdk_pixbuf::{Pixbuf, PixbufFormat, PixbufLoader};
|
2025-06-07 00:10:52 +01:00
|
|
|
use gdk_pixbuf::prelude::PixbufLoaderExt;
|
|
|
|
use resvg::usvg;
|
|
|
|
use serde::Deserialize;
|
|
|
|
use serenity::all::GuildId;
|
|
|
|
use tokio::sync::RwLock;
|
2025-06-07 01:05:08 +01:00
|
|
|
use skynet_discord_bot::common::renderer::{Args, Renderer};
|
2025-06-07 00:10:52 +01:00
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() {
|
|
|
|
let config = get_config();
|
|
|
|
let db = match db_init(&config).await {
|
|
|
|
Ok(x) => x,
|
|
|
|
Err(_) => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
|
|
|
// pull in the open governance repo
|
|
|
|
// u[date committee server
|
|
|
|
update_icon_main(Arc::clone(&ctx)).await;
|
|
|
|
|
|
|
|
// finish up
|
|
|
|
process::exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn update_icon_main(ctx: Arc<Context>) {
|
|
|
|
let db_lock = {
|
|
|
|
let data_read = ctx.data.read().await;
|
|
|
|
data_read.get::<DataBase>().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::<Config>().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
|
2025-06-07 19:55:36 +01:00
|
|
|
let logos = get_logos(&config_global, &config_toml);
|
2025-06-07 00:10:52 +01:00
|
|
|
}
|
|
|
|
|
2025-06-07 21:24:59 +01:00
|
|
|
struct FestivalData{
|
|
|
|
current: Option<String>,
|
|
|
|
exclusions: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_festival(config_toml: &ConfigToml)-> FestivalData {
|
2025-06-07 00:10:52 +01:00
|
|
|
let today = Utc::now();
|
|
|
|
let day = today.day();
|
|
|
|
let month = today.month();
|
|
|
|
let year = today.year();
|
|
|
|
|
2025-06-07 21:24:59 +01:00
|
|
|
let mut result = FestivalData {
|
|
|
|
current: None,
|
|
|
|
exclusions: vec![],
|
|
|
|
};
|
2025-06-07 00:10:52 +01:00
|
|
|
|
|
|
|
for festival in &config_toml.festivals {
|
2025-06-07 22:29:00 +01:00
|
|
|
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 {
|
|
|
|
result.current = Some(festival.name.to_owned());
|
|
|
|
} else if (year >= festival.start.year && year <= festival.end.year) {
|
|
|
|
result.current = Some(festival.name.to_owned());
|
|
|
|
}
|
2025-06-07 00:10:52 +01:00
|
|
|
} else if !festival.all_year {
|
2025-06-07 21:24:59 +01:00
|
|
|
result.exclusions.push(festival.name.to_owned());
|
2025-06-07 00:10:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-07 21:24:59 +01:00
|
|
|
result
|
2025-06-07 00:10:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct ConfigToml {
|
|
|
|
source: ConfigTomlSource,
|
|
|
|
festivals: Vec<ConfigTomlFestivals>,
|
|
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct ConfigTomlSource {
|
|
|
|
repo: String,
|
|
|
|
directory: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct ConfigTomlFestivals {
|
|
|
|
name: String,
|
|
|
|
all_year: bool,
|
|
|
|
start: ConfigTomlFestivalsTime,
|
|
|
|
end: ConfigTomlFestivalsTime,
|
|
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2025-06-07 01:05:08 +01:00
|
|
|
fn convert_svg_to_png(original: &PathBuf, out: &PathBuf){
|
2025-06-07 00:10:52 +01:00
|
|
|
let mut f = File::open(original).unwrap();
|
|
|
|
let mut buffer = Vec::new();
|
|
|
|
// read the whole file
|
|
|
|
f.read_to_end(&mut buffer).unwrap();
|
|
|
|
let loader = PixbufLoader::with_mime_type("image/svg+xml")
|
|
|
|
.expect("error loader");
|
|
|
|
loader.write(&buffer).expect("TODO: panic message");
|
|
|
|
loader.close().expect("TODO: panic message");
|
|
|
|
let pixbuf = loader.pixbuf().expect("no pixbuf");
|
|
|
|
|
|
|
|
let (width, height) = (pixbuf.width(), pixbuf.height());
|
|
|
|
println!("size: {}x{}", width, height);
|
|
|
|
let bytes: Vec<u8> =
|
|
|
|
pixbuf.save_to_bufferv("png", &[]).expect("must not error");
|
|
|
|
fs::write(out, &bytes).expect("TODO: panic message");
|
|
|
|
}
|
|
|
|
|
2025-06-07 21:24:59 +01:00
|
|
|
struct LogoData {
|
|
|
|
name: OsString,
|
|
|
|
path: PathBuf,
|
|
|
|
}
|
|
|
|
fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec<LogoData> {
|
2025-06-07 00:10:52 +01:00
|
|
|
let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory);
|
2025-06-07 01:05:08 +01:00
|
|
|
let folder_path = PathBuf::from(&folder);
|
2025-06-07 22:44:47 +01:00
|
|
|
let mut folder_output = folder_path.clone();
|
|
|
|
folder_output.push("converted");
|
2025-06-07 00:10:52 +01:00
|
|
|
let paths = fs::read_dir(folder).unwrap();
|
2025-06-07 01:05:08 +01:00
|
|
|
|
|
|
|
let args = Args{
|
|
|
|
input: folder_path.clone(),
|
2025-06-07 22:44:47 +01:00
|
|
|
output: folder_output,
|
2025-06-07 01:05:08 +01:00
|
|
|
colors: String::from(""),
|
|
|
|
width: 1024,
|
|
|
|
height: 1024,
|
|
|
|
};
|
|
|
|
let mut r = Renderer::new(&args).unwrap();
|
|
|
|
|
2025-06-07 19:55:36 +01:00
|
|
|
let mut logos = vec![];
|
|
|
|
|
|
|
|
for tmp in paths.flatten() {
|
|
|
|
let path_local = tmp.path().to_owned();
|
|
|
|
let path_local2 = tmp.path().to_owned();
|
2025-06-07 21:24:59 +01:00
|
|
|
let name = path_local2.file_name().unwrap().to_owned();
|
|
|
|
let mut path = tmp.path();
|
2025-06-07 22:44:47 +01:00
|
|
|
|
|
|
|
if path.is_dir() {
|
|
|
|
continue;
|
|
|
|
}
|
2025-06-07 19:55:36 +01:00
|
|
|
|
2025-06-07 01:05:08 +01:00
|
|
|
match tmp.path().extension() {
|
|
|
|
None => {}
|
|
|
|
Some(ext) => {
|
|
|
|
if ext == "svg" {
|
|
|
|
let mut path_new = path_local.clone();
|
|
|
|
path_new.set_extension("png");
|
2025-06-07 22:44:47 +01:00
|
|
|
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);
|
2025-06-07 01:05:08 +01:00
|
|
|
|
|
|
|
// check if exists
|
|
|
|
match r.render(&path_local, &args) {
|
|
|
|
Ok(_) => log::info!("Successfully rendered all colors of {path_local:?}"),
|
|
|
|
Err(e) => {
|
|
|
|
log::error!("Failed to render {path_local:?}: {}", e)
|
|
|
|
}
|
|
|
|
}
|
2025-06-07 21:24:59 +01:00
|
|
|
path = path_new;
|
2025-06-07 01:05:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-06-07 21:24:59 +01:00
|
|
|
logos.push(LogoData{
|
|
|
|
name,
|
|
|
|
path,
|
|
|
|
});
|
2025-06-07 19:55:36 +01:00
|
|
|
|
2025-06-07 00:10:52 +01:00
|
|
|
println!("Name: {}", &tmp.path().display());
|
|
|
|
}
|
2025-06-07 19:55:36 +01:00
|
|
|
|
|
|
|
logos
|
2025-06-07 00:10:52 +01:00
|
|
|
}
|