errands
small jobs the runtime does for the studio.
git clone https://owenrusk.dev/errands.git
commit 8b3743220846bdc4d9f6b0ffcdbf9e72945a4ecf parent ba320ecb39c81b07c9a26a897ec87d5706fd241c author Owen Rusk <owen@papermothgames.com> date 2026-09-16 15:30:52 -0500
mail: keep a copy of everything sent tess wants to read what goes out.
| errands/mail.py | +12 | -0 |
| tests/test_mail.py | +20 | -1 |
diff --git a/errands/mail.py b/errands/mail.py index f9e88ab..5e99730 100644 --- a/errands/mail.py +++ b/errands/mail.py @@ -1,6 +1,7 @@ # the support inbox. imap to read, smtp to send. import email import imaplib +import mailbox import smtplib from dataclasses import dataclass from email.header import decode_header, make_header @@ -97,9 +98,20 @@ def send(msg: EmailMessage, open_server=smtp) -> str: server.send_message(msg) finally: server.quit() + keep(msg) return msg["Message-ID"] +def keep(msg: EmailMessage) -> None: + settings.LOGS.mkdir(parents=True, exist_ok=True) + box = mailbox.mbox(settings.LOGS / "sent.mbox") + try: + box.add(msg) + box.flush() + finally: + box.close() + + def reply(original: Message, body: str, open_server=smtp) -> str: subject = original.subject if original.subject.lower().startswith("re:") else f"Re: {original.subject}" return send(compose(original.sender, subject, body, reply_to=original), open_server) diff --git a/tests/test_mail.py b/tests/test_mail.py index 0b13037..156a91e 100644 --- a/tests/test_mail.py +++ b/tests/test_mail.py @@ -1,7 +1,10 @@ +import mailbox +import tempfile import unittest from email.message import EmailMessage +from pathlib import Path-from errands import mail+from errands import mail, settings from tests.fakes import FakeBox, FakeServer, raw @@ -43,6 +46,22 @@ class UnreadTest(unittest.TestCase): class SendTest(unittest.TestCase): + def setUp(self): + self.dir = tempfile.TemporaryDirectory() + self.logs = settings.LOGS + settings.LOGS = Path(self.dir.name) + + def tearDown(self): + settings.LOGS = self.logs + self.dir.cleanup() + + def test_keeps_a_copy(self): + mail.send(mail.compose("someone@example.com", "hello", "hello\n"), FakeServer) + mail.send(mail.compose("ann@example.com", "again", "hello\n"), FakeServer) + kept = mailbox.mbox(settings.LOGS / "sent.mbox") + self.assertEqual([m["To"] for m in kept], ["someone@example.com", "ann@example.com"]) + kept.close() + def test_reply_keeps_the_subject_and_thread(self): original = mail.parse("7", raw(subject="a question", message_id="<q@example.com>")) server = FakeServer()