From d450e32359c8ea64d66d3413f8e0f318122d499e Mon Sep 17 00:00:00 2001 From: MxMarx <ruby.e.marx@gmail.com> Date: Fri, 15 Sep 2023 15:15:51 -0700 Subject: [PATCH] recognize '2w' as weeks Cleaned up date parsing. Also react with :+1: regardless of is verbose is true so it's more clear to people what to react to in order to subscribe --- reminder/bot.py | 5 +++-- reminder/reminder.py | 7 ++++++- reminder/util.py | 45 ++++++++++++++++++++++---------------------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/reminder/bot.py b/reminder/bot.py index 3aad7f2..d70e9e8 100644 --- a/reminder/bot.py +++ b/reminder/bot.py @@ -205,6 +205,8 @@ class ReminderBot(Plugin): again: Is this a reminder that was rescheduled? agenda: Is this an agenda instead of a reminder? """ + confirmation_event = await evt.react("\U0001F44D") + if self.config["verbose"]: action = "add this to the agenda for" if agenda else "remind" if reminder.message else "ping" @@ -227,8 +229,7 @@ class ReminderBot(Plugin): confirmation_event = await evt.reply(f"{msg}\n\n" f"(others can \U0001F44D the message above to get pinged too)") - else: - confirmation_event = await evt.react("\U0001F44D") + await reminder.set_confirmation(confirmation_event) diff --git a/reminder/reminder.py b/reminder/reminder.py index 3ff3c29..fb964c5 100644 --- a/reminder/reminder.py +++ b/reminder/reminder.py @@ -125,13 +125,18 @@ class Reminder(object): logger.debug(f"User {self.creator} is rate limited skipping reminder: {self.message}") else: # Build the message with the format "(users to ping) ⬆ï¸(link to the reminder): message text [next run] + # Note: ï¸using "⬆ï¸" as a link seems to have trouble rendering in element for android, but "⬆" works and element on my PC still renders it as the emoji targets = list(self.subscribed_users.values()) link = f"https://matrix.to/#/{self.room_id}/{self.event_id}" users = " ".join([(await make_pill(user_id=user_id, client=self.bot.client)) for user_id in targets]) + body = f"{users}: [⬆]({link}) {self.message}" + if self.recur_every or self.cron_tab: - body += f"\n\nReminding again {self.formatted_time(user_info)}" + body += f"\n\nReminding again {self.formatted_time(user_info)}." \ + f" Reply `!{self.bot.base_command[0]} {self.bot.cancel_command[0]}` to stop." + # Warn the user before rate limiting happens if reminder_count == self.bot.config["rate_limit"]: body += f"\n\n*You've reached the rate limit " \ diff --git a/reminder/util.py b/reminder/util.py index 76d9855..78fa00f 100644 --- a/reminder/util.py +++ b/reminder/util.py @@ -178,7 +178,9 @@ def parse_date(str_with_time: str, user_info: UserInfo, search_text: bool=False) # Until dateparser makes it so locales can be used in the searcher, use this to get date order date_order = validate_locale(user_info.locale).info["date_order"] - date_str = str_with_time + + # Replace "3w" with "3wk" to satisfy dateparser + str_with_time = re.sub(r"(\b\d+)\s?w\b", r"\1wk", str_with_time, count=1) settings = {'TIMEZONE': user_info.timezone, 'TO_TIMEZONE': 'UTC', @@ -186,33 +188,32 @@ def parse_date(str_with_time: str, user_info: UserInfo, search_text: bool=False) 'PREFER_DATES_FROM': 'future', 'RETURN_AS_TIMEZONE_AWARE': True} - if search_text: - # dateparser.parse is more reliable than search_dates. If the date is at the beginning of the message, - # try dateparser.parse on the first 6 words and use the date from the longest sequence that successfully parses. - # If this doesn't work or the date isn't at the beginning of the string, fallback to search_dates - date = [] - for i in islice(re.finditer(r"\S+", str_with_time), 6): - extracted_date = dateparser.parse(str_with_time[:i.end()], locales=[user_info.locale], settings=settings) - if extracted_date: - date = extracted_date - date_str = str_with_time[:i.end()] - - if not date: - results = search_dates(str_with_time, languages=[user_info.locale.split('-')[0]], settings=settings) - if not results: - raise CommandSyntaxError("Unable to extract date from string", CommandSyntax.PARSE_DATE_EXAMPLES) - date_str, date = results[0] - else: - date = dateparser.parse(str_with_time, locales=[user_info.locale], settings=settings) - if not date: - raise CommandSyntaxError(f"The given time `{str_with_time}` is invalid.", CommandSyntax.PARSE_DATE_EXAMPLES) + # dateparser.parse is more reliable than search_dates. If the date is at the beginning of the message, + # try dateparser.parse on the first 8 words and use the date from the longest sequence that successfully parses. + date = [] + date_str = [] + for i in reversed(list(islice(re.finditer(r"\S+", str_with_time), 8))): + extracted_date = dateparser.parse(str_with_time[:i.end()], locales=[user_info.locale], settings=settings) + if extracted_date: + date = extracted_date + date_str = str_with_time[:i.end()] + break + + # If the above doesn't work or the date isn't at the beginning of the string, fallback to search_dates + if not date: + extracted_date = search_dates(str_with_time, languages=[user_info.locale.split('-')[0]], settings=settings) + if extracted_date: + date_str, date = extracted_date[0] + + if not date: + raise CommandSyntaxError("Unable to extract date from string", CommandSyntax.PARSE_DATE_EXAMPLES) # Round datetime object to the nearest second for nicer display date = date.replace(microsecond=0) # Disallow times in the past if date < datetime.now(tz=pytz.UTC): - raise CommandSyntaxError(f"Sorry, `{date}` is in the past and I don't have a time machine (yet...)") + raise CommandSyntaxError(f"Sorry, `{format_time(date, user_info)}` is in the past and I don't have a time machine (yet...)") return date, date_str -- GitLab