From 8f8235a86e7d9ed84d06e70e36035a338b37cdaa Mon Sep 17 00:00:00 2001 From: jude Date: Mon, 12 Sep 2022 16:45:00 +0100 Subject: [PATCH] Move macro commands to own module Lots of code here --- src/commands/command_macro/delete.rs | 46 ++ src/commands/command_macro/list.rs | 127 ++++++ src/commands/command_macro/migrate.rs | 229 ++++++++++ src/commands/command_macro/mod.rs | 19 + src/commands/command_macro/record.rs | 139 ++++++ src/commands/command_macro/run.rs | 46 ++ src/commands/mod.rs | 1 + src/commands/moderation_cmds.rs | 596 +------------------------- src/component_models/mod.rs | 2 +- src/main.rs | 16 +- 10 files changed, 618 insertions(+), 603 deletions(-) create mode 100644 src/commands/command_macro/delete.rs create mode 100644 src/commands/command_macro/list.rs create mode 100644 src/commands/command_macro/migrate.rs create mode 100644 src/commands/command_macro/mod.rs create mode 100644 src/commands/command_macro/record.rs create mode 100644 src/commands/command_macro/run.rs diff --git a/src/commands/command_macro/delete.rs b/src/commands/command_macro/delete.rs new file mode 100644 index 0000000..ebc9acc --- /dev/null +++ b/src/commands/command_macro/delete.rs @@ -0,0 +1,46 @@ +use super::super::autocomplete::macro_name_autocomplete; +use crate::{Context, Error}; + +/// Delete a recorded macro +#[poise::command( + slash_command, + rename = "delete", + guild_only = true, + default_member_permissions = "MANAGE_GUILD", + identifying_name = "delete_macro" +)] +pub async fn delete_macro( + ctx: Context<'_>, + #[description = "Name of macro to delete"] + #[autocomplete = "macro_name_autocomplete"] + name: String, +) -> Result<(), Error> { + match sqlx::query!( + " +SELECT id FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?", + ctx.guild_id().unwrap().0, + name + ) + .fetch_one(&ctx.data().database) + .await + { + Ok(row) => { + sqlx::query!("DELETE FROM macro WHERE id = ?", row.id) + .execute(&ctx.data().database) + .await + .unwrap(); + + ctx.say(format!("Macro \"{}\" deleted", name)).await?; + } + + Err(sqlx::Error::RowNotFound) => { + ctx.say(format!("Macro \"{}\" not found", name)).await?; + } + + Err(e) => { + panic!("{}", e); + } + } + + Ok(()) +} diff --git a/src/commands/command_macro/list.rs b/src/commands/command_macro/list.rs new file mode 100644 index 0000000..efb894a --- /dev/null +++ b/src/commands/command_macro/list.rs @@ -0,0 +1,127 @@ +use poise::CreateReply; + +use crate::{ + component_models::pager::{MacroPager, Pager}, + consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR}, + models::{command_macro::CommandMacro, CtxData}, + Context, Error, +}; + +/// List recorded macros +#[poise::command( + slash_command, + rename = "list", + guild_only = true, + default_member_permissions = "MANAGE_GUILD", + identifying_name = "list_macro" +)] +pub async fn list_macro(ctx: Context<'_>) -> Result<(), Error> { + let macros = ctx.command_macros().await?; + + let resp = show_macro_page(¯os, 0); + + ctx.send(|m| { + *m = resp; + m + }) + .await?; + + Ok(()) +} + +pub fn max_macro_page(macros: &[CommandMacro]) -> usize { + let mut skipped_char_count = 0; + + macros + .iter() + .map(|m| { + if let Some(description) = &m.description { + format!("**{}**\n- *{}*\n- Has {} commands", m.name, description, m.commands.len()) + } else { + format!("**{}**\n- Has {} commands", m.name, m.commands.len()) + } + }) + .fold(1, |mut pages, p| { + skipped_char_count += p.len(); + + if skipped_char_count > EMBED_DESCRIPTION_MAX_LENGTH { + skipped_char_count = p.len(); + pages += 1; + } + + pages + }) +} + +pub fn show_macro_page(macros: &[CommandMacro], page: usize) -> CreateReply { + let pager = MacroPager::new(page); + + if macros.is_empty() { + let mut reply = CreateReply::default(); + + reply.embed(|e| { + e.title("Macros") + .description("No Macros Set Up. Use `/macro record` to get started.") + .color(*THEME_COLOR) + }); + + return reply; + } + + let pages = max_macro_page(macros); + + let mut page = page; + if page >= pages { + page = pages - 1; + } + + let mut char_count = 0; + let mut skipped_char_count = 0; + + let mut skipped_pages = 0; + + let display_vec: Vec = macros + .iter() + .map(|m| { + if let Some(description) = &m.description { + format!("**{}**\n- *{}*\n- Has {} commands", m.name, description, m.commands.len()) + } else { + format!("**{}**\n- Has {} commands", m.name, m.commands.len()) + } + }) + .skip_while(|p| { + skipped_char_count += p.len(); + + if skipped_char_count > EMBED_DESCRIPTION_MAX_LENGTH { + skipped_char_count = p.len(); + skipped_pages += 1; + } + + skipped_pages < page + }) + .take_while(|p| { + char_count += p.len(); + + char_count < EMBED_DESCRIPTION_MAX_LENGTH + }) + .collect::>(); + + let display = display_vec.join("\n"); + + let mut reply = CreateReply::default(); + + reply + .embed(|e| { + e.title("Macros") + .description(display) + .footer(|f| f.text(format!("Page {} of {}", page + 1, pages))) + .color(*THEME_COLOR) + }) + .components(|comp| { + pager.create_button_row(pages, comp); + + comp + }); + + reply +} diff --git a/src/commands/command_macro/migrate.rs b/src/commands/command_macro/migrate.rs new file mode 100644 index 0000000..6a5bc24 --- /dev/null +++ b/src/commands/command_macro/migrate.rs @@ -0,0 +1,229 @@ +use lazy_regex::regex; +use poise::serenity_prelude::command::CommandOptionType; +use regex::Captures; +use serde_json::{json, Value}; + +use crate::{models::command_macro::RawCommandMacro, Context, Error, GuildId}; + +struct Alias { + name: String, + command: String, +} + +/// Migrate old $alias reminder commands to macros. Only macro names that are not taken will be used. +#[poise::command( + slash_command, + rename = "migrate", + guild_only = true, + default_member_permissions = "MANAGE_GUILD", + identifying_name = "migrate_macro" +)] +pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> { + let guild_id = ctx.guild_id().unwrap(); + let mut transaction = ctx.data().database.begin().await?; + + let aliases = sqlx::query_as!( + Alias, + "SELECT name, command FROM command_aliases WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)", + guild_id.0 + ) + .fetch_all(&mut transaction) + .await?; + + let mut added_aliases = 0; + + for alias in aliases { + match parse_text_command(guild_id, alias.name, &alias.command) { + Some(cmd_macro) => { + sqlx::query!( + "INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)", + cmd_macro.guild_id.0, + cmd_macro.name, + cmd_macro.description, + cmd_macro.commands + ) + .execute(&mut transaction) + .await?; + + added_aliases += 1; + } + + None => {} + } + } + + transaction.commit().await?; + + ctx.send(|b| b.content(format!("Added {} macros.", added_aliases))).await?; + + Ok(()) +} + +fn parse_text_command( + guild_id: GuildId, + alias_name: String, + command: &str, +) -> Option { + match command.split_once(" ") { + Some((command_word, args)) => { + let command_word = command_word.to_lowercase(); + + if command_word == "r" + || command_word == "i" + || command_word == "remind" + || command_word == "interval" + { + let matcher = regex!( + r#"(?P(?:<@\d+>\s+|<@!\d+>\s+|<#\d+>\s+)*)(?P