diff --git a/src/commands/allowed_dm/mod.rs b/src/commands/allowed_dm/mod.rs index 6fed3ea..c6a3e2b 100644 --- a/src/commands/allowed_dm/mod.rs +++ b/src/commands/allowed_dm/mod.rs @@ -1,5 +1,5 @@ -pub mod set_allowed_dm; -pub mod unset_allowed_dm; +pub mod set; +pub mod unset; use crate::{Context, Error}; diff --git a/src/commands/allowed_dm/set.rs b/src/commands/allowed_dm/set.rs new file mode 100644 index 0000000..4a14acc --- /dev/null +++ b/src/commands/allowed_dm/set.rs @@ -0,0 +1,38 @@ +use poise::{serenity_prelude::CreateEmbed, CreateReply}; +use serde::{Deserialize, Serialize}; + +use crate::{ + consts::THEME_COLOR, + models::CtxData, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + let mut user_data = ctx.author_data().await?; + user_data.allowed_dm = true; + user_data.commit_changes(&ctx.data().database).await; + + ctx.send( + CreateReply::default().ephemeral(true).embed( + CreateEmbed::new() + .title("DMs permitted") + .description("You will receive a message if a user sets a DM reminder for you.") + .color(*THEME_COLOR), + ), + ) + .await?; + + Ok(()) + } +} + +/// Allow other users to set reminders in your direct messages +#[poise::command(slash_command, rename = "allow", identifying_name = "set_allowed_dm")] +pub async fn set(ctx: Context<'_>) -> Result<(), Error> { + (Options {}).run(ctx).await +} diff --git a/src/commands/allowed_dm/set_allowed_dm.rs b/src/commands/allowed_dm/set_allowed_dm.rs deleted file mode 100644 index 4fd463b..0000000 --- a/src/commands/allowed_dm/set_allowed_dm.rs +++ /dev/null @@ -1,23 +0,0 @@ -use poise::{serenity_prelude::CreateEmbed, CreateReply}; - -use crate::{consts::THEME_COLOR, models::CtxData, Context, Error}; - -/// Allow other users to set reminders in your direct messages -#[poise::command(slash_command, rename = "allow", identifying_name = "set_allowed_dm")] -pub async fn set_allowed_dm(ctx: Context<'_>) -> Result<(), Error> { - let mut user_data = ctx.author_data().await?; - user_data.allowed_dm = true; - user_data.commit_changes(&ctx.data().database).await; - - ctx.send( - CreateReply::default().ephemeral(true).embed( - CreateEmbed::new() - .title("DMs permitted") - .description("You will receive a message if a user sets a DM reminder for you.") - .color(*THEME_COLOR), - ), - ) - .await?; - - Ok(()) -} diff --git a/src/commands/allowed_dm/unset.rs b/src/commands/allowed_dm/unset.rs new file mode 100644 index 0000000..75111fe --- /dev/null +++ b/src/commands/allowed_dm/unset.rs @@ -0,0 +1,41 @@ +use poise::{serenity_prelude::CreateEmbed, CreateReply}; +use serde::{Deserialize, Serialize}; + +use crate::{ + consts::THEME_COLOR, + models::CtxData, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + let mut user_data = ctx.author_data().await?; + user_data.allowed_dm = false; + user_data.commit_changes(&ctx.data().database).await; + + ctx.send( + CreateReply::default().ephemeral(true).embed( + CreateEmbed::new() + .title("DMs blocked") + .description(concat!( + "You can still set DM reminders for yourself or for users with", + " DMs enabled." + )) + .color(*THEME_COLOR), + ), + ) + .await?; + + Ok(()) + } +} + +/// Block other users from setting reminders in your direct messages +#[poise::command(slash_command, rename = "block", identifying_name = "unset_allowed_dm")] +pub async fn unset(ctx: Context<'_>) -> Result<(), Error> { + (Options {}).run(ctx).await +} diff --git a/src/commands/allowed_dm/unset_allowed_dm.rs b/src/commands/allowed_dm/unset_allowed_dm.rs deleted file mode 100644 index 76cd523..0000000 --- a/src/commands/allowed_dm/unset_allowed_dm.rs +++ /dev/null @@ -1,25 +0,0 @@ -use poise::{serenity_prelude::CreateEmbed, CreateReply}; - -use crate::{consts::THEME_COLOR, models::CtxData, Context, Error}; - -/// Block other users from setting reminders in your direct messages -#[poise::command(slash_command, rename = "block", identifying_name = "unset_allowed_dm")] -pub async fn unset_allowed_dm(ctx: Context<'_>) -> Result<(), Error> { - let mut user_data = ctx.author_data().await?; - user_data.allowed_dm = false; - user_data.commit_changes(&ctx.data().database).await; - - ctx.send( - CreateReply::default().ephemeral(true).embed( - CreateEmbed::new() - .title("DMs blocked") - .description( - "You can still set DM reminders for yourself or for users with DMs enabled.", - ) - .color(*THEME_COLOR), - ), - ) - .await?; - - Ok(()) -} diff --git a/src/commands/settings/ephemeral_confirmations/mod.rs b/src/commands/settings/ephemeral_confirmations/mod.rs index 74a23ce..2196ce6 100644 --- a/src/commands/settings/ephemeral_confirmations/mod.rs +++ b/src/commands/settings/ephemeral_confirmations/mod.rs @@ -1,7 +1,7 @@ use crate::{Context, Error}; -pub mod set_ephemeral_confirmations; -pub mod unset_ephemeral_confirmations; +pub mod set; +pub mod unset; /// Configure ephemeral setup #[poise::command( diff --git a/src/commands/settings/ephemeral_confirmations/set.rs b/src/commands/settings/ephemeral_confirmations/set.rs new file mode 100644 index 0000000..eab4433 --- /dev/null +++ b/src/commands/settings/ephemeral_confirmations/set.rs @@ -0,0 +1,46 @@ +use poise::{serenity_prelude::CreateEmbed, CreateReply}; +use serde::{Deserialize, Serialize}; + +use crate::{ + consts::THEME_COLOR, + models::CtxData, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + let mut guild_data = ctx.guild_data().await.unwrap()?; + guild_data.ephemeral_confirmations = true; + guild_data.commit_changes(&ctx.data().database).await; + + ctx.send( + CreateReply::default().ephemeral(true).embed( + CreateEmbed::new() + .title("Confirmations ephemeral") + .description(concat!( + "Reminder confirmations will be sent privately, and removed when your client", + " restarts." + )) + .color(*THEME_COLOR), + ), + ) + .await?; + + Ok(()) + } +} + +/// Set reminder confirmations to be sent "ephemerally" (private and cleared automatically) +#[poise::command( + slash_command, + rename = "on", + identifying_name = "set_ephemeral_confirmations", + guild_only = true +)] +pub async fn set(ctx: Context<'_>) -> Result<(), Error> { + (Options {}).run(ctx).await +} diff --git a/src/commands/settings/ephemeral_confirmations/set_ephemeral_confirmations.rs b/src/commands/settings/ephemeral_confirmations/set_ephemeral_confirmations.rs deleted file mode 100644 index cd21966..0000000 --- a/src/commands/settings/ephemeral_confirmations/set_ephemeral_confirmations.rs +++ /dev/null @@ -1,31 +0,0 @@ -use poise::{serenity_prelude::CreateEmbed, CreateReply}; - -use crate::{consts::THEME_COLOR, models::CtxData, Context, Error}; - -/// Set reminder confirmations to be sent "ephemerally" (private and cleared automatically) -#[poise::command( - slash_command, - rename = "on", - identifying_name = "set_ephemeral_confirmations", - guild_only = true -)] -pub async fn set_ephemeral_confirmations(ctx: Context<'_>) -> Result<(), Error> { - let mut guild_data = ctx.guild_data().await.unwrap()?; - guild_data.ephemeral_confirmations = true; - guild_data.commit_changes(&ctx.data().database).await; - - ctx.send( - CreateReply::default().ephemeral(true).embed( - CreateEmbed::new() - .title("Confirmations ephemeral") - .description(concat!( - "Reminder confirmations will be sent privately, and removed when your client", - " restarts." - )) - .color(*THEME_COLOR), - ), - ) - .await?; - - Ok(()) -} diff --git a/src/commands/settings/ephemeral_confirmations/unset.rs b/src/commands/settings/ephemeral_confirmations/unset.rs new file mode 100644 index 0000000..8f971fd --- /dev/null +++ b/src/commands/settings/ephemeral_confirmations/unset.rs @@ -0,0 +1,46 @@ +use poise::{serenity_prelude::CreateEmbed, CreateReply}; +use serde::{Deserialize, Serialize}; + +use crate::{ + consts::THEME_COLOR, + models::CtxData, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + let mut guild_data = ctx.guild_data().await.unwrap()?; + guild_data.ephemeral_confirmations = false; + guild_data.commit_changes(&ctx.data().database).await; + + ctx.send( + CreateReply::default().ephemeral(true).embed( + CreateEmbed::new() + .title("Confirmations public") + .description(concat!( + "Reminder confirmations will be sent as regular messages, and won't be ", + "removed automatically." + )) + .color(*THEME_COLOR), + ), + ) + .await?; + + Ok(()) + } +} + +/// Set reminder confirmations to persist indefinitely +#[poise::command( + slash_command, + rename = "off", + identifying_name = "unset_ephemeral_confirmations", + guild_only = true +)] +pub async fn unset(ctx: Context<'_>) -> Result<(), Error> { + (Options {}).run(ctx).await +} diff --git a/src/commands/settings/ephemeral_confirmations/unset_ephemeral_confirmations.rs b/src/commands/settings/ephemeral_confirmations/unset_ephemeral_confirmations.rs deleted file mode 100644 index 7331962..0000000 --- a/src/commands/settings/ephemeral_confirmations/unset_ephemeral_confirmations.rs +++ /dev/null @@ -1,31 +0,0 @@ -use poise::{serenity_prelude::CreateEmbed, CreateReply}; - -use crate::{consts::THEME_COLOR, models::CtxData, Context, Error}; - -/// Set reminder confirmations to persist indefinitely -#[poise::command( - slash_command, - rename = "off", - identifying_name = "unset_ephemeral_confirmations", - guild_only = true -)] -pub async fn unset_ephemeral_confirmations(ctx: Context<'_>) -> Result<(), Error> { - let mut guild_data = ctx.guild_data().await.unwrap()?; - guild_data.ephemeral_confirmations = false; - guild_data.commit_changes(&ctx.data().database).await; - - ctx.send( - CreateReply::default().ephemeral(true).embed( - CreateEmbed::new() - .title("Confirmations public") - .description(concat!( - "Reminder confirmations will be sent as regular messages, and won't be ", - "removed automatically." - )) - .color(*THEME_COLOR), - ), - ) - .await?; - - Ok(()) -} diff --git a/src/commands/timer/delete.rs b/src/commands/timer/delete.rs new file mode 100644 index 0000000..ea46412 --- /dev/null +++ b/src/commands/timer/delete.rs @@ -0,0 +1,65 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options { + name: String, +} + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> 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, + self.name + ) + .fetch_one(&ctx.data().database) + .await; + + if exists.is_ok() { + sqlx::query!( + " + DELETE FROM timers + WHERE owner = ? + AND name = ? + ", + owner, + self.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(()) + } +} + +/// Delete a timer +#[poise::command( + slash_command, + rename = "delete", + identifying_name = "delete_timer", + default_member_permissions = "MANAGE_GUILD" +)] +pub async fn delete( + ctx: Context<'_>, + #[description = "Name of timer to delete"] name: String, +) -> Result<(), Error> { + (Options { name }).run(ctx).await +} diff --git a/src/commands/timer/delete_timer.rs b/src/commands/timer/delete_timer.rs deleted file mode 100644 index ad57e34..0000000 --- a/src/commands/timer/delete_timer.rs +++ /dev/null @@ -1,33 +0,0 @@ -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.rs b/src/commands/timer/list.rs new file mode 100644 index 0000000..72d756a --- /dev/null +++ b/src/commands/timer/list.rs @@ -0,0 +1,64 @@ +use chrono::{DateTime, Utc}; +use num_integer::Integer; +use poise::{serenity_prelude::CreateEmbed, CreateReply}; +use serde::{Deserialize, Serialize}; + +use crate::{ + consts::THEME_COLOR, + models::timer::Timer, + utils::{Extract, Recordable}, + 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) +} + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, 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(()) + } +} + +/// 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(ctx: Context<'_>) -> Result<(), Error> { + (Options {}).run(ctx).await +} diff --git a/src/commands/timer/list_timer.rs b/src/commands/timer/list_timer.rs deleted file mode 100644 index 367c878..0000000 --- a/src/commands/timer/list_timer.rs +++ /dev/null @@ -1,45 +0,0 @@ -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 index 799d412..3497ef9 100644 --- a/src/commands/timer/mod.rs +++ b/src/commands/timer/mod.rs @@ -1,6 +1,6 @@ -pub mod delete_timer; -pub mod list_timer; -pub mod start_timer; +pub mod delete; +pub mod list; +pub mod start; use crate::{Context, Error}; diff --git a/src/commands/timer/start.rs b/src/commands/timer/start.rs new file mode 100644 index 0000000..eea9cb6 --- /dev/null +++ b/src/commands/timer/start.rs @@ -0,0 +1,53 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + models::timer::Timer, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options { + name: String, +} + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> 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 self.name.len() <= 32 { + Timer::create(&self.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 {})", + self.name.len() + )) + .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( + ctx: Context<'_>, + #[description = "Name for the new timer"] name: String, +) -> Result<(), Error> { + (Options { name }).run(ctx).await +} diff --git a/src/commands/timer/start_timer.rs b/src/commands/timer/start_timer.rs deleted file mode 100644 index fa2235b..0000000 --- a/src/commands/timer/start_timer.rs +++ /dev/null @@ -1,34 +0,0 @@ -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/todo/channel/add.rs b/src/commands/todo/channel/add.rs index 0e31de5..de0fdbd 100644 --- a/src/commands/todo/channel/add.rs +++ b/src/commands/todo/channel/add.rs @@ -1,4 +1,43 @@ -use crate::{models::CtxData, Context, Error}; +use serde::{Deserialize, Serialize}; + +use crate::{ + models::CtxData, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options { + task: String, +} + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + // ensure channel is cached + ctx.channel_data().await?; + + sqlx::query!( + " + INSERT INTO todos (guild_id, channel_id, value) + VALUES ( + (SELECT id FROM guilds WHERE guild = ?), + (SELECT id FROM channels WHERE channel = ?), + ? + ) + ", + ctx.guild_id().unwrap().get(), + ctx.channel_id().get(), + self.task + ) + .execute(&ctx.data().database) + .await + .unwrap(); + + ctx.say("Item added to todo list").await?; + + Ok(()) + } +} /// Add an item to the channel todo list #[poise::command( @@ -12,27 +51,5 @@ pub async fn add( ctx: Context<'_>, #[description = "The task to add to the todo list"] task: String, ) -> Result<(), Error> { - // ensure channel is cached - let _ = ctx.channel_data().await; - - sqlx::query!( - " - INSERT INTO todos (guild_id, channel_id, value) - VALUES ( - (SELECT id FROM guilds WHERE guild = ?), - (SELECT id FROM channels WHERE channel = ?), - ? - ) - ", - ctx.guild_id().unwrap().get(), - ctx.channel_id().get(), - task - ) - .execute(&ctx.data().database) - .await - .unwrap(); - - ctx.say("Item added to todo list").await?; - - Ok(()) + (Options { task }).run(ctx).await } diff --git a/src/commands/todo/channel/view.rs b/src/commands/todo/channel/view.rs index 3680912..11b2429 100644 --- a/src/commands/todo/channel/view.rs +++ b/src/commands/todo/channel/view.rs @@ -1,4 +1,44 @@ -use crate::{commands::todo::show_todo_page, Context, Error}; +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::todo::show_todo_page, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + let values = sqlx::query!( + " + SELECT todos.id, value FROM todos + INNER JOIN channels ON todos.channel_id = channels.id + WHERE channels.channel = ? + ", + ctx.channel_id().get(), + ) + .fetch_all(&ctx.data().database) + .await + .unwrap() + .iter() + .map(|row| (row.id as usize, row.value.clone())) + .collect::>(); + + let resp = show_todo_page( + &values, + 0, + None, + Some(ctx.channel_id().get()), + ctx.guild_id().map(|g| g.get()), + ); + + ctx.send(resp).await?; + + Ok(()) + } +} /// View and remove from the channel todo list #[poise::command( @@ -9,30 +49,5 @@ use crate::{commands::todo::show_todo_page, Context, Error}; default_member_permissions = "MANAGE_GUILD" )] pub async fn view(ctx: Context<'_>) -> Result<(), Error> { - let values = sqlx::query!( - " - SELECT todos.id, value FROM todos - INNER JOIN channels ON todos.channel_id = channels.id - WHERE channels.channel = ? - ", - ctx.channel_id().get(), - ) - .fetch_all(&ctx.data().database) - .await - .unwrap() - .iter() - .map(|row| (row.id as usize, row.value.clone())) - .collect::>(); - - let resp = show_todo_page( - &values, - 0, - None, - Some(ctx.channel_id().get()), - ctx.guild_id().map(|g| g.get()), - ); - - ctx.send(resp).await?; - - Ok(()) + (Options {}).run(ctx).await } diff --git a/src/commands/todo/guild/add.rs b/src/commands/todo/guild/add.rs index 97cd262..aa2c370 100644 --- a/src/commands/todo/guild/add.rs +++ b/src/commands/todo/guild/add.rs @@ -1,4 +1,36 @@ -use crate::{Context, Error}; +use serde::{Deserialize, Serialize}; + +use crate::{ + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options { + task: String, +} + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + sqlx::query!( + " + INSERT INTO todos (guild_id, value) + VALUES ( + (SELECT id FROM guilds WHERE guild = ?), ? + ) + ", + ctx.guild_id().unwrap().get(), + self.task + ) + .execute(&ctx.data().database) + .await + .unwrap(); + + ctx.say("Item added to todo list").await?; + + Ok(()) + } +} /// Add an item to the server todo list #[poise::command( @@ -12,20 +44,5 @@ pub async fn add( ctx: Context<'_>, #[description = "The task to add to the todo list"] task: String, ) -> Result<(), Error> { - sqlx::query!( - " - INSERT INTO todos (guild_id, value) - VALUES ( - (SELECT id FROM guilds WHERE guild = ?), ? - )", - ctx.guild_id().unwrap().get(), - task - ) - .execute(&ctx.data().database) - .await - .unwrap(); - - ctx.say("Item added to todo list").await?; - - Ok(()) + (Options { task }).run(ctx).await } diff --git a/src/commands/todo/guild/view.rs b/src/commands/todo/guild/view.rs index f555abc..c607e33 100644 --- a/src/commands/todo/guild/view.rs +++ b/src/commands/todo/guild/view.rs @@ -1,4 +1,38 @@ -use crate::{commands::todo::show_todo_page, Context, Error}; +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::todo::show_todo_page, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + let values = sqlx::query!( + " + SELECT todos.id, value FROM todos + INNER JOIN guilds ON todos.guild_id = guilds.id + WHERE guilds.guild = ? + ", + ctx.guild_id().unwrap().get(), + ) + .fetch_all(&ctx.data().database) + .await + .unwrap() + .iter() + .map(|row| (row.id as usize, row.value.clone())) + .collect::>(); + + let resp = show_todo_page(&values, 0, None, None, ctx.guild_id().map(|g| g.get())); + + ctx.send(resp).await?; + + Ok(()) + } +} /// View and remove from the server todo list #[poise::command( @@ -9,24 +43,5 @@ use crate::{commands::todo::show_todo_page, Context, Error}; default_member_permissions = "MANAGE_GUILD" )] pub async fn view(ctx: Context<'_>) -> Result<(), Error> { - let values = sqlx::query!( - " - SELECT todos.id, value FROM todos - INNER JOIN guilds ON todos.guild_id = guilds.id - WHERE guilds.guild = ? - ", - ctx.guild_id().unwrap().get(), - ) - .fetch_all(&ctx.data().database) - .await - .unwrap() - .iter() - .map(|row| (row.id as usize, row.value.clone())) - .collect::>(); - - let resp = show_todo_page(&values, 0, None, None, ctx.guild_id().map(|g| g.get())); - - ctx.send(resp).await?; - - Ok(()) + (Options {}).run(ctx).await } diff --git a/src/commands/todo/user/add.rs b/src/commands/todo/user/add.rs index 6c4bb25..1797ffb 100644 --- a/src/commands/todo/user/add.rs +++ b/src/commands/todo/user/add.rs @@ -1,4 +1,37 @@ -use crate::{Context, Error}; +use serde::{Deserialize, Serialize}; + +use crate::{ + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options { + task: String, +} + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + sqlx::query!( + " + INSERT INTO todos (user_id, value) + VALUES ( + (SELECT id FROM users WHERE user = ?), + ? + ) + ", + ctx.author().id.get(), + self.task + ) + .execute(&ctx.data().database) + .await + .unwrap(); + + ctx.say("Item added to todo list").await?; + + Ok(()) + } +} /// Add an item to your personal todo list #[poise::command(slash_command, rename = "add", identifying_name = "todo_user_add")] @@ -6,22 +39,5 @@ pub async fn add( ctx: Context<'_>, #[description = "The task to add to the todo list"] task: String, ) -> Result<(), Error> { - sqlx::query!( - " - INSERT INTO todos (user_id, value) - VALUES ( - (SELECT id FROM users WHERE user = ?), - ? - ) - ", - ctx.author().id.get(), - task - ) - .execute(&ctx.data().database) - .await - .unwrap(); - - ctx.say("Item added to todo list").await?; - - Ok(()) + (Options { task }).run(ctx).await } diff --git a/src/commands/todo/user/view.rs b/src/commands/todo/user/view.rs index 088f201..ecb61ab 100644 --- a/src/commands/todo/user/view.rs +++ b/src/commands/todo/user/view.rs @@ -1,26 +1,41 @@ -use crate::{commands::todo::show_todo_page, Context, Error}; +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::todo::show_todo_page, + utils::{Extract, Recordable}, + Context, Error, +}; + +#[derive(Serialize, Deserialize, Extract)] +pub struct Options; + +impl Recordable for Options { + async fn run(self, ctx: Context<'_>) -> Result<(), Error> { + let values = sqlx::query!( + " + SELECT todos.id, value FROM todos + INNER JOIN users ON todos.user_id = users.id + WHERE users.user = ? + ", + ctx.author().id.get(), + ) + .fetch_all(&ctx.data().database) + .await + .unwrap() + .iter() + .map(|row| (row.id as usize, row.value.clone())) + .collect::>(); + + let resp = show_todo_page(&values, 0, Some(ctx.author().id.get()), None, None); + + ctx.send(resp).await?; + + Ok(()) + } +} /// View and remove from your personal todo list #[poise::command(slash_command, rename = "view", identifying_name = "todo_user_view")] pub async fn view(ctx: Context<'_>) -> Result<(), Error> { - let values = sqlx::query!( - " - SELECT todos.id, value FROM todos - INNER JOIN users ON todos.user_id = users.id - WHERE users.user = ? - ", - ctx.author().id.get(), - ) - .fetch_all(&ctx.data().database) - .await - .unwrap() - .iter() - .map(|row| (row.id as usize, row.value.clone())) - .collect::>(); - - let resp = show_todo_page(&values, 0, Some(ctx.author().id.get()), None, None); - - ctx.send(resp).await?; - - Ok(()) + (Options {}).run(ctx).await } diff --git a/src/main.rs b/src/main.rs index 0cbf931..d778235 100644 --- a/src/main.rs +++ b/src/main.rs @@ -111,17 +111,14 @@ async fn _main(tx: Sender<()>) -> Result<(), Box> { dashboard::command(), timezone::command(), poise::Command { - subcommands: vec![ - allowed_dm::set_allowed_dm::set_allowed_dm(), - allowed_dm::unset_allowed_dm::unset_allowed_dm(), - ], + subcommands: vec![allowed_dm::set::set(), allowed_dm::unset::unset()], ..allowed_dm::allowed_dm() }, poise::Command { subcommands: vec![poise::Command { subcommands: vec![ - settings::ephemeral_confirmations::set_ephemeral_confirmations::set_ephemeral_confirmations(), - settings::ephemeral_confirmations::unset_ephemeral_confirmations::unset_ephemeral_confirmations(), + settings::ephemeral_confirmations::set::set(), + settings::ephemeral_confirmations::unset::unset(), ], ..settings::ephemeral_confirmations::ephemeral_confirmations() }], @@ -145,9 +142,9 @@ async fn _main(tx: Sender<()>) -> Result<(), Box> { delete::command(), poise::Command { subcommands: vec![ - timer::list_timer::list_timer(), - timer::start_timer::start_timer(), - timer::delete_timer::delete_timer(), + timer::list::list(), + timer::start::start(), + timer::delete::delete(), ], ..timer::timer() }, diff --git a/src/models/command_macro.rs b/src/models/command_macro.rs index 6095299..90fdf1e 100644 --- a/src/models/command_macro.rs +++ b/src/models/command_macro.rs @@ -10,6 +10,32 @@ use crate::{ #[derive(Serialize, Deserialize, Recordable)] #[serde(tag = "command_name")] pub enum RecordedCommand { + #[serde(rename = "unset_allowed_dm")] + UnsetAllowedDm(crate::commands::allowed_dm::unset::Options), + #[serde(rename = "set_allowed_dm")] + SetAllowedDm(crate::commands::allowed_dm::set::Options), + #[serde(rename = "unset_ephemeral_confirmations")] + UnsetEphemeralConfirmations(crate::commands::settings::ephemeral_confirmations::unset::Options), + #[serde(rename = "set_ephemeral_confirmations")] + SetEphemeralConfirmations(crate::commands::settings::ephemeral_confirmations::set::Options), + #[serde(rename = "timer_delete")] + TimerDelete(crate::commands::timer::delete::Options), + #[serde(rename = "timer_list")] + TimerList(crate::commands::timer::list::Options), + #[serde(rename = "timer_start")] + TimerStart(crate::commands::timer::start::Options), + #[serde(rename = "todo_channel_add")] + TodoChannelAdd(crate::commands::todo::channel::add::Options), + #[serde(rename = "todo_channel_view")] + TodoChannelView(crate::commands::todo::channel::view::Options), + #[serde(rename = "todo_guild_add")] + TodoGuildAdd(crate::commands::todo::guild::add::Options), + #[serde(rename = "todo_guild_view")] + TodoGuildView(crate::commands::todo::guild::view::Options), + #[serde(rename = "todo_user_add")] + TodoUserAdd(crate::commands::todo::user::add::Options), + #[serde(rename = "todo_user_view")] + TodoUserView(crate::commands::todo::user::view::Options), #[serde(rename = "clock")] Clock(crate::commands::clock::Options), #[serde(rename = "dashboard")] @@ -43,6 +69,45 @@ pub enum RecordedCommand { impl RecordedCommand { pub fn from_context(ctx: ApplicationContext) -> Option { match ctx.command().identifying_name.as_str() { + "unset_allowed_dm" => Some(Self::UnsetAllowedDm( + crate::commands::allowed_dm::unset::Options::extract(ctx), + )), + "set_allowed_dm" => { + Some(Self::SetAllowedDm(crate::commands::allowed_dm::set::Options::extract(ctx))) + } + "unset_ephemeral_confirmations" => Some(Self::UnsetEphemeralConfirmations( + crate::commands::settings::ephemeral_confirmations::unset::Options::extract(ctx), + )), + "set_ephemeral_confirmations" => Some(Self::SetEphemeralConfirmations( + crate::commands::settings::ephemeral_confirmations::set::Options::extract(ctx), + )), + "timer_delete" => { + Some(Self::TimerDelete(crate::commands::timer::delete::Options::extract(ctx))) + } + "timer_list" => { + Some(Self::TimerList(crate::commands::timer::list::Options::extract(ctx))) + } + "timer_start" => { + Some(Self::TimerStart(crate::commands::timer::start::Options::extract(ctx))) + } + "todo_channel_add" => Some(Self::TodoChannelAdd( + crate::commands::todo::channel::add::Options::extract(ctx), + )), + "todo_channel_view" => Some(Self::TodoChannelView( + crate::commands::todo::channel::view::Options::extract(ctx), + )), + "todo_guild_add" => { + Some(Self::TodoGuildAdd(crate::commands::todo::guild::add::Options::extract(ctx))) + } + "todo_guild_view" => { + Some(Self::TodoGuildView(crate::commands::todo::guild::view::Options::extract(ctx))) + } + "todo_user_add" => { + Some(Self::TodoUserAdd(crate::commands::todo::user::add::Options::extract(ctx))) + } + "todo_user_view" => { + Some(Self::TodoUserView(crate::commands::todo::user::view::Options::extract(ctx))) + } "clock" => Some(Self::Clock(crate::commands::clock::Options::extract(ctx))), "dashboard" => Some(Self::Dashboard(crate::commands::dashboard::Options::extract(ctx))), "delete" => Some(Self::Delete(crate::commands::delete::Options::extract(ctx))),