# -*- coding: utf-8 -*- """ 新闻智能跟踪系统 - 邮件通知 支持 plain / starttls / ssl 三种 SMTP 模式,发送实时重要资讯与每日汇总。 """ import smtplib from email.header import Header from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.utils import formataddr import config import db def get_mail_cfg(): cfg = dict(config.MAIL_DEFAULTS) cfg.update(db.get_all_settings().get("mail", {})) return cfg def send_email(subject, html, to=None): cfg = get_mail_cfg() to = to or cfg["email_to"] msg = MIMEMultipart("alternative") msg["From"] = formataddr((str(Header(cfg.get("sender_name", "新闻智能跟踪"), "utf-8")), cfg["smtp_user"])) msg["To"] = to msg["Subject"] = Header(subject, "utf-8") msg.attach(MIMEText(html, "html", "utf-8")) mode = cfg.get("smtp_mode", "plain") if mode == "ssl": server = smtplib.SMTP_SSL(cfg["smtp_host"], int(cfg["smtp_port"]), timeout=20) else: server = smtplib.SMTP(cfg["smtp_host"], int(cfg["smtp_port"]), timeout=20) if mode == "starttls": server.starttls() try: server.login(cfg["smtp_user"], cfg["smtp_pass"]) server.sendmail(cfg["smtp_user"], [to], msg.as_string()) finally: server.quit() return True def _score_color(score): if score >= 80: return "#e74c3c" if score >= 60: return "#e67e22" return "#7f8c8d" def _card(art): ents = "、".join(art.get("entities") or []) return f"""
{art['title']}
综合分 {art.get('total_score',0)} · 相关度 {art.get('relevance',0)} · {art.get('source_name','')} · {art.get('published_at','')}
{art.get('summary') or art.get('content','')[:120]}
涉及:{ents if ents else '—'}
{('
💡 ' + art.get('analysis','') + '
') if art.get('analysis') else ''}
""" def _html_wrap(title, body): return f"""

{title}

{body}
由 新闻智能跟踪系统 自动生成 · {config.SERVICE_NAME}
""" def send_realtime(articles): """实时重要资讯通知(一次一批)""" if not articles: return 0 cards = "".join(_card(a) for a in articles) subject = f"🔥 重要AI资讯 {len(articles)}条 · {articles[0]['published_at'][:16]}" html = _html_wrap( "重要资讯实时提醒", f"

以下 {len(articles)} 条资讯对您重点关注领域很重要,已自动甄别:

{cards}", ) send_email(subject, html) ids = [a["id"] for a in articles] for aid in ids: db.update_article(aid, notified=1) db.add_log("realtime", subject, len(articles), ids, status="ok", detail="实时通知") return len(articles) def send_daily_summary(articles, window_label): """每日汇总(默认每天10点):昨天至今的重要/相关资讯""" if not articles: return 0 top = articles[: int(db.get_setting("max_summary_items", config.AUTO_DEFAULTS["max_summary_items"]))] cards = "".join(_card(a) for a in top) # 分领域统计 from collections import Counter dom_cnt = Counter(a.get("domain") or "未分类" for a in articles) stats = " · ".join(f"{k} {v}条" for k, v in dom_cnt.most_common(6)) subject = f"📰 AI资讯日报 {window_label} · 共{len(articles)}条 重点{len(top)}条" html = _html_wrap( "AI 重要资讯日报", f"""

汇总时段:{window_label}

领域分布:{stats}

重点资讯(按综合分排序):

{cards} """, ) send_email(subject, html) ids = [a["id"] for a in articles if a["id"]] db.add_log("summary", subject, len(articles), ids, status="ok", detail=f"汇总{len(top)}条") for aid in ids: art = db.get_article(aid) if art and art.get("status") != "summarized": db.update_article(aid, status="summarized") return len(top)