diff --git a/src/commands/moderation_cmds.rs b/src/commands/moderation_cmds.rs index d0893c4..62fa0c0 100644 --- a/src/commands/moderation_cmds.rs +++ b/src/commands/moderation_cmds.rs @@ -6,8 +6,11 @@ use poise::CreateReply; use crate::{ consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR}, hooks::guild_only, - models::{command_macro::CommandMacro, CtxData}, - Context, Error, + models::{ + command_macro::{CommandMacro, CommandOptions}, + CtxData, + }, + Context, Data, Error, }; async fn timezone_autocomplete(ctx: Context<'_>, partial: String) -> Vec { @@ -303,6 +306,26 @@ pub async fn list_macro(ctx: Context<'_>) -> Result<(), Error> { Ok(()) } +fn find_command<'a>( + commands: &'a [poise::Command], + searching_name: &str, + command_options: &CommandOptions, +) -> Option<&'a poise::Command> { + commands.iter().find_map(|cmd| { + if searching_name != cmd.name { + None + } else { + if let Some(subgroup) = &command_options.subcommand_group { + find_command(&cmd.subcommands, &subgroup, &command_options) + } else if let Some(subcommand) = &command_options.subcommand { + find_command(&cmd.subcommands, &subcommand, &command_options) + } else { + Some(cmd) + } + } + }) +} + /// Run a recorded macro #[poise::command(slash_command, rename = "run", check = "guild_only")] pub async fn run_macro( @@ -323,7 +346,24 @@ SELECT commands FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = Ok(row) => { ctx.defer().await?; - // TODO TODO TODO!!!!!!!! RUN COMMAND FROM MACRO + let commands: Vec = serde_json::from_str(&row.commands)?; + + for command in commands { + let cmd = + find_command(&ctx.framework().options().commands, &command.command, &command); + + if let Some(cmd) = cmd { + let mut executing_ctx = ctx.clone(); + + executing_ctx.command = cmd; + } else { + ctx.send(|m| { + m.ephemeral(true) + .content(format!("Command `{}` not found", command.command)) + }) + .await?; + } + } } Err(sqlx::Error::RowNotFound) => { diff --git a/src/hooks.rs b/src/hooks.rs index 0fa08a5..ab8d058 100644 --- a/src/hooks.rs +++ b/src/hooks.rs @@ -24,7 +24,7 @@ async fn macro_check(ctx: Context<'_>) -> bool { if let Some(command_macro) = lock.get_mut(&(guild_id, ctx.author().id)) { if command_macro.commands.len() >= MACRO_MAX_COMMANDS { let _ = ctx.send(|m| { - m.ephemeral(false).content( + m.ephemeral(true).content( "5 commands already recorded. Please use `/macro finish` to end recording.", ) }) @@ -36,7 +36,7 @@ async fn macro_check(ctx: Context<'_>) -> bool { command_macro.commands.push(command_options); let _ = ctx - .send(|m| m.ephemeral(false).content("Command recorded to macro")) + .send(|m| m.ephemeral(true).content("Command recorded to macro")) .await; } diff --git a/src/models/command_macro.rs b/src/models/command_macro.rs index cc1565a..8544e5f 100644 --- a/src/models/command_macro.rs +++ b/src/models/command_macro.rs @@ -1,13 +1,21 @@ use std::collections::HashMap; -use poise::serenity::model::{ - id::{ChannelId, GuildId, RoleId, UserId}, - interactions::application_command::{ - ApplicationCommandInteraction, ApplicationCommandInteractionDataOption, - ApplicationCommandOptionType, +use poise::{ + serenity::{ + json::Value, + model::{ + id::{ChannelId, GuildId, RoleId, UserId}, + interactions::application_command::{ + ApplicationCommandInteraction, ApplicationCommandInteractionData, + ApplicationCommandInteractionDataOption, ApplicationCommandOptionType, + ApplicationCommandType, + }, + }, }, + ApplicationCommandOrAutocompleteInteraction, }; use serde::{Deserialize, Serialize}; +use serde_json::Number; use sqlx::Executor; use crate::Database; @@ -90,6 +98,32 @@ impl OptionValue { OptionValue::Number(n) => n.to_string(), } } + + fn as_value(&self) -> Value { + match self { + OptionValue::String(s) => Value::String(s.to_string()), + OptionValue::Integer(i) => Value::Number(i.to_owned().into()), + OptionValue::Boolean(b) => Value::Bool(b.to_owned()), + OptionValue::User(u) => Value::String(u.to_string()), + OptionValue::Channel(c) => Value::String(c.to_string()), + OptionValue::Role(r) => Value::String(r.to_string()), + OptionValue::Mentionable(m) => Value::String(m.to_string()), + OptionValue::Number(n) => Value::Number(Number::from_f64(n.to_owned()).unwrap()), + } + } + + fn kind(&self) -> ApplicationCommandOptionType { + match self { + OptionValue::String(_) => ApplicationCommandOptionType::String, + OptionValue::Integer(_) => ApplicationCommandOptionType::Integer, + OptionValue::Boolean(_) => ApplicationCommandOptionType::Boolean, + OptionValue::User(_) => ApplicationCommandOptionType::User, + OptionValue::Channel(_) => ApplicationCommandOptionType::Channel, + OptionValue::Role(_) => ApplicationCommandOptionType::Role, + OptionValue::Mentionable(_) => ApplicationCommandOptionType::Mentionable, + OptionValue::Number(_) => ApplicationCommandOptionType::Number, + } + } } #[derive(Serialize, Deserialize, Clone)] @@ -100,6 +134,27 @@ pub struct CommandOptions { pub options: HashMap, } +impl Into for CommandOptions { + fn into(self) -> ApplicationCommandInteractionData { + ApplicationCommandInteractionData { + name: self.command, + kind: ApplicationCommandType::ChatInput, + options: self + .options + .iter() + .map(|(name, value)| ApplicationCommandInteractionDataOption { + name: name.to_string(), + value: Some(value.as_value()), + kind: value.kind(), + options: vec![], + ..Default::default() + }) + .collect(), + ..Default::default() + } + } +} + impl CommandOptions { pub fn new(command: impl ToString) -> Self { Self {