added functionality for reusable hook functions that will execute on commands
This commit is contained in:
@ -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: &(dyn CommandInvoke + Send + Sync)) {
|
||||
async fn info(ctx: &Context, invoke: 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: &(dyn CommandInvoke + Send + Sync)) {
|
||||
async fn donate(ctx: &Context, invoke: 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: &(dyn CommandInvoke + Send + Sync)) {
|
||||
async fn dashboard(ctx: &Context, invoke: CommandInvoke) {
|
||||
let footer = footer(ctx);
|
||||
|
||||
let _ = invoke
|
||||
@ -113,7 +113,7 @@ async fn dashboard(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
#[command]
|
||||
#[description("View the current time in your selected timezone")]
|
||||
#[group("Info")]
|
||||
async fn clock(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
async fn clock(ctx: &Context, invoke: CommandInvoke) {
|
||||
let ud = ctx.user_data(&invoke.author_id()).await.unwrap();
|
||||
let now = Utc::now().with_timezone(&ud.timezone());
|
||||
|
||||
|
@ -2,16 +2,21 @@ use chrono::offset::Utc;
|
||||
use chrono_tz::{Tz, TZ_VARIANTS};
|
||||
use levenshtein::levenshtein;
|
||||
use regex_command_attr::command;
|
||||
use serenity::{client::Context, model::misc::Mentionable};
|
||||
use serenity::{
|
||||
client::Context,
|
||||
model::{
|
||||
interactions::InteractionResponseType, misc::Mentionable,
|
||||
prelude::InteractionApplicationCommandCallbackDataFlags,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
component_models::{ComponentDataModel, Restrict},
|
||||
consts::THEME_COLOR,
|
||||
framework::{
|
||||
CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue, PermissionLevel,
|
||||
},
|
||||
models::{channel_data::ChannelData, CtxData},
|
||||
PopularTimezones, RegexFramework, SQLPool,
|
||||
framework::{CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue},
|
||||
hooks::{CHECK_GUILD_PERMISSIONS_HOOK, CHECK_MANAGED_PERMISSIONS_HOOK},
|
||||
models::{channel_data::ChannelData, command_macro::CommandMacro, CtxData},
|
||||
PopularTimezones, RecordingMacros, RegexFramework, SQLPool,
|
||||
};
|
||||
|
||||
#[command("blacklist")]
|
||||
@ -23,13 +28,9 @@ use crate::{
|
||||
required = false
|
||||
)]
|
||||
#[supports_dm(false)]
|
||||
#[required_permissions(Restricted)]
|
||||
#[hook(CHECK_GUILD_PERMISSIONS_HOOK)]
|
||||
#[can_blacklist(false)]
|
||||
async fn blacklist(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||
args: CommandOptions,
|
||||
) {
|
||||
async fn blacklist(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let channel = match args.get("channel") {
|
||||
@ -72,7 +73,7 @@ async fn blacklist(
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
async fn timezone(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
async fn timezone(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
let mut user_data = ctx.user_data(invoke.author_id()).await.unwrap();
|
||||
|
||||
@ -178,8 +179,8 @@ You may want to use one of the popular timezones below, otherwise click [here](h
|
||||
#[command("prefix")]
|
||||
#[description("Configure a prefix for text-based commands (deprecated)")]
|
||||
#[supports_dm(false)]
|
||||
#[required_permissions(Restricted)]
|
||||
async fn prefix(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: String) {
|
||||
#[hook(CHECK_GUILD_PERMISSIONS_HOOK)]
|
||||
async fn prefix(ctx: &Context, invoke: CommandInvoke, args: String) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let guild_data = ctx.guild_data(invoke.guild_id().unwrap()).await.unwrap();
|
||||
@ -222,8 +223,8 @@ async fn prefix(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args:
|
||||
required = true
|
||||
)]
|
||||
#[supports_dm(false)]
|
||||
#[required_permissions(Restricted)]
|
||||
async fn restrict(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
#[hook(CHECK_GUILD_PERMISSIONS_HOOK)]
|
||||
async fn restrict(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
let framework = ctx.data.read().await.get::<RegexFramework>().cloned().unwrap();
|
||||
|
||||
@ -240,7 +241,7 @@ async fn restrict(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), arg
|
||||
let restrictable_commands = framework
|
||||
.commands
|
||||
.iter()
|
||||
.filter(|c| c.required_permissions == PermissionLevel::Managed)
|
||||
.filter(|c| c.hooks.contains(&&CHECK_MANAGED_PERMISSIONS_HOOK))
|
||||
.map(|c| c.names[0].to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
@ -289,6 +290,132 @@ async fn restrict(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), arg
|
||||
}
|
||||
}
|
||||
|
||||
#[command("macro")]
|
||||
#[description("Record and replay command sequences")]
|
||||
#[subcommand("record")]
|
||||
#[description("Start recording up to 5 commands to replay")]
|
||||
#[arg(name = "name", description = "Name for the new macro", kind = "String", required = true)]
|
||||
#[arg(
|
||||
name = "description",
|
||||
description = "Description for the new macro",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[subcommand("finish")]
|
||||
#[description("Finish current recording")]
|
||||
#[subcommand("list")]
|
||||
#[description("List recorded macros")]
|
||||
#[subcommand("run")]
|
||||
#[description("Run a recorded macro")]
|
||||
#[arg(name = "name", description = "Name of the macro to run", 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();
|
||||
|
||||
match args.subcommand.clone().unwrap().as_str() {
|
||||
"record" => {
|
||||
let macro_buffer = ctx.data.read().await.get::<RecordingMacros>().cloned().unwrap();
|
||||
|
||||
{
|
||||
let mut lock = macro_buffer.write().await;
|
||||
|
||||
let guild_id = interaction.guild_id.unwrap();
|
||||
|
||||
lock.insert(
|
||||
(guild_id, interaction.user.id),
|
||||
CommandMacro {
|
||||
guild_id,
|
||||
name: args.get("name").unwrap().to_string(),
|
||||
description: args.get("description").map(|d| d.to_string()),
|
||||
commands: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|d| {
|
||||
d.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
.create_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 macro_buffer = ctx.data.read().await.get::<RecordingMacros>().cloned().unwrap();
|
||||
|
||||
{
|
||||
let lock = macro_buffer.read().await;
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
.await;
|
||||
} else {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().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 = ?), ?, ?, ?)",
|
||||
command_macro.guild_id.0,
|
||||
command_macro.name,
|
||||
command_macro.description,
|
||||
json
|
||||
)
|
||||
.execute(&pool)
|
||||
.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)
|
||||
})
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut lock = macro_buffer.write().await;
|
||||
lock.remove(&key);
|
||||
}
|
||||
}
|
||||
"list" => {}
|
||||
"run" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[command("alias")]
|
||||
#[supports_dm(false)]
|
||||
|
@ -5,16 +5,13 @@ use std::{
|
||||
};
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
use chrono_tz::Tz;
|
||||
use num_integer::Integer;
|
||||
use regex_command_attr::command;
|
||||
use serenity::{
|
||||
builder::CreateEmbed,
|
||||
builder::{CreateEmbed, CreateInteractionResponse},
|
||||
client::Context,
|
||||
model::{
|
||||
channel::Channel,
|
||||
id::{GuildId, UserId},
|
||||
interactions::InteractionResponseType,
|
||||
},
|
||||
model::{channel::Channel, interactions::InteractionResponseType},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -28,6 +25,7 @@ use crate::{
|
||||
REGEX_NATURAL_COMMAND_2, THEME_COLOR,
|
||||
},
|
||||
framework::{CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue},
|
||||
hooks::{CHECK_GUILD_PERMISSIONS_HOOK, CHECK_MANAGED_PERMISSIONS_HOOK},
|
||||
models::{
|
||||
channel_data::ChannelData,
|
||||
guild_data::GuildData,
|
||||
@ -55,8 +53,8 @@ use crate::{
|
||||
required = false
|
||||
)]
|
||||
#[supports_dm(false)]
|
||||
#[required_permissions(Restricted)]
|
||||
async fn pause(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
#[hook(CHECK_GUILD_PERMISSIONS_HOOK)]
|
||||
async fn pause(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await;
|
||||
@ -141,8 +139,8 @@ async fn pause(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args:
|
||||
kind = "Integer",
|
||||
required = false
|
||||
)]
|
||||
#[required_permissions(Restricted)]
|
||||
async fn offset(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
#[hook(CHECK_GUILD_PERMISSIONS_HOOK)]
|
||||
async fn offset(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let combined_time = args.get("hours").map_or(0, |h| h.as_i64().unwrap() * 3600)
|
||||
@ -218,8 +216,8 @@ WHERE FIND_IN_SET(channels.`channel`, ?)",
|
||||
kind = "Integer",
|
||||
required = false
|
||||
)]
|
||||
#[required_permissions(Restricted)]
|
||||
async fn nudge(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
#[hook(CHECK_GUILD_PERMISSIONS_HOOK)]
|
||||
async fn nudge(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let combined_time = args.get("minutes").map_or(0, |m| m.as_i64().unwrap() * 60)
|
||||
@ -270,8 +268,8 @@ async fn nudge(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args:
|
||||
kind = "Boolean",
|
||||
required = false
|
||||
)]
|
||||
#[required_permissions(Managed)]
|
||||
async fn look(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
#[hook(CHECK_MANAGED_PERMISSIONS_HOOK)]
|
||||
async fn look(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await;
|
||||
@ -363,103 +361,132 @@ async fn look(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: C
|
||||
|
||||
#[command("del")]
|
||||
#[description("Delete reminders")]
|
||||
#[required_permissions(Managed)]
|
||||
async fn delete(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
#[hook(CHECK_MANAGED_PERMISSIONS_HOOK)]
|
||||
async fn delete(ctx: &Context, invoke: CommandInvoke, _args: CommandOptions) {
|
||||
let interaction = invoke.interaction().unwrap();
|
||||
|
||||
let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await;
|
||||
let timezone = ctx.timezone(interaction.user.id).await;
|
||||
|
||||
let reminders = Reminder::from_guild(ctx, invoke.guild_id(), invoke.author_id()).await;
|
||||
let reminders = Reminder::from_guild(ctx, interaction.guild_id, interaction.user.id).await;
|
||||
|
||||
if reminders.is_empty() {
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content("No reminders to delete!"),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
let mut char_count = 0;
|
||||
let resp = show_delete_page(&reminders, 0, timezone).await;
|
||||
|
||||
let (shown_reminders, display_vec): (Vec<&Reminder>, Vec<String>) = reminders
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(count, reminder)| (reminder, reminder.display_del(count, &timezone)))
|
||||
.take_while(|(_, p)| {
|
||||
char_count += p.len();
|
||||
|
||||
char_count < EMBED_DESCRIPTION_MAX_LENGTH
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let display = display_vec.join("\n");
|
||||
|
||||
let pages = reminders
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(count, reminder)| reminder.display_del(count, &timezone))
|
||||
.fold(0, |t, r| t + r.len())
|
||||
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||
|
||||
let pager = DelPager::new(timezone);
|
||||
|
||||
let del_selector = ComponentDataModel::DelSelector(DelSelector { page: 0, timezone });
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.embed(|e| {
|
||||
e.title("Delete Reminders")
|
||||
.description(display)
|
||||
.footer(|f| f.text(format!("Page {} of {}", 1, pages)))
|
||||
.color(*THEME_COLOR)
|
||||
})
|
||||
.components(|comp| {
|
||||
pager.create_button_row(pages, comp);
|
||||
|
||||
comp.create_action_row(|row| {
|
||||
row.create_select_menu(|menu| {
|
||||
menu.custom_id(del_selector.to_custom_id()).options(|opt| {
|
||||
for (count, reminder) in shown_reminders.iter().enumerate() {
|
||||
opt.create_option(|o| {
|
||||
o.label(count + 1).value(reminder.id).description({
|
||||
let c = reminder.display_content();
|
||||
|
||||
if c.len() > 100 {
|
||||
format!(
|
||||
"{}...",
|
||||
reminder
|
||||
.display_content()
|
||||
.chars()
|
||||
.take(97)
|
||||
.collect::<String>()
|
||||
)
|
||||
} else {
|
||||
c.to_string()
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
opt
|
||||
})
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
*r = resp;
|
||||
r
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn show_delete_page(
|
||||
ctx: &Context,
|
||||
guild_id: Option<GuildId>,
|
||||
user_id: UserId,
|
||||
pub fn max_delete_page(reminders: &Vec<Reminder>, timezone: &Tz) -> usize {
|
||||
reminders
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(count, reminder)| reminder.display_del(count, timezone))
|
||||
.fold(0, |t, r| t + r.len())
|
||||
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH)
|
||||
}
|
||||
|
||||
pub async fn show_delete_page(
|
||||
reminders: &Vec<Reminder>,
|
||||
page: usize,
|
||||
timezone: Tz,
|
||||
) {
|
||||
) -> CreateInteractionResponse {
|
||||
let pager = DelPager::new(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.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
||||
|response| {
|
||||
response.embeds(vec![embed]).components(|comp| {
|
||||
pager.create_button_row(0, comp);
|
||||
comp
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
let pages = max_delete_page(&reminders, &timezone);
|
||||
|
||||
let mut page = page;
|
||||
if page >= pages {
|
||||
page = pages - 1;
|
||||
}
|
||||
|
||||
let mut char_count = 0;
|
||||
let mut skip_char_count = 0;
|
||||
let mut first_num = 0;
|
||||
|
||||
let (shown_reminders, display_vec): (Vec<&Reminder>, Vec<String>) = reminders
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(count, reminder)| (reminder, reminder.display_del(count, &timezone)))
|
||||
.skip_while(|(_, p)| {
|
||||
first_num += 1;
|
||||
skip_char_count += p.len();
|
||||
|
||||
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * page
|
||||
})
|
||||
.take_while(|(_, p)| {
|
||||
char_count += p.len();
|
||||
|
||||
char_count < EMBED_DESCRIPTION_MAX_LENGTH
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let display = display_vec.join("\n");
|
||||
|
||||
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.kind(InteractionResponseType::UpdateMessage).interaction_response_data(|d| {
|
||||
d.embeds(vec![embed]).components(|comp| {
|
||||
pager.create_button_row(pages, comp);
|
||||
|
||||
comp.create_action_row(|row| {
|
||||
row.create_select_menu(|menu| {
|
||||
menu.custom_id(del_selector.to_custom_id()).options(|opt| {
|
||||
for (count, reminder) in shown_reminders.iter().enumerate() {
|
||||
opt.create_option(|o| {
|
||||
o.label(count + first_num).value(reminder.id).description({
|
||||
let c = reminder.display_content();
|
||||
|
||||
if c.len() > 100 {
|
||||
format!(
|
||||
"{}...",
|
||||
reminder
|
||||
.display_content()
|
||||
.chars()
|
||||
.take(97)
|
||||
.collect::<String>()
|
||||
)
|
||||
} else {
|
||||
c.to_string()
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
opt
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
response
|
||||
}
|
||||
|
||||
#[command("timer")]
|
||||
@ -472,8 +499,8 @@ async fn show_delete_page(
|
||||
#[subcommand("delete")]
|
||||
#[description("Delete a timer")]
|
||||
#[arg(name = "name", description = "Name of the timer to delete", kind = "String", required = true)]
|
||||
#[required_permissions(Managed)]
|
||||
async fn timer(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
#[hook(CHECK_MANAGED_PERMISSIONS_HOOK)]
|
||||
async fn timer(ctx: &Context, invoke: 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);
|
||||
@ -638,8 +665,8 @@ DELETE FROM timers WHERE owner = ? AND name = ?
|
||||
kind = "Boolean",
|
||||
required = false
|
||||
)]
|
||||
#[required_permissions(Managed)]
|
||||
async fn remind(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
#[hook(CHECK_MANAGED_PERMISSIONS_HOOK)]
|
||||
async fn remind(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
||||
let interaction = invoke.interaction().unwrap();
|
||||
|
||||
// defer response since processing times can take some time
|
||||
@ -650,7 +677,7 @@ async fn remind(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args:
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_data = ctx.user_data(invoke.author_id()).await.unwrap();
|
||||
let user_data = ctx.user_data(interaction.user.id).await.unwrap();
|
||||
let timezone = user_data.timezone();
|
||||
|
||||
let time = {
|
||||
@ -675,7 +702,7 @@ async fn remind(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args:
|
||||
.unwrap_or(vec![]);
|
||||
|
||||
if list.is_empty() {
|
||||
vec![ReminderScope::Channel(invoke.channel_id().0)]
|
||||
vec![ReminderScope::Channel(interaction.channel_id.0)]
|
||||
} else {
|
||||
list
|
||||
}
|
||||
@ -698,7 +725,7 @@ async fn remind(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args:
|
||||
}
|
||||
};
|
||||
|
||||
let mut builder = MultiReminderBuilder::new(ctx, invoke.guild_id())
|
||||
let mut builder = MultiReminderBuilder::new(ctx, interaction.guild_id)
|
||||
.author(user_data)
|
||||
.content(content)
|
||||
.time(time)
|
||||
|
Reference in New Issue
Block a user