-
Notifications
You must be signed in to change notification settings - Fork 92
新增邮件通知功能 #348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
iceNo9
wants to merge
1
commit into
cc004:dev
Choose a base branch
from
iceNo9:add_smtp_ntf
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+278
−43
Open
新增邮件通知功能 #348
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| # ============ module/notify.py ============ | ||
|
|
||
| import smtplib | ||
| from email.header import Header | ||
| from email.mime.text import MIMEText | ||
|
|
||
| from ...model.enums import * | ||
| from ...model.error import * | ||
| from ...util.logger import instance as logger | ||
| from ..config import * | ||
| from ..modulebase import * | ||
|
|
||
| # ============ 配置定义 ============ | ||
|
|
||
| EMAIL_PROVIDERS = ["QQ邮箱", "163邮箱", "126邮箱"] | ||
|
|
||
| SMTP_CONFIGS = { | ||
| "QQ邮箱": {"host": "smtp.qq.com", "port": 587}, | ||
| "163邮箱": {"host": "smtp.163.com", "port": 465}, | ||
| "126邮箱": {"host": "smtp.126.com", "port": 465}, | ||
| } | ||
|
|
||
|
|
||
| # ============ 抽象基类 ============ | ||
|
|
||
| class NotifyModule(Module): | ||
| """通知模块抽象基类""" | ||
|
|
||
| def is_configured(self) -> bool: | ||
| raise NotImplementedError | ||
|
|
||
| async def send_notification( | ||
| self, subject: str, body: str, is_html: bool = False | ||
| ) -> bool: | ||
| raise NotImplementedError | ||
|
|
||
|
|
||
| # ============ SMTP 实现 ============ | ||
|
|
||
| class SmtpNotify(NotifyModule): | ||
| """SMTP 邮件通知实现(每次新建实例,但复用连接)""" | ||
|
|
||
| # 🔥 类变量共享 SMTP 连接(所有实例复用同一个连接) | ||
| _server = None | ||
|
|
||
| def __init__(self, modulemgr=None): | ||
| super().__init__(modulemgr) | ||
|
|
||
| def get_notify_email_enable(self) -> bool: | ||
| return self.get_config("notify_email_enable") | ||
|
|
||
| def get_notify_email_user(self) -> str: | ||
| return self.get_config("notify_email_user") | ||
|
|
||
| def get_notify_email_to(self) -> str: | ||
| return self.get_config("notify_email_to") | ||
|
|
||
| def get_notify_email_password(self) -> str: | ||
| return self.get_config("notify_email_password") | ||
|
|
||
| def get_notify_email_provider(self) -> str: | ||
| return self.get_config("notify_email_provider") | ||
|
|
||
| def get_smtp_config(self) -> dict: | ||
| provider = self.get_notify_email_provider() | ||
| return SMTP_CONFIGS.get(provider, SMTP_CONFIGS["QQ邮箱"]) | ||
|
|
||
| def _get_server(self): | ||
| """获取或创建 SMTP 连接(类级别复用)""" | ||
| if SmtpNotify._server is not None: | ||
| return SmtpNotify._server | ||
|
|
||
| smtp = self.get_smtp_config() | ||
| try: | ||
| if smtp.get("port") == 465: | ||
| server = smtplib.SMTP_SSL(smtp["host"], smtp["port"]) | ||
| else: | ||
| server = smtplib.SMTP(smtp["host"], smtp["port"]) | ||
| server.starttls() | ||
|
|
||
| user = self.get_notify_email_user() | ||
| password = self.get_notify_email_password() | ||
| server.login(user, password) | ||
|
|
||
| SmtpNotify._server = server | ||
| return server | ||
|
|
||
| except smtplib.SMTPException as e: | ||
| logger.error(f"SMTP 连接失败: {e}") | ||
| return None | ||
| except Exception as e: | ||
| logger.exception("连接失败: ") | ||
| return None | ||
|
|
||
| def _close_server(self): | ||
| """关闭 SMTP 连接""" | ||
| if SmtpNotify._server is not None: | ||
| try: | ||
| SmtpNotify._server.quit() | ||
| except Exception as e: | ||
| logger.exception("关闭 SMTP 连接时出错:") | ||
| SmtpNotify._server = None | ||
|
|
||
| def is_configured(self) -> bool: | ||
| enable = self.get_notify_email_enable() | ||
| user = self.get_notify_email_user() | ||
| to = self.get_notify_email_to() | ||
| password = self.get_notify_email_password() | ||
|
|
||
| if not enable: | ||
| return False | ||
| if not bool(user): | ||
| return False | ||
| if not bool(to): | ||
| return False | ||
| return bool(password) | ||
|
|
||
| async def send_notification( | ||
| self, subject: str, body: str, is_html: bool = False | ||
| ) -> bool: | ||
| if not self.is_configured(): | ||
| logger.warning("邮件通知未配置,跳过发送") | ||
| return False | ||
|
|
||
| user = self.get_notify_email_user() | ||
| to = self.get_notify_email_to() | ||
|
|
||
| content_type = "html" if is_html else "plain" | ||
| msg = MIMEText(body, content_type, "utf-8") | ||
| msg["From"] = Header(user) | ||
| msg["To"] = Header(to) | ||
| msg["Subject"] = Header(subject, "utf-8") | ||
|
|
||
| try: | ||
| server = self._get_server() | ||
| if server is None: | ||
| return False | ||
|
|
||
| server.sendmail(user, [to], msg.as_string()) | ||
| logger.info(f"邮件发送成功: {subject} -> {to}") | ||
| return True | ||
|
|
||
| except smtplib.SMTPServerDisconnected: | ||
| logger.warning("SMTP 连接已断开,尝试重新连接...") | ||
| self._close_server() | ||
| server = self._get_server() | ||
| if server is None: | ||
| logger.error("重新连接 SMTP 失败") | ||
| return False | ||
| try: | ||
| server.sendmail(user, [to], msg.as_string()) | ||
| logger.info(f"邮件重发成功: {subject} -> {to}") | ||
| return True | ||
| except Exception as e: | ||
| logger.exception("重发邮件失败: ") | ||
| return False | ||
|
|
||
| except smtplib.SMTPException as e: | ||
| logger.exception("SMTP 邮件发送失败: ") | ||
| self._close_server() | ||
| return False | ||
| except Exception: | ||
| logger.exception("邮件发送失败:") | ||
| return False | ||
|
|
||
|
|
||
| # ============ 配置类 ============ | ||
|
|
||
| @texttype("notify_email_to", "接收通知的邮箱", "") | ||
| @texttype("notify_email_password", "邮箱授权码", "") | ||
| @texttype("notify_email_user", "发送通知的邮箱", "") | ||
| @singlechoice("notify_email_provider", "邮箱服务商", "QQ邮箱", EMAIL_PROVIDERS) | ||
| @booltype("notify_email_enable", "启用邮件通知", False) | ||
| @description("邮件通知配置 - 任务完成后发送邮件提醒") | ||
| @name("邮件通知") | ||
| @default(False) | ||
| @notrunnable | ||
| class email_notify(SmtpNotify): | ||
| """邮件通知模块""" | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
邮件的发送者,是由用户配置的吗,不应该是搭建者配置?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我自己是服务端就自己使用。我觉得应该用户配置通知用户自己把,搭建者配置的话,所有用户的执行结果都通知搭建者么?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不过发送通知的邮箱和授权码可以搭建者配置,被通知的邮箱可以使用用户自己的