From efb399d97a2caee98eb7823d0f66c59e815592a4 Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Sun, 22 Dec 2019 17:48:23 +0000 Subject: [PATCH] Implement basic functionality Do not yet support repeated reminders --- remindme.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 remindme.py diff --git a/remindme.py b/remindme.py new file mode 100644 index 0000000..3454eab --- /dev/null +++ b/remindme.py @@ -0,0 +1,140 @@ +import os +from smtplib import SMTP_SSL +import email +import datetime + +from imapclient import IMAPClient, SEEN +from dateutil.parser import parse +from cron_descriptor import get_description + + +IMAP_USER = os.environ["REMINDME_IMAP_USER"] +IMAP_PASS = os.environ["REMINDME_IMAP_PASS"] +IMAP_HOST = os.environ["REMINDME_IMAP_HOST"] +SMTP_USER = os.environ.get("REMINDME_SMTP_USER", IMAP_USER) +SMTP_PASS = os.environ.get("REMINDME_IMAP_PASS", IMAP_PASS) +SMTP_HOST = os.environ.get("REMINDME_IMAP_HOST", IMAP_HOST) + +USAGE = "" + + +__iclient = None + + +def iclient(): + global __iclient + if not __iclient: + __iclient = IMAPClient(IMAP_HOST, use_uid=True) + __iclient.login(IMAP_USER, IMAP_PASS) + + __iclient.select_folder("INBOX") + return __iclient + + +def make_reply(orig, subject, body): + reply = email.message.Message() + reply["Subject"] = subject + reply["In-Reply-To"] = orig["Message-ID"] + reply["References"] = orig["Message-ID"] + reply.set_payload(body) + reply["From"] = "remindme@friedersdorff.com" + reply["To"] = orig["From"] + return reply + + +def ls(msg, reminders): + reply_lines = [] + for uid, reminder in reminders: + body = reminder.get_payload().strip() + if body.startswith("repeat"): + parsed = get_description(body[6:].strip()) + reply_lines.append(f"({uid}) {reminder['subject']}: {body} ; {parsed}") + pass + else: + reply_lines.append(f"({uid}) {reminder['subject']}: {body}") + + return make_reply(msg, "Your current reminders", "\n".join(reply_lines)) + + +def ack_reminder(uid, reminder): + body = reminder.get_payload().strip() + if body.startswith("repeat"): + parsed = get_description(body[6:].strip()) + reply_body = f"({uid}) {reminder['subject']}: {body} ; {parsed}" + pass + else: + reply_body = f"({uid}) {reminder['subject']}: {body}" + + return make_reply(reminder, f"Received Reminder: {reminder['subject']}", reply_body) + + +def remind(reminder): + f_line = reminder.get_payload().strip().splitlines()[0] + if f_line.lower().startswith("repeat"): + return False, None + else: + try: + parsed_time = parse(f_line, fuzzy=True) + except ValueError: + return False, None + + if not parsed_time.tzinfo: + send_date = email.utils.parsedate_to_datetime(reminder["Date"]) + sender_tz = send_date.tzinfo or datetime.timezone.utc + parsed_time = parsed_time.replace(tzinfo=sender_tz) + + if datetime.datetime.now(datetime.timezone.utc) > parsed_time: + return ( + True, + make_reply( + reminder, f"Re: {reminder['subject']} now", reminder.get_payload() + ), + ) + + +def main(): + msgs = iclient().search(["ALL"]) + new_rems, old_rems, metas, to_delete, to_send = [], [], [], [], [] + for uid, data in iclient().fetch(msgs, ["BODY.PEEK[]", "FLAGS"]).items(): + mail = email.message_from_bytes(data[b"BODY[]"]) + if mail.is_multipart(): + to_delete.append(uid) + + payload = mail.get_payload().strip() + if payload.lower().startswith("help") or payload.lower().startswith("list"): + metas.append(mail) + to_delete.append(uid) + else: + if SEEN in data[b"FLAGS"]: + old_rems.append((uid, mail)) + else: + new_rems.append((uid, mail)) + + for meta in metas: + if meta.get_payload().strip().startswith("help"): + to_send.append(make_reply(meta, "Remindme Help", USAGE)) + elif meta.get_payload().strip().startswith("list"): + to_send.append(ls(meta, old_rems + new_rems)) + + for uid, reminder in new_rems: + to_send.append(ack_reminder(uid, reminder)) + + for uid, reminder in old_rems + new_rems: + done, reply = remind(reminder) + if reply: + to_send.append(reply) + if done: + to_delete.append(uid) + + iclient().delete_messages(to_delete) + iclient().expunge() + iclient().add_flags([u[0] for u in new_rems], SEEN) + + with SMTP_SSL(SMTP_HOST) as smtp: + smtp.login(SMTP_USER, SMTP_PASS) + for reply in to_send: + smtp.send_message(reply) + + +if __name__ == "__main__": + main() -- 2.46.2