Files
openclaw-backups/archive/inactive-skills/apple-mail/scripts/mail-read-emlx.py

164 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""
Read email content from Apple Mail's database and emlx files
Usage: mail-read-emlx.py <message-row-id>
"""
import sys
import sqlite3
import os
import email
from email import policy
from pathlib import Path
def find_mail_db():
"""Find the Apple Mail database"""
for v in [11, 10, 9]:
db_path = Path.home() / "Library" / "Mail" / f"V{v}" / "MailData" / "Envelope Index"
if db_path.exists():
return str(db_path)
return None
def find_emlx_file(mail_dir, account_id, mailbox_path, remote_id):
"""Try to find the emlx file for a message"""
# Common locations to search
mail_v_dir = Path(mail_dir)
account_dir = mail_v_dir / account_id
if not account_dir.exists():
return None
# Search for emlx files with the remote_id as filename
for emlx_file in account_dir.rglob(f"{remote_id}.emlx"):
return str(emlx_file)
return None
def parse_emlx(emlx_path):
"""Parse an emlx file and return the email message"""
with open(emlx_path, 'rb') as f:
# First line is the byte count, skip it
first_line = f.readline()
# Rest is the raw email
raw_email = f.read()
msg = email.message_from_bytes(raw_email, policy=policy.default)
return msg
def get_message_info(db_path, msg_id):
"""Get message information from the database"""
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
query = """
SELECT
mgd.message_id_header,
s.subject,
a.comment as sender,
datetime(m.date_received, 'unixepoch', '31 years', 'localtime') as date_received,
m.remote_id,
mb.url
FROM messages m
LEFT JOIN message_global_data mgd ON m.global_message_id = mgd.ROWID
LEFT JOIN subjects s ON m.subject = s.ROWID
LEFT JOIN addresses a ON m.sender = a.ROWID
LEFT JOIN mailboxes mb ON m.mailbox = mb.ROWID
WHERE m.ROWID = ?
"""
cursor.execute(query, (msg_id,))
result = cursor.fetchone()
conn.close()
if not result:
return None
return {
'message_id_header': result[0],
'subject': result[1],
'sender': result[2],
'date_received': result[3],
'remote_id': result[4],
'mailbox_url': result[5]
}
def format_email_output(msg_info, email_msg=None):
"""Format email information for output"""
output = []
output.append(f"From: {msg_info['sender']}")
if email_msg:
if email_msg.get('To'):
output.append(f"To: {email_msg.get('To')}")
if email_msg.get('Cc'):
output.append(f"Cc: {email_msg.get('Cc')}")
output.append(f"Date: {msg_info['date_received']}")
output.append(f"Subject: {msg_info['subject']}")
output.append("")
output.append("---")
output.append("")
if email_msg:
# Get the email body
if email_msg.is_multipart():
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
body = part.get_content()
output.append(body)
break
elif part.get_content_type() == "text/html":
# Fallback to HTML if no plain text
body = part.get_content()
output.append(body)
else:
body = email_msg.get_content()
output.append(body)
else:
output.append("(Message body not available - emlx file not found)")
return "\n".join(output)
def main():
if len(sys.argv) < 2:
print("Usage: mail-read-emlx.py <message-row-id>", file=sys.stderr)
sys.exit(1)
msg_id = sys.argv[1]
# Find the database
db_path = find_mail_db()
if not db_path:
print("Error: Mail database not found", file=sys.stderr)
sys.exit(1)
# Get message info from database
msg_info = get_message_info(db_path, msg_id)
if not msg_info:
print(f"Message not found with ID: {msg_id}", file=sys.stderr)
sys.exit(1)
# Try to find and parse the emlx file
email_msg = None
if msg_info['remote_id'] and msg_info['mailbox_url']:
# Parse account ID from mailbox URL
# Format: imap://ACCOUNT-ID/MAILBOX-PATH
if msg_info['mailbox_url'].startswith('imap://'):
parts = msg_info['mailbox_url'][7:].split('/', 1)
if len(parts) >= 1:
account_id = parts[0]
mail_dir = Path(db_path).parent.parent
emlx_file = find_emlx_file(mail_dir, account_id, None, msg_info['remote_id'])
if emlx_file:
try:
email_msg = parse_emlx(emlx_file)
except Exception as e:
print(f"Warning: Could not parse emlx file: {e}", file=sys.stderr)
# Output the formatted message
print(format_email_output(msg_info, email_msg))
if __name__ == "__main__":
main()