use std::collections::HashMap; use std::fmt::Debug; use std::sync::LazyLock; use std::sync::RwLock; use std::time::Duration; use serenity::all::Context; use serenity::all::GuildId; use serenity::all::Message; use serenity::all::Permissions; use crate::arguments::ArgumentStorage; use crate::constants::CAT_HID; pub static COMMANDS: LazyLock>> = LazyLock::new(|| RwLock::new(HashMap::new())); #[derive(Debug)] pub enum CommandFnKind { Lua(()), Rust(fn(Context, Message, Option)), } #[derive(Debug)] pub struct BotCommand { pub run_dm_command: Option, pub run_guild_command: Option, pub aliases: Vec, pub pretty_name: Option, pub name: String, pub command_category: Option, pub help: Option, // pub usage: String, // will be calculated from arguments automatically pub timeout: Option, // TODO make this dynamic? pub hidden: bool, pub arguments: ArgumentStorage, pub required_caller_discord_permissions: serenity::all::Permissions, #[cfg(feature = "nsfw_features")] pub is_nsfw: bool, #[cfg(feature = "premium_features")] pub premium_kind: usize, } impl BotCommand { pub fn new(name: String) -> Self { Self { run_dm_command: None, run_guild_command: None, aliases: vec![], pretty_name: None, name, hidden: false, command_category: None, help: None, timeout: None, arguments: ArgumentStorage::new(), required_caller_discord_permissions: Permissions::empty(), #[cfg(feature = "nsfw_features")] is_nsfw: false, #[cfg(feature = "premium_features")] premium_kind: 0, } } pub fn dm_command(mut self, dm_command: CommandFnKind) -> Self { self.run_dm_command = Some(dm_command); self } pub fn guild_command(mut self, guild_command: CommandFnKind) -> Self { self.run_guild_command = Some(guild_command); self } pub fn pretty_name(mut self, pretty_name: String) -> Self { self.pretty_name = Some(pretty_name); self } // pub fn name(mut self, name: String) -> Self { // self.name = name; // self // } pub fn alias(mut self, alias: String) -> Self { self.aliases.push(alias); self } pub fn aliases(mut self, aliases: &mut Vec) -> Self { self.aliases.append(aliases); self } pub fn category(mut self, category: String) -> Self { self.command_category = Some(category); self } pub fn help(mut self, help: String) -> Self { self.help = Some(help); self } pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = Some(timeout); self } // pub fn argument(mut self, argument: Argument) -> Self { // self.arguments.add(argument); // self // } pub fn hidden(mut self, hidden: bool) -> Self { self.hidden = hidden; self } } unsafe impl Sync for BotCommand {} unsafe impl Send for BotCommand {} //impl Debug for BotCommand { // fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result { // let mut binding = f.debug_struct("Command"); // binding // .field( // "run_dm_command", // &::std::any::type_name_of_val(&self.run_dm_command), // ) // .field( // "run_guild_command", // &::std::any::type_name_of_val(&self.run_guild_command), // ) // .field("aliases", &self.aliases) // .field("name", &self.name) // .field("pretty_name", &self.pretty_name) // .field("command_type", &self.command_category) // .field("help", &self.help) // .field("hidden", &self.hidden) // // .field("usage", &self.usage) // .field("timeout", &self.timeout) // .field("arguments", &self.arguments) // .field("permissions", &self.required_caller_discord_permissions); // #[cfg(feature = "nsfw_features")] // binding.field("is_nsfw", &self.is_nsfw); // #[cfg(feature = "premium_features")] // binding.field("premium_kind", &self.premium_kind); // binding.finish() // } //} // impl Default for Command { // fn default() -> Self { // } // } /// The last set of commands, these are used by the hoster of the engine. pub fn insert_lua(_lua: &mlua::Lua) { // commands.insert( // "stop".to_owned(), // Command::new("stop".to_owned()) // .alias("svs".to_owned()) // .category(CAT_HID.to_owned()) // .hidden(true) // .dm_command(CommandFnKind::Rust(stop_command)) // .guild_command(CommandFnKind::Rust(stop_command)) // .pretty_name("Stop the bot".to_owned()) // .help("Stops the bot. Does nothing unless you are a developer.".to_owned()), // ); } /// Cannot use any command names of stock commands, but gets to pick before lua commands are loaded. pub fn insert_rust() { // COMMANDS.blocking_write().insert( // "stop2".to_owned(), // BotCommand::new("stop".to_owned()) // .alias("svs".to_owned()) // .category(CAT_HID.to_owned()) // .hidden(true) // .dm_command(CommandFnKind::Rust(stop_command)) // .guild_command(CommandFnKind::Rust(stop_command)) // .pretty_name("Stop the bot".to_owned()) // .help("Stops the bot. Does nothing unless you are a developer.".to_owned()), // ); } /// Will never fail, gets first pick of command names and properties. It is up to the maintainer to make damn sure this works. pub fn insert_stock() { let shutdown_command = |_, msg: Message, _| { // hardcode my id for now if crate::databases::get_db().is_dev(msg.author.id) { return; } crate::shutdown_handler(); }; // TODO fix this unwrap COMMANDS.write().unwrap().insert( "stop".to_owned(), BotCommand::new("stop".to_owned()) .alias("svs".to_owned()) .category(CAT_HID.to_owned()) .hidden(true) .dm_command(CommandFnKind::Rust(shutdown_command)) .guild_command(CommandFnKind::Rust(shutdown_command)) .pretty_name("Stop the bot".to_owned()) .help("Stops the bot. Does nothing unless you are a developer.".to_owned()), ); }