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", "How many times the particular icon has been used")) } 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 serenity::all::{CreateAttachment, EditInteractionResponse}; 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 { if let Ok(attachment) = CreateAttachment::path(&logo.path).await { match command.edit_response(&ctx.http, EditInteractionResponse::new().new_attachment(attachment)).await { Ok(_) => {} Err(e) => { dbg!(e); } } } format!("[{}]({})", &logo.name, 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, Context}; use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon::get_festival}; 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 response = get_festival(&config_toml).current; if response.is_empty() { "No festival currently active".to_string() } else { 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: &[CountResult]) -> String { let mut totals_local = totals.to_owned(); totals_local.sort_by_key(|x| x.times); totals_local.reverse(); // msg can be a max 2000 chars long let mut limit = 2000 - 3; let mut response = vec![]; for CountResult { name, times, } in &totals_local { let current_leading = if times < &10 { "00" } else if times < &100 { "0" } else { "" }; let url = get_logo_url(config_toml, name); // the `` is so that the numbers will be rendered in monospaced font // the <> is to suppress the URL embed let line = format!("``{}{}`` [{}](<{}>)", current_leading, times, name, 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") } } }