Files
openclaw-backups/automations/openclaw-digest/send_digest.py

189 lines
7.2 KiB
Python

#!/usr/bin/env python3
"""
Email Generation and Delivery for OpenClaw Daily Digest
Generates HTML/text emails from aggregated content and sends via SMTP
"""
import json
import sys
import os
import subprocess
from datetime import datetime
from pathlib import Path
def load_template(template_path: str) -> str:
"""Load an email template file"""
with open(template_path, 'r') as f:
return f.read()
def generate_email_content(digest_data: dict) -> tuple:
"""Generate HTML and plain-text email content from digest data"""
# Load templates - use Spark-safe version for better cross-client compatibility
html_template = load_template('/home/openclaw/.openclaw/workspace/automations/openclaw-digest/templates/email-spark-safe.html')
text_template = load_template('/home/openclaw/.openclaw/workspace/automations/openclaw-digest/templates/email.txt')
# Get data
meta = digest_data.get('meta', {})
stats = digest_data.get('stats', {})
formatted = digest_data.get('formatted', {})
# Replace template variables for HTML
html_content = html_template
html_content = html_content.replace('{{DATE}}', meta.get('date', datetime.utcnow().strftime('%A, %B %d, %Y')))
html_content = html_content.replace('{{REDDIT_COUNT}}', str(stats.get('reddit_count', 0)))
html_content = html_content.replace('{{NEWS_COUNT}}', str(stats.get('news_count', 0)))
html_content = html_content.replace('{{TWITTER_COUNT}}', str(stats.get('twitter_count', 0)))
html_content = html_content.replace('{{TOP_TOPICS}}', formatted.get('top_topics_html', ''))
html_content = html_content.replace('{{REDDIT_STORIES}}', formatted.get('reddit_html', '<p>No Reddit posts today.</p>'))
html_content = html_content.replace('{{NEWS_STORIES}}', formatted.get('news_html', '<p>No news articles today.</p>'))
html_content = html_content.replace('{{TWITTER_STORIES}}', formatted.get('twitter_html', ''))
html_content = html_content.replace('{{GENERATED_AT}}', meta.get('generated_at', datetime.utcnow().isoformat()))
# Replace template variables for text
text_content = text_template
text_content = text_content.replace('{{DATE}}', meta.get('date', datetime.utcnow().strftime('%A, %B %d, %Y')))
text_content = text_content.replace('{{REDDIT_COUNT}}', str(stats.get('reddit_count', 0)))
text_content = text_content.replace('{{NEWS_COUNT}}', str(stats.get('news_count', 0)))
text_content = text_content.replace('{{TWITTER_COUNT}}', str(stats.get('twitter_count', 0)))
text_content = text_content.replace('{{REDDIT_STORIES_TEXT}}', formatted.get('reddit_text', 'No Reddit posts today.'))
text_content = text_content.replace('{{NEWS_STORIES_TEXT}}', formatted.get('news_text', 'No news articles today.'))
text_content = text_content.replace('{{TWITTER_STORIES_TEXT}}', formatted.get('twitter_text', ''))
text_content = text_content.replace('{{GENERATED_AT}}', meta.get('generated_at', datetime.utcnow().isoformat()))
return html_content, text_content
def save_email_files(html_content: str, text_content: str, output_dir: str) -> tuple:
"""Save email content to files for sending"""
timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
html_path = os.path.join(output_dir, f'email_{timestamp}.html')
text_path = os.path.join(output_dir, f'email_{timestamp}.txt')
with open(html_path, 'w') as f:
f.write(html_content)
with open(text_path, 'w') as f:
f.write(text_content)
return html_path, text_path
def send_email(html_path: str, text_path: str, to_email: str, from_email: str) -> bool:
"""Send email using the imap-smtp-email skill"""
skill_path = '/home/openclaw/.openclaw/workspace/skills/imap-smtp-email'
smtp_script = os.path.join(skill_path, 'scripts/smtp.js')
# Read content files
with open(html_path, 'r') as f:
html_content = f.read()
with open(text_path, 'r') as f:
text_content = f.read()
# Create combined HTML email (with text fallback)
subject = f"🦀 OpenClaw Daily Digest - {datetime.utcnow().strftime('%B %d, %Y')}"
# Write HTML to temp file for --html-file
temp_html = os.path.join(os.path.dirname(html_path), 'temp_email.html')
with open(temp_html, 'w') as f:
f.write(html_content)
# Build the send command
cmd = [
'node', smtp_script, 'send',
'--to', to_email,
'--from', from_email,
'--subject', subject,
'--html-file', temp_html
]
print(f"📧 Sending email to {to_email}...")
print(f" Subject: {subject}")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
cwd=skill_path,
timeout=60
)
if result.returncode == 0:
print("✅ Email sent successfully!")
return True
else:
print(f"❌ Email send failed:")
print(f" stdout: {result.stdout}")
print(f" stderr: {result.stderr}")
return False
except subprocess.TimeoutExpired:
print("❌ Email send timed out")
return False
except Exception as e:
print(f"❌ Error sending email: {e}")
return False
finally:
# Clean up temp file
if os.path.exists(temp_html):
os.remove(temp_html)
def main():
"""Main function to generate and send digest email"""
# Parse arguments
digest_file = sys.argv[1] if len(sys.argv) > 1 else '/home/openclaw/.openclaw/workspace/automations/openclaw-digest/output/digest.json'
to_email = sys.argv[2] if len(sys.argv) > 2 else 'anthony@martinwa.org'
from_email = sys.argv[3] if len(sys.argv) > 3 else 'krillyclaw@gmail.com'
output_dir = '/home/openclaw/.openclaw/workspace/automations/openclaw-digest/output'
print("🦀 OpenClaw Daily Digest - Email Generator")
print("=" * 50)
# Load digest data
print(f"\n📂 Loading digest from: {digest_file}")
try:
with open(digest_file, 'r') as f:
digest_data = json.load(f)
except Exception as e:
print(f"❌ Error loading digest: {e}")
return False
# Generate email content
print("\n✍️ Generating email content...")
html_content, text_content = generate_email_content(digest_data)
# Save to files
html_path, text_path = save_email_files(html_content, text_content, output_dir)
print(f" HTML: {html_path}")
print(f" Text: {text_path}")
# Send email
print("\n📤 Sending email...")
success = send_email(html_path, text_path, to_email, from_email)
# Save metadata
metadata = {
"sent_at": datetime.utcnow().isoformat(),
"to": to_email,
"from": from_email,
"success": success,
"html_file": html_path,
"text_file": text_path,
"stats": digest_data.get('stats', {})
}
metadata_path = os.path.join(output_dir, 'last_send_metadata.json')
with open(metadata_path, 'w') as f:
json.dump(metadata, f, indent=2)
print(f"\n{'' if success else ''} Email delivery {'successful' if success else 'failed'}")
return success
if __name__ == '__main__':
success = main()
sys.exit(0 if success else 1)