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