help command now no longer relies on help_strings const
timeparser updated to work with partially specified times
This commit is contained in:
		
							
								
								
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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/
 | 
			
		||||
							
								
								
									
										11
									
								
								.idea/dictionaries/jude.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								.idea/dictionaries/jude.xml
									
									
									
										generated
									
									
									
								
							@@ -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>
 | 
			
		||||
							
								
								
									
										6
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							@@ -1,6 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="JavaScriptSettings">
 | 
			
		||||
    <option name="languageLevel" value="ES6" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										7
									
								
								.idea/sqldialects.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								.idea/sqldialects.xml
									
									
									
										generated
									
									
									
								
							@@ -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
									
									
									
								
							
							
						
						
									
										2
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -1303,7 +1303,7 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "reminder_rs"
 | 
			
		||||
version = "1.4.6"
 | 
			
		||||
version = "1.4.7"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "Inflector",
 | 
			
		||||
 "chrono",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "reminder_rs"
 | 
			
		||||
version = "1.4.6"
 | 
			
		||||
version = "1.4.7"
 | 
			
		||||
authors = ["jellywx <judesouthworth@pm.me>"]
 | 
			
		||||
edition = "2018"
 | 
			
		||||
 | 
			
		||||
@@ -25,4 +25,4 @@ serenity = { version = "0.10", features = ["collector"] }
 | 
			
		||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]}
 | 
			
		||||
 | 
			
		||||
[dependencies.regex_command_attr]
 | 
			
		||||
path = "./regex_command_attr"
 | 
			
		||||
path = "./regex_command_attr"
 | 
			
		||||
@@ -6,14 +6,16 @@ use chrono::offset::Utc;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    command_help,
 | 
			
		||||
    consts::{DEFAULT_PREFIX, HELP_STRINGS},
 | 
			
		||||
    consts::DEFAULT_PREFIX,
 | 
			
		||||
    get_ctx_data,
 | 
			
		||||
    language_manager::LanguageManager,
 | 
			
		||||
    models::{GuildData, UserData},
 | 
			
		||||
    THEME_COLOR,
 | 
			
		||||
    FrameworkCtx, THEME_COLOR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::framework::RegexFramework;
 | 
			
		||||
use serenity::builder::CreateEmbedFooter;
 | 
			
		||||
