diff --git a/Cargo.lock b/Cargo.lock index 053fbbb..145c9da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,7 +177,6 @@ dependencies = [ [[package]] name = "command_attr" version = "0.3.7" -source = "git+https://github.com/serenity-rs/serenity?branch=next#40f3247f0012b0aa944dcf0132cc763f557707d4" dependencies = [ "proc-macro2", "quote", @@ -1453,7 +1452,6 @@ dependencies = [ [[package]] name = "serenity" version = "0.10.9" -source = "git+https://github.com/serenity-rs/serenity?branch=next#40f3247f0012b0aa944dcf0132cc763f557707d4" dependencies = [ "async-trait", "async-tungstenite", diff --git a/Cargo.toml b/Cargo.toml index 3a7b0ac..4d08e9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ serde_repr = "0.1" rmp-serde = "0.15" rand = "0.7" levenshtein = "1.0" -serenity = { git = "https://github.com/serenity-rs/serenity", branch = "next", features = ["collector", "unstable_discord_api"] } +serenity = { path = "/home/jude/serenity", features = ["collector", "unstable_discord_api"] } +# serenity = { git = "https://github.com/serenity-rs/serenity", branch = "next", features = ["collector", "unstable_discord_api"] } sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]} base64 = "0.13.0" diff --git a/migration/02-macro.sql b/migration/02-macro.sql index 1018f97..9a99d2c 100644 --- a/migration/02-macro.sql +++ b/migration/02-macro.sql @@ -1,11 +1,11 @@ CREATE TABLE macro ( id INT UNSIGNED AUTO_INCREMENT, - guild_id INT UNSIGNED NOT NULL, + guild_id BIGINT UNSIGNED NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(100), - commands TEXT, + commands TEXT NOT NULL, - FOREIGN KEY (guild_id) REFERENCES guilds(id), + FOREIGN KEY (guild_id) REFERENCES guilds(guild) ON DELETE CASCADE, PRIMARY KEY (id) ); diff --git a/src/commands/info_cmds.rs b/src/commands/info_cmds.rs index 9388d65..7aa0410 100644 --- a/src/commands/info_cmds.rs +++ b/src/commands/info_cmds.rs @@ -27,7 +27,7 @@ fn footer(ctx: &Context) -> impl FnOnce(&mut CreateEmbedFooter) -> &mut CreateEm #[aliases("invite")] #[description("Get information about the bot")] #[group("Info")] -async fn info(ctx: &Context, invoke: CommandInvoke) { +async fn info(ctx: &Context, invoke: &mut CommandInvoke) { let prefix = ctx.prefix(invoke.guild_id()).await; let current_user = ctx.cache.current_user(); let footer = footer(ctx); @@ -61,7 +61,7 @@ Use our dashboard: https://reminder-bot.com/", #[command] #[description("Details on supporting the bot and Patreon benefits")] #[group("Info")] -async fn donate(ctx: &Context, invoke: CommandInvoke) { +async fn donate(ctx: &Context, invoke: &mut CommandInvoke) { let footer = footer(ctx); let _ = invoke @@ -94,7 +94,7 @@ Just $2 USD/month! #[command] #[description("Get the link to the online dashboard")] #[group("Info")] -async fn dashboard(ctx: &Context, invoke: CommandInvoke) { +async fn dashboard(ctx: &Context, invoke: &mut CommandInvoke) { let footer = footer(ctx); let _ = invoke @@ -113,7 +113,7 @@ async fn dashboard(ctx: &Context, invoke: CommandInvoke) { #[command] #[description("View the current time in your selected timezone")] #[group("Info")] -async fn clock(ctx: &Context, invoke: CommandInvoke) { +async fn clock(ctx: &Context, invoke: &mut CommandInvoke) { let ud = ctx.user_data(&invoke.author_id()).await.unwrap(); let now = Utc::now().with_timezone(&ud.timezone()); diff --git a/src/commands/moderation_cmds.rs b/src/commands/moderation_cmds.rs index 7ea13d8..e56dcf4 100644 --- a/src/commands/moderation_cmds.rs +++ b/src/commands/moderation_cmds.rs @@ -2,18 +2,10 @@ use chrono::offset::Utc; use chrono_tz::{Tz, TZ_VARIANTS}; use levenshtein::levenshtein; use regex_command_attr::command; -use serenity::{ - client::Context, - http::AttachmentType, - model::{ - interactions::InteractionResponseType, misc::Mentionable, - prelude::InteractionApplicationCommandCallbackDataFlags, - }, -}; +use serenity::{client::Context, model::misc::Mentionable}; use crate::{ component_models::{ComponentDataModel, Restrict}, - consts, consts::THEME_COLOR, framework::{CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue}, hooks::{CHECK_GUILD_PERMISSIONS_HOOK, CHECK_MANAGED_PERMISSIONS_HOOK}, @@ -32,7 +24,7 @@ use crate::{ #[supports_dm(false)] #[hook(CHECK_GUILD_PERMISSIONS_HOOK)] #[can_blacklist(false)] -async fn blacklist(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn blacklist(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let channel = match args.get("channel") { @@ -75,7 +67,7 @@ async fn blacklist(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { kind = "String", required = false )] -async fn timezone(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn timezone(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let mut user_data = ctx.user_data(invoke.author_id()).await.unwrap(); @@ -182,7 +174,7 @@ You may want to use one of the popular timezones below, otherwise click [here](h #[description("Configure a prefix for text-based commands (deprecated)")] #[supports_dm(false)] #[hook(CHECK_GUILD_PERMISSIONS_HOOK)] -async fn prefix(ctx: &Context, invoke: CommandInvoke, args: String) { +async fn prefix(ctx: &Context, invoke: &mut CommandInvoke, args: String) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let guild_data = ctx.guild_data(invoke.guild_id().unwrap()).await.unwrap(); @@ -226,7 +218,7 @@ async fn prefix(ctx: &Context, invoke: CommandInvoke, args: String) { )] #[supports_dm(false)] #[hook(CHECK_GUILD_PERMISSIONS_HOOK)] -async fn restrict(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn restrict(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let framework = ctx.data.read().await.get::().cloned().unwrap(); @@ -310,10 +302,13 @@ async fn restrict(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { #[subcommand("run")] #[description("Run a recorded macro")] #[arg(name = "name", description = "Name of the macro to run", kind = "String", required = true)] +#[subcommand("delete")] +#[description("Delete a recorded macro")] +#[arg(name = "name", description = "Name of the macro to delete", kind = "String", required = true)] #[supports_dm(false)] #[hook(CHECK_MANAGED_PERMISSIONS_HOOK)] -async fn macro_cmd(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { - let interaction = invoke.interaction().unwrap(); +async fn macro_cmd(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { + let pool = ctx.data.read().await.get::().cloned().unwrap(); match args.subcommand.clone().unwrap().as_str() { "record" => { @@ -322,10 +317,10 @@ async fn macro_cmd(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { { let mut lock = macro_buffer.write().await; - let guild_id = interaction.guild_id.unwrap(); + let guild_id = invoke.guild_id().unwrap(); lock.insert( - (guild_id, interaction.user.id), + (guild_id, invoke.author_id()), CommandMacro { guild_id, name: args.get("name").unwrap().to_string(), @@ -335,25 +330,22 @@ async fn macro_cmd(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { ); } - let _ = interaction - .create_interaction_response(&ctx, |r| { - r.kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|d| { - d.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) - .create_embed(|e| { - e + let _ = invoke + .respond( + &ctx, + CreateGenericResponse::new().ephemeral().embed(|e| { + e .title("Macro Recording Started") .description( "Run up to 5 commands, or type `/macro finish` to stop at any point. Any commands ran as part of recording will be inconsequential") .color(*THEME_COLOR) - }) - }) - }) + }), + ) .await; } "finish" => { - let key = (interaction.guild_id.unwrap(), interaction.user.id); + let key = (invoke.guild_id().unwrap(), invoke.author_id()); let macro_buffer = ctx.data.read().await.get::().cloned().unwrap(); { @@ -361,28 +353,22 @@ Any commands ran as part of recording will be inconsequential") let contained = lock.get(&key); if contained.map_or(true, |cmacro| cmacro.commands.is_empty()) { - let _ = interaction - .create_interaction_response(&ctx, |r| { - r.kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|d| { - d.create_embed(|e| { - e.title("No Macro Recorded") - .description( - "Use `/macro record` to start recording a macro", - ) - .color(*THEME_COLOR) - }) - }) - }) + let _ = invoke + .respond( + &ctx, + CreateGenericResponse::new().embed(|e| { + e.title("No Macro Recorded") + .description("Use `/macro record` to start recording a macro") + .color(*THEME_COLOR) + }), + ) .await; } else { - let pool = ctx.data.read().await.get::().cloned().unwrap(); - let command_macro = contained.unwrap(); let json = serde_json::to_string(&command_macro.commands).unwrap(); sqlx::query!( - "INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)", + "INSERT INTO macro (guild_id, name, description, commands) VALUES (?, ?, ?, ?)", command_macro.guild_id.0, command_macro.name, command_macro.description, @@ -392,17 +378,15 @@ Any commands ran as part of recording will be inconsequential") .await .unwrap(); - let _ = interaction - .create_interaction_response(&ctx, |r| { - r.kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|d| { - d.create_embed(|e| { - e.title("Macro Recorded") - .description("Use `/macro run` to execute the macro") - .color(*THEME_COLOR) - }) - }) - }) + let _ = invoke + .respond( + &ctx, + CreateGenericResponse::new().embed(|e| { + e.title("Macro Recorded") + .description("Use `/macro run` to execute the macro") + .color(*THEME_COLOR) + }), + ) .await; } } @@ -413,54 +397,45 @@ Any commands ran as part of recording will be inconsequential") } } "list" => {} - "run" => {} + "run" => { + let macro_name = args.get("name").unwrap().to_string(); + + match sqlx::query!( + "SELECT commands FROM macro WHERE guild_id = ? AND name = ?", + invoke.guild_id().unwrap().0, + macro_name + ) + .fetch_one(&pool) + .await + { + Ok(row) => { + invoke.defer(&ctx).await; + + let commands: Vec = + serde_json::from_str(&row.commands).unwrap(); + let framework = ctx.data.read().await.get::().cloned().unwrap(); + + for command in commands { + framework.run_command_from_options(ctx, invoke, command).await; + } + } + + Err(sqlx::Error::RowNotFound) => { + let _ = invoke + .respond( + &ctx, + CreateGenericResponse::new() + .content(format!("Macro \"{}\" not found", macro_name)), + ) + .await; + } + + Err(e) => { + panic!("{}", e); + } + } + } + "delete" => {} _ => {} } } - -#[command("webhook")] -#[description("Modify this channel's webhooks")] -#[subcommand("username")] -#[description("Change the webhook username")] -#[arg(name = "username", description = "The username to use", kind = "String", required = true)] -#[subcommand("avatar")] -#[description("Change the webhook avatar")] -#[arg(name = "url", description = "The URL of the image to use", kind = "String", required = true)] -async fn configure_webhook(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { - let pool = ctx.data.read().await.get::().cloned().unwrap(); - let mut channel_data = ctx.channel_data(invoke.channel_id()).await.unwrap(); - - let (username, avatar) = ( - args.get("username").map_or("Reminder".to_string(), |i| i.to_string()), - args.get("url").map_or(consts::DEFAULT_AVATAR, |i| AttachmentType::Image(&i.to_string())), - ); - - if let (Some(id), Some(token)) = (channel_data.webhook_id, channel_data.webhook_token) { - match ctx.http.get_webhook_with_token(id, &token).await { - Ok(mut webhook) => { - webhook.edit(&ctx, Some(username), Some(avatar)).await; - } - - Err(_) => { - let webhook = invoke - .channel_id() - .create_webhook_with_avatar(&ctx, username, avatar) - .await - .unwrap(); - - channel_data.webhook_token = webhook.token; - channel_data.webhook_id = Some(webhook.id.0); - - channel_data.commit_changes(&pool).await; - } - } - } else { - let webhook = - invoke.channel_id().create_webhook_with_avatar(&ctx, username, avatar).await.unwrap(); - - channel_data.webhook_token = webhook.token; - channel_data.webhook_id = Some(webhook.id.0); - - channel_data.commit_changes(&pool).await; - } -} diff --git a/src/commands/reminder_cmds.rs b/src/commands/reminder_cmds.rs index 7e42d41..b046011 100644 --- a/src/commands/reminder_cmds.rs +++ b/src/commands/reminder_cmds.rs @@ -8,11 +8,7 @@ use chrono::NaiveDateTime; use chrono_tz::Tz; use num_integer::Integer; use regex_command_attr::command; -use serenity::{ - builder::{CreateEmbed, CreateInteractionResponse}, - client::Context, - model::{channel::Channel, interactions::InteractionResponseType}, -}; +use serenity::{builder::CreateEmbed, client::Context, model::channel::Channel}; use crate::{ check_subscription_on_message, @@ -54,7 +50,7 @@ use crate::{ )] #[supports_dm(false)] #[hook(CHECK_GUILD_PERMISSIONS_HOOK)] -async fn pause(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn pause(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await; @@ -140,7 +136,7 @@ async fn pause(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { required = false )] #[hook(CHECK_GUILD_PERMISSIONS_HOOK)] -async fn offset(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn offset(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let combined_time = args.get("hours").map_or(0, |h| h.as_i64().unwrap() * 3600) @@ -217,7 +213,7 @@ WHERE FIND_IN_SET(channels.`channel`, ?)", required = false )] #[hook(CHECK_GUILD_PERMISSIONS_HOOK)] -async fn nudge(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn nudge(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let combined_time = args.get("minutes").map_or(0, |m| m.as_i64().unwrap() * 60) @@ -269,7 +265,7 @@ async fn nudge(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { required = false )] #[hook(CHECK_MANAGED_PERMISSIONS_HOOK)] -async fn look(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn look(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { let pool = ctx.data.read().await.get::().cloned().unwrap(); let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await; @@ -362,22 +358,14 @@ async fn look(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { #[command("del")] #[description("Delete reminders")] #[hook(CHECK_MANAGED_PERMISSIONS_HOOK)] -async fn delete(ctx: &Context, invoke: CommandInvoke, _args: CommandOptions) { - let interaction = invoke.interaction().unwrap(); +async fn delete(ctx: &Context, invoke: &mut CommandInvoke, _args: CommandOptions) { + let timezone = ctx.timezone(invoke.author_id()).await; - let timezone = ctx.timezone(interaction.user.id).await; - - let reminders = Reminder::from_guild(ctx, interaction.guild_id, interaction.user.id).await; + let reminders = Reminder::from_guild(ctx, invoke.guild_id(), invoke.author_id()).await; let resp = show_delete_page(&reminders, 0, timezone); - let _ = interaction - .create_interaction_response(&ctx, |r| { - *r = resp; - r.kind(InteractionResponseType::ChannelMessageWithSource) - }) - .await - .unwrap(); + let _ = invoke.respond(&ctx, resp).await; } pub fn max_delete_page(reminders: &[Reminder], timezone: &Tz) -> usize { @@ -406,22 +394,16 @@ pub fn show_delete_page( reminders: &[Reminder], page: usize, timezone: Tz, -) -> CreateInteractionResponse { +) -> CreateGenericResponse { let pager = DelPager::new(page, timezone); if reminders.is_empty() { - let mut embed = CreateEmbed::default(); - embed.title("Delete Reminders").description("No Reminders").color(*THEME_COLOR); - - let mut response = CreateInteractionResponse::default(); - response.interaction_response_data(|response| { - response.embeds(vec![embed]).components(|comp| { + return CreateGenericResponse::new() + .embed(|e| e.title("Delete Reminders").description("No Reminders").color(*THEME_COLOR)) + .components(|comp| { pager.create_button_row(0, comp); comp - }) - }); - - return response; + }); } let pages = max_delete_page(reminders, &timezone); @@ -470,16 +452,14 @@ pub fn show_delete_page( let del_selector = ComponentDataModel::DelSelector(DelSelector { page, timezone }); - let mut embed = CreateEmbed::default(); - embed - .title("Delete Reminders") - .description(display) - .footer(|f| f.text(format!("Page {} of {}", page + 1, pages))) - .color(*THEME_COLOR); - - let mut response = CreateInteractionResponse::default(); - response.interaction_response_data(|d| { - d.embeds(vec![embed]).components(|comp| { + CreateGenericResponse::new() + .embed(|e| { + e.title("Delete Reminders") + .description(display) + .footer(|f| f.text(format!("Page {} of {}", page + 1, pages))) + .color(*THEME_COLOR) + }) + .components(|comp| { pager.create_button_row(pages, comp); comp.create_action_row(|row| { @@ -511,8 +491,6 @@ pub fn show_delete_page( }) }) }) - }); - response } #[command("timer")] @@ -526,7 +504,7 @@ pub fn show_delete_page( #[description("Delete a timer")] #[arg(name = "name", description = "Name of the timer to delete", kind = "String", required = true)] #[hook(CHECK_MANAGED_PERMISSIONS_HOOK)] -async fn timer(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn timer(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { fn time_difference(start_time: NaiveDateTime) -> String { let unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64; let now = NaiveDateTime::from_timestamp(unix_time, 0); @@ -692,18 +670,10 @@ DELETE FROM timers WHERE owner = ? AND name = ? required = false )] #[hook(CHECK_MANAGED_PERMISSIONS_HOOK)] -async fn remind(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { - let interaction = invoke.interaction().unwrap(); +async fn remind(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { + invoke.defer(&ctx).await; - // defer response since processing times can take some time - interaction - .create_interaction_response(&ctx, |r| { - r.kind(InteractionResponseType::DeferredChannelMessageWithSource) - }) - .await - .unwrap(); - - let user_data = ctx.user_data(interaction.user.id).await.unwrap(); + let user_data = ctx.user_data(invoke.author_id()).await.unwrap(); let timezone = user_data.timezone(); let time = { @@ -728,7 +698,7 @@ async fn remind(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { .unwrap_or_default(); if list.is_empty() { - vec![ReminderScope::Channel(interaction.channel_id.0)] + vec![ReminderScope::Channel(invoke.channel_id().0)] } else { list } @@ -751,7 +721,7 @@ async fn remind(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { } }; - let mut builder = MultiReminderBuilder::new(ctx, interaction.guild_id) + let mut builder = MultiReminderBuilder::new(ctx, invoke.guild_id()) .author(user_data) .content(content) .time(time) @@ -764,16 +734,19 @@ async fn remind(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { let embed = create_response(successes, errors, time); - interaction - .edit_original_interaction_response(&ctx, |r| r.add_embed(embed)) - .await - .unwrap(); + let _ = invoke + .respond( + &ctx, + CreateGenericResponse::new().embed(|c| { + *c = embed; + c + }), + ) + .await; } None => { - let _ = interaction - .edit_original_interaction_response(&ctx, |r| { - r.content("Time could not be processed.") - }) + let _ = invoke + .respond(&ctx, CreateGenericResponse::new().content("Time could not be processed")) .await; } } diff --git a/src/commands/todo_cmds.rs b/src/commands/todo_cmds.rs index e5499ff..789ee69 100644 --- a/src/commands/todo_cmds.rs +++ b/src/commands/todo_cmds.rs @@ -1,9 +1,5 @@ use regex_command_attr::command; -use serenity::{ - builder::{CreateEmbed, CreateInteractionResponse}, - client::Context, - model::interactions::InteractionResponseType, -}; +use serenity::client::Context; use crate::{ component_models::{ @@ -53,7 +49,7 @@ use crate::{ )] #[subcommand("view")] #[description("View and remove from your personal todo list")] -async fn todo(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { +async fn todo(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions) { if invoke.guild_id().is_none() && args.subcommand_group != Some("user".to_string()) { let _ = invoke .respond( @@ -106,15 +102,7 @@ async fn todo(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) { let resp = show_todo_page(&values, 0, keys.0, keys.1, keys.2); - let interaction = invoke.interaction().unwrap(); - - let _ = interaction - .create_interaction_response(&ctx, |r| { - *r = resp; - r.kind(InteractionResponseType::ChannelMessageWithSource) - }) - .await - .unwrap(); + let _ = invoke.respond(&ctx, resp).await; } } } @@ -147,7 +135,7 @@ pub fn show_todo_page( user_id: Option, channel_id: Option, guild_id: Option, -) -> CreateInteractionResponse { +) -> CreateGenericResponse { let pager = TodoPager::new(page, user_id, channel_id, guild_id); let pages = max_todo_page(todo_values); @@ -204,16 +192,14 @@ pub fn show_todo_page( let todo_selector = ComponentDataModel::TodoSelector(TodoSelector { page, user_id, channel_id, guild_id }); - let mut embed = CreateEmbed::default(); - embed - .title(format!("{} Todo List", title)) - .description(display) - .footer(|f| f.text(format!("Page {} of {}", page + 1, pages))) - .color(*THEME_COLOR); - - let mut response = CreateInteractionResponse::default(); - response.interaction_response_data(|d| { - d.embeds(vec![embed]).components(|comp| { + CreateGenericResponse::new() + .embed(|e| { + e.title(format!("{} Todo List", title)) + .description(display) + .footer(|f| f.text(format!("Page {} of {}", page + 1, pages))) + .color(*THEME_COLOR) + }) + .components(|comp| { pager.create_button_row(pages, comp); comp.create_action_row(|row| { @@ -232,7 +218,4 @@ pub fn show_todo_page( }) }) }) - }); - - response } diff --git a/src/component_models/mod.rs b/src/component_models/mod.rs index 2ecebb2..d0ba9d9 100644 --- a/src/component_models/mod.rs +++ b/src/component_models/mod.rs @@ -23,6 +23,7 @@ use crate::{ }, component_models::pager::{DelPager, LookPager, Pager, TodoPager}, consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR}, + framework::CommandInvoke, models::reminder::Reminder, SQLPool, }; @@ -175,12 +176,8 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id let resp = show_delete_page(&reminders, pager.next_page(max_pages), pager.timezone); - let _ = component - .create_interaction_response(&ctx, move |r| { - *r = resp; - r.kind(InteractionResponseType::UpdateMessage) - }) - .await; + let mut invoke = CommandInvoke::component(component); + let _ = invoke.respond(&ctx, resp).await; } ComponentDataModel::DelSelector(selector) => { let pool = ctx.data.read().await.get::().cloned().unwrap(); @@ -196,12 +193,8 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id let resp = show_delete_page(&reminders, selector.page, selector.timezone); - let _ = component - .create_interaction_response(&ctx, move |r| { - *r = resp; - r.kind(InteractionResponseType::UpdateMessage) - }) - .await; + let mut invoke = CommandInvoke::component(component); + let _ = invoke.respond(&ctx, resp).await; } ComponentDataModel::TodoPager(pager) => { let pool = ctx.data.read().await.get::().cloned().unwrap(); @@ -230,12 +223,8 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id pager.guild_id, ); - let _ = component - .create_interaction_response(&ctx, move |r| { - *r = resp; - r.kind(InteractionResponseType::UpdateMessage) - }) - .await; + let mut invoke = CommandInvoke::component(component); + let _ = invoke.respond(&ctx, resp).await; } ComponentDataModel::TodoSelector(selector) => { let pool = ctx.data.read().await.get::().cloned().unwrap(); @@ -260,8 +249,6 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id .map(|row| (row.id as usize, row.value.clone())) .collect::>(); - let max_pages = max_todo_page(&values); - let resp = show_todo_page( &values, selector.page, @@ -270,12 +257,8 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id selector.guild_id, ); - let _ = component - .create_interaction_response(&ctx, move |r| { - *r = resp; - r.kind(InteractionResponseType::UpdateMessage) - }) - .await; + let mut invoke = CommandInvoke::component(component); + let _ = invoke.respond(&ctx, resp).await; } } } diff --git a/src/component_models/pager.rs b/src/component_models/pager.rs index 6344ced..0d41ab0 100644 --- a/src/component_models/pager.rs +++ b/src/component_models/pager.rs @@ -2,13 +2,7 @@ use chrono_tz::Tz; use serde::{Deserialize, Serialize}; use serde_repr::*; -use serenity::{ - builder::CreateComponents, - model::{ - id::{ChannelId, GuildId, UserId}, - interactions::message_component::ButtonStyle, - }, -}; +use serenity::{builder::CreateComponents, model::interactions::message_component::ButtonStyle}; use crate::{component_models::ComponentDataModel, models::reminder::look_flags::LookFlags}; diff --git a/src/consts.rs b/src/consts.rs index be4a527..7cb18a9 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -8,7 +8,13 @@ pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX const THEME_COLOR_FALLBACK: u32 = 0x8fb677; -pub const DEFAULT_AVATAR: AttachmentType = ( +use std::{collections::HashSet, env, iter::FromIterator}; + +use regex::{Regex, RegexBuilder}; +use serenity::http::AttachmentType; + +lazy_static! { + pub static ref DEFAULT_AVATAR: AttachmentType<'static> = ( include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/assets/", @@ -18,32 +24,14 @@ pub const DEFAULT_AVATAR: AttachmentType = ( ) .into(); -use std::{collections::HashSet, env, iter::FromIterator}; - -use regex::{Regex, RegexBuilder}; -use serenity::http::AttachmentType; - -lazy_static! { pub static ref REGEX_CHANNEL: Regex = Regex::new(r#"^\s*<#(\d+)>\s*$"#).unwrap(); pub static ref REGEX_ROLE: Regex = Regex::new(r#"<@&(\d+)>"#).unwrap(); - pub static ref REGEX_COMMANDS: Regex = Regex::new(r#"([a-z]+)"#).unwrap(); - - pub static ref REGEX_ALIAS: Regex = - Regex::new(r#"(?P[\S]{1,12})(?:(?: (?P.*)$)|$)"#).unwrap(); - pub static ref REGEX_CONTENT_SUBSTITUTION: Regex = Regex::new(r#"<<((?P\d+)|(?P.{1,100}))>>"#).unwrap(); pub static ref REGEX_CHANNEL_USER: Regex = Regex::new(r#"\s*<(#|@)(?:!)?(\d+)>\s*"#).unwrap(); - pub static ref REGEX_REMIND_COMMAND: Regex = RegexBuilder::new( - r#"(?P(?:<@\d+>\s+|<@!\d+>\s+|<#\d+>\s+)*)(?P