...
This commit is contained in:
parent
a9c91bee93
commit
a0974795e1
@ -32,7 +32,7 @@ async fn info(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
let current_user = ctx.cache.current_user();
|
||||
let footer = footer(ctx);
|
||||
|
||||
invoke
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
@ -40,15 +40,13 @@ async fn info(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
.description(format!(
|
||||
"Default prefix: `{default_prefix}`
|
||||
Reset prefix: `@{user} prefix {default_prefix}`
|
||||
Help: `{prefix}help`**Welcome \
|
||||
to Reminder Bot!**
|
||||
Help: `{prefix}help`**Welcome to Reminder Bot!**
|
||||
Developer: <@203532103185465344>
|
||||
Icon: <@253202252821430272>
|
||||
Find me on https://discord.jellywx.com \
|
||||
and on https://github.com/JellyWX :)
|
||||
Find me on https://discord.jellywx.com and on https://github.com/JellyWX :)
|
||||
|
||||
Invite the bot: https://invite.reminder-bot.com/Use our dashboard: \
|
||||
https://reminder-bot.com/",
|
||||
Invite the bot: https://invite.reminder-bot.com/
|
||||
Use our dashboard: https://reminder-bot.com/",
|
||||
default_prefix = *DEFAULT_PREFIX,
|
||||
user = current_user.name,
|
||||
prefix = prefix
|
||||
@ -66,7 +64,7 @@ Invite the bot: https://invite.reminder-bot.com/Use our dashboard: \
|
||||
async fn donate(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
let footer = footer(ctx);
|
||||
|
||||
invoke
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
@ -99,7 +97,7 @@ Just $2 USD/month!
|
||||
async fn dashboard(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
let footer = footer(ctx);
|
||||
|
||||
invoke
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
@ -119,7 +117,7 @@ async fn clock(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
let ud = ctx.user_data(&invoke.author_id()).await.unwrap();
|
||||
let now = Utc::now().with_timezone(&ud.timezone());
|
||||
|
||||
invoke
|
||||
let _ = invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content(format!("Current time: {}", now.format("%H:%M"))),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
default::Default,
|
||||
collections::HashSet,
|
||||
string::ToString,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
@ -7,7 +7,15 @@ use std::{
|
||||
use chrono::NaiveDateTime;
|
||||
use num_integer::Integer;
|
||||
use regex_command_attr::command;
|
||||
use serenity::{client::Context, model::channel::Channel};
|
||||
use serenity::{
|
||||
builder::CreateEmbed,
|
||||
client::Context,
|
||||
model::{
|
||||
channel::Channel,
|
||||
id::{GuildId, UserId},
|
||||
interactions::InteractionResponseType,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
check_subscription_on_message,
|
||||
@ -17,7 +25,7 @@ use crate::{
|
||||
},
|
||||
consts::{
|
||||
EMBED_DESCRIPTION_MAX_LENGTH, REGEX_CHANNEL_USER, REGEX_NATURAL_COMMAND_1,
|
||||
REGEX_NATURAL_COMMAND_2, REGEX_REMIND_COMMAND, THEME_COLOR,
|
||||
REGEX_NATURAL_COMMAND_2, THEME_COLOR,
|
||||
},
|
||||
framework::{CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue},
|
||||
models::{
|
||||
@ -26,6 +34,7 @@ use crate::{
|
||||
reminder::{
|
||||
builder::{MultiReminderBuilder, ReminderScope},
|
||||
content::Content,
|
||||
errors::ReminderError,
|
||||
look_flags::{LookFlags, TimeDisplayType},
|
||||
Reminder,
|
||||
},
|
||||
@ -33,7 +42,7 @@ use crate::{
|
||||
user_data::UserData,
|
||||
CtxData,
|
||||
},
|
||||
time_parser::{natural_parser, TimeParser},
|
||||
time_parser::natural_parser,
|
||||
SQLPool,
|
||||
};
|
||||
|
||||
@ -444,6 +453,15 @@ async fn delete(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn show_delete_page(
|
||||
ctx: &Context,
|
||||
guild_id: Option<GuildId>,
|
||||
user_id: UserId,
|
||||
page: usize,
|
||||
timezone: Tz,
|
||||
) {
|
||||
}
|
||||
|
||||
#[command("timer")]
|
||||
#[description("Manage timers")]
|
||||
#[subcommand("list")]
|
||||
@ -582,23 +600,168 @@ DELETE FROM timers WHERE owner = ? AND name = ?
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(PartialEq)]
|
||||
enum RemindCommand {
|
||||
Remind,
|
||||
Interval,
|
||||
}
|
||||
|
||||
#[command("remind")]
|
||||
#[permission_level(Managed)]
|
||||
async fn remind(ctx: &Context, msg: &Message, args: String) {
|
||||
remind_command(ctx, msg, args, RemindCommand::Remind).await;
|
||||
#[description("Create a new reminder")]
|
||||
#[arg(
|
||||
name = "time",
|
||||
description = "A description of the time to set the reminder for",
|
||||
kind = "String",
|
||||
required = true
|
||||
)]
|
||||
#[arg(
|
||||
name = "content",
|
||||
description = "The message content to send",
|
||||
kind = "String",
|
||||
required = true
|
||||
)]
|
||||
#[arg(
|
||||
name = "channels",
|
||||
description = "Channel or user mentions to set the reminder for",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "repeat",
|
||||
description = "(Patreon only) Time to wait before repeating the reminder. Leave blank for one-shot reminder",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "expires",
|
||||
description = "(Patreon only) For repeating reminders, the time at which the reminder will stop sending",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "tts",
|
||||
description = "Set the TTS flag on the reminder message (like the /tts command)",
|
||||
kind = "Boolean",
|
||||
required = false
|
||||
)]
|
||||
#[required_permissions(Managed)]
|
||||
async fn remind(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: CommandOptions) {
|
||||
let interaction = invoke.interaction().unwrap();
|
||||
|
||||
// defer response since processing times can take some time
|
||||
interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::DeferredChannelMessageWithSource)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_data = ctx.user_data(invoke.author_id()).await.unwrap();
|
||||
let timezone = user_data.timezone();
|
||||
|
||||
let time = {
|
||||
let time_str = args.get("time").unwrap().to_string();
|
||||
|
||||
natural_parser(&time_str, &timezone.to_string()).await
|
||||
};
|
||||
|
||||
match time {
|
||||
Some(time) => {
|
||||
let content = {
|
||||
let content = args.get("content").unwrap().to_string();
|
||||
let tts = args.get("tts").map_or(false, |arg| arg.as_bool().unwrap_or(false));
|
||||
|
||||
Content { content, tts, attachment: None, attachment_name: None }
|
||||
};
|
||||
|
||||
let scopes = {
|
||||
let list = args
|
||||
.get("channels")
|
||||
.map(|arg| parse_mention_list(&arg.to_string()))
|
||||
.unwrap_or(vec![]);
|
||||
|
||||
if list.is_empty() {
|
||||
vec![ReminderScope::Channel(invoke.channel_id().0)]
|
||||
} else {
|
||||
list
|
||||
}
|
||||
};
|
||||
|
||||
let interval = args
|
||||
.get("repeat")
|
||||
.map(|arg| {
|
||||
humantime::parse_duration(&arg.to_string())
|
||||
.or_else(|_| humantime::parse_duration(&format!("1 {}", arg.to_string())))
|
||||
.map(|duration| duration.as_secs() as i64)
|
||||
.ok()
|
||||
})
|
||||
.flatten();
|
||||
let expires = {
|
||||
if let Some(arg) = args.get("expires") {
|
||||
natural_parser(&arg.to_string(), &timezone.to_string()).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut builder = MultiReminderBuilder::new(ctx, invoke.guild_id())
|
||||
.author(user_data)
|
||||
.content(content)
|
||||
.time(time)
|
||||
.expires(expires)
|
||||
.interval(interval);
|
||||
|
||||
builder.set_scopes(scopes);
|
||||
|
||||
let (errors, successes) = builder.build().await;
|
||||
|
||||
let embed = create_response(successes, errors, time);
|
||||
|
||||
interaction
|
||||
.edit_original_interaction_response(&ctx, |r| r.add_embed(embed))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
None => {
|
||||
let _ = interaction
|
||||
.edit_original_interaction_response(&ctx, |r| {
|
||||
r.content("Time could not be processed.")
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[command("interval")]
|
||||
#[permission_level(Managed)]
|
||||
async fn interval(ctx: &Context, msg: &Message, args: String) {
|
||||
remind_command(ctx, msg, args, RemindCommand::Interval).await;
|
||||
fn create_response(
|
||||
successes: HashSet<ReminderScope>,
|
||||
errors: HashSet<ReminderError>,
|
||||
time: i64,
|
||||
) -> CreateEmbed {
|
||||
let success_part = match successes.len() {
|
||||
0 => "".to_string(),
|
||||
n => format!(
|
||||
"Reminder{s} for {locations} set for <t:{offset}:R>",
|
||||
s = if n > 1 { "s" } else { "" },
|
||||
locations = successes.iter().map(|l| l.mention()).collect::<Vec<String>>().join(", "),
|
||||
offset = time
|
||||
),
|
||||
};
|
||||
|
||||
let error_part = match errors.len() {
|
||||
0 => "".to_string(),
|
||||
n => format!(
|
||||
"{n} reminder{s} failed to set:\n{errors}",
|
||||
s = if n > 1 { "s" } else { "" },
|
||||
n = n,
|
||||
errors = errors.iter().map(|e| e.to_string()).collect::<Vec<String>>().join("\n")
|
||||
),
|
||||
};
|
||||
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed
|
||||
.title(format!(
|
||||
"{n} Reminder{s} Set",
|
||||
n = successes.len(),
|
||||
s = if successes.len() > 1 { "s" } else { "" }
|
||||
))
|
||||
.description(format!("{}\n\n{}", success_part, error_part))
|
||||
.color(*THEME_COLOR);
|
||||
|
||||
embed
|
||||
}
|
||||
|
||||
fn parse_mention_list(mentions: &str) -> Vec<ReminderScope> {
|
||||
@ -617,158 +780,7 @@ fn parse_mention_list(mentions: &str) -> Vec<ReminderScope> {
|
||||
.collect::<Vec<ReminderScope>>()
|
||||
}
|
||||
|
||||
async fn remind_command(ctx: &Context, msg: &Message, args: String, command: RemindCommand) {
|
||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
||||
|
||||
let timezone = UserData::timezone_of(&msg.author, &pool).await;
|
||||
let language = UserData::language_of(&msg.author, &pool).await;
|
||||
|
||||
match REGEX_REMIND_COMMAND.captures(&args) {
|
||||
Some(captures) => {
|
||||
let parsed = parse_mention_list(captures.name("mentions").unwrap().as_str());
|
||||
|
||||
let scopes = if parsed.is_empty() {
|
||||
vec![ReminderScope::Channel(msg.channel_id.into())]
|
||||
} else {
|
||||
parsed
|
||||
};
|
||||
|
||||
let time_parser = TimeParser::new(captures.name("time").unwrap().as_str(), timezone);
|
||||
|
||||
let expires_parser =
|
||||
captures.name("expires").map(|mat| TimeParser::new(mat.as_str(), timezone));
|
||||
|
||||
let interval_parser = captures
|
||||
.name("interval")
|
||||
.map(|mat| TimeParser::new(mat.as_str(), timezone))
|
||||
.map(|parser| parser.displacement())
|
||||
.transpose();
|
||||
|
||||
if let Ok(interval) = interval_parser {
|
||||
if interval.is_some() && !check_subscription_on_message(&ctx, msg).await {
|
||||
// no patreon
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.say(
|
||||
&ctx,
|
||||
lm.get(&language, "interval/donor")
|
||||
.replace("{prefix}", &ctx.prefix(msg.guild_id).await),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
let content_res = Content::build(
|
||||
captures.name("content").map(|mat| mat.as_str()).unwrap(),
|
||||
msg,
|
||||
)
|
||||
.await;
|
||||
|
||||
match content_res {
|
||||
Ok(mut content) => {
|
||||
if let Some(guild) = msg.guild(&ctx) {
|
||||
content.substitute(guild);
|
||||
}
|
||||
|
||||
let user_data = ctx.user_data(&msg.author).await.unwrap();
|
||||
|
||||
let mut builder = MultiReminderBuilder::new(ctx, msg.guild_id)
|
||||
.author(user_data)
|
||||
.content(content)
|
||||
.interval(interval)
|
||||
.expires_parser(expires_parser)
|
||||
.time_parser(time_parser.clone());
|
||||
|
||||
builder.set_scopes(scopes);
|
||||
|
||||
let (errors, successes) = builder.build().await;
|
||||
|
||||
let success_part = match successes.len() {
|
||||
0 => "".to_string(),
|
||||
n => format!(
|
||||
"Reminder{s} for {locations} set for <t:{offset}:R>",
|
||||
s = if n > 1 { "s" } else { "" },
|
||||
locations = successes
|
||||
.iter()
|
||||
.map(|l| l.mention())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
offset = time_parser.timestamp().unwrap()
|
||||
),
|
||||
};
|
||||
|
||||
let error_part = match errors.len() {
|
||||
0 => "".to_string(),
|
||||
n => format!(
|
||||
"{n} reminder{s} failed to set:\n{errors}",
|
||||
s = if n > 1 { "s" } else { "" },
|
||||
n = n,
|
||||
errors = errors
|
||||
.iter()
|
||||
.map(|e| e.display(false))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
),
|
||||
};
|
||||
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(&ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title(format!(
|
||||
"{n} Reminder{s} Set",
|
||||
n = successes.len(),
|
||||
s = if successes.len() > 1 { "s" } else { "" }
|
||||
))
|
||||
.description(format!("{}\n\n{}", success_part, error_part))
|
||||
.color(*THEME_COLOR)
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
Err(content_error) => {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(ctx, |m| {
|
||||
m.embed(move |e| {
|
||||
e.title("0 Reminders Set")
|
||||
.description(content_error.to_string())
|
||||
.color(*THEME_COLOR)
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = msg
|
||||
.channel_id
|
||||
.send_message(ctx, |m| {
|
||||
m.embed(move |e| {
|
||||
e.title(lm.get(&language, "remind/title").replace("{number}", "0"))
|
||||
.description(lm.get(&language, "interval/invalid_interval"))
|
||||
.color(*THEME_COLOR)
|
||||
})
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
let prefix = ctx.prefix(msg.guild_id).await;
|
||||
|
||||
match command {
|
||||
RemindCommand::Remind => {
|
||||
command_help(ctx, msg, lm, &prefix, &language, "remind").await
|
||||
}
|
||||
|
||||
RemindCommand::Interval => {
|
||||
command_help(ctx, msg, lm, &prefix, &language, "interval").await
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[command("natural")]
|
||||
#[permission_level(Managed)]
|
||||
async fn natural(ctx: &Context, msg: &Message, args: String) {
|
||||
|
@ -272,6 +272,34 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
let reminders =
|
||||
Reminder::from_guild(ctx, component.guild_id, component.user.id).await;
|
||||
|
||||
if reminders.is_empty() {
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed.title("Delete Reminders").description("No Reminders").color(*THEME_COLOR);
|
||||
|
||||
component
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|response| {
|
||||
response.embeds(vec![embed]).components(|comp| comp)
|
||||
})
|
||||
})
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let pages = reminders
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(count, reminder)| reminder.display_del(count, &selector.timezone))
|
||||
.fold(0, |t, r| t + r.len())
|
||||
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||
|
||||
let mut page = selector.page;
|
||||
if page >= pages {
|
||||
page = pages - 1;
|
||||
}
|
||||
|
||||
let mut char_count = 0;
|
||||
let mut skip_char_count = 0;
|
||||
let mut first_num = 0;
|
||||
@ -286,7 +314,7 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
first_num += 1;
|
||||
skip_char_count += p.len();
|
||||
|
||||
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * selector.page
|
||||
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * page
|
||||
})
|
||||
.take_while(|(_, p)| {
|
||||
char_count += p.len();
|
||||
@ -297,17 +325,10 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
|
||||
let display = display_vec.join("\n");
|
||||
|
||||
let pages = reminders
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(count, reminder)| reminder.display_del(count, &selector.timezone))
|
||||
.fold(0, |t, r| t + r.len())
|
||||
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||
|
||||
let pager = DelPager::new(selector.timezone);
|
||||
|
||||
let del_selector = ComponentDataModel::DelSelector(DelSelector {
|
||||
page: selector.page,
|
||||
page,
|
||||
timezone: selector.timezone,
|
||||
});
|
||||
|
||||
@ -315,7 +336,7 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
embed
|
||||
.title("Delete Reminders")
|
||||
.description(display)
|
||||
.footer(|f| f.text(format!("Page {} of {}", selector.page + 1, pages)))
|
||||
.footer(|f| f.text(format!("Page {} of {}", page + 1, pages)))
|
||||
.color(*THEME_COLOR);
|
||||
|
||||
component
|
||||
@ -333,7 +354,7 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
shown_reminders.iter().enumerate()
|
||||
{
|
||||
opt.create_option(|o| {
|
||||
o.label(count + 1)
|
||||
o.label(count + first_num)
|
||||
.value(reminder.id)
|
||||
.description({
|
||||
let c =
|
||||
|
@ -16,8 +16,9 @@ pub trait Pager {
|
||||
enum PageAction {
|
||||
First = 0,
|
||||
Previous = 1,
|
||||
Next = 2,
|
||||
Last = 3,
|
||||
Refresh = 2,
|
||||
Next = 3,
|
||||
Last = 4,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -33,6 +34,7 @@ impl Pager for LookPager {
|
||||
match self.action {
|
||||
PageAction::First => 0,
|
||||
PageAction::Previous => 0.max(self.page - 1),
|
||||
PageAction::Refresh => self.page,
|
||||
PageAction::Next => (max_pages - 1).min(self.page + 1),
|
||||
PageAction::Last => max_pages - 1,
|
||||
}
|
||||
@ -41,7 +43,7 @@ impl Pager for LookPager {
|
||||
fn create_button_row(&self, max_pages: usize, comp: &mut CreateComponents) {
|
||||
let next_page = self.next_page(max_pages);
|
||||
|
||||
let (page_first, page_prev, page_next, page_last) =
|
||||
let (page_first, page_prev, page_refresh, page_next, page_last) =
|
||||
LookPager::buttons(self.flags, next_page, self.timezone);
|
||||
|
||||
comp.create_action_row(|row| {
|
||||
@ -57,6 +59,9 @@ impl Pager for LookPager {
|
||||
.custom_id(page_prev.to_custom_id())
|
||||
.disabled(next_page == 0)
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.label("🔁").style(ButtonStyle::Secondary).custom_id(page_refresh.to_custom_id())
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.label("▶️")
|
||||
.style(ButtonStyle::Secondary)
|
||||
@ -82,7 +87,13 @@ impl LookPager {
|
||||
flags: LookFlags,
|
||||
page: usize,
|
||||
timezone: Tz,
|
||||
) -> (ComponentDataModel, ComponentDataModel, ComponentDataModel, ComponentDataModel) {
|
||||
) -> (
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
) {
|
||||
(
|
||||
ComponentDataModel::LookPager(LookPager {
|
||||
flags,
|
||||
@ -96,6 +107,12 @@ impl LookPager {
|
||||
action: PageAction::Previous,
|
||||
timezone,
|
||||
}),
|
||||
ComponentDataModel::LookPager(LookPager {
|
||||
flags,
|
||||
page,
|
||||
action: PageAction::Refresh,
|
||||
timezone,
|
||||
}),
|
||||
ComponentDataModel::LookPager(LookPager {
|
||||
flags,
|
||||
page,
|
||||
@ -124,6 +141,7 @@ impl Pager for DelPager {
|
||||
match self.action {
|
||||
PageAction::First => 0,
|
||||
PageAction::Previous => 0.max(self.page - 1),
|
||||
PageAction::Refresh => self.page,
|
||||
PageAction::Next => (max_pages - 1).min(self.page + 1),
|
||||
PageAction::Last => max_pages - 1,
|
||||
}
|
||||
@ -132,7 +150,7 @@ impl Pager for DelPager {
|
||||
fn create_button_row(&self, max_pages: usize, comp: &mut CreateComponents) {
|
||||
let next_page = self.next_page(max_pages);
|
||||
|
||||
let (page_first, page_prev, page_next, page_last) =
|
||||
let (page_first, page_prev, page_refresh, page_next, page_last) =
|
||||
DelPager::buttons(next_page, self.timezone);
|
||||
|
||||
comp.create_action_row(|row| {
|
||||
@ -148,6 +166,9 @@ impl Pager for DelPager {
|
||||
.custom_id(page_prev.to_custom_id())
|
||||
.disabled(next_page == 0)
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.label("🔁").style(ButtonStyle::Secondary).custom_id(page_refresh.to_custom_id())
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.label("▶️")
|
||||
.style(ButtonStyle::Secondary)
|
||||
@ -172,10 +193,17 @@ impl DelPager {
|
||||
pub fn buttons(
|
||||
page: usize,
|
||||
timezone: Tz,
|
||||
) -> (ComponentDataModel, ComponentDataModel, ComponentDataModel, ComponentDataModel) {
|
||||
) -> (
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
ComponentDataModel,
|
||||
) {
|
||||
(
|
||||
ComponentDataModel::DelPager(DelPager { page, action: PageAction::First, timezone }),
|
||||
ComponentDataModel::DelPager(DelPager { page, action: PageAction::Previous, timezone }),
|
||||
ComponentDataModel::DelPager(DelPager { page, action: PageAction::Refresh, timezone }),
|
||||
ComponentDataModel::DelPager(DelPager { page, action: PageAction::Next, timezone }),
|
||||
ComponentDataModel::DelPager(DelPager { page, action: PageAction::Last, timezone }),
|
||||
)
|
||||
|
@ -296,8 +296,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
.add_command(&info_cmds::CLOCK_COMMAND)
|
||||
// reminder commands
|
||||
.add_command(&reminder_cmds::TIMER_COMMAND)
|
||||
.add_command(&reminder_cmds::REMIND_COMMAND)
|
||||
/*
|
||||
.add_command("remind", &reminder_cmds::REMIND_COMMAND)
|
||||
.add_command("r", &reminder_cmds::REMIND_COMMAND)
|
||||
.add_command("interval", &reminder_cmds::INTERVAL_COMMAND)
|
||||
.add_command("i", &reminder_cmds::INTERVAL_COMMAND)
|
||||
|
@ -6,6 +6,7 @@ pub mod user_data;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono_tz::Tz;
|
||||
use serenity::{
|
||||
async_trait,
|
||||
model::id::{ChannelId, GuildId, UserId},
|
||||
@ -33,6 +34,8 @@ pub trait CtxData {
|
||||
user_id: U,
|
||||
) -> Result<UserData, Box<dyn std::error::Error + Sync + Send>>;
|
||||
|
||||
async fn timezone<U: Into<UserId> + Send + Sync>(&self, user_id: U) -> Tz;
|
||||
|
||||
async fn channel_data<C: Into<ChannelId> + Send + Sync>(
|
||||
&self,
|
||||
channel_id: C,
|
||||
@ -92,6 +95,13 @@ impl CtxData for Context {
|
||||
UserData::from_user(&user, &self, &pool).await
|
||||
}
|
||||
|
||||
async fn timezone<U: Into<UserId> + Send + Sync>(&self, user_id: U) -> Tz {
|
||||
let user_id = user_id.into();
|
||||
let pool = self.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
UserData::timezone_of(user_id, &pool).await
|
||||
}
|
||||
|
||||
async fn channel_data<C: Into<ChannelId> + Send + Sync>(
|
||||
&self,
|
||||
channel_id: C,
|
||||
|
@ -21,7 +21,6 @@ use crate::{
|
||||
reminder::{content::Content, errors::ReminderError, helper::generate_uid, Reminder},
|
||||
user_data::UserData,
|
||||
},
|
||||
time_parser::TimeParser,
|
||||
SQLPool,
|
||||
};
|
||||
|
||||
@ -133,7 +132,8 @@ INSERT INTO reminders (
|
||||
self.set_by
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(Reminder::from_uid(&self.pool, self.uid).await.unwrap())
|
||||
}
|
||||
@ -147,11 +147,9 @@ INSERT INTO reminders (
|
||||
pub struct MultiReminderBuilder<'a> {
|
||||
scopes: Vec<ReminderScope>,
|
||||
utc_time: NaiveDateTime,
|
||||
utc_time_parser: Option<TimeParser>,
|
||||
timezone: Tz,
|
||||
interval: Option<i64>,
|
||||
expires: Option<NaiveDateTime>,
|
||||
expires_parser: Option<TimeParser>,
|
||||
content: Content,
|
||||
set_by: Option<u32>,
|
||||
ctx: &'a Context,
|
||||
@ -163,11 +161,9 @@ impl<'a> MultiReminderBuilder<'a> {
|
||||
MultiReminderBuilder {
|
||||
scopes: vec![],
|
||||
utc_time: Utc::now().naive_utc(),
|
||||
utc_time_parser: None,
|
||||
timezone: Tz::UTC,
|
||||
interval: None,
|
||||
expires: None,
|
||||
expires_parser: None,
|
||||
content: Content::new(),
|
||||
set_by: None,
|
||||
ctx,
|
||||
@ -187,12 +183,6 @@ impl<'a> MultiReminderBuilder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn time_parser(mut self, parser: TimeParser) -> Self {
|
||||
self.utc_time_parser = Some(parser);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn expires<T: Into<i64>>(mut self, time: Option<T>) -> Self {
|
||||
if let Some(t) = time {
|
||||
self.expires = Some(NaiveDateTime::from_timestamp(t.into(), 0));
|
||||
@ -203,12 +193,6 @@ impl<'a> MultiReminderBuilder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn expires_parser(mut self, parser: Option<TimeParser>) -> Self {
|
||||
self.expires_parser = parser;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn author(mut self, user: UserData) -> Self {
|
||||
self.set_by = Some(user.id);
|
||||
self.timezone = user.timezone();
|
||||
@ -226,33 +210,13 @@ impl<'a> MultiReminderBuilder<'a> {
|
||||
self.scopes = scopes;
|
||||
}
|
||||
|
||||
pub async fn build(mut self) -> (HashSet<ReminderError>, HashSet<ReminderScope>) {
|
||||
pub async fn build(self) -> (HashSet<ReminderError>, HashSet<ReminderScope>) {
|
||||
let pool = self.ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
|
||||
let mut errors = HashSet::new();
|
||||
|
||||
let mut ok_locs = HashSet::new();
|
||||
|
||||
if let Some(expire_parser) = self.expires_parser {
|
||||
if let Ok(expires) = expire_parser.timestamp() {
|
||||
self.expires = Some(NaiveDateTime::from_timestamp(expires, 0));
|
||||
} else {
|
||||
errors.insert(ReminderError::InvalidExpiration);
|
||||
|
||||
return (errors, ok_locs);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(time_parser) = self.utc_time_parser {
|
||||
if let Ok(time) = time_parser.timestamp() {
|
||||
self.utc_time = NaiveDateTime::from_timestamp(time, 0);
|
||||
} else {
|
||||
errors.insert(ReminderError::InvalidTime);
|
||||
|
||||
return (errors, ok_locs);
|
||||
}
|
||||
}
|
||||
|
||||
if self.interval.map_or(false, |i| (i as i64) < *MIN_INTERVAL) {
|
||||
errors.insert(ReminderError::ShortInterval);
|
||||
} else if self.interval.map_or(false, |i| (i as i64) > *MAX_TIME) {
|
||||
|
@ -39,8 +39,8 @@ pub enum ReminderError {
|
||||
DiscordError(String),
|
||||
}
|
||||
|
||||
impl ReminderError {
|
||||
pub fn display(&self, is_natural: bool) -> String {
|
||||
impl ToString for ReminderError {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
ReminderError::LongTime => {
|
||||
"That time is too far in the future. Please specify a shorter time.".to_string()
|
||||
@ -50,40 +50,20 @@ impl ReminderError {
|
||||
max_time = *MAX_TIME / 86_400
|
||||
),
|
||||
ReminderError::PastTime => {
|
||||
"Please ensure the time provided is in the future. If the time should be in \
|
||||
the future, please be more specific with the definition."
|
||||
.to_string()
|
||||
"Please ensure the time provided is in the future. If the time should be in the future, please be more specific with the definition.".to_string()
|
||||
}
|
||||
ReminderError::ShortInterval => format!(
|
||||
"Please ensure the interval provided is longer than {min_interval} seconds",
|
||||
min_interval = *MIN_INTERVAL
|
||||
),
|
||||
ReminderError::InvalidTag => {
|
||||
"Couldn't find a location by your tag. Your tag must be either a channel or \
|
||||
a user (not a role)"
|
||||
.to_string()
|
||||
"Couldn't find a location by your tag. Your tag must be either a channel or a user (not a role)".to_string()
|
||||
}
|
||||
ReminderError::InvalidTime => {
|
||||
if is_natural {
|
||||
"Your time failed to process. Please make it as clear as possible, for example `\"16th of july\"` \
|
||||
or `\"in 20 minutes\"`"
|
||||
.to_string()
|
||||
} else {
|
||||
"Make sure the time you have provided is in the format of [num][s/m/h/d][num][s/m/h/d] etc. or \
|
||||
`day/month/year-hour:minute:second`"
|
||||
.to_string()
|
||||
}
|
||||
"Your time failed to process. Please make it as clear as possible, for example `\"16th of july\"` or `\"in 20 minutes\"`".to_string()
|
||||
}
|
||||
ReminderError::InvalidExpiration => {
|
||||
if is_natural {
|
||||
"Your expiration time failed to process. Please make it as clear as possible, for example `\"16th \
|
||||
of july\"` or `\"in 20 minutes\"`"
|
||||
.to_string()
|
||||
} else {
|
||||
"Make sure the expiration time you have provided is in the format of [num][s/m/h/d][num][s/m/h/d] \
|
||||
etc. or `day/month/year-hour:minute:second`"
|
||||
.to_string()
|
||||
}
|
||||
"Your expiration time failed to process. Please make it as clear as possible, for example `\"16th of july\"` or `\"in 20 minutes\"`".to_string()
|
||||
}
|
||||
ReminderError::DiscordError(s) => format!("A Discord error occurred: **{}**", s),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user