newsreport_agent_for_traffic/email_sender.py

313 lines
9.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
邮件发送模块
自动将生成的报告通过QQ邮箱发送给指定用户
"""
import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from datetime import datetime
from typing import List, Optional
class EmailSender:
"""邮件发送器"""
def __init__(self, sender_email: str, sender_password: str, smtp_server: str = "smtp.qq.com", smtp_port: int = 465):
"""
初始化邮件发送器
Args:
sender_email: 发件人邮箱QQ邮箱
sender_password: 发件人密码QQ邮箱授权码
smtp_server: SMTP服务器地址
smtp_port: SMTP服务器端口
"""
self.sender_email = sender_email
self.sender_password = sender_password
self.smtp_server = smtp_server
self.smtp_port = smtp_port
def send_report(self,
recipient_emails: List[str],
report_path: str,
subject: Optional[str] = None,
body: Optional[str] = None,
cc_emails: Optional[List[str]] = None) -> bool:
"""
发送报告邮件
Args:
recipient_emails: 收件人邮箱列表
report_path: 报告文件路径
subject: 邮件主题(可选)
body: 邮件正文(可选)
cc_emails: 抄送邮箱列表(可选)
Returns:
是否发送成功
"""
try:
# 创建邮件对象
message = MIMEMultipart()
message['From'] = self.sender_email
message['To'] = ', '.join(recipient_emails)
if cc_emails:
message['Cc'] = ', '.join(cc_emails)
# 设置邮件主题
if subject is None:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
subject = f"交通新闻分析报告 - {timestamp}"
message['Subject'] = subject
# 设置邮件正文
if body is None:
body = self._generate_default_body(report_path)
message.attach(MIMEText(body, 'plain', 'utf-8'))
# 添加附件
if os.path.exists(report_path):
self._attach_file(message, report_path)
else:
print(f"[警告] 报告文件不存在: {report_path}")
return False
# 发送邮件
print(f"\n[邮件] 正在发送邮件...")
print(f" 发件人: {self.sender_email}")
print(f" 收件人: {', '.join(recipient_emails)}")
if cc_emails:
print(f" 抄送: {', '.join(cc_emails)}")
print(f" 主题: {subject}")
# 连接SMTP服务器并发送
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
server.login(self.sender_email, self.sender_password)
# 合并收件人和抄送人
all_recipients = recipient_emails.copy()
if cc_emails:
all_recipients.extend(cc_emails)
server.send_message(message)
print(f"[成功] 邮件发送成功!")
return True
except smtplib.SMTPAuthenticationError:
print(f"[错误] 邮箱认证失败,请检查邮箱和授权码是否正确")
print(f"提示: QQ邮箱需要使用授权码不是QQ密码")
return False
except smtplib.SMTPException as e:
print(f"[错误] 邮件发送失败: {str(e)}")
return False
except Exception as e:
print(f"[错误] 发送邮件时出错: {str(e)}")
return False
def _attach_file(self, message: MIMEMultipart, file_path: str) -> None:
"""
添加附件到邮件
Args:
message: 邮件对象
file_path: 文件路径
"""
filename = os.path.basename(file_path)
with open(file_path, 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename="{filename}"')
message.attach(part)
def _generate_default_body(self, report_path: str) -> str:
"""
生成默认邮件正文
Args:
report_path: 报告文件路径
Returns:
邮件正文
"""
timestamp = datetime.now().strftime("%Y年%m月%d%H:%M")
filename = os.path.basename(report_path)
body = f"""您好!
这是交通新闻自动报表系统生成的分析报告。
报告时间: {timestamp}
报告文件: {filename}
报告内容包括:
- 多源数据采集(赛文交通网、政府采购网等)
- 智能分析与总结
- 热点话题提取
- 趋势分析
请查收附件中的详细报告。
---
此邮件由交通新闻自动报表系统自动发送
如有问题,请联系系统管理员
"""
return body
def send_multiple_reports(self,
recipient_emails: List[str],
report_paths: List[str],
subject: Optional[str] = None,
body: Optional[str] = None) -> bool:
"""
发送多个报告文件
Args:
recipient_emails: 收件人邮箱列表
report_paths: 报告文件路径列表
subject: 邮件主题(可选)
body: 邮件正文(可选)
Returns:
是否发送成功
"""
try:
# 创建邮件对象
message = MIMEMultipart()
message['From'] = self.sender_email
message['To'] = ', '.join(recipient_emails)
# 设置邮件主题
if subject is None:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
subject = f"交通新闻分析报告({len(report_paths)}份)- {timestamp}"
message['Subject'] = subject
# 设置邮件正文
if body is None:
body = self._generate_multiple_reports_body(report_paths)
message.attach(MIMEText(body, 'plain', 'utf-8'))
# 添加所有附件
for report_path in report_paths:
if os.path.exists(report_path):
self._attach_file(message, report_path)
else:
print(f"[警告] 报告文件不存在: {report_path}")
# 发送邮件
print(f"\n[邮件] 正在发送邮件...")
print(f" 发件人: {self.sender_email}")
print(f" 收件人: {', '.join(recipient_emails)}")
print(f" 主题: {subject}")
print(f" 附件数量: {len(report_paths)}")
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
server.login(self.sender_email, self.sender_password)
server.send_message(message)
print(f"[成功] 邮件发送成功!")
return True
except Exception as e:
print(f"[错误] 发送邮件时出错: {str(e)}")
return False
def _generate_multiple_reports_body(self, report_paths: List[str]) -> str:
"""
生成多个报告的邮件正文
Args:
report_paths: 报告文件路径列表
Returns:
邮件正文
"""
timestamp = datetime.now().strftime("%Y年%m月%d%H:%M")
files_list = "\n".join([f" - {os.path.basename(path)}" for path in report_paths])
body = f"""您好!
这是交通新闻自动报表系统生成的分析报告。
报告时间: {timestamp}
报告数量: {len(report_paths)}
报告文件:
{files_list}
请查收附件中的详细报告。
---
此邮件由交通新闻自动报表系统自动发送
如有问题,请联系系统管理员
"""
return body
def main():
"""测试函数"""
from dotenv import load_dotenv
load_dotenv()
# 从环境变量获取配置
sender_email = os.getenv("EMAIL_SENDER")
sender_password = os.getenv("EMAIL_PASSWORD")
recipient_email = os.getenv("EMAIL_RECIPIENT")
if not sender_email or not sender_password:
print("错误: 请在.env文件中配置邮箱信息")
print("\n请添加以下配置到.env文件:")
print("EMAIL_SENDER=你的QQ邮箱")
print("EMAIL_PASSWORD=你的QQ邮箱授权码")
print("EMAIL_RECIPIENT=收件人邮箱")
print("\n注意: EMAIL_PASSWORD是QQ邮箱的授权码不是QQ密码")
print("获取授权码: QQ邮箱 > 设置 > 账户 > POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务")
return
if not recipient_email:
print("警告: 未配置收件人邮箱,使用发件人邮箱作为收件人")
recipient_email = sender_email
# 创建邮件发送器
email_sender = EmailSender(sender_email, sender_password)
# 查找最新的报告文件
data_dir = os.getenv("DATA_DIR", "./data")
report_files = [f for f in os.listdir(data_dir) if f.startswith("report_") and (f.endswith(".md") or f.endswith(".txt"))]
if not report_files:
print("错误: 未找到报告文件,请先生成报告")
print("运行: python main.py --mode report")
return
# 获取最新的报告
report_files.sort(reverse=True)
latest_report = os.path.join(data_dir, report_files[0])
print(f"找到最新报告: {latest_report}")
# 发送邮件
success = email_sender.send_report(
recipient_emails=[recipient_email],
report_path=latest_report
)
if success:
print("\n测试成功!")
else:
print("\n测试失败,请检查配置")
if __name__ == "__main__":
main()