#!/bin/env/python3 import os import email from datetime import datetime, timezone from imapclient import IMAPClient from smtplib import SMTP_SSL from dateutil.parser import parse as d_parse from cron_descriptor import get_description from croniter import croniter help_text = """ A very comprehensive help text """ service_address = "remindme@friedersdorff.com" def empty_reply(msg): reply = email.message.EmailMessage() reply["Subject"] = f"RE: {msg['Subject']}" reply["Message-ID"] = email.utils.make_msgid() reply["In-Reply-To"] = msg["Message-ID"] reply["References"] = msg["Message-ID"] reply["To"] = msg["Reply-To"] or msg["From"] reply["From"] = service_address return reply def list_reminders(person, client): persons_messages = client.fetch( client.search(["FROM", person]), "BODY.PEEK[]" ) reminders = [] for uid, reminder in persons_messages.items(): reminder = email.message_from_bytes(reminder[b"BODY[]"]) reminders.append( f"({uid}) {reminder['Subject']}: {reminder.get_payload().strip()}" ) return "\n".join(reminders) def ack_message(uid, message, client): msg = email.message_from_bytes(message[b"BODY[]"]) if msg.is_multipart(): return None, True client.delete_messages([uid]) payload = msg.get_payload().strip() if payload.startswith("help"): reply = empty_reply(msg) reply.set_content(help_text) return reply, True reply = empty_reply(msg) reply.set_content(list_reminders(msg["From"], client)) if payload.startswith("list"): reply["Subject"] = f"List of active reminders (was: {reply['Subject']})" return reply, True else: msg.replace_header("Subject", f"Set reminder: {reply['Subject']}") prefix = ( f"Successfully set reminder: {payload}.\n\n" "Your reminders are:" ) reply.set_content(f"{prefix}\n{reply.get_content()}") return reply, False def remind(uid, message, client): msg = email.message_from_bytes(message[b"BODY[]"]) payload = msg.get_payload().strip() if payload.startswith("repeat"): try: croniter(payload, datetime.now()) except Exception as e: print("An error occured", e) return None, False remind_time = d_parse(payload.splitlines()[0]) if not remind_time.tzinfo: sender_timezone = d_parse(msg["Date"]).tzinfo remind_time = remind_time.replace(tzinfo=sender_timezone) now = datetime.now(timezone.utc) if now > remind_time: reply = empty_reply(msg) reply.replace_header("Subject", f"Reminder: {msg['Subject']}") reply.set_content(f"This is a reminder:\n{msg['Subject']}") return reply, True else: return None, False def main(): mhost = "mail.friedersdorff.com" user = os.environ["REMINDME_USERNAME"] passwd = os.environ["REMINDME_PASS"] with IMAPClient(host=mhost) as iclient, SMTP_SSL(mhost) as sclient: iclient.login(user, passwd) sclient.login(user, passwd) iclient.select_folder("INBOX") unseen_msgs = iclient.fetch(iclient.search(["UNSEEN"]), "BODY.PEEK[]") for uid, message in unseen_msgs.items(): reply, delete = ack_message(uid, message, iclient) try: if reply: sclient.send_message(reply) if delete: pass # iclient.delete_messages([uid]) else: pass # iclient.set_flags([uid], [r"\SEEN"]) except Exception as e: print("An error occured", e) all_msgs = iclient.fetch(iclient.search(["NOT", "DELETED"]), "BODY[]") for uid, message in all_msgs.items(): reply, delete = remind(uid, message, iclient) try: if reply: sclient.send_message(reply) if delete: pass # iclient.delete_messages([uid]) else: pass # iclient.set_flags([uid], [r"\SEEN"]) except Exception as e: print("An error occured", e) if __name__ == "__main__": main()