interval months/interval seconds
This commit is contained in:
parent
4f9eb58c16
commit
fad28faabb
565
Cargo.lock
generated
565
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
4
migration/03-reminder_variable_intervals.sql
Normal file
4
migration/03-reminder_variable_intervals.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
USE reminders;
|
||||||
|
|
||||||
|
ALTER TABLE reminders.reminders RENAME COLUMN `interval` TO `interval_seconds`;
|
||||||
|
ALTER TABLE reminders.reminders ADD COLUMN `interval_months` INT UNSIGNED DEFAULT NULL;
|
@ -19,6 +19,7 @@ use crate::{
|
|||||||
consts::{EMBED_DESCRIPTION_MAX_LENGTH, REGEX_CHANNEL_USER, SELECT_MAX_ENTRIES, THEME_COLOR},
|
consts::{EMBED_DESCRIPTION_MAX_LENGTH, REGEX_CHANNEL_USER, SELECT_MAX_ENTRIES, THEME_COLOR},
|
||||||
framework::{CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue},
|
framework::{CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue},
|
||||||
hooks::CHECK_GUILD_PERMISSIONS_HOOK,
|
hooks::CHECK_GUILD_PERMISSIONS_HOOK,
|
||||||
|
interval_parser::parse_duration,
|
||||||
models::{
|
models::{
|
||||||
reminder::{
|
reminder::{
|
||||||
builder::{MultiReminderBuilder, ReminderScope},
|
builder::{MultiReminderBuilder, ReminderScope},
|
||||||
@ -720,11 +721,8 @@ async fn remind(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions)
|
|||||||
&& check_guild_subscription(&ctx, invoke.guild_id().unwrap()).await)
|
&& check_guild_subscription(&ctx, invoke.guild_id().unwrap()).await)
|
||||||
{
|
{
|
||||||
(
|
(
|
||||||
humantime::parse_duration(&repeat.to_string())
|
parse_duration(&repeat.to_string())
|
||||||
.or_else(|_| {
|
.or_else(|_| parse_duration(&format!("1 {}", repeat.to_string())))
|
||||||
humantime::parse_duration(&format!("1 {}", repeat.to_string()))
|
|
||||||
})
|
|
||||||
.map(|duration| duration.as_secs() as i64)
|
|
||||||
.ok(),
|
.ok(),
|
||||||
{
|
{
|
||||||
if let Some(arg) = args.get("expires") {
|
if let Some(arg) = args.get("expires") {
|
||||||
@ -769,6 +767,7 @@ async fn remind(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptions)
|
|||||||
.author(user_data)
|
.author(user_data)
|
||||||
.content(content)
|
.content(content)
|
||||||
.time(time)
|
.time(time)
|
||||||
|
.timezone(timezone)
|
||||||
.expires(expires)
|
.expires(expires)
|
||||||
.interval(interval);
|
.interval(interval);
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const THEME_COLOR_FALLBACK: u32 = 0x8fb677;
|
|||||||
use std::{collections::HashSet, env, iter::FromIterator};
|
use std::{collections::HashSet, env, iter::FromIterator};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serenity::http::AttachmentType;
|
use serenity::model::prelude::AttachmentType;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref DEFAULT_AVATAR: AttachmentType<'static> = (
|
pub static ref DEFAULT_AVATAR: AttachmentType<'static> = (
|
||||||
|
247
src/interval_parser.rs
Normal file
247
src/interval_parser.rs
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 Paul Colomiets, 2022 Jude Southworth
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||||
|
and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||||
|
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||||
|
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::{error::Error as StdError, fmt, str::Chars};
|
||||||
|
|
||||||
|
/// Error parsing human-friendly duration
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Invalid character during parsing
|
||||||
|
///
|
||||||
|
/// More specifically anything that is not alphanumeric is prohibited
|
||||||
|
///
|
||||||
|
/// The field is an byte offset of the character in the string.
|
||||||
|
InvalidCharacter(usize),
|
||||||
|
/// Non-numeric value where number is expected
|
||||||
|
///
|
||||||
|
/// This usually means that either time unit is broken into words,
|
||||||
|
/// e.g. `m sec` instead of `msec`, or just number is omitted,
|
||||||
|
/// for example `2 hours min` instead of `2 hours 1 min`
|
||||||
|
///
|
||||||
|
/// The field is an byte offset of the errorneous character
|
||||||
|
/// in the string.
|
||||||
|
NumberExpected(usize),
|
||||||
|
/// Unit in the number is not one of allowed units
|
||||||
|
///
|
||||||
|
/// See documentation of `parse_duration` for the list of supported
|
||||||
|
/// time units.
|
||||||
|
///
|
||||||
|
/// The two fields are start and end (exclusive) of the slice from
|
||||||
|
/// the original string, containing errorneous value
|
||||||
|
UnknownUnit {
|
||||||
|
/// Start of the invalid unit inside the original string
|
||||||
|
start: usize,
|
||||||
|
/// End of the invalid unit inside the original string
|
||||||
|
end: usize,
|
||||||
|
/// The unit verbatim
|
||||||
|
unit: String,
|
||||||
|
/// A number associated with the unit
|
||||||
|
value: u64,
|
||||||
|
},
|
||||||
|
/// The numeric value is too large
|
||||||
|
///
|
||||||
|
/// Usually this means value is too large to be useful. If user writes
|
||||||
|
/// data in subsecond units, then the maximum is about 3k years. When
|
||||||
|
/// using seconds, or larger units, the limit is even larger.
|
||||||
|
NumberOverflow,
|
||||||
|
/// The value was an empty string (or consists only whitespace)
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdError for Error {}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::InvalidCharacter(offset) => write!(f, "invalid character at {}", offset),
|
||||||
|
Error::NumberExpected(offset) => write!(f, "expected number at {}", offset),
|
||||||
|
Error::UnknownUnit { unit, value, .. } if &unit == &"" => {
|
||||||
|
write!(f, "time unit needed, for example {0}sec or {0}ms", value,)
|
||||||
|
}
|
||||||
|
Error::UnknownUnit { unit, .. } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"unknown time unit {:?}, \
|
||||||
|
supported units: ns, us, ms, sec, min, hours, days, \
|
||||||
|
weeks, months, years (and few variations)",
|
||||||
|
unit
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Error::NumberOverflow => write!(f, "number is too large"),
|
||||||
|
Error::Empty => write!(f, "value was empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait OverflowOp: Sized {
|
||||||
|
fn mul(self, other: Self) -> Result<Self, Error>;
|
||||||
|
fn add(self, other: Self) -> Result<Self, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OverflowOp for u64 {
|
||||||
|
fn mul(self, other: Self) -> Result<Self, Error> {
|
||||||
|
self.checked_mul(other).ok_or(Error::NumberOverflow)
|
||||||
|
}
|
||||||
|
fn add(self, other: Self) -> Result<Self, Error> {
|
||||||
|
self.checked_add(other).ok_or(Error::NumberOverflow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Interval {
|
||||||
|
pub month: u64,
|
||||||
|
pub sec: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parser<'a> {
|
||||||
|
iter: Chars<'a>,
|
||||||
|
src: &'a str,
|
||||||
|
current: (u64, u64, u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
fn off(&self) -> usize {
|
||||||
|
self.src.len() - self.iter.as_str().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_first_char(&mut self) -> Result<Option<u64>, Error> {
|
||||||
|
let off = self.off();
|
||||||
|
for c in self.iter.by_ref() {
|
||||||
|
match c {
|
||||||
|
'0'..='9' => {
|
||||||
|
return Ok(Some(c as u64 - '0' as u64));
|
||||||
|
}
|
||||||
|
c if c.is_whitespace() => continue,
|
||||||
|
_ => {
|
||||||
|
return Err(Error::NumberExpected(off));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
fn parse_unit(&mut self, n: u64, start: usize, end: usize) -> Result<(), Error> {
|
||||||
|
let (mut month, mut sec, nsec) = match &self.src[start..end] {
|
||||||
|
"nanos" | "nsec" | "ns" => (0u64, 0u64, n),
|
||||||
|
"usec" | "us" => (0, 0u64, n.mul(1000)?),
|
||||||
|
"millis" | "msec" | "ms" => (0, 0u64, n.mul(1_000_000)?),
|
||||||
|
"seconds" | "second" | "secs" | "sec" | "s" => (0, n, 0),
|
||||||
|
"minutes" | "minute" | "min" | "mins" | "m" => (0, n.mul(60)?, 0),
|
||||||
|
"hours" | "hour" | "hr" | "hrs" | "h" => (0, n.mul(3600)?, 0),
|
||||||
|
"days" | "day" | "d" => (0, n.mul(86400)?, 0),
|
||||||
|
"weeks" | "week" | "w" => (0, n.mul(86400 * 7)?, 0),
|
||||||
|
"months" | "month" | "M" => (n, 0, 0),
|
||||||
|
"years" | "year" | "y" => (12, 0, 0),
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnknownUnit {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
unit: self.src[start..end].to_string(),
|
||||||
|
value: n,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut nsec = self.current.2 + nsec;
|
||||||
|
if nsec > 1_000_000_000 {
|
||||||
|
sec = sec + nsec / 1_000_000_000;
|
||||||
|
nsec %= 1_000_000_000;
|
||||||
|
}
|
||||||
|
sec = self.current.1 + sec;
|
||||||
|
month = self.current.0 + month;
|
||||||
|
|
||||||
|
self.current = (month, sec, nsec);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(mut self) -> Result<Interval, Error> {
|
||||||
|
let mut n = self.parse_first_char()?.ok_or(Error::Empty)?;
|
||||||
|
'outer: loop {
|
||||||
|
let mut off = self.off();
|
||||||
|
while let Some(c) = self.iter.next() {
|
||||||
|
match c {
|
||||||
|
'0'..='9' => {
|
||||||
|
n = n
|
||||||
|
.checked_mul(10)
|
||||||
|
.and_then(|x| x.checked_add(c as u64 - '0' as u64))
|
||||||
|
.ok_or(Error::NumberOverflow)?;
|
||||||
|
}
|
||||||
|
c if c.is_whitespace() => {}
|
||||||
|
'a'..='z' | 'A'..='Z' => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::InvalidCharacter(off));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
off = self.off();
|
||||||
|
}
|
||||||
|
let start = off;
|
||||||
|
let mut off = self.off();
|
||||||
|
while let Some(c) = self.iter.next() {
|
||||||
|
match c {
|
||||||
|
'0'..='9' => {
|
||||||
|
self.parse_unit(n, start, off)?;
|
||||||
|
n = c as u64 - '0' as u64;
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
c if c.is_whitespace() => break,
|
||||||
|
'a'..='z' | 'A'..='Z' => {}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::InvalidCharacter(off));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
off = self.off();
|
||||||
|
}
|
||||||
|
self.parse_unit(n, start, off)?;
|
||||||
|
n = match self.parse_first_char()? {
|
||||||
|
Some(n) => n,
|
||||||
|
None => return Ok(Interval { month: self.current.0, sec: self.current.1 }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse duration object `1hour 12min 5s`
|
||||||
|
///
|
||||||
|
/// The duration object is a concatenation of time spans. Where each time
|
||||||
|
/// span is an integer number and a suffix. Supported suffixes:
|
||||||
|
///
|
||||||
|
/// * `nsec`, `ns` -- nanoseconds
|
||||||
|
/// * `usec`, `us` -- microseconds
|
||||||
|
/// * `msec`, `ms` -- milliseconds
|
||||||
|
/// * `seconds`, `second`, `sec`, `s`
|
||||||
|
/// * `minutes`, `minute`, `min`, `m`
|
||||||
|
/// * `hours`, `hour`, `hr`, `h`
|
||||||
|
/// * `days`, `day`, `d`
|
||||||
|
/// * `weeks`, `week`, `w`
|
||||||
|
/// * `months`, `month`, `M` -- defined as 30.44 days
|
||||||
|
/// * `years`, `year`, `y` -- defined as 365.25 days
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::time::Duration;
|
||||||
|
/// use humantime::parse_duration;
|
||||||
|
///
|
||||||
|
/// assert_eq!(parse_duration("2h 37min"), Ok(Duration::new(9420, 0)));
|
||||||
|
/// assert_eq!(parse_duration("32ms"), Ok(Duration::new(0, 32_000_000)));
|
||||||
|
/// ```
|
||||||
|
pub fn parse_duration(s: &str) -> Result<Interval, Error> {
|
||||||
|
Parser { iter: s.chars(), src: s, current: (0, 0, 0) }.parse()
|
||||||
|
}
|
@ -7,6 +7,7 @@ mod component_models;
|
|||||||
mod consts;
|
mod consts;
|
||||||
mod framework;
|
mod framework;
|
||||||
mod hooks;
|
mod hooks;
|
||||||
|
mod interval_parser;
|
||||||
mod models;
|
mod models;
|
||||||
mod time_parser;
|
mod time_parser;
|
||||||
|
|
||||||
@ -17,12 +18,12 @@ use dotenv::dotenv;
|
|||||||
use log::info;
|
use log::info;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
async_trait,
|
async_trait,
|
||||||
client::{bridge::gateway::GatewayIntents, Client},
|
client::Client,
|
||||||
http::{client::Http, CacheHttp},
|
http::{client::Http, CacheHttp},
|
||||||
model::{
|
model::{
|
||||||
channel::GuildChannel,
|
channel::GuildChannel,
|
||||||
gateway::{Activity, Ready},
|
gateway::{Activity, GatewayIntents, Ready},
|
||||||
guild::{Guild, GuildUnavailable},
|
guild::{Guild, UnavailableGuild},
|
||||||
id::{GuildId, UserId},
|
id::{GuildId, UserId},
|
||||||
interactions::Interaction,
|
interactions::Interaction,
|
||||||
},
|
},
|
||||||
@ -144,7 +145,7 @@ DELETE FROM channels WHERE channel = ?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn guild_delete(&self, ctx: Context, incomplete: GuildUnavailable, _full: Option<Guild>) {
|
async fn guild_delete(&self, ctx: Context, incomplete: UnavailableGuild, _full: Option<Guild>) {
|
||||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
let _ = sqlx::query!("DELETE FROM guilds WHERE guild = ?", incomplete.id.0)
|
let _ = sqlx::query!("DELETE FROM guilds WHERE guild = ?", incomplete.id.0)
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
|
@ -16,7 +16,8 @@ use sqlx::MySqlPool;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
consts,
|
||||||
consts::{MAX_TIME, MIN_INTERVAL},
|
consts::{DAY, MAX_TIME, MIN_INTERVAL},
|
||||||
|
interval_parser::Interval,
|
||||||
models::{
|
models::{
|
||||||
channel_data::ChannelData,
|
channel_data::ChannelData,
|
||||||
reminder::{content::Content, errors::ReminderError, helper::generate_uid, Reminder},
|
reminder::{content::Content, errors::ReminderError, helper::generate_uid, Reminder},
|
||||||
@ -54,7 +55,8 @@ pub struct ReminderBuilder {
|
|||||||
channel: u32,
|
channel: u32,
|
||||||
utc_time: NaiveDateTime,
|
utc_time: NaiveDateTime,
|
||||||
timezone: String,
|
timezone: String,
|
||||||
interval: Option<i64>,
|
interval_secs: Option<i64>,
|
||||||
|
interval_months: Option<i64>,
|
||||||
expires: Option<NaiveDateTime>,
|
expires: Option<NaiveDateTime>,
|
||||||
content: String,
|
content: String,
|
||||||
tts: bool,
|
tts: bool,
|
||||||
@ -86,7 +88,8 @@ INSERT INTO reminders (
|
|||||||
`channel_id`,
|
`channel_id`,
|
||||||
`utc_time`,
|
`utc_time`,
|
||||||
`timezone`,
|
`timezone`,
|
||||||
`interval`,
|
`interval_seconds`,
|
||||||
|
`interval_months`,
|
||||||
`expires`,
|
`expires`,
|
||||||
`content`,
|
`content`,
|
||||||
`tts`,
|
`tts`,
|
||||||
@ -104,6 +107,7 @@ INSERT INTO reminders (
|
|||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
|
?,
|
||||||
?
|
?
|
||||||
)
|
)
|
||||||
",
|
",
|
||||||
@ -111,7 +115,8 @@ INSERT INTO reminders (
|
|||||||
self.channel,
|
self.channel,
|
||||||
utc_time,
|
utc_time,
|
||||||
self.timezone,
|
self.timezone,
|
||||||
self.interval,
|
self.interval_secs,
|
||||||
|
self.interval_months,
|
||||||
self.expires,
|
self.expires,
|
||||||
self.content,
|
self.content,
|
||||||
self.tts,
|
self.tts,
|
||||||
@ -136,7 +141,7 @@ pub struct MultiReminderBuilder<'a> {
|
|||||||
scopes: Vec<ReminderScope>,
|
scopes: Vec<ReminderScope>,
|
||||||
utc_time: NaiveDateTime,
|
utc_time: NaiveDateTime,
|
||||||
timezone: Tz,
|
timezone: Tz,
|
||||||
interval: Option<i64>,
|
interval: Option<Interval>,
|
||||||
expires: Option<NaiveDateTime>,
|
expires: Option<NaiveDateTime>,
|
||||||
content: Content,
|
content: Content,
|
||||||
set_by: Option<u32>,
|
set_by: Option<u32>,
|
||||||
@ -159,6 +164,12 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn timezone(mut self, timezone: Tz) -> Self {
|
||||||
|
self.timezone = timezone;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn content(mut self, content: Content) -> Self {
|
pub fn content(mut self, content: Content) -> Self {
|
||||||
self.content = content;
|
self.content = content;
|
||||||
|
|
||||||
@ -188,7 +199,7 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interval(mut self, interval: Option<i64>) -> Self {
|
pub fn interval(mut self, interval: Option<Interval>) -> Self {
|
||||||
self.interval = interval;
|
self.interval = interval;
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -205,9 +216,10 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
|
|
||||||
let mut ok_locs = HashSet::new();
|
let mut ok_locs = HashSet::new();
|
||||||
|
|
||||||
if self.interval.map_or(false, |i| (i as i64) < *MIN_INTERVAL) {
|
if self.interval.map_or(false, |i| ((i.sec + i.month * 30 * DAY) as i64) < *MIN_INTERVAL) {
|
||||||
errors.insert(ReminderError::ShortInterval);
|
errors.insert(ReminderError::ShortInterval);
|
||||||
} else if self.interval.map_or(false, |i| (i as i64) > *MAX_TIME) {
|
} else if self.interval.map_or(false, |i| ((i.sec + i.month * 30 * DAY) as i64) > *MAX_TIME)
|
||||||
|
{
|
||||||
errors.insert(ReminderError::LongInterval);
|
errors.insert(ReminderError::LongInterval);
|
||||||
} else {
|
} else {
|
||||||
for scope in self.scopes {
|
for scope in self.scopes {
|
||||||
@ -275,7 +287,8 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
channel: c,
|
channel: c,
|
||||||
utc_time: self.utc_time,
|
utc_time: self.utc_time,
|
||||||
timezone: self.timezone.to_string(),
|
timezone: self.timezone.to_string(),
|
||||||
interval: self.interval,
|
interval_secs: self.interval.map(|i| i.sec as i64),
|
||||||
|
interval_months: self.interval.map(|i| i.month as i64),
|
||||||
expires: self.expires,
|
expires: self.expires,
|
||||||
content: self.content.content.clone(),
|
content: self.content.content.clone(),
|
||||||
tts: self.content.tts,
|
tts: self.content.tts,
|
||||||
|
@ -13,10 +13,7 @@ use serenity::{
|
|||||||
use sqlx::MySqlPool;
|
use sqlx::MySqlPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::reminder::{
|
models::reminder::look_flags::{LookFlags, TimeDisplayType},
|
||||||
helper::longhand_displacement,
|
|
||||||
look_flags::{LookFlags, TimeDisplayType},
|
|
||||||
},
|
|
||||||
SQLPool,
|
SQLPool,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,7 +23,8 @@ pub struct Reminder {
|
|||||||
pub uid: String,
|
pub uid: String,
|
||||||
pub channel: u64,
|
pub channel: u64,
|
||||||
pub utc_time: NaiveDateTime,
|
pub utc_time: NaiveDateTime,
|
||||||
pub interval: Option<u32>,
|
pub interval_seconds: Option<u32>,
|
||||||
|
pub interval_months: Option<u32>,
|
||||||
pub expires: Option<NaiveDateTime>,
|
pub expires: Option<NaiveDateTime>,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
@ -44,7 +42,8 @@ SELECT
|
|||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.interval,
|
reminders.interval_seconds,
|
||||||
|
reminders.interval_months,
|
||||||
reminders.expires,
|
reminders.expires,
|
||||||
reminders.enabled,
|
reminders.enabled,
|
||||||
reminders.content,
|
reminders.content,
|
||||||
@ -88,7 +87,8 @@ SELECT
|
|||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.interval,
|
reminders.interval_seconds,
|
||||||
|
reminders.interval_months,
|
||||||
reminders.expires,
|
reminders.expires,
|
||||||
reminders.enabled,
|
reminders.enabled,
|
||||||
reminders.content,
|
reminders.content,
|
||||||
@ -141,7 +141,8 @@ SELECT
|
|||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.interval,
|
reminders.interval_seconds,
|
||||||
|
reminders.interval_months,
|
||||||
reminders.expires,
|
reminders.expires,
|
||||||
reminders.enabled,
|
reminders.enabled,
|
||||||
reminders.content,
|
reminders.content,
|
||||||
@ -173,7 +174,8 @@ SELECT
|
|||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.interval,
|
reminders.interval_seconds,
|
||||||
|
reminders.interval_months,
|
||||||
reminders.expires,
|
reminders.expires,
|
||||||
reminders.enabled,
|
reminders.enabled,
|
||||||
reminders.content,
|
reminders.content,
|
||||||
@ -206,7 +208,8 @@ SELECT
|
|||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.interval,
|
reminders.interval_seconds,
|
||||||
|
reminders.interval_months,
|
||||||
reminders.expires,
|
reminders.expires,
|
||||||
reminders.enabled,
|
reminders.enabled,
|
||||||
reminders.content,
|
reminders.content,
|
||||||
@ -264,12 +267,11 @@ WHERE
|
|||||||
TimeDisplayType::Relative => format!("<t:{}:R>", self.utc_time.timestamp()),
|
TimeDisplayType::Relative => format!("<t:{}:R>", self.utc_time.timestamp()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(interval) = self.interval {
|
if self.interval_seconds.is_some() || self.interval_months.is_some() {
|
||||||
format!(
|
format!(
|
||||||
"'{}' *occurs next at* **{}**, repeating every **{}** (set by {})",
|
"'{}' *occurs next at* **{}**, repeating (set by {})",
|
||||||
self.display_content(),
|
self.display_content(),
|
||||||
time_display,
|
time_display,
|
||||||
longhand_displacement(interval as u64),
|
|
||||||
self.set_by.map(|i| format!("<@{}>", i)).unwrap_or_else(|| "unknown".to_string())
|
self.set_by.map(|i| format!("<@{}>", i)).unwrap_or_else(|| "unknown".to_string())
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user