diff --git a/src/commands/committee.rs b/src/commands/committee.rs index 4813c6a..944fbc0 100644 --- a/src/commands/committee.rs +++ b/src/commands/committee.rs @@ -20,4 +20,8 @@ pub fn register() -> CreateCommand { .add_sub_option(CreateCommandOption::new(CommandOptionType::Role, "role_c", "Sum of A and B").required(true)) .add_sub_option(CreateCommandOption::new(CommandOptionType::Boolean, "delete", "Delete this entry.").required(false)), ) + .add_option( + CreateCommandOption::new(CommandOptionType::SubCommandGroup, "icon", "Committee commands for the server icon") + .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "change", "Change the server icon.")), + ) } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index b513fc2..5815541 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -3,4 +3,5 @@ pub mod committee; pub mod count; pub mod minecraft; pub mod role_adder; +pub mod server_icon; pub mod wolves; diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs new file mode 100644 index 0000000..a6114c5 --- /dev/null +++ b/src/commands/server_icon.rs @@ -0,0 +1,208 @@ +use serenity::all::{CommandInteraction, Context}; +use skynet_discord_bot::{ + common::{ + database::DataBase, + server_icon::{get_config_icons, update_icon::update_icon_main, ServerIcons}, + }, + Config, +}; + +use serenity::all::{CommandOptionType, CreateCommand, CreateCommandOption}; + +// commands that server mods are able to use +pub(crate) mod admin { + use super::*; + + // Moderators can force a icon change + pub(crate) mod change { + use super::*; + + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Databse 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::minimal(); + update_icon_main(ctx, &db, &config_global, &config_toml).await; + + "Changed server Icon".to_string() + } + } +} + +// commands for general users +pub(crate) mod user { + use super::*; + use skynet_discord_bot::common::server_icon::get_config_icons::ConfigTomlLocal; + + pub fn register() -> CreateCommand { + CreateCommand::new("icon") + .description("Commands related to the Server Icon") + .add_option( + CreateCommandOption::new(CommandOptionType::SubCommandGroup, "current", "Information on current items.") + .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "icon", "Information on current icon.")) + .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "festival", "Information on current festival.")), + ) + .add_option(CreateCommandOption::new(CommandOptionType::SubCommand, "stats", "Some Stats.")) + } + + fn get_logo_url(config_toml: &ConfigTomlLocal, logo_name: &str) -> String { + format!("{}/src/branch/main/{}/{}", &config_toml.source.repo, &config_toml.source.directory, logo_name) + } + + /// Regular users can get teh link to teh current icon + pub(crate) mod current { + use super::*; + + pub(crate) mod icon { + use super::*; + + use sqlx::{Pool, Sqlite}; + + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Databse in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_toml = get_config_icons::minimal(); + + if let Some(logo) = get_current_icon(&db).await { + get_logo_url(&config_toml, &logo.name) + } else { + "Could not find current icon".to_string() + } + } + + async fn get_current_icon(db: &Pool) -> Option { + match sqlx::query_as::<_, ServerIcons>( + " + SELECT * from server_icons ORDER BY id DESC LIMIT 1 + ", + ) + .fetch_one(db) + .await + { + Ok(res) => Some(res), + Err(e) => { + dbg!(e); + None + } + } + } + } + + pub(crate) mod festival { + use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption}; + use skynet_discord_bot::common::server_icon::get_config_icons; + use skynet_discord_bot::Config; + + // use this to return what current festivals are active? + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + 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; + + let config_toml = get_config_icons::full(&config); + + let mut response = vec![]; + + for festival in &config_toml.festivals { + response.push(festival.name.to_owned()); + } + + format!("Festivals active: {}", response.join(", ")) + } + } + } + + /// Get the statistics of the icons + pub(crate) mod stats { + use super::*; + use sqlx::{Pool, Sqlite}; + + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Databse in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_toml = get_config_icons::minimal(); + + let totals = get_totals(&db).await; + + fmt_msg(&config_toml, &totals) + } + + #[derive(Debug, Clone, sqlx::FromRow)] + pub struct CountResult { + pub name: String, + pub times: i64, + } + + async fn get_totals(db: &Pool) -> Vec { + sqlx::query_as::<_, CountResult>( + " + SELECT + DISTINCT name, + COUNT(*) OVER(PARTITION BY name) AS times + FROM server_icons + ", + ) + .fetch_all(db) + .await + .unwrap_or_else(|e| { + dbg!(e); + vec![] + }) + } + + fn fmt_msg(config_toml: &ConfigTomlLocal, totals: &Vec) -> String { + // msg can be a max 2000 chars long + let mut limit = 2000 - 3; + + let mut response = vec![]; + for CountResult { + name, + times, + } in totals + { + let current_leading = if times < &10 { + "00" + } else if times < &100 { + "0" + } else { + "" + }; + + let url = get_logo_url(config_toml, name); + + let line = format!("{}{} <{}>", current_leading, times, url); + + let length = line.len() + 1; + + // +3 is to account for the closing fense + if length < (limit + 3) { + response.push(line); + limit -= length; + } else { + break; + } + } + + response.join("\n") + } + } +} diff --git a/src/main.rs b/src/main.rs index 6d5e849..3b57470 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; -use serenity::all::{ActivityData, Command, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, Interaction}; +use serenity::all::{ + ActivityData, Command, CommandDataOption, CommandDataOptionValue, CommandOptionType, CreateMessage, EditInteractionResponse, GuildId, + GuildMemberUpdateEvent, Interaction, +}; use serenity::model::guild::Member; use serenity::{ async_trait, @@ -129,6 +132,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } } + // Inter-Committee server match GuildId::new(1220150752656363520) .set_commands(&ctx.http, vec![commands::count::committee::register()]) .await @@ -139,8 +143,16 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } } + // compsoc Server match GuildId::new(689189992417067052) - .set_commands(&ctx.http, vec![commands::count::servers::register()]) + .set_commands( + &ctx.http, + vec![ + // commands just for the compsoc server + commands::count::servers::register(), + commands::server_icon::user::register(), + ], + ) .await { Ok(_) => {} @@ -175,6 +187,18 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use Some(x) => match x.name.as_str() { "add" => commands::add_server::run(&command, &ctx).await, "roles_adder" => commands::role_adder::edit::run(&command, &ctx).await, + "icon" => match &x.value { + CommandDataOptionValue::SubCommandGroup(y) => match y.first() { + None => "error".to_string(), + Some(z) => match z.name.as_str() { + "change" => commands::server_icon::admin::change::run(&command, &ctx).await, + &_ => format!("not implemented :( count {}", x.name.as_str()), + }, + }, + _ => { + format!("not implemented :( committee {}", x.name.as_str()) + } + }, // "link" => commands::count::servers::run(&command, &ctx).await, &_ => format!("not implemented :( committee {}", x.name.as_str()), }, @@ -191,6 +215,29 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use &_ => format!("not implemented :( count {}", x.name.as_str()), }, }, + + "icon" => match command.data.options.first() { + None => "Invalid Command".to_string(), + Some(x) => match x.name.as_str() { + "current" => { + let result = match &x.value { + CommandDataOptionValue::SubCommandGroup(y) => match y.first() { + None => "error".to_string(), + Some(z) => match z.name.as_str() { + "icon" => commands::server_icon::user::current::icon::run(&command, &ctx).await, + "festival" => commands::server_icon::user::current::festival::run(&command, &ctx).await, + &_ => format!("not implemented :( count {}", x.name.as_str()), + }, + }, + &_ => format!("not implemented :( {}", command.data.name.as_str()), + }; + + result + } + "stats" => commands::server_icon::user::stats::run(&command, &ctx).await, + &_ => format!("not implemented :( count {}", x.name.as_str()), + }, + }, _ => format!("not implemented :( {}", command.data.name.as_str()), };