help command now no longer relies on help_strings const

timeparser updated to work with partially specified times
This commit is contained in:
jude 2021-02-23 13:45:25 +00:00
parent 3075e34fe1
commit 7b6464d5a4
14 changed files with 92 additions and 100 deletions

8
.idea/.gitignore vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@ -1,11 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="jude">
<words>
<w>reqwest</w>
<w>subcommand</w>
<w>todos</w>
<w>webhook</w>
<w>webhooks</w>
</words>
</dictionary>
</component>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/create.sql" dialect="GenericSQL" />
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

2
Cargo.lock generated
View File

@ -1303,7 +1303,7 @@ dependencies = [
[[package]] [[package]]
name = "reminder_rs" name = "reminder_rs"
version = "1.4.6" version = "1.4.7"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"chrono", "chrono",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "reminder_rs" name = "reminder_rs"
version = "1.4.6" version = "1.4.7"
authors = ["jellywx <judesouthworth@pm.me>"] authors = ["jellywx <judesouthworth@pm.me>"]
edition = "2018" edition = "2018"
@ -25,4 +25,4 @@ serenity = { version = "0.10", features = ["collector"] }
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]} sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]}
[dependencies.regex_command_attr] [dependencies.regex_command_attr]
path = "./regex_command_attr" path = "./regex_command_attr"

View File

