diff --git a/src/commands/info_cmds.rs b/src/commands/info_cmds.rs index 51a3d56..b2590ea 100644 --- a/src/commands/info_cmds.rs +++ b/src/commands/info_cmds.rs @@ -72,7 +72,12 @@ async fn clock(ctx: &Context, msg: &Message, args: String) -> CommandResult { let now = Utc::now().with_timezone(&tz); - let _ = msg.channel_id.say(&ctx, format!("Current time: **{}**", now.format("%H:%M:%S"))).await; + if args == "12".to_string() { + let _ = msg.channel_id.say(&ctx, format!("Current time: **{}**", now.format("%I:%M:%S %p"))).await; + } + else { + let _ = msg.channel_id.say(&ctx, format!("Current time: **{}**", now.format("%H:%M:%S"))).await; + } Ok(()) } diff --git a/src/commands/moderation_cmds.rs b/src/commands/moderation_cmds.rs index 955677c..c1790e5 100644 --- a/src/commands/moderation_cmds.rs +++ b/src/commands/moderation_cmds.rs @@ -22,6 +22,7 @@ use crate::{ }, SQLPool, framework::SendFromDb, + time_parser::TimeParser, }; lazy_static! { @@ -143,15 +144,3 @@ async fn prefix(ctx: &Context, msg: &Message, args: String) -> CommandResult { Ok(()) } - -#[command] -async fn pause(ctx: &Context, msg: &Message, args: String) -> CommandResult { - let pool = ctx.data.read().await - .get::().cloned().expect("Could not get SQLPool from data"); - - let channel = ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), pool.clone()).await.unwrap(); - - channel.commit_changes(pool).await; - - Ok(()) -} diff --git a/src/commands/reminder_cmds.rs b/src/commands/reminder_cmds.rs index e69de29..0b31cdc 100644 --- a/src/commands/reminder_cmds.rs +++ b/src/commands/reminder_cmds.rs @@ -0,0 +1,73 @@ +use regex_command_attr::command; + +use serenity::{ + client::Context, + model::{ + channel::{ + Message, + }, + }, + framework::standard::CommandResult, +}; + +use chrono_tz::{ + Tz, + Etc::UTC, +}; + +use crate::{ + models::{ + ChannelData, + UserData, + }, + SQLPool, + framework::SendFromDb, + time_parser::TimeParser, +}; + +use chrono::NaiveDateTime; + + +#[command] +async fn pause(ctx: &Context, msg: &Message, args: String) -> CommandResult { + let pool = ctx.data.read().await + .get::().cloned().expect("Could not get SQLPool from data"); + + let user_data = UserData::from_id(&msg.author, &ctx, pool.clone()).await.unwrap(); + let mut channel = ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), pool.clone()).await.unwrap(); + + if args.len() == 0 { + channel.paused = !channel.paused; + channel.paused_until = None; + + channel.commit_changes(pool).await; + + if channel.paused { + let _ = msg.channel_id.say_named(&ctx, user_data.language, "paused/paused_indefinite").await; + } + else { + let _ = msg.channel_id.say_named(&ctx, user_data.language, "paused/unpaused").await; + } + } + else { + let parser = TimeParser::new(args, user_data.timezone.parse().unwrap()); + let pause_until = parser.timestamp(); + + match pause_until { + Ok(timestamp) => { + channel.paused = true; + channel.paused_until = Some(NaiveDateTime::from_timestamp(timestamp, 0)); + + channel.commit_changes(pool).await; + + let _ = msg.channel_id.say_named(&ctx, user_data.language, "paused/paused_until").await; + }, + + Err(_) => { + let _ = msg.channel_id.say_named(&ctx, user_data.language, "paused/invalid_time").await; + }, + } + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 32e0b23..1e8add1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,7 +65,8 @@ async fn main() -> Result<(), Box> { .add_command("todo", &todo_cmds::TODO_PARSE_COMMAND) .add_command("blacklist", &moderation_cmds::BLACKLIST_COMMAND) .add_command("timezone", &moderation_cmds::TIMEZONE_COMMAND) - .add_command("language", &moderation_cmds::LANGUAGE_COMMAND) + .add_command("lang", &moderation_cmds::LANGUAGE_COMMAND) + .add_command("pause", &reminder_cmds::PAUSE_COMMAND) .build(); let mut client = Client::new(&env::var("DISCORD_TOKEN").expect("Missing DISCORD_TOKEN from environment")) diff --git a/src/time_parser.rs b/src/time_parser.rs index db59949..2f506ec 100644 --- a/src/time_parser.rs +++ b/src/time_parser.rs @@ -3,16 +3,38 @@ use std::time::{ UNIX_EPOCH, }; +use std::fmt::{ + Formatter, + Display, + Result as FmtResult, +}; + use chrono_tz::Tz; use chrono::offset::Utc; use chrono::{Timelike, Datelike, TimeZone}; +#[derive(Debug)] +pub enum InvalidTime { + ParseErrorDMY, + ParseErrorHMS, + ParseErrorDisplacement, + ParseErrorChrono, +} + +impl Display for InvalidTime { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "InvalidTime: {:?}", self) + } +} + +impl std::error::Error for InvalidTime {} + enum ParseType { Explicit, Displacement, } -struct TimeParser { +pub struct TimeParser { timezone: Tz, inverted: bool, time_string: String, @@ -43,19 +65,10 @@ impl TimeParser { } } - pub fn timestamp(&self) -> i32 { - 0 - } - - pub fn displacement(&self) -> i32 { - 0 - } - - fn process(&self) -> i32 { + pub fn timestamp(&self) -> Result { match self.parse_type { ParseType::Explicit => { - // TODO remove unwrap from here - self.process_explicit().unwrap() + Ok(self.process_explicit()?) }, ParseType::Displacement => { @@ -64,45 +77,88 @@ impl TimeParser { .duration_since(UNIX_EPOCH) .expect("Time calculated as going backwards. Very bad"); - since_epoch.as_secs() as i32 + self.process_displacement() + Ok(since_epoch.as_secs() as i64 + self.process_displacement()?) }, } } - fn process_explicit(&self) -> Result> { - let dt = self.timezone.datetime_from_str(self.time_string.as_str(), "%d/%m/%Y-%H:%M:%S")?; + pub fn displacement(&self) -> Result { + match self.parse_type { + ParseType::Explicit => { + let now = SystemTime::now(); + let since_epoch = now + .duration_since(UNIX_EPOCH) + .expect("Time calculated as going backwards. Very bad"); - Ok(dt.timestamp() as i32) + Ok(self.process_explicit()? - since_epoch.as_secs() as i64) + }, + + ParseType::Displacement => { + Ok(self.process_displacement()?) + }, + } } - fn process_displacement(&self) -> i32 { + + fn process_explicit(&self) -> Result { + let segments = self.time_string.matches("-").count(); + + let parse_string = if segments == 1 { + let slashes = self.time_string.matches("/").count(); + + match slashes { + 0 => Ok("%d-".to_string()), + 1 => Ok("%d/%m-".to_string()), + 2 => Ok("%d/%m/%Y-".to_string()), + _ => Err(InvalidTime::ParseErrorDMY) + } + } else { + Ok("".to_string()) + }? + if segments == 1 { + let colons = self.time_string.matches(":").count(); + + match colons { + 1 => Ok("%H:%M"), + 2 => Ok("%H:%M:%S"), + _ => Err(InvalidTime::ParseErrorHMS) + } + } else { + Ok("") + }?; + + let dt = self.timezone.datetime_from_str(self.time_string.as_str(), &parse_string).map_err(|_| InvalidTime::ParseErrorChrono)?; + + Ok(dt.timestamp() as i64) + } + + fn process_displacement(&self) -> Result { let mut current_buffer = "0".to_string(); - let mut seconds = 0 as i32; - let mut minutes = 0 as i32; - let mut hours = 0 as i32; - let mut days = 0 as i32; + let mut seconds = 0 as i64; + let mut minutes = 0 as i64; + let mut hours = 0 as i64; + let mut days = 0 as i64; for character in self.time_string.chars() { match character { 's' => { - seconds = current_buffer.parse::().unwrap(); + seconds = current_buffer.parse::().unwrap(); current_buffer = String::from("0"); }, 'm' => { - minutes = current_buffer.parse::().unwrap(); + minutes = current_buffer.parse::().unwrap(); current_buffer = String::from("0"); }, 'h' => { - hours = current_buffer.parse::().unwrap(); + hours = current_buffer.parse::().unwrap(); current_buffer = String::from("0"); }, 'd' => { - days = current_buffer.parse::().unwrap(); + days = current_buffer.parse::().unwrap(); current_buffer = String::from("0"); }, @@ -111,15 +167,15 @@ impl TimeParser { current_buffer += &c.to_string(); } else { - // raise exception + return Err(InvalidTime::ParseErrorDisplacement) } }, } } - let full = seconds + (minutes * 60) + (hours * 3600) + (days * 86400) + current_buffer.parse::().unwrap() * + let full = seconds + (minutes * 60) + (hours * 3600) + (days * 86400) + current_buffer.parse::().unwrap() * if self.inverted { -1 } else { 1 }; - full + Ok(full) } }