From d1c8f984082929b095fdfe67670ffadfb387042f Mon Sep 17 00:00:00 2001 From: DLmaster Date: Sun, 2 Mar 2025 17:14:09 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(ui):=20=E4=B8=BB=E9=A1=B5=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=B8=BB=E9=A2=98=E6=B4=BB=E5=8A=A8=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/ui/home.py | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/app/ui/home.py b/app/ui/home.py index 031b3c7..900f724 100644 --- a/app/ui/home.py +++ b/app/ui/home.py @@ -36,7 +36,14 @@ from PySide6.QtWidgets import ( ) from PySide6.QtCore import Qt, QSize, QUrl from PySide6.QtGui import QDesktopServices, QColor -from qfluentwidgets import FluentIcon, ScrollArea, SimpleCardWidget, PrimaryToolButton +from qfluentwidgets import ( + FluentIcon, + ScrollArea, + SimpleCardWidget, + PrimaryToolButton, + TextBrowser, +) +import re import shutil import requests import json @@ -55,9 +62,13 @@ class Home(QWidget): self.setObjectName("主页") self.banner = Banner() - self.banner.set_percentage_size( - 0.8, 0.5 - ) # 设置 Banner 大小为窗口的 80% 宽度和 50% 高度 + self.banner_text = TextBrowser() + + widget = QWidget() + Layout = QVBoxLayout(widget) + + Layout.addWidget(self.banner) + Layout.addWidget(self.banner_text) v_layout = QVBoxLayout(self.banner) v_layout.setContentsMargins(0, 0, 0, 15) @@ -138,7 +149,7 @@ class Home(QWidget): layout = QVBoxLayout() scrollArea = ScrollArea() scrollArea.setWidgetResizable(True) - scrollArea.setWidget(self.banner) + scrollArea.setWidget(widget) layout.addWidget(scrollArea) self.setLayout(layout) @@ -243,11 +254,11 @@ class Home(QWidget): ) as file: file.write(response.content) - logger.info("主题图像下载成功") + logger.info(f"主题图像「{theme_image["name"]}」下载成功") MainInfoBar.push_info_bar( "success", "主题图像下载成功", - "主题图像下载成功!", + f"「{theme_image["name"]}」下载成功!", 3000, ) @@ -288,6 +299,7 @@ class Home(QWidget): str(Config.app_path / "resources/images/Home/BannerDefault.png") ) self.imageButton.hide() + self.banner_text.setVisible(False) elif ( Config.global_config.get(Config.global_config.function_HomeImageMode) == "自定义" @@ -296,6 +308,7 @@ class Home(QWidget): self.banner.set_banner_image(str(file)) break self.imageButton.show() + self.banner_text.setVisible(False) elif ( Config.global_config.get(Config.global_config.function_HomeImageMode) == "主题图像" @@ -304,6 +317,18 @@ class Home(QWidget): str(Config.app_path / "resources/images/Home/BannerTheme.jpg") ) self.imageButton.show() + self.banner_text.setVisible(True) + + if (Config.app_path / "resources/theme_image.json").exists(): + with (Config.app_path / "resources/theme_image.json").open( + mode="r", encoding="utf-8" + ) as f: + theme_image = json.load(f) + html_content = theme_image["html"] + else: + html_content = "

主题图像

主题图像信息未知

