From eb92eacb90899664f2b7c396c5617bdd42b6c9b5 Mon Sep 17 00:00:00 2001 From: jude Date: Sat, 17 Feb 2024 14:09:01 +0000 Subject: [PATCH] Rearranged some commands Working on a macro to automatically add option wrappers --- Cargo.lock | 61 ++++----- Cargo.toml | 3 + .../{delete.rs => delete_macro.rs} | 0 src/commands/command_macro/finish_macro.rs | 63 +++++++++ .../command_macro/{list.rs => list_macro.rs} | 0 src/commands/command_macro/macro_base.rs | 13 ++ src/commands/command_macro/mod.rs | 24 +--- .../{record.rs => record_macro.rs} | 60 --------- .../command_macro/{run.rs => run_macro.rs} | 0 src/commands/command_proc/mod.rs | 80 ++++++++++++ src/commands/mod.rs | 2 + src/commands/reminder_cmds.rs | 120 +----------------- src/commands/timer/delete_timer.rs | 33 +++++ src/commands/timer/list_timer.rs | 45 +++++++ src/commands/timer/mod.rs | 4 + src/commands/timer/start_timer.rs | 34 +++++ src/commands/timer/timer_base.rs | 12 ++ src/component_models/mod.rs | 2 +- src/main.rs | 22 ++-- 19 files changed, 340 insertions(+), 238 deletions(-) rename src/commands/command_macro/{delete.rs => delete_macro.rs} (100%) create mode 100644 src/commands/command_macro/finish_macro.rs rename src/commands/command_macro/{list.rs => list_macro.rs} (100%) create mode 100644 src/commands/command_macro/macro_base.rs rename src/commands/command_macro/{record.rs => record_macro.rs} (61%) rename src/commands/command_macro/{run.rs => run_macro.rs} (100%) create mode 100644 src/commands/command_proc/mod.rs create mode 100644 src/commands/timer/delete_timer.rs create mode 100644 src/commands/timer/list_timer.rs create mode 100644 src/commands/timer/mod.rs create mode 100644 src/commands/timer/start_timer.rs create mode 100644 src/commands/timer/timer_base.rs diff --git a/Cargo.lock b/Cargo.lock index b081a91..cb27f75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,7 +123,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -134,7 +134,7 @@ checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -571,7 +571,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -582,7 +582,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -673,7 +673,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -937,7 +937,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -1440,7 +1440,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -1840,7 +1840,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -1925,7 +1925,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -1974,7 +1974,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -2091,7 +2091,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -2136,9 +2136,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -2151,7 +2151,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", "version_check", "yansi", ] @@ -2190,9 +2190,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -2253,7 +2253,7 @@ checksum = "2566c4bf6845f2c2e83b27043c3f5dfcd5ba8f2937d6c00dc009bfb51a079dc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -2316,6 +2316,8 @@ dependencies = [ "num-integer", "poise", "postman", + "proc-macro2", + "quote", "rand", "regex", "reminder_web", @@ -2326,6 +2328,7 @@ dependencies = [ "serde_json", "serde_repr", "sqlx", + "syn 2.0.49", "tokio", ] @@ -2481,7 +2484,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.42", + "syn 2.0.49", "unicode-xid", "version_check", ] @@ -2749,7 +2752,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -2781,7 +2784,7 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -3241,9 +3244,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" dependencies = [ "proc-macro2", "quote", @@ -3338,7 +3341,7 @@ checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -3422,7 +3425,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -3556,7 +3559,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -3668,7 +3671,7 @@ checksum = "0b122284365ba8497be951b9a21491f70c9688eb6fddc582931a0703f6a00ece" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] @@ -3898,7 +3901,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", "wasm-bindgen-shared", ] @@ -3932,7 +3935,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4204,7 +4207,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.49", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 27cec1d..987c79c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,9 @@ license = "AGPL-3.0 only" description = "Reminder Bot for Discord, now in Rust" [dependencies] +quote = "1.0.35" +proc-macro2 = "1.0.78" +syn = { version = "2.0.49", features = ["full"] } poise = "0.6.1" dotenv = "0.15" tokio = { version = "1", features = ["process", "full"] } diff --git a/src/commands/command_macro/delete.rs b/src/commands/command_macro/delete_macro.rs similarity index 100% rename from src/commands/command_macro/delete.rs rename to src/commands/command_macro/delete_macro.rs diff --git a/src/commands/command_macro/finish_macro.rs b/src/commands/command_macro/finish_macro.rs new file mode 100644 index 0000000..26de52c --- /dev/null +++ b/src/commands/command_macro/finish_macro.rs @@ -0,0 +1,63 @@ +use poise::{serenity_prelude::CreateEmbed, CreateReply}; + +use crate::{consts::THEME_COLOR, Context, Error}; + +/// Finish current macro recording +#[poise::command( + slash_command, + rename = "finish", + guild_only = true, + default_member_permissions = "MANAGE_GUILD", + identifying_name = "finish_macro" +)] +pub async fn finish_macro(ctx: Context<'_>) -> Result<(), Error> { + let key = (ctx.guild_id().unwrap(), ctx.author().id); + + { + let lock = ctx.data().recording_macros.read().await; + let contained = lock.get(&key); + + if contained.map_or(true, |r#macro| r#macro.commands.is_empty()) { + ctx.send( + CreateReply::default().embed( + CreateEmbed::new() + .title("No Macro Recorded") + .description("Use `/macro record` to start recording a macro") + .color(*THEME_COLOR), + ), + ) + .await?; + } else { + let command_macro = contained.unwrap(); + let json = serde_json::to_string(&command_macro.commands).unwrap(); + + sqlx::query!( + "INSERT INTO command_macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)", + command_macro.guild_id.get(), + command_macro.name, + command_macro.description, + json + ) + .execute(&ctx.data().database) + .await + .unwrap(); + + ctx.send( + CreateReply::default().embed( + CreateEmbed::new() + .title("Macro Recorded") + .description("Use `/macro run` to execute the macro") + .color(*THEME_COLOR), + ), + ) + .await?; + } + } + + { + let mut lock = ctx.data().recording_macros.write().await; + lock.remove(&key); + } + + Ok(()) +} diff --git a/src/commands/command_macro/list.rs b/src/commands/command_macro/list_macro.rs similarity index 100% rename from src/commands/command_macro/list.rs rename to src/commands/command_macro/list_macro.rs diff --git a/src/commands/command_macro/macro_base.rs b/src/commands/command_macro/macro_base.rs new file mode 100644 index 0000000..733199a --- /dev/null +++ b/src/commands/command_macro/macro_base.rs @@ -0,0 +1,13 @@ +use crate::{Context, Error}; + +/// Record and replay command sequences +#[poise::command( + slash_command, + rename = "macro", + guild_only = true, + default_member_permissions = "MANAGE_GUILD", + identifying_name = "macro_base" +)] +pub async fn macro_base(_ctx: Context<'_>) -> Result<(), Error> { + Ok(()) +} diff --git a/src/commands/command_macro/mod.rs b/src/commands/command_macro/mod.rs index 19b7084..ecaddf0 100644 --- a/src/commands/command_macro/mod.rs +++ b/src/commands/command_macro/mod.rs @@ -1,18 +1,6 @@ -use crate::{Context, Error}; - -pub mod delete; -pub mod list; -pub mod record; -pub mod run; - -/// Record and replay command sequences -#[poise::command( - slash_command, - rename = "macro", - guild_only = true, - default_member_permissions = "MANAGE_GUILD", - identifying_name = "macro_base" -)] -pub async fn macro_base(_ctx: Context<'_>) -> Result<(), Error> { - Ok(()) -} +pub mod delete_macro; +pub mod finish_macro; +pub mod list_macro; +pub mod macro_base; +pub mod record_macro; +pub mod run_macro; diff --git a/src/commands/command_macro/record.rs b/src/commands/command_macro/record_macro.rs similarity index 61% rename from src/commands/command_macro/record.rs rename to src/commands/command_macro/record_macro.rs index 22369e4..b60bcec 100644 --- a/src/commands/command_macro/record.rs +++ b/src/commands/command_macro/record_macro.rs @@ -100,63 +100,3 @@ Please use `/macro finish` to end this recording before starting another.", Ok(()) } - -/// Finish current macro recording -#[poise::command( - slash_command, - rename = "finish", - guild_only = true, - default_member_permissions = "MANAGE_GUILD", - identifying_name = "finish_macro" -)] -pub async fn finish_macro(ctx: Context<'_>) -> Result<(), Error> { - let key = (ctx.guild_id().unwrap(), ctx.author().id); - - { - let lock = ctx.data().recording_macros.read().await; - let contained = lock.get(&key); - - if contained.map_or(true, |r#macro| r#macro.commands.is_empty()) { - ctx.send( - CreateReply::default().embed( - CreateEmbed::new() - .title("No Macro Recorded") - .description("Use `/macro record` to start recording a macro") - .color(*THEME_COLOR), - ), - ) - .await?; - } else { - let command_macro = contained.unwrap(); - let json = serde_json::to_string(&command_macro.commands).unwrap(); - - sqlx::query!( - "INSERT INTO command_macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)", - command_macro.guild_id.get(), - command_macro.name, - command_macro.description, - json - ) - .execute(&ctx.data().database) - .await - .unwrap(); - - ctx.send( - CreateReply::default().embed( - CreateEmbed::new() - .title("Macro Recorded") - .description("Use `/macro run` to execute the macro") - .color(*THEME_COLOR), - ), - ) - .await?; - } - } - - { - let mut lock = ctx.data().recording_macros.write().await; - lock.remove(&key); - } - - Ok(()) -} diff --git a/src/commands/command_macro/run.rs b/src/commands/command_macro/run_macro.rs similarity index 100% rename from src/commands/command_macro/run.rs rename to src/commands/command_macro/run_macro.rs diff --git a/src/commands/command_proc/mod.rs b/src/commands/command_proc/mod.rs new file mode 100644 index 0000000..373e380 --- /dev/null +++ b/src/commands/command_proc/mod.rs @@ -0,0 +1,80 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use serde::{de::DeserializeOwned, Serialize}; +use syn::{punctuated::Punctuated, token::Comma, FnArg, Pat}; + +struct RecordableCommand { + args: T, + func: dyn Fn(T) -> R, +} + +/// Takes a function and produces a serializable struct of its args. +pub fn arg_struct(mut function: syn::ItemFn) -> TokenStream { + let struct_name = Ident::new(&format!("{}_args", function.sig.ident), Span::call_site()); + let wrapped_fn_name = Ident::new(&format!("{}_fn", function.sig.ident), Span::call_site()); + + let fn_name = &function.sig.ident; + let fn_generics = &function.sig.generics; + let fn_inputs = &function.sig.inputs; + let fn_args = &function + .sig + .inputs + .iter() + .map(|arg| match arg { + FnArg::Receiver(_) => { + panic!("Can't accept Receiver arg") + } + FnArg::Typed(p) => p.pat.clone(), + }) + .collect::, Comma>>(); + let fn_retval = &function.sig.output; + let fn_body = &function.block; + + quote::quote! { + pub async fn #fn_name #fn_generics(#fn_inputs) -> #fn_retval { + #wrapped_fn_name(#fn_args).await + } + + pub async fn #wrapped_fn_name #fn_generics(#fn_inputs) -> #fn_retval { + #fn_body + } + + } +} + +/* + +#[poise] +#[wrapper] +pub async fn command(...args) { + ...block +} + + ... becomes ... + +#[poise] +fn command(...args) { + command_fn( + ...args + ) +} + +struct RecordableCommand { + args: T + func: Func +} + +impl Execute for RecordableCommand { + fn execute() { + // Unpack self.args into self.func + } +} + +struct command_args { + ...args +} + +fn command_fn(...args) { + ...block +} + + */ diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 0f91b19..9db7d75 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,6 +1,8 @@ mod autocomplete; pub mod command_macro; +mod command_proc; pub mod info_cmds; pub mod moderation_cmds; pub mod reminder_cmds; +pub mod timer; pub mod todo_cmds; diff --git a/src/commands/reminder_cmds.rs b/src/commands/reminder_cmds.rs index 35608c4..4239a62 100644 --- a/src/commands/reminder_cmds.rs +++ b/src/commands/reminder_cmds.rs @@ -1,9 +1,8 @@ use std::{collections::HashSet, string::ToString}; -use chrono::{DateTime, NaiveDateTime, Utc}; +use chrono::NaiveDateTime; use chrono_tz::Tz; use log::warn; -use num_integer::Integer; use poise::{ serenity_prelude::{ builder::CreateEmbed, model::channel::Channel, ButtonStyle, CreateActionRow, CreateButton, @@ -32,7 +31,6 @@ use crate::{ look_flags::{LookFlags, TimeDisplayType}, Reminder, }, - timer::Timer, CtxData, }, time_parser::natural_parser, @@ -422,122 +420,6 @@ pub fn show_delete_page(reminders: &[Reminder], page: usize, timezone: Tz) -> Cr .components(vec![pager.create_button_row(pages), CreateActionRow::SelectMenu(select_menu)]) } -fn time_difference(start_time: DateTime) -> String { - let delta = (Utc::now() - start_time).num_seconds(); - - let (minutes, seconds) = delta.div_rem(&60); - let (hours, minutes) = minutes.div_rem(&60); - let (days, hours) = hours.div_rem(&24); - - format!("{} days, {:02}:{:02}:{:02}", days, hours, minutes, seconds) -} - -/// Manage timers -#[poise::command( - slash_command, - rename = "timer", - identifying_name = "timer_base", - default_member_permissions = "MANAGE_GUILD" -)] -pub async fn timer_base(_ctx: Context<'_>) -> Result<(), Error> { - Ok(()) -} - -/// List the timers in this server or DM channel -#[poise::command( - slash_command, - rename = "list", - identifying_name = "list_timer", - default_member_permissions = "MANAGE_GUILD" -)] -pub async fn list_timer(ctx: Context<'_>) -> Result<(), Error> { - let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get()); - - let timers = Timer::from_owner(owner, &ctx.data().database).await; - - if !timers.is_empty() { - ctx.send( - CreateReply::default().embed( - CreateEmbed::new() - .fields(timers.iter().map(|timer| { - (&timer.name, format!("⌚ `{}`", time_difference(timer.start_time)), false) - })) - .color(*THEME_COLOR), - ), - ) - .await?; - } else { - ctx.say("No timers currently. Use `/timer start` to create a new timer").await?; - } - - Ok(()) -} - -/// Start a new timer from now -#[poise::command( - slash_command, - rename = "start", - identifying_name = "start_timer", - default_member_permissions = "MANAGE_GUILD" -)] -pub async fn start_timer( - ctx: Context<'_>, - #[description = "Name for the new timer"] name: String, -) -> Result<(), Error> { - let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get()); - - let count = Timer::count_from_owner(owner, &ctx.data().database).await; - - if count >= 25 { - ctx.say("You already have 25 timers. Please delete some timers before creating a new one") - .await?; - } else if name.len() <= 32 { - Timer::create(&name, owner, &ctx.data().database).await; - - ctx.say("Created a new timer").await?; - } else { - ctx.say(format!( - "Please name your timer something shorted (max. 32 characters, you used {})", - name.len() - )) - .await?; - } - - Ok(()) -} - -/// Delete a timer -#[poise::command( - slash_command, - rename = "delete", - identifying_name = "delete_timer", - default_member_permissions = "MANAGE_GUILD" -)] -pub async fn delete_timer( - ctx: Context<'_>, - #[description = "Name of timer to delete"] name: String, -) -> Result<(), Error> { - let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get()); - - let exists = - sqlx::query!("SELECT 1 as _r FROM timers WHERE owner = ? AND name = ?", owner, name) - .fetch_one(&ctx.data().database) - .await; - - if exists.is_ok() { - sqlx::query!("DELETE FROM timers WHERE owner = ? AND name = ?", owner, name) - .execute(&ctx.data().database) - .await - .unwrap(); - - ctx.say("Deleted a timer").await?; - } else { - ctx.say("Could not find a timer by that name").await?; - } - - Ok(()) -} - #[derive(poise::Modal)] #[name = "Reminder"] struct ContentModal { diff --git a/src/commands/timer/delete_timer.rs b/src/commands/timer/delete_timer.rs new file mode 100644 index 0000000..ad57e34 --- /dev/null +++ b/src/commands/timer/delete_timer.rs @@ -0,0 +1,33 @@ +use crate::{Context, Error}; + +/// Delete a timer +#[poise::command( + slash_command, + rename = "delete", + identifying_name = "delete_timer", + default_member_permissions = "MANAGE_GUILD" +)] +pub async fn delete_timer( + ctx: Context<'_>, + #[description = "Name of timer to delete"] name: String, +) -> Result<(), Error> { + let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get()); + + let exists = + sqlx::query!("SELECT 1 as _r FROM timers WHERE owner = ? AND name = ?", owner, name) + .fetch_one(&ctx.data().database) + .await; + + if exists.is_ok() { + sqlx::query!("DELETE FROM timers WHERE owner = ? AND name = ?", owner, name) + .execute(&ctx.data().database) + .await + .unwrap(); + + ctx.say("Deleted a timer").await?; + } else { + ctx.say("Could not find a timer by that name").await?; + } + + Ok(()) +} diff --git a/src/commands/timer/list_timer.rs b/src/commands/timer/list_timer.rs new file mode 100644 index 0000000..367c878 --- /dev/null +++ b/src/commands/timer/list_timer.rs @@ -0,0 +1,45 @@ +use chrono::{DateTime, Utc}; +use num_integer::Integer; +use poise::{serenity_prelude::CreateEmbed, CreateReply}; + +use crate::{consts::THEME_COLOR, models::timer::Timer, Context, Error}; + +fn time_difference(start_time: DateTime) -> String { + let delta = (Utc::now() - start_time).num_seconds(); + + let (minutes, seconds) = delta.div_rem(&60); + let (hours, minutes) = minutes.div_rem(&60); + let (days, hours) = hours.div_rem(&24); + + format!("{} days, {:02}:{:02}:{:02}", days, hours, minutes, seconds) +} + +/// List the timers in this server or DM channel +#[poise::command( + slash_command, + rename = "list", + identifying_name = "list_timer", + default_member_permissions = "MANAGE_GUILD" +)] +pub async fn list_timer(ctx: Context<'_>) -> Result<(), Error> { + let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get()); + + let timers = Timer::from_owner(owner, &ctx.data().database).await; + + if !timers.is_empty() { + ctx.send( + CreateReply::default().embed( + CreateEmbed::new() + .fields(timers.iter().map(|timer| { + (&timer.name, format!("⌚ `{}`", time_difference(timer.start_time)), false) + })) + .color(*THEME_COLOR), + ), + ) + .await?; + } else { + ctx.say("No timers currently. Use `/timer start` to create a new timer").await?; + } + + Ok(()) +} diff --git a/src/commands/timer/mod.rs b/src/commands/timer/mod.rs new file mode 100644 index 0000000..40128bb --- /dev/null +++ b/src/commands/timer/mod.rs @@ -0,0 +1,4 @@ +pub mod delete_timer; +pub mod list_timer; +pub mod start_timer; +pub mod timer_base; diff --git a/src/commands/timer/start_timer.rs b/src/commands/timer/start_timer.rs new file mode 100644 index 0000000..fa2235b --- /dev/null +++ b/src/commands/timer/start_timer.rs @@ -0,0 +1,34 @@ +use crate::{models::timer::Timer, Context, Error}; + +/// Start a new timer from now +#[poise::command( + slash_command, + rename = "start", + identifying_name = "start_timer", + default_member_permissions = "MANAGE_GUILD" +)] +pub async fn start_timer( + ctx: Context<'_>, + #[description = "Name for the new timer"] name: String, +) -> Result<(), Error> { + let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get()); + + let count = Timer::count_from_owner(owner, &ctx.data().database).await; + + if count >= 25 { + ctx.say("You already have 25 timers. Please delete some timers before creating a new one") + .await?; + } else if name.len() <= 32 { + Timer::create(&name, owner, &ctx.data().database).await; + + ctx.say("Created a new timer").await?; + } else { + ctx.say(format!( + "Please name your timer something shorted (max. 32 characters, you used {})", + name.len() + )) + .await?; + } + + Ok(()) +} diff --git a/src/commands/timer/timer_base.rs b/src/commands/timer/timer_base.rs new file mode 100644 index 0000000..9baf15f --- /dev/null +++ b/src/commands/timer/timer_base.rs @@ -0,0 +1,12 @@ +use crate::{Context, Error}; + +/// Manage timers +#[poise::command( + slash_command, + rename = "timer", + identifying_name = "timer_base", + default_member_permissions = "MANAGE_GUILD" +)] +pub async fn timer_base(_ctx: Context<'_>) -> Result<(), Error> { + Ok(()) +} diff --git a/src/component_models/mod.rs b/src/component_models/mod.rs index 52dab53..c27693a 100644 --- a/src/component_models/mod.rs +++ b/src/component_models/mod.rs @@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize}; use crate::{ commands::{ - command_macro::list::{max_macro_page, show_macro_page}, + command_macro::list_macro::{max_macro_page, show_macro_page}, reminder_cmds::{max_delete_page, show_delete_page}, todo_cmds::{max_todo_page, show_todo_page}, }, diff --git a/src/main.rs b/src/main.rs index 5076edd..3c46809 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ use sqlx::{MySql, Pool}; use tokio::sync::{broadcast, broadcast::Sender, RwLock}; use crate::{ - commands::{command_macro, info_cmds, moderation_cmds, reminder_cmds, todo_cmds}, + commands::{command_macro, info_cmds, moderation_cmds, reminder_cmds, timer, todo_cmds}, consts::THEME_COLOR, event_handlers::listener, hooks::all_checks, @@ -126,13 +126,13 @@ async fn _main(tx: Sender<()>) -> Result<(), Box> { moderation_cmds::webhook(), poise::Command { subcommands: vec![ - command_macro::delete::delete_macro(), - command_macro::record::finish_macro(), - command_macro::list::list_macro(), - command_macro::record::record_macro(), - command_macro::run::run_macro(), + command_macro::delete_macro::delete_macro(), + command_macro::finish_macro::finish_macro(), + command_macro::list_macro::list_macro(), + command_macro::record_macro::record_macro(), + command_macro::run_macro::run_macro(), ], - ..command_macro::macro_base() + ..command_macro::macro_base::macro_base() }, reminder_cmds::pause(), reminder_cmds::offset(), @@ -141,11 +141,11 @@ async fn _main(tx: Sender<()>) -> Result<(), Box> { reminder_cmds::delete(), poise::Command { subcommands: vec![ - reminder_cmds::list_timer(), - reminder_cmds::start_timer(), - reminder_cmds::delete_timer(), + timer::list_timer::list_timer(), + timer::start_timer::start_timer(), + timer::delete_timer::delete_timer(), ], - ..reminder_cmds::timer_base() + ..timer::timer_base::timer_base() }, reminder_cmds::multiline(), reminder_cmds::remind(),