@ -6,14 +6,16 @@ use chrono::offset::Utc;
use crate::{ use crate::{
command_help, command_help,
consts::{DEFAULT_PREFIX, HELP_STRINGS}, consts::DEFAULT_PREFIX,
get_ctx_data, get_ctx_data,
language_manager::LanguageManager, language_manager::LanguageManager,
models::{GuildData, UserData}, models::{GuildData, UserData},
THEME_COLOR, FrameworkCtx, THEME_COLOR,
}; };
use crate::framework::RegexFramework;
use serenity::builder::CreateEmbedFooter; use serenity::builder::CreateEmbedFooter;
use std::mem;
use std::sync::Arc; use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@ -110,14 +112,24 @@ async fn help(ctx: &Context, msg: &Message, args: String) {
let prefix = GuildData::prefix_from_id(msg.guild_id, &pool); let prefix = GuildData::prefix_from_id(msg.guild_id, &pool);
if !args.is_empty() { if !args.is_empty() {
let matched = HELP_STRINGS let framework: Arc<Box<RegexFramework>> = {
.iter() let framework_trait = ctx
.filter(|h| h.split_at(5).1 == args) .data
.next(); .read()
.await
.get::<FrameworkCtx>()
.cloned()
.expect("Could not get FrameworkCtx from data");
if let Some(help_str) = matched { unsafe { mem::transmute(framework_trait.clone()) }
let command_name = help_str.split_at(5).1; };
let matched = framework
.commands
.get(args.as_str())
.map(|inner| inner.name);
if let Some(command_name) = matched {
command_help(ctx, msg, lm, &prefix.await, &language.await, command_name).await command_help(ctx, msg, lm, &prefix.await, &language.await, command_name).await
} else { } else {
default_help(ctx, msg, lm, &prefix.await, &language.await).await; default_help(ctx, msg, lm, &prefix.await, &language.await).await;

View File

@ -215,7 +215,7 @@ async fn timezone(ctx: &Context, msg: &Message, args: String) {
} }
} }
#[command] #[command("meridian")]
async fn change_meridian(ctx: &Context, msg: &Message, args: String) { async fn change_meridian(ctx: &Context, msg: &Message, args: String) {
let (pool, lm) = get_ctx_data(&ctx).await; let (pool, lm) = get_ctx_data(&ctx).await;
@ -258,7 +258,7 @@ async fn change_meridian(ctx: &Context, msg: &Message, args: String) {
} }
} }
#[command] #[command("lang")]
async fn language(ctx: &Context, msg: &Message, args: String) { async fn language(ctx: &Context, msg: &Message, args: String) {
let (pool, lm) = get_ctx_data(&ctx).await; let (pool, lm) = get_ctx_data(&ctx).await;

View File

@ -982,14 +982,23 @@ impl Content {
#[command("countdown")] #[command("countdown")]
#[permission_level(Managed)] #[permission_level(Managed)]
async fn countdown(ctx: &Context, msg: &Message, args: String) { async fn countdown(ctx: &Context, msg: &Message, args: String) {
if !check_subscription_on_message(&ctx, &msg).await { if check_subscription_on_message(&ctx, &msg).await {
} else {
let (pool, lm) = get_ctx_data(&ctx).await; let (pool, lm) = get_ctx_data(&ctx).await;
let timezone = UserData::timezone_of(&msg.author, &pool).await;
let split_args = args.splitn(3, ' ').collect::<Vec<&str>>(); let split_args = args.splitn(3, ' ').collect::<Vec<&str>>();
if split_args.len() == 3 { if split_args.len() == 3 {
let time = split_args.get(0).unwrap();
let interval = split_args.get(1).unwrap();
let event_name = split_args.get(2).unwrap();
let time_parser = TimeParser::new(*time, &timezone);
if let Ok(target_ts) = time_parser.timestamp() {}
} else { } else {
} }
} else {
} }
} }
*/ */

View File

@ -367,7 +367,7 @@ impl Execute for Result<SubCommand, ()> {
} }
} }
#[command] #[command("todo")]
async fn todo_user(ctx: &Context, msg: &Message, args: String) { async fn todo_user(ctx: &Context, msg: &Message, args: String) {
let mut split = args.split(' '); let mut split = args.split(' ');
@ -384,7 +384,7 @@ async fn todo_user(ctx: &Context, msg: &Message, args: String) {
.await; .await;
} }
#[command("todos")] #[command("todoc")]
#[supports_dm(false)] #[supports_dm(false)]
#[permission_level(Managed)] #[permission_level(Managed)]
async fn todo_channel(ctx: &Context, msg: &Message, args: String) { async fn todo_channel(ctx: &Context, msg: &Message, args: String) {

View File

@ -1,31 +1,6 @@
pub const DAY: u64 = 86_400; pub const DAY: u64 = 86_400;
pub const HOUR: u64 = 3_600; pub const HOUR: u64 = 3_600;
pub const MINUTE: u64 = 60; pub const MINUTE: u64 = 60;
pub const HELP_STRINGS: [&'static str; 23] = [
"help/lang",
"help/meridian",
"help/timezone",
"help/prefix",
"help/blacklist",
"help/restrict",
"help/alias",
"help/remind",
"help/interval",
"help/natural",
"help/look",
"help/del",
"help/offset",
"help/pause",
"help/nudge",
"help/info",
"help/help",
"help/donate",
"help/clock",
"help/todo",
"help/todos",
"help/todoc",
"help/timer",
];
pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";

View File

@ -176,7 +176,7 @@ impl SendIterator for ChannelId {
} }
pub struct RegexFramework { pub struct RegexFramework {
commands: HashMap<String, &'static Command>, pub commands: HashMap<String, &'static Command>,
command_matcher: Regex, command_matcher: Regex,
dm_regex_matcher: Regex, dm_regex_matcher: Regex,
default_prefix: String, default_prefix: String,

View File

@ -290,7 +290,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
data.insert::<SQLPool>(pool); data.insert::<SQLPool>(pool);
data.insert::<PopularTimezones>(Arc::new(popular_timezones)); data.insert::<PopularTimezones>(Arc::new(popular_timezones));
data.insert::<ReqwestClient>(Arc::new(reqwest::Client::new())); data.insert::<ReqwestClient>(Arc::new(reqwest::Client::new()));
data.insert::<FrameworkCtx>(framework_arc); data.insert::<FrameworkCtx>(framework_arc.clone());
data.insert::<LanguageManager>(Arc::new(language_manager)) data.insert::<LanguageManager>(Arc::new(language_manager))
} }

View File

@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
use crate::consts::{LOCAL_TIMEZONE, PYTHON_LOCATION}; use crate::consts::{LOCAL_TIMEZONE, PYTHON_LOCATION};
use chrono::TimeZone; use chrono::{DateTime, Datelike, Timelike, Utc};
use chrono_tz::Tz; use chrono_tz::Tz;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::str::from_utf8; use std::str::from_utf8;
@ -95,35 +95,63 @@ impl TimeParser {
} }
fn process_explicit(&self) -> Result<i64, InvalidTime> { fn process_explicit(&self) -> Result<i64, InvalidTime> {
let segments = self.time_string.matches('-').count(); let mut time = Utc::now()
.with_timezone(&self.timezone)
.with_second(0)
.unwrap();
let parse_string = if segments == 1 { let mut segments = self.time_string.rsplit('-');
let slashes = self.time_string.matches('/').count(); // this segment will always exist even if split fails
let hms = segments.next().unwrap();
match slashes { let h_m_s = hms.split(':');
0 => Ok("%d-".to_string()),
1 => Ok("%d/%m-".to_string()), for (t, setter) in h_m_s.take(3).zip(&[
2 => Ok("%d/%m/%Y-".to_string()), DateTime::with_hour,
_ => Err(InvalidTime::ParseErrorDMY), DateTime::with_minute,
DateTime::with_second,
]) {
time = setter(&time, t.parse().map_err(|_| InvalidTime::ParseErrorHMS)?)
.map_or_else(|| Err(InvalidTime::ParseErrorHMS), |inner| Ok(inner))?;
}
if let Some(dmy) = segments.next() {
let mut d_m_y = dmy.split('/');
let day = d_m_y.next();
let month = d_m_y.next();
let year = d_m_y.next();
for (t, setter) in [day, month]
.iter()
.zip(&[DateTime::with_day, DateTime::with_month])
{
if let Some(t) = t {
time = setter(&time, t.parse().map_err(|_| InvalidTime::ParseErrorDMY)?)
.map_or_else(|| Err(InvalidTime::ParseErrorDMY), |inner| Ok(inner))?;
}
} }
} else {
Ok("".to_string())
}? + {
let colons = self.time_string.matches(':').count();
match colons { if let Some(year) = year {
1 => Ok("%H:%M"), if year.len() == 4 {
2 => Ok("%H:%M:%S"), time = time
_ => Err(InvalidTime::ParseErrorHMS), .with_year(year.parse().map_err(|_| InvalidTime::ParseErrorDMY)?)
.map_or_else(|| Err(InvalidTime::ParseErrorDMY), |inner| Ok(inner))?;
} else if year.len() == 2 {
time = time
.with_year(
format!("20{}", year)
.parse()
.map_err(|_| InvalidTime::ParseErrorDMY)?,
)
.map_or_else(|| Err(InvalidTime::ParseErrorDMY), |inner| Ok(inner))?;
} else {
Err(InvalidTime::ParseErrorDMY)?;
}
} }
}?; }
let dt = self Ok(time.timestamp() as i64)
.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<i64, InvalidTime> { fn process_displacement(&self) -> Result<i64, InvalidTime> {