From 0d904b229e622932ea928147e7fc594e5653f516 Mon Sep 17 00:00:00 2001 From: aoxuan Date: Sun, 18 May 2025 01:57:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E5=88=9D=E6=AD=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7=E5=8D=95=E7=8B=AC?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/config.py | 14 +++ app/models/MAA.py | 41 ++++++- app/services/notification.py | 122 +++++++++++++++++++++ app/ui/member_manager.py | 207 +++++++++++++++++++++++++++++++++++ 4 files changed, 381 insertions(+), 3 deletions(-) diff --git a/app/core/config.py b/app/core/config.py index 77cfbbf..6a5fb17 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -439,6 +439,20 @@ class MaaUserConfig(LQConfig): "Data", "CustomInfrastPlanIndex", "0" ) + # 新增用户单独通知字段 + self.Notify_Enable = ConfigItem("Notify_Enable", False, False) + self.Notify_IfSMTP = ConfigItem("Notify_IfSMTP", False, False) + self.Notify_SMTPServerAddress = ConfigItem("Notify_SMTPServerAddress", "", "") + self.Notify_AuthorizationCode = ConfigItem("Notify_AuthorizationCode", "", "") + self.Notify_FromAddress = ConfigItem("Notify_FromAddress", "", "") + self.Notify_ToAddress = ConfigItem("Notify_ToAddress", "", "") + self.Notify_IfServerChan = ConfigItem("Notify_IfServerChan", False, False) + self.Notify_ServerChanKey = ConfigItem("Notify_ServerChanKey", "", "") + self.Notify_ServerChanChannel = ConfigItem("Notify_ServerChanChannel", "", "") + self.Notify_ServerChanTag = ConfigItem("Notify_ServerChanTag", "", "") + self.Notify_IfCompanyWebHookBot = ConfigItem("Notify_IfCompanyWebHookBot", False, False) + self.Notify_CompanyWebHookBotUrl = ConfigItem("Notify_CompanyWebHookBotUrl", "", "") + class MaaPlanConfig(LQConfig): """MAA计划表配置""" diff --git a/app/models/MAA.py b/app/models/MAA.py index 260d0ca..6662a30 100644 --- a/app/models/MAA.py +++ b/app/models/MAA.py @@ -40,6 +40,7 @@ from typing import Union, List, Dict from app.core import Config, MaaConfig, MaaUserConfig from app.services import Notify, System +from app.services.notification import Notification, UserNotification class MaaManager(QObject): @@ -1746,7 +1747,6 @@ class MaaManager(QObject): ) if mode == "代理结果": - # 生成文本通知内容 message_text = ( f"任务开始时间:{message["start_time"]},结束时间:{message["end_time"]}\n" @@ -1768,14 +1768,26 @@ class MaaManager(QObject): template = env.get_template("MAA_result.html") message_html = template.render(message) + # 发送全局通知 Notify.send_mail("网页", title, message_html) Notify.ServerChanPush(title, f"{message_text}\n\nAUTO_MAA 敬上") Notify.CompanyWebHookBotPush(title, f"{message_text}\n\nAUTO_MAA 敬上") + # 发送用户单独通知 + for user_name in message["failed_user"].split("、") + message["waiting_user"].split("、"): + if not user_name: # 跳过空字符串 + continue + user_config = Config.member_dict.get(user_name) + if user_config and user_config.get(user_config.Notify_Enable): + user_notify = UserNotification(user_config) + user_notify.send_notification( + f"{self.mode[2:4]}任务未完成通知", + f"您的{self.mode[2:4]}任务未完成,请检查相关设置。\n\n{message_text}" + ) + return message_text elif mode == "统计信息": - # 生成文本通知内容 formatted = [] for stage, items in message["drop_statistics"].items(): @@ -1801,18 +1813,41 @@ class MaaManager(QObject): template = env.get_template("MAA_statistics.html") message_html = template.render(message) + # 发送全局通知 Notify.send_mail("网页", title, message_html) # ServerChan的换行是两个换行符。故而将\n替换为\n\n serverchan_message = message_text.replace("\n", "\n\n") Notify.ServerChanPush(title, f"{serverchan_message}\n\nAUTO_MAA 敬上") Notify.CompanyWebHookBotPush(title, f"{message_text}\n\nAUTO_MAA 敬上") - elif mode == "公招六星": + # 发送用户单独通知 + user_name = message.get("user_info") + if user_name: + user_config = Config.member_dict.get(user_name) + if user_config and user_config.get(user_config.Notify_Enable): + user_notify = UserNotification(user_config) + user_notify.send_notification( + f"{self.mode[2:4]}任务统计报告", + f"您的{self.mode[2:4]}任务统计报告如下:\n\n{message_text}" + ) + elif mode == "公招六星": # 生成HTML通知内容 template = env.get_template("MAA_six_star.html") message_html = template.render(message) + # 发送全局通知 Notify.send_mail("网页", title, message_html) Notify.ServerChanPush(title, "好羡慕~\n\nAUTO_MAA 敬上") Notify.CompanyWebHookBotPush(title, "好羡慕~\n\nAUTO_MAA 敬上") + + # 发送用户单独通知 + user_name = message.get("user_name") + if user_name: + user_config = Config.member_dict.get(user_name) + if user_config and user_config.get(user_config.Notify_Enable): + user_notify = UserNotification(user_config) + user_notify.send_notification( + "公招六星通知", + "恭喜您在公招中获得了六星干员!" + ) diff --git a/app/services/notification.py b/app/services/notification.py index 0f63acf..9666f9a 100644 --- a/app/services/notification.py +++ b/app/services/notification.py @@ -313,4 +313,126 @@ class Notification(QWidget): return True +class UserNotification: + """用户单独通知服务""" + + def __init__(self, user_config): + self.config = user_config + + def send_notification(self, title: str, content: str) -> bool: + """发送用户通知 + + Args: + title: 通知标题 + content: 通知内容 + + Returns: + bool: 是否发送成功 + """ + if not self.config.get(self.config.Notify_Enable): + return False + + success = False + + # 发送邮件通知 + if self._check_smtp_config(): + try: + self._send_email(title, content) + success = True + except Exception as e: + logger.error(f"发送邮件通知失败: {str(e)}") + + # 发送ServerChan通知 + if self.config.get(self.config.Notify_IfServerChan) and self._check_serverchan_config(): + try: + self._send_serverchan(title, content) + success = True + except Exception as e: + logger.error(f"发送ServerChan通知失败: {str(e)}") + + # 发送企业微信机器人通知 + if self.config.get(self.config.Notify_IfCompanyWebHookBot) and self._check_webhook_config(): + try: + self._send_webhook(title, content) + success = True + except Exception as e: + logger.error(f"发送企业微信机器人通知失败: {str(e)}") + + return success + + def _check_smtp_config(self) -> bool: + """检查SMTP配置是否完整""" + return all([ + self.config.get(self.config.Notify_IfSMTP), + self.config.get(self.config.Notify_SMTPServerAddress), + self.config.get(self.config.Notify_AuthorizationCode), + self.config.get(self.config.Notify_FromAddress), + self.config.get(self.config.Notify_ToAddress) + ]) + + def _check_serverchan_config(self) -> bool: + """检查ServerChan配置是否完整""" + return bool(self.config.get(self.config.Notify_ServerChanKey)) + + def _check_webhook_config(self) -> bool: + """检查企业微信机器人配置是否完整""" + return bool(self.config.get(self.config.Notify_CompanyWebHookBotUrl)) + + def _send_email(self, title: str, content: str): + """发送邮件通知""" + import smtplib + from email.mime.text import MIMEText + from email.header import Header + + msg = MIMEText(content, 'plain', 'utf-8') + msg['Subject'] = Header(title, 'utf-8') + msg['From'] = self.config.get(self.config.Notify_FromAddress) + msg['To'] = self.config.get(self.config.Notify_ToAddress) + + server = smtplib.SMTP_SSL(self.config.get(self.config.Notify_SMTPServerAddress)) + server.login( + self.config.get(self.config.Notify_FromAddress), + self.config.get(self.config.Notify_AuthorizationCode) + ) + server.send_message(msg) + server.quit() + + def _send_serverchan(self, title: str, content: str): + """发送ServerChan通知""" + import requests + + key = self.config.get(self.config.Notify_ServerChanKey) + channel = self.config.get(self.config.Notify_ServerChanChannel) + tag = self.config.get(self.config.Notify_ServerChanTag) + + url = f"https://sctapi.ftqq.com/{key}.send" + data = { + "title": title, + "desp": content, + "channel": channel if channel else 9, # 默认使用企业微信通道 + "tag": tag if tag else "" + } + + response = requests.post(url, data=data) + if response.status_code != 200: + raise Exception(f"ServerChan API返回错误: {response.text}") + + def _send_webhook(self, title: str, content: str): + """发送企业微信机器人通知""" + import requests + import json + + url = self.config.get(self.config.Notify_CompanyWebHookBotUrl) + data = { + "msgtype": "markdown", + "markdown": { + "content": f"### {title}\n{content}" + } + } + + response = requests.post(url, json=data) + if response.status_code != 200: + raise Exception(f"企业微信机器人API返回错误: {response.text}") + + Notify = Notification() diff --git a/app/ui/member_manager.py b/app/ui/member_manager.py index 34ff739..71fc30f 100644 --- a/app/ui/member_manager.py +++ b/app/ui/member_manager.py @@ -1529,6 +1529,140 @@ class MemberManager(QWidget): parent=self, ) + # 新增单独通知卡片 + self.card_NotifyEnable = SwitchSettingCard( + icon=FluentIcon.INFO, + title="启用单独通知", + content="启用后,任务结束将向该用户单独推送通知", + qconfig=self.config, + configItem=self.config.Notify_Enable, + parent=self + ) + self.card_NotifyIfSMTP = SwitchSettingCard( + icon=FluentIcon.INFO, + title="启用SMTP通知", + content="是否启用单独通知的SMTP邮件推送", + qconfig=self.config, + configItem=self.config.Notify_IfSMTP, + parent=self + ) + self.card_NotifySMTP = LineEditSettingCard( + icon=FluentIcon.INFO, + title="SMTP服务器地址", + content="单独通知的SMTP服务器地址", + text="请输入SMTP服务器地址", + qconfig=self.config, + configItem=self.config.Notify_SMTPServerAddress, + parent=self + ) + self.card_NotifyAuthCode = PasswordLineEditSettingCard( + icon=FluentIcon.INFO, + title="授权码", + content="单独通知的邮箱授权码", + text="请输入授权码", + algorithm="AUTO", + qconfig=self.config, + configItem=self.config.Notify_AuthorizationCode, + parent=self + ) + self.card_NotifyFrom = LineEditSettingCard( + icon=FluentIcon.INFO, + title="发件人邮箱", + content="单独通知的发件人邮箱", + text="请输入发件人邮箱", + qconfig=self.config, + configItem=self.config.Notify_FromAddress, + parent=self + ) + self.card_NotifyTo = LineEditSettingCard( + icon=FluentIcon.INFO, + title="收件人邮箱", + content="单独通知的收件人邮箱", + text="请输入收件人邮箱", + qconfig=self.config, + configItem=self.config.Notify_ToAddress, + parent=self + ) + self.card_NotifyIfServerChan = SwitchSettingCard( + icon=FluentIcon.INFO, + title="启用ServerChan", + content="是否启用单独通知的ServerChan推送", + qconfig=self.config, + configItem=self.config.Notify_IfServerChan, + parent=self + ) + self.card_NotifyServerChanKey = LineEditSettingCard( + icon=FluentIcon.INFO, + title="ServerChanKey", + content="单独通知的ServerChan SendKey", + text="请输入ServerChanKey", + qconfig=self.config, + configItem=self.config.Notify_ServerChanKey, + parent=self + ) + self.card_NotifyServerChanChannel = LineEditSettingCard( + icon=FluentIcon.INFO, + title="ServerChanChannel", + content="单独通知的ServerChan Channel", + text="请输入ServerChanChannel", + qconfig=self.config, + configItem=self.config.Notify_ServerChanChannel, + parent=self + ) + self.card_NotifyServerChanTag = LineEditSettingCard( + icon=FluentIcon.INFO, + title="ServerChanTag", + content="单独通知的ServerChan Tag", + text="请输入ServerChanTag", + qconfig=self.config, + configItem=self.config.Notify_ServerChanTag, + parent=self + ) + self.card_NotifyIfCompanyWebHookBot = SwitchSettingCard( + icon=FluentIcon.INFO, + title="启用企业微信机器人", + content="是否启用单独通知的企业微信机器人推送", + qconfig=self.config, + configItem=self.config.Notify_IfCompanyWebHookBot, + parent=self + ) + self.card_NotifyCompanyWebHookBotUrl = LineEditSettingCard( + icon=FluentIcon.INFO, + title="企业微信机器人WebhookUrl", + content="单独通知的企业微信机器人Webhook地址", + text="请输入WebhookUrl", + qconfig=self.config, + configItem=self.config.Notify_CompanyWebHookBotUrl, + parent=self + ) + + # 设置通知卡片默认隐藏 + self.card_NotifyIfSMTP.setVisible(False) + self.card_NotifySMTP.setVisible(False) + self.card_NotifyAuthCode.setVisible(False) + self.card_NotifyFrom.setVisible(False) + self.card_NotifyTo.setVisible(False) + self.card_NotifyIfServerChan.setVisible(False) + self.card_NotifyServerChanKey.setVisible(False) + self.card_NotifyServerChanChannel.setVisible(False) + self.card_NotifyServerChanTag.setVisible(False) + self.card_NotifyIfCompanyWebHookBot.setVisible(False) + self.card_NotifyCompanyWebHookBotUrl.setVisible(False) + + # 连接通知启用开关的信号 + self.card_NotifyEnable.checkedChanged.connect(self.toggle_notify_settings) + self.card_NotifyIfSMTP.checkedChanged.connect(self.toggle_smtp_settings) + + # 根据配置状态初始化显示 + if self.config.get(self.config.Notify_Enable): + self.toggle_notify_settings(True) + if self.config.get(self.config.Notify_IfSMTP): + self.toggle_smtp_settings(True) + if self.config.get(self.config.Notify_IfServerChan): + self.toggle_serverchan_settings(True) + if self.config.get(self.config.Notify_IfCompanyWebHookBot): + self.toggle_webhook_settings(True) + h1_layout = QHBoxLayout() h1_layout.addWidget(self.card_Name) h1_layout.addWidget(self.card_Id) @@ -1566,6 +1700,27 @@ class MemberManager(QWidget): Layout.addLayout(h6_layout) Layout.addLayout(h7_layout) Layout.addLayout(h8_layout) + + # 创建通知设置容器 + notify_container = QWidget() + notify_layout = QVBoxLayout(notify_container) + notify_layout.setContentsMargins(0, 0, 0, 0) + notify_layout.setSpacing(0) + + notify_layout.addWidget(self.card_NotifyEnable) + notify_layout.addWidget(self.card_NotifyIfSMTP) + notify_layout.addWidget(self.card_NotifySMTP) + notify_layout.addWidget(self.card_NotifyAuthCode) + notify_layout.addWidget(self.card_NotifyFrom) + notify_layout.addWidget(self.card_NotifyTo) + notify_layout.addWidget(self.card_NotifyIfServerChan) + notify_layout.addWidget(self.card_NotifyServerChanKey) + notify_layout.addWidget(self.card_NotifyServerChanChannel) + notify_layout.addWidget(self.card_NotifyServerChanTag) + notify_layout.addWidget(self.card_NotifyIfCompanyWebHookBot) + notify_layout.addWidget(self.card_NotifyCompanyWebHookBotUrl) + + Layout.addWidget(notify_container) self.viewLayout.addLayout(Layout) self.viewLayout.setContentsMargins(3, 0, 3, 3) @@ -1588,6 +1743,14 @@ class MemberManager(QWidget): Config.gameid_refreshed.connect(self.refresh_gameid) Config.PASSWORD_refreshed.connect(self.refresh_password) + # 连接ServerChan和企业微信机器人的开关信号 + self.card_NotifyIfServerChan.checkedChanged.connect( + lambda checked: self.toggle_serverchan_settings(checked) + ) + self.card_NotifyIfCompanyWebHookBot.checkedChanged.connect( + lambda checked: self.toggle_webhook_settings(checked) + ) + self.switch_mode() self.switch_infrastructure() @@ -1709,3 +1872,47 @@ class MemberManager(QWidget): } }, ) + + def toggle_notify_settings(self, checked: bool): + """切换通知设置卡片的显示状态""" + self.card_NotifyIfSMTP.setVisible(checked) + self.card_NotifyIfServerChan.setVisible(checked) + self.card_NotifyIfCompanyWebHookBot.setVisible(checked) + + # 根据SMTP开关状态控制相关设置 + if checked and self.config.get(self.config.Notify_IfSMTP): + self.toggle_smtp_settings(True) + else: + self.toggle_smtp_settings(False) + + # 根据ServerChan开关状态控制相关设置 + if checked and self.config.get(self.config.Notify_IfServerChan): + self.toggle_serverchan_settings(True) + else: + self.toggle_serverchan_settings(False) + + # 根据企业微信机器人开关状态控制相关设置 + if checked and self.config.get(self.config.Notify_IfCompanyWebHookBot): + self.toggle_webhook_settings(True) + else: + self.toggle_webhook_settings(False) + + def toggle_smtp_settings(self, checked: bool): + """切换SMTP相关设置的显示状态""" + if self.config.get(self.config.Notify_Enable): + self.card_NotifySMTP.setVisible(checked) + self.card_NotifyAuthCode.setVisible(checked) + self.card_NotifyFrom.setVisible(checked) + self.card_NotifyTo.setVisible(checked) + + def toggle_serverchan_settings(self, checked: bool): + """切换ServerChan相关设置的显示状态""" + if self.config.get(self.config.Notify_Enable): + self.card_NotifyServerChanKey.setVisible(checked) + self.card_NotifyServerChanChannel.setVisible(checked) + self.card_NotifyServerChanTag.setVisible(checked) + + def toggle_webhook_settings(self, checked: bool): + """切换企业微信机器人相关设置的显示状态""" + if self.config.get(self.config.Notify_Enable): + self.card_NotifyCompanyWebHookBotUrl.setVisible(checked)