diff --git a/.env b/.env index 2ce50b9..1600cde 100644 --- a/.env +++ b/.env @@ -12,4 +12,13 @@ DB_HOST= 47.119.128.161 # 192.168.1.200 DB_PORT=19732 DB_USER=postgres DB_PASS=postgres -DB_NAME=peter \ No newline at end of file +DB_NAME=peter + +# 邮件发送配置 +SMTP_HOST=smtp.qq.com +SMTP_PORT=587 +SMTP_USER=1026090807@qq.com +SMTP_PASSWORD=hqorvminmnuubebf +SMTP_USE_TLS=true +SMTP_FROM=Peter +ERROR_NOTIFICATION_EMAIL=changsongd@126.com \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index 7254194..1951aa6 100644 --- a/config/settings.py +++ b/config/settings.py @@ -22,6 +22,15 @@ class Settings(BaseSettings): DB_PASS: str DB_NAME: str + # 邮件发送配置 + SMTP_HOST: str = Field("SMTP_HOST") + SMTP_PORT: int = Field(587, env="SMTP_PORT") + SMTP_USER: str = Field("SMTP_USER") + SMTP_PASSWORD: str = Field("SMTP_PASSWORD") + SMTP_USE_TLS: bool = Field(True, env="SMTP_USE_TLS") + SMTP_FROM: str = Field("SMTP_FROM") + ERROR_NOTIFICATION_EMAIL: str = Field("ERROR_NOTIFICATION_EMAIL") + class Config: env_file = ".env" env_file_encoding = "utf-8" diff --git a/peter.py b/peter.py index e09ff91..d8eaa3a 100644 --- a/peter.py +++ b/peter.py @@ -2,15 +2,24 @@ from functools import partial from apscheduler.schedulers.blocking import BlockingScheduler from apscheduler.events import EVENT_JOB_ERROR -from config import config +from config import config, settings from task import manager_task -from utils import logger +from utils import logger, MailSender def job_error_listener(event): if event.exception: logger.error(f"Job {event.job_id} crashed: {str(event.exception)}") # 可添加邮件/钉钉告警逻辑 + try: + mail_sender = MailSender() + mail_sender.execute( + to_addrs=settings.ERROR_NOTIFICATION_EMAIL, + subject=f"Job {event.job_id} crashed", + body=f"Job {event.job_id} crashed with error: {str(event.exception)}" + ) + except Exception as e: + logger.error(f"Failed to send error notification email: {e}") if __name__ == '__main__': diff --git a/utils/__init__.py b/utils/__init__.py index 649c832..151d68f 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1 +1,2 @@ -from utils.logger import logger \ No newline at end of file +from utils.logger import logger +from utils.mail_sender import MailSender \ No newline at end of file diff --git a/utils/__pycache__/__init__.cpython-312.pyc b/utils/__pycache__/__init__.cpython-312.pyc index 6f65b6d..ffd02f3 100644 Binary files a/utils/__pycache__/__init__.cpython-312.pyc and b/utils/__pycache__/__init__.cpython-312.pyc differ diff --git a/utils/mail_sender.py b/utils/mail_sender.py new file mode 100644 index 0000000..e53450f --- /dev/null +++ b/utils/mail_sender.py @@ -0,0 +1,71 @@ +import smtplib +from email.mime.text import MIMEText +from email.header import Header +from email.utils import formataddr +from utils import logger +from config import settings + + +class MailSender: + """邮件发送工具,从环境变量读取SMTP配置""" + def execute(self, to_addrs, subject, body, from_addr=None, body_type='plain'): + """ + 发送邮件 + :param to_addrs: 收件人,可以是单个邮箱字符串或邮箱列表 + :param subject: 邮件主题 + :param body: 邮件正文 + :param from_addr: 发件人地址,若为None则使用 SMTP_USER + :param body_type: 正文类型,'plain' 或 'html' + :return: True 表示发送成功 + :raises ValueError: 配置缺失或发送失败时抛出异常 + """ + # ----- 1. 获取SMTP配置 ----- + smtp_host = settings.SMTP_HOST + smtp_port = settings.SMTP_PORT + username = settings.SMTP_USER + password = settings.SMTP_PASSWORD + use_tls = settings.SMTP_USE_TLS + from_name = settings.SMTP_FROM # 发件人显示名称,可选 + + # 必要配置校验 + if not all([smtp_host, smtp_port, username, password]): + missing = [] + if not smtp_host: missing.append(self.ENV_SMTP_HOST) + if not smtp_port: missing.append(self.ENV_SMTP_PORT) + if not username: missing.append(self.ENV_SMTP_USER) + if not password: missing.append(self.ENV_SMTP_PASSWORD) + raise ValueError(f"缺少必要的SMTP环境变量: {', '.join(missing)}") + + # ----- 2. 构建邮件对象 ----- + # 处理收件人格式 + if isinstance(to_addrs, str): + to_addrs = [to_addrs] + to_str = ','.join(to_addrs) + + # 确定发件人地址 + if from_addr is None: + from_addr = username + + # 创建邮件正文 + msg = MIMEText(body, body_type, 'utf-8') + msg['Subject'] = Header(subject, 'utf-8') + # 发件人:可包含显示名称 + if from_name: + msg['From'] = formataddr((Header(from_name, 'utf-8').encode(), from_addr)) + else: + msg['From'] = from_addr + msg['To'] = to_str + + # ----- 3. 发送邮件 ----- + try: + logger.info(f"准备发送邮件,收件人: {to_str}, 主题: {subject}") + with smtplib.SMTP(smtp_host, int(smtp_port)) as server: + if use_tls: + server.starttls() + server.login(username, password) + server.send_message(msg) + logger.info(f"邮件发送成功,收件人: {to_str}") + return True + except Exception as e: + logger.error(f"邮件发送失败: {e}") + raise ValueError(f"邮件发送失败: {e}") \ No newline at end of file