" + + self.banner_text.setHtml(re.sub(r"]*>", "", html_content)) class ButtonGroup(SimpleCardWidget): From 175d6860a3737675dedfb994842cf11f644b13cc Mon Sep 17 00:00:00 2001 From: DLmaster Date: Mon, 3 Mar 2025 20:40:28 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(core):=20=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E4=BF=A1=E6=81=AF=E9=80=9A=E7=9F=A5=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/config.py | 37 ++++--- app/models/MAA.py | 206 +++++++++++++++++++++++++++++++++-- app/services/notification.py | 13 ++- app/ui/home.py | 2 + app/ui/setting.py | 7 ++ resources/version.json | 4 +- 6 files changed, 240 insertions(+), 29 deletions(-) diff --git a/app/core/config.py b/app/core/config.py index 7516600..ed5bce7 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -31,7 +31,7 @@ import json import sys import shutil import re -from datetime import datetime, timedelta +from datetime import datetime from collections import defaultdict from pathlib import Path from qfluentwidgets import ( @@ -45,7 +45,7 @@ from qfluentwidgets import ( OptionsValidator, qconfig, ) -from typing import Union, Dict, List +from typing import Union, Dict, List, Tuple class AppConfig: @@ -469,8 +469,6 @@ class AppConfig: def save_maa_log(self, log_path: Path, logs: list, maa_result: str) -> None: """保存MAA日志""" - log_path.parent.mkdir(parents=True, exist_ok=True) - data: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = { "recruit_statistics": defaultdict(int), "drop_statistics": defaultdict(dict), @@ -545,6 +543,7 @@ class AppConfig: data["drop_statistics"][current_stage] = stage_drops # 保存日志 + log_path.parent.mkdir(parents=True, exist_ok=True) with log_path.open("w", encoding="utf-8") as f: f.writelines(logs) with log_path.with_suffix(".json").open("w", encoding="utf-8") as f: @@ -552,10 +551,10 @@ class AppConfig: logger.info(f"处理完成:{log_path}") - self.merge_maa_logs(log_path.parent) + self.merge_maa_logs("所有项", log_path.parent) - def merge_maa_logs(self, logs_path: Path) -> None: - """合并所有 .log 文件""" + def merge_maa_logs(self, mode: str, logs_path: Union[Path, List[Path]]) -> dict: + """合并指定数据统计信息文件""" data = { "recruit_statistics": defaultdict(int), @@ -563,7 +562,12 @@ class AppConfig: "maa_result": defaultdict(str), } - for json_file in logs_path.glob("*.json"): + if mode == "所有项": + logs_path_list = list(logs_path.glob("*.json")) + elif mode == "指定项": + logs_path_list = logs_path + + for json_file in logs_path_list: with json_file.open("r", encoding="utf-8") as f: single_data: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = ( @@ -592,10 +596,14 @@ class AppConfig: ] = single_data["maa_result"] # 生成汇总 JSON 文件 - with logs_path.with_suffix(".json").open("w", encoding="utf-8") as f: - json.dump(data, f, ensure_ascii=False, indent=4) + if mode == "所有项": - logger.info(f"统计完成:{logs_path.with_suffix(".json")}") + with logs_path.with_suffix(".json").open("w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=4) + + logger.info(f"统计完成:{logs_path.with_suffix(".json")}") + + return data def load_maa_logs( self, mode: str, json_path: Path @@ -779,11 +787,14 @@ class GlobalConfig(QConfig): ui_location = ConfigItem("UI", "location", "100x100") ui_maximized = ConfigItem("UI", "maximized", False, BoolValidator()) - notify_IfPushPlyer = ConfigItem("Notify", "IfPushPlyer", False, BoolValidator()) - notify_IfSendMail = ConfigItem("Notify", "IfSendMail", False, BoolValidator()) notify_IfSendErrorOnly = ConfigItem( "Notify", "IfSendErrorOnly", False, BoolValidator() ) + notify_IfSendStatistic = ConfigItem( + "Notify", "IfSendStatistic", False, BoolValidator() + ) + notify_IfPushPlyer = ConfigItem("Notify", "IfPushPlyer", False, BoolValidator()) + notify_IfSendMail = ConfigItem("Notify", "IfSendMail", False, BoolValidator()) notify_SMTPServerAddress = ConfigItem("Notify", "SMTPServerAddress", "") notify_AuthorizationCode = ConfigItem("Notify", "AuthorizationCode", "") notify_FromAddress = ConfigItem("Notify", "FromAddress", "") diff --git a/app/models/MAA.py b/app/models/MAA.py index f95ea2c..8ff7b90 100644 --- a/app/models/MAA.py +++ b/app/models/MAA.py @@ -34,7 +34,7 @@ import subprocess import shutil import time from pathlib import Path -from typing import List +from typing import Union, List from app.core import Config from app.services import Notify, System @@ -176,6 +176,9 @@ class MaaManager(QObject): [True, "routine", "日常"], ] + user_logs_list = [] + user_start_time = datetime.now() + # 尝试次数循环 for i in range(self.set["RunSet"]["RunTimesLimit"]): @@ -273,7 +276,7 @@ class MaaManager(QObject): System.kill_process(self.maa_exe_path) self.if_open_emulator = True # 推送异常通知 - Notify.push_notification( + Notify.push_plyer( "用户自动代理出现异常!", f"用户 {user[0].replace("_", " 今天的")}的{mode_book[j][5:7]}部分出现一次异常", f"{user[0].replace("_", " ")}的{mode_book[j][5:7]}出现异常", @@ -287,13 +290,17 @@ class MaaManager(QObject): # 移除静默进程标记 Config.silence_list.remove(self.emulator_path) - # 保存运行日志 + # 保存运行日志以及统计信息 Config.save_maa_log( Config.app_path / f"history/{curdate}/{self.data[user[2]][0]}/{start_time.strftime("%H-%M-%S")}.log", self.check_maa_log(start_time, mode_book[j]), self.maa_result, ) + user_logs_list.append( + Config.app_path + / f"history/{curdate}/{self.data[user[2]][0]}/{start_time.strftime("%H-%M-%S")}.json", + ) # 成功完成代理的用户修改相关参数 if run_book[0] and run_book[1]: @@ -301,7 +308,7 @@ class MaaManager(QObject): self.data[user[2]][3] -= 1 self.data[user[2]][14] += 1 user[1] = "完成" - Notify.push_notification( + Notify.push_plyer( "成功完成一个自动代理任务!", f"已完成用户 {user[0].replace("_", " 今天的")}任务", f"已完成 {user[0].replace("_", " 的")}", @@ -309,6 +316,26 @@ class MaaManager(QObject): ) break + if Config.global_config.get( + Config.global_config.notify_IfSendStatistic + ): + + statistics = Config.merge_maa_logs("指定项", user_logs_list) + statistics["start_time"] = user_start_time.strftime( + "%Y-%m-%d %H:%M:%S" + ) + statistics["end_time"] = datetime.now().strftime( + "%Y-%m-%d %H:%M:%S" + ) + statistics["maa_result"] = ( + "代理任务全部完成" + if (run_book[0] and run_book[1]) + else "代理任务未全部完成" + ) + self.push_notification( + "统计信息", f"用户 {user[0]} 的自动代理统计报告", statistics + ) + # 录入代理失败的用户 if not (run_book[0] and run_book[1]): user[1] = "异常" @@ -487,7 +514,7 @@ class MaaManager(QObject): else f"{self.mode[:4]}任务报告" ) # 推送代理结果通知 - Notify.push_notification( + Notify.push_plyer( title.replace("报告", "已完成!"), f"已完成用户数:{len(over_index)},未完成用户数:{len(error_index) + len(wait_index)}", f"已完成用户数:{len(over_index)},未完成用户数:{len(error_index) + len(wait_index)}", @@ -499,12 +526,7 @@ class MaaManager(QObject): Config.global_config.get(Config.global_config.notify_IfSendErrorOnly) and len(error_index) + len(wait_index) != 0 ): - Notify.send_mail( - title, - f"{end_log}\n\nAUTO_MAA 敬上\n\n我们根据您在 AUTO_MAA 中的设置发送了这封电子邮件,本邮件无需回复\n", - ) - Notify.ServerChanPush(title, f"{end_log}\n\nAUTO_MAA 敬上") - Notify.CompanyWebHookBotPush(title, f"{end_log}AUTO_MAA 敬上") + self.push_notification("代理结果", title, end_log) self.agree_bilibili(False) self.log_monitor.deleteLater() @@ -1130,3 +1152,165 @@ class MaaManager(QObject): if dt.time() < datetime.min.time().replace(hour=4): dt = dt - timedelta(days=1) return dt.strftime("%Y-%m-%d") + + def push_notification( + self, mode: str, title: str, message: Union[str, dict] + ) -> None: + """通过所有渠道推送通知""" + + if mode == "代理结果": + + Notify.send_mail( + "文本", + title, + f"{message}\n\nAUTO_MAA 敬上\n\n我们根据您在 AUTO_MAA 中的设置发送了这封电子邮件,本邮件无需回复\n", + ) + Notify.ServerChanPush(title, f"{message}\n\nAUTO_MAA 敬上") + Notify.CompanyWebHookBotPush(title, f"{message}AUTO_MAA 敬上") + + elif mode == "统计信息": + + # HTML模板 + html_template = """ + + + + + {title} + + + +
+