use std::mem;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
    if !args.is_empty() {
 | 
			
		||||
        let matched = HELP_STRINGS
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter(|h| h.split_at(5).1 == args)
 | 
			
		||||
            .next();
 | 
			
		||||
        let framework: Arc<Box<RegexFramework>> = {
 | 
			
		||||
            let framework_trait = ctx
 | 
			
		||||
                .data
 | 
			
		||||
                .read()
 | 
			
		||||
                .await
 | 
			
		||||
                .get::<FrameworkCtx>()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .expect("Could not get FrameworkCtx from data");
 | 
			
		||||
 | 
			
		||||
        if let Some(help_str) = matched {
 | 
			
		||||
            let command_name = help_str.split_at(5).1;
 | 
			
		||||
            unsafe { mem::transmute(framework_trait.clone()) }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        } else {
 | 
			
		||||
            default_help(ctx, msg, lm, &prefix.await, &language.await).await;
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
    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) {
 | 
			
		||||
    let (pool, lm) = get_ctx_data(&ctx).await;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -982,14 +982,23 @@ impl Content {
 | 
			
		||||
#[command("countdown")]
 | 
			
		||||
#[permission_level(Managed)]
 | 
			
		||||
async fn countdown(ctx: &Context, msg: &Message, args: String) {
 | 
			
		||||
    if !check_subscription_on_message(&ctx, &msg).await {
 | 
			
		||||
    } else {
 | 
			
		||||
    if check_subscription_on_message(&ctx, &msg).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>>();
 | 
			
		||||
 | 
			
		||||
        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 {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 
 | 
			
		||||
@@ -367,7 +367,7 @@ impl Execute for Result<SubCommand, ()> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[command]
 | 
			
		||||
#[command("todo")]
 | 
			
		||||
async fn todo_user(ctx: &Context, msg: &Message, args: String) {
 | 
			
		||||
    let mut split = args.split(' ');
 | 
			
		||||
 | 
			
		||||
@@ -384,7 +384,7 @@ async fn todo_user(ctx: &Context, msg: &Message, args: String) {
 | 
			
		||||
        .await;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[command("todos")]
 | 
			
		||||
#[command("todoc")]
 | 
			
		||||
#[supports_dm(false)]
 | 
			
		||||
#[permission_level(Managed)]
 | 
			
		||||
async fn todo_channel(ctx: &Context, msg: &Message, args: String) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,6 @@
 | 
			
		||||
pub const DAY: u64 = 86_400;
 | 
			
		||||
pub const HOUR: u64 = 3_600;
 | 
			
		||||
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_";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -176,7 +176,7 @@ impl SendIterator for ChannelId {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct RegexFramework {
 | 
			
		||||
    commands: HashMap<String, &'static Command>,
 | 
			
		||||
    pub commands: HashMap<String, &'static Command>,
 | 
			
		||||
    command_matcher: Regex,
 | 
			
		||||
    dm_regex_matcher: Regex,
 | 
			
		||||
    default_prefix: String,
 | 
			
		||||
 
 | 
			
		||||
@@ -290,7 +290,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
			
		||||
        data.insert::<SQLPool>(pool);
 | 
			
		||||
        data.insert::<PopularTimezones>(Arc::new(popular_timezones));
 | 
			
		||||
        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))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
 | 
			
		||||
 | 
			
		||||
use crate::consts::{LOCAL_TIMEZONE, PYTHON_LOCATION};
 | 
			
		||||
 | 
			
		||||
use chrono::TimeZone;
 | 
			
		||||
use chrono::{DateTime, Datelike, Timelike, Utc};
 | 
			
		||||
use chrono_tz::Tz;
 | 
			
		||||
use std::convert::TryFrom;
 | 
			
		||||
use std::str::from_utf8;
 | 
			
		||||
@@ -95,35 +95,63 @@ impl TimeParser {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 slashes = self.time_string.matches('/').count();
 | 
			
		||||
        let mut segments = self.time_string.rsplit('-');
 | 
			
		||||
        // this segment will always exist even if split fails
 | 
			
		||||
        let hms = segments.next().unwrap();
 | 
			
		||||
 | 
			
		||||
            match slashes {
 | 
			
		||||
                0 => Ok("%d-".to_string()),
 | 
			
		||||
                1 => Ok("%d/%m-".to_string()),
 | 
			
		||||
                2 => Ok("%d/%m/%Y-".to_string()),
 | 
			
		||||
                _ => Err(InvalidTime::ParseErrorDMY),
 | 
			
		||||
        let h_m_s = hms.split(':');
 | 
			
		||||
 | 
			
		||||
        for (t, setter) in h_m_s.take(3).zip(&[
 | 
			
		||||
            DateTime::with_hour,
 | 
			
		||||
            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 {
 | 
			
		||||
                1 => Ok("%H:%M"),
 | 
			
		||||
                2 => Ok("%H:%M:%S"),
 | 
			
		||||
                _ => Err(InvalidTime::ParseErrorHMS),
 | 
			
		||||
            if let Some(year) = year {
 | 
			
		||||
                if year.len() == 4 {
 | 
			
		||||
                    time = time
 | 
			
		||||
                        .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
 | 
			
		||||
            .timezone
 | 
			
		||||
            .datetime_from_str(self.time_string.as_str(), &parse_string)
 | 
			
		||||
            .map_err(|_| InvalidTime::ParseErrorChrono)?;
 | 
			
		||||
 | 
			
		||||
        Ok(dt.timestamp() as i64)
 | 
			
		||||
        Ok(time.timestamp() as i64)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn process_displacement(&self) -> Result<i64, InvalidTime> {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user