2.6 KiB
2.6 KiB
Python Email Fallback (when himalaya is not installed)
When himalaya CLI is unavailable, use Python's built-in smtplib and imaplib with credentials from ~/.hermes/config.yaml.
Sending Email
import smtplib
from email.mime.text import MIMEText
smtp_host = "claw.163.com"
sender = "ephronren@claw.163.com"
password = "<from config>"
recipient = "recipient@example.com"
msg = MIMEText("Body text", "plain", "utf-8")
msg["From"] = sender
msg["To"] = recipient
msg["Subject"] = "Subject"
with smtplib.SMTP_SSL(smtp_host, 465) as server:
server.login(sender, password)
server.sendmail(sender, recipient, msg.as_string())
Reading Email
import imaplib
from email import message_from_bytes
from email.header import decode_header
imap_host = "claw.163.com"
imap_port = 993
user = "ephronren@claw.163.com"
password = "<from config>"
with imaplib.IMAP4_SSL(imap_host, imap_port) as mail:
mail.login(user, password)
mail.select("INBOX")
_, data = mail.search(None, "ALL")
ids = data[0].split()
eid = ids[-1] # latest
_, msg_data = mail.fetch(eid, "(RFC822)")
msg = message_from_bytes(msg_data[0][1])
def decode_str(s):
if s is None:
return ""
parts = decode_header(s)
return " ".join(
p.decode(c or "utf-8", errors="replace") if isinstance(p, bytes) else p
for p, c in parts
)
print(f"From: {decode_str(msg['From'])}")
print(f"Subject: {decode_str(msg['Subject'])}")
if msg.is_multipart():
for part in msg.walk():
if part.get_content_type() == "text/plain":
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or "utf-8"
print(payload.decode(charset, errors="replace"))
break
else:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or "utf-8"
print(payload.decode(charset, errors="replace"))
Pitfalls
- Port 25 times out — 163 SMTP blocks plain SMTP. Use
SMTP_SSLwith port 465 instead. - Port 465 config mismatch — config.yaml says port 25, but actual working port is 465.
- send_message tool limitation —
send_message(action='send', target='email:addr@domain')does NOT resolve external addresses. Must use Python/terminal directly. - IMAP fetch auto-marks as read —
mail.fetch(eid, "(RFC822)")and evenfetch(eid, "(BODY[HEADER.FIELDS ...])")automatically set the\Seenflag. If you need to check unread count after reading, the count will drop. Usemail.search(None, "UNSEEN")before fetching to get accurate unread count.