{title}

+ +

开始时间: {start_time}

+

结束时间: {end_time}

+

MAA执行结果: {maa_result}

+ +

招募统计

+ + + + + + + + + {recruit_rows} + +
星级数量
+ + {drop_tables} +
+ + + """ + + # 构建招募统计表格行 + recruit_rows = "".join( + f"{star}{count}" + for star, count in message["recruit_statistics"].items() + ) + + # 构建掉落统计数据表格 + drop_tables_html = "" + for stage, items in message["drop_statistics"].items(): + drop_rows = "".join( + f"{item}{quantity}" + for item, quantity in items.items() + ) + drop_table = f""" +

掉落统计 ({stage})

+ + + + + + + + + {drop_rows} + +
物品数量
+ """ + drop_tables_html += drop_table + + # 填充HTML模板 + html_content = html_template.format( + title=title, + start_time=message["start_time"], + end_time=message["end_time"], + maa_result=message["maa_result"], + recruit_rows=recruit_rows, + drop_tables=drop_tables_html, + ) + + formatted = [] + for stage, items in message["drop_statistics"].items(): + formatted.append(f"掉落统计({stage}):") + for item, quantity in items.items(): + formatted.append(f" {item}: {quantity}") + drop_text = "\n".join(formatted) + + formatted = ["招募统计:"] + for star, count in message["recruit_statistics"].items(): + formatted.append(f" {star}: {count}") + recruit_text = "\n".join(formatted) + + # 构建通知消息 + message_text = ( + f"开始时间: {message['start_time']}\n" + f"结束时间: {message['end_time']}\n" + f"MAA执行结果: {message['maa_result']}\n\n" + f"{recruit_text}\n" + f"{drop_text}" + ) + + Notify.send_mail("网页", title, html_content) + Notify.ServerChanPush(title, f"{message_text}\n\nAUTO_MAA 敬上") + Notify.CompanyWebHookBotPush(title, f"{message_text}AUTO_MAA 敬上") diff --git a/app/services/notification.py b/app/services/notification.py index 4f21411..b5b0566 100644 --- a/app/services/notification.py +++ b/app/services/notification.py @@ -29,6 +29,7 @@ from loguru import logger from plyer import notification import smtplib from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart from email.header import Header from email.utils import formataddr @@ -40,7 +41,7 @@ from app.services.security import Crypto class Notification: - def push_notification(self, title, message, ticker, t): + def push_plyer(self, title, message, ticker, t): """推送系统通知""" if Config.global_config.get(Config.global_config.notify_IfPushPlyer): @@ -57,14 +58,17 @@ class Notification: return True - def send_mail(self, title, content): + def send_mail(self, mode, title, content): """推送邮件通知""" if Config.global_config.get(Config.global_config.notify_IfSendMail): try: # 定义邮件正文 - message = MIMEText(content, "plain", "utf-8") + if mode == "文本": + message = MIMEText(content, "plain", "utf-8") + elif mode == "网页": + message = MIMEMultipart("alternative") message["From"] = formataddr( ( Header("AUTO_MAA通知服务", "utf-8").encode(), @@ -81,6 +85,9 @@ class Notification: ) # 收件人显示的名字 message["Subject"] = Header(title, "utf-8") + if mode == "网页": + message.attach(MIMEText(content, "html", "utf-8")) + smtpObj = smtplib.SMTP_SSL( Config.global_config.get( Config.global_config.notify_SMTPServerAddress diff --git a/app/ui/home.py b/app/ui/home.py index 900f724..e9ae41a 100644 --- a/app/ui/home.py +++ b/app/ui/home.py @@ -69,6 +69,8 @@ class Home(QWidget): Layout.addWidget(self.banner) Layout.addWidget(self.banner_text) + Layout.setStretch(0, 2) + Layout.setStretch(1, 3) v_layout = QVBoxLayout(self.banner) v_layout.setContentsMargins(0, 0, 0, 15) diff --git a/app/ui/setting.py b/app/ui/setting.py index db398bc..bb47843 100644 --- a/app/ui/setting.py +++ b/app/ui/setting.py @@ -548,6 +548,12 @@ class NotifySettingCard(HeaderCardWidget): content="仅在任务出现异常时推送通知", configItem=Config.global_config.notify_IfSendErrorOnly, ) + self.caer_IfSendStatistic = SwitchSettingCard( + icon=FluentIcon.PAGE_RIGHT, + title="推送统计信息", + content="推送自动代理统计信息的通知", + configItem=Config.global_config.notify_IfSendStatistic, + ) self.card_IfPushPlyer = SwitchSettingCard( icon=FluentIcon.PAGE_RIGHT, title="推送系统通知", @@ -560,6 +566,7 @@ class NotifySettingCard(HeaderCardWidget): Layout = QVBoxLayout() Layout.addWidget(self.card_IfSendErrorOnly) + Layout.addWidget(self.caer_IfSendStatistic) Layout.addWidget(self.card_IfPushPlyer) Layout.addWidget(self.card_SendMail) Layout.addWidget(self.card_ServerChan) diff --git a/resources/version.json b/resources/version.json index 82ecef0..364384f 100644 --- a/resources/version.json +++ b/resources/version.json @@ -1,7 +1,7 @@ { - "main_version": "4.2.4.6", + "main_version": "4.2.4.7", "updater_version": "1.1.2.1", - "announcement": "\n## 新增功能\n- 历史记录统计功能上线\n- 添加软件主页\n- 添加启动时直接最小化功能\n- 更新器拥有多网址测速功能\n## 修复BUG\n- RMA70-12不能正确统计的问题\n- 更新器修正`channel`\n## 程序优化\n- 添加MAA监测字段:`未检测到任何模拟器`\n- 取消MAA运行中自动更新", + "announcement": "\n## 新增功能\n- 历史记录统计功能上线\n- 添加软件主页\n- 添加启动时直接最小化功能\n- 更新器拥有多网址测速功能\n- 添加统计信息通知功能\n## 修复BUG\n- RMA70-12不能正确统计的问题\n- 更新器修正`channel`\n## 程序优化\n- 添加MAA监测字段:`未检测到任何模拟器`\n- 取消MAA运行中自动更新", "proxy_list": [ "", "https://gitproxy.click/",