{safe_title}
群組:{safe_group_name}
日期:{report.report_date}
摘要
{safe_summary}
""" 通知服務模組 處理 Email 發送 """ import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from typing import Optional from html import escape from sqlalchemy.orm import Session import logging from app.core.config import settings from app.models import Report, Subscription, User, NotificationLog, NotificationStatus logger = logging.getLogger(__name__) def send_email(to_email: str, subject: str, html_content: str) -> bool: """ 發送 Email Returns: 是否發送成功 """ if not settings.smtp_host: logger.warning("SMTP 未設定,跳過發送") return False try: msg = MIMEMultipart("alternative") msg["Subject"] = subject msg["From"] = f"{settings.smtp_from_name} <{settings.smtp_from_email}>" msg["To"] = to_email html_part = MIMEText(html_content, "html", "utf-8") msg.attach(html_part) with smtplib.SMTP(settings.smtp_host, settings.smtp_port) as server: server.starttls() if settings.smtp_username and settings.smtp_password: server.login(settings.smtp_username, settings.smtp_password) server.sendmail(settings.smtp_from_email, to_email, msg.as_string()) return True except Exception as e: logger.error("Email 發送失敗", exc_info=True) return False def create_report_email_content(report: Report, base_url: str = "") -> str: """建立報告通知 Email 內容""" summary = report.edited_summary or report.ai_summary or "無摘要內容" # 截取摘要前 500 字 if len(summary) > 500: summary = summary[:500] + "..." # 轉義 HTML 特殊字元,防止 XSS safe_title = escape(report.title) safe_group_name = escape(report.group.name) safe_summary = escape(summary) safe_base_url = escape(base_url) html = f"""
您訂閱的「{safe_group_name}」今日報告延遲發布,敬請稍後。
造成不便,敬請見諒。
""" success = send_email( user.email, f"【每日報導】{report.group.name} 報告延遲通知", html_content ) if success: sent_count += 1 return sent_count