#!/usr/bin/env python3 """ 周报邮件发送脚本 SMTP 凭证从环境变量读取,不接受命令行参数传入凭证。 必须设置的环境变量: SMTP_HOST - SMTP 服务器地址 SMTP_PORT - SMTP 端口(SSL用465,STARTTLS用587) SMTP_USER - 发件人邮箱 SMTP_PASSWORD - SMTP 密码或授权码 """ import argparse import json import os import smtplib import sys from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText def load_config(config_path: str) -> dict: try: with open(config_path, "r", encoding="utf-8") as f: return json.load(f) except FileNotFoundError: print(f"错误:找不到配置文件 {config_path}", file=sys.stderr) print("请在项目根目录创建 report-config.json,格式:", file=sys.stderr) print('{"recipients": ["email@example.com"], "project_name": "项目名"}', file=sys.stderr) sys.exit(1) except json.JSONDecodeError as e: print(f"错误:report-config.json 格式错误 - {e}", file=sys.stderr) sys.exit(1) def get_smtp_config() -> tuple: host = os.environ.get("SMTP_HOST") port = os.environ.get("SMTP_PORT", "465") user = os.environ.get("SMTP_USER") password = os.environ.get("SMTP_PASSWORD") missing = [k for k, v in { "SMTP_HOST": host, "SMTP_USER": user, "SMTP_PASSWORD": password, }.items() if not v] if missing: print(f"错误:缺少以下环境变量:{', '.join(missing)}", file=sys.stderr) print("请在 shell 中设置(不要写入任何文件):", file=sys.stderr) for k in missing: print(f" export {k}=your_value", file=sys.stderr) sys.exit(1) try: port = int(port) except ValueError: print(f"错误:SMTP_PORT 必须是数字,当前值为 '{port}'", file=sys.stderr) sys.exit(1) return host, port, user, password def send_email(recipients: list, subject: str, body_html: str, sender: str): host, port, user, password = get_smtp_config() msg = MIMEMultipart("alternative") msg["Subject"] = subject msg["From"] = sender or user msg["To"] = ", ".join(recipients) msg.attach(MIMEText(body_html, "html", "utf-8")) try: if port == 465: with smtplib.SMTP_SSL(host, port, timeout=30) as smtp: smtp.login(user, password) smtp.sendmail(user, recipients, msg.as_string()) else: with smtplib.SMTP(host, port, timeout=30) as smtp: smtp.ehlo() smtp.starttls() smtp.login(user, password) smtp.sendmail(user, recipients, msg.as_string()) except smtplib.SMTPAuthenticationError: print("错误:SMTP 认证失败,请检查 SMTP_USER 和 SMTP_PASSWORD", file=sys.stderr) sys.exit(1) except smtplib.SMTPConnectError: print(f"错误:无法连接到 {host}:{port},请检查 SMTP_HOST 和 SMTP_PORT", file=sys.stderr) sys.exit(1) except Exception as e: print(f"错误:发送失败 - {e}", file=sys.stderr) sys.exit(1) print(f"邮件已发送至:{', '.join(recipients)}") def main(): parser = argparse.ArgumentParser(description="发送周报邮件") parser.add_argument("--config", required=True, help="report-config.json 路径") parser.add_argument("--subject", required=True, help="邮件主题") parser.add_argument("--body-file", required=True, help="HTML 正文文件路径") args = parser.parse_args() config = load_config(args.config) recipients = config.get("recipients", []) if not recipients: print("错误:report-config.json 中 recipients 为空", file=sys.stderr) sys.exit(1) try: with open(args.body_file, "r", encoding="utf-8") as f: body_html = f.read() except FileNotFoundError: print(f"错误:找不到邮件正文文件 {args.body_file}", file=sys.stderr) sys.exit(1) send_email(recipients, args.subject, body_html, sender=config.get("sender", "")) if __name__ == "__main__": main()