Rearranged some commands

Working on a macro to automatically add option wrappers
This commit is contained in:
jude
2024-02-17 14:09:01 +00:00
parent d0833b7bca
commit eb92eacb90
19 changed files with 340 additions and 238 deletions

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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;

View File

@ -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(())
}

View File

@ -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<T: Serialize + DeserializeOwned, R> {
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::<Punctuated<Box<Pat>, 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<T> {
args: T
func: Func<T>
}
impl Execute<T> for RecordableCommand<T> {
fn execute() {
// Unpack self.args into self.func
}
}
struct command_args {
...args
}
fn command_fn(...args) {
...block
}
*/

View File

@ -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;

View File

@ -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<Utc>) -> 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 {

View File

@ -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(())
}

View File

@ -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<Utc>) -> 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(())
}

View File

@ -0,0 +1,4 @@
pub mod delete_timer;
pub mod list_timer;
pub mod start_timer;
pub mod timer_base;

View File

@ -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(())
}

View File

@ -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(())
}