Skip to content

Commit b871efd

Browse files
authored
More reasonable filenames, and creating messages de novo (#9)
* feat: rename files so that they are human readably ID'd * feat: Name files based upon a human readable string * feat: Allow creation of emails de novo
1 parent 608c459 commit b871efd

File tree

4 files changed

+88
-32
lines changed

4 files changed

+88
-32
lines changed

epistolary/mailbox_manager/mailbox_manager.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ def get_email(self, uid: EmailID) -> EmailMessage:
3434
"""
3535
...
3636

37+
def get_email_address_and_subject(self, uid: EmailID) -> tuple[str, str]:
38+
"""Get the email address and subject of an email.
39+
40+
Arguments:
41+
uid: The ID of the email to get.
42+
43+
Returns:
44+
The email address and subject of the email.
45+
46+
"""
47+
...
48+
3749
def get_email_subject_and_text(self, uid: EmailID) -> tuple[str, str]:
3850
"""Get the subject and text of an email.
3951

epistolary/mailbox_manager/smtpimap_mailbox_manager.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
import pathlib
2+
from urllib.parse import quote
3+
from urllib.parse import unquote
24
from redbox import EmailBox
35
from redbox.models import EmailMessage
4-
from redmail import EmailSender
6+
from redmail.email.sender import EmailSender
57

68
from ..epiconfig import EpistolaryConfig
79
from ..types import EmailID
810
from .mailbox_manager import MailboxManager
911

1012

11-
def _get_msgid_from_header_dict(header_dict: dict[str, str]) -> EmailID:
12-
_possible_message_id_keys = [
13-
"Message-ID",
14-
"Message-Id",
15-
"Message-id",
16-
"message-id",
17-
"message_id",
18-
"messageid",
19-
]
20-
for possible_key in _possible_message_id_keys:
21-
if possible_key in header_dict:
22-
return EmailID(header_dict[possible_key])
13+
def sanitize_fname(fname: str) -> str:
14+
return quote(fname, safe="-_.@")
2315

24-
raise ValueError("No message ID found in header dict")
16+
17+
def desanitize_fname(fname: str) -> str:
18+
return unquote(fname)
19+
20+
21+
def _get_mail_filename(header_dict: dict[str, str]) -> EmailID:
22+
sender = sanitize_fname(header_dict.get("From", "unknown"))
23+
subject = header_dict.get("Subject", "no subject")
24+
combined = f"{sender}:::{subject}"
25+
return EmailID(combined)
2526

2627

2728
class SMTPIMAPMailboxManager(MailboxManager):
@@ -108,7 +109,7 @@ def get_emails(
108109
messages = self._box[folder].search(unseen=True)
109110
result = {}
110111
for message in messages:
111-
result[EmailID(_get_msgid_from_header_dict(message.headers))] = message
112+
result[_get_mail_filename(message.headers)] = message
112113
if limit is not None and len(result) >= limit:
113114
break
114115
return result
@@ -127,6 +128,27 @@ def get_email(self, email_id: EmailID) -> EmailMessage:
127128
# every time we want to get a single email.
128129
return self.get_emails()[email_id]
129130

131+
def get_email_address_and_subject(
132+
self, uid: EmailID, append_re: bool = True
133+
) -> tuple[str, str]:
134+
"""
135+
Get the email address and subject of the email with the specified ID.
136+
137+
Arguments:
138+
uid (EmailID): The email ID.
139+
140+
Returns:
141+
tuple[str, str]: The email address and subject of the email.
142+
"""
143+
try:
144+
email = self.get_email(uid)
145+
return email.from_, ("Re: " + email.subject) if append_re else email.subject
146+
except KeyError:
147+
# Luckily the email ID is literally the address and subject through
148+
# our sanitization function.
149+
dest, subject = uid.split(":::")
150+
return desanitize_fname(dest).strip(), subject.strip()
151+
130152
def get_email_subject_and_text(self, uid: EmailID) -> tuple[str, str]:
131153
"""
132154
Get the subject and text of the email with the specified ID.
@@ -144,6 +166,10 @@ def send_message(
144166
self, to: str, subject: str, body: str, in_reply_to: EmailID | None = None
145167
) -> bool:
146168
"""Send a message."""
169+
# print(f"Sending email to {to} with subject {subject} and body:\n{body}")
170+
# conf = input("Is this correct? (y/n) ")
171+
# if conf.lower() != "y":
172+
# return False
147173
self._sender.send(
148174
subject=subject,
149175
receivers=[to],

epistolary/orchestrator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,10 @@ def send_outbox(self) -> list[EmailID]:
165165
sent_emails = []
166166
for did, doc in outbox.items():
167167
outgoing_text = self.get_last_page_ocr_text_for_document(doc)
168-
relevant_received_email = self.mailbox_manager.get_email(did)
168+
addr, subj = self.mailbox_manager.get_email_address_and_subject(did)
169169
result = self.mailbox_manager.send_message(
170-
to=relevant_received_email.from_,
171-
subject="Re: " + relevant_received_email.subject,
170+
to=addr,
171+
subject=subj,
172172
body=outgoing_text,
173173
in_reply_to=did,
174174
)

epistolary/remarkable/__init__.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import enum
22
import pathlib
33
import tempfile
4-
54
import subprocess
65

76

@@ -82,16 +81,41 @@ def download(self, remote_path: str, local_path: pathlib.Path):
8281
# Create the local path if it doesn't exist
8382
local_path.parent.mkdir(parents=True, exist_ok=True)
8483
self._run_rmapi("get", remote_path)
84+
# print("DEBUG: Listing files in cache directory:")
85+
# for f in self._cache_dir.iterdir():
86+
# print("DEBUG: file in cache:", f)
8587
base_fname = pathlib.Path(remote_path).name
86-
pathlib.Path(str(self._cache_dir / base_fname) + ".zip").rename(
87-
str(local_path) + ".zip"
88-
)
89-
90-
def upload(self, local_path: pathlib.Path, remote_path: str):
88+
# print(f"DEBUG: Cache directory: {self._cache_dir}")
89+
# print(f"DEBUG: Base file name: {base_fname}")
90+
matching_zips = list(self._cache_dir.rglob(f"*{base_fname}*.zip"))
91+
if not matching_zips:
92+
matching_rmdocs = list(self._cache_dir.rglob(f"*{base_fname}*.rmdoc"))
93+
if matching_rmdocs:
94+
print(f"DEBUG: Found rmdoc file: {matching_rmdocs[0]}, renaming to zip")
95+
matching_rmdocs[0].rename(pathlib.Path(str(local_path) + ".zip"))
96+
else:
97+
return
98+
# all_zips = list(self._cache_dir.rglob("*.zip"))
99+
# if len(all_zips) == 1:
100+
# print(
101+
# f"DEBUG: Fallback: using the only zip file found: {all_zips[0]}"
102+
# )
103+
# all_zips[0].rename(pathlib.Path(str(local_path) + ".zip"))
104+
# else:
105+
# print(
106+
# f"ERROR: No matching zip or rmdoc file found in {self._cache_dir} for base name {base_fname}"
107+
# )
108+
# return
109+
else:
110+
zip_file = matching_zips[0]
111+
print(f"DEBUG: Found zip file: {zip_file}")
112+
zip_file.rename(pathlib.Path(str(local_path) + ".zip"))
113+
114+
def upload(self, local_path: pathlib.Path, remote: str):
91115
"""
92116
Upload a file to the reMarkable.
93117
"""
94-
self._run_rmapi("put", str(local_path), remote_path)
118+
self._run_rmapi("put", str(local_path), remote)
95119

96120
def delete(self, remote_path: str):
97121
"""
@@ -142,7 +166,6 @@ def _run_remarks(self, *args):
142166
stdout=subprocess.PIPE,
143167
stderr=subprocess.PIPE,
144168
check=True,
145-
# shell=True
146169
)
147170
except subprocess.CalledProcessError as e:
148171
raise Exception(e.stderr.decode("utf-8")) from e
@@ -164,7 +187,7 @@ def rm_to_pdf(
164187
"""
165188
input_path = pathlib.Path(input_path)
166189
output_path = pathlib.Path(output_path)
167-
# Create outputpath.parent if it doesn't exist
190+
# Create output path parent if it doesn't exist
168191
output_path.parent.mkdir(parents=True, exist_ok=True)
169192
output_path_without_extension = output_path.with_suffix("")
170193
# If input path is a .zip, extract it
@@ -180,8 +203,3 @@ def rm_to_pdf(
180203
list(output_path_without_extension.glob("*.pdf"))[0].rename(
181204
str(output_path.parent / output_path.name)
182205
)
183-
# # Delete the temp file
184-
# (output_path.parent / output_path.name).rmdir()
185-
# list(output_path.parent.glob("*.this-is-a-tempfile"))[0].rename(
186-
# str(output_path)
187-
# )

0 commit comments

Comments
 (0)