diff --git a/AUTO_MAA.py b/AUTO_MAA.py index db99426..68e335d 100644 --- a/AUTO_MAA.py +++ b/AUTO_MAA.py @@ -112,6 +112,9 @@ class MaaRunner(QtCore.QThread): self.annihilation = self.config["Default"]["TimeLimit.annihilation"] self.num = self.config["Default"]["TimesLimit.run"] self.if_send_mail = bool(self.config["Default"]["SelfSet.IfSendMail"] == "True") + self.if_send_error_only = bool( + self.config["Default"]["SelfSet.IfSendMail.OnlyError"] == "True" + ) def run(self): """主进程,运行MAA代理进程""" @@ -544,7 +547,10 @@ class MaaRunner(QtCore.QThread): f"已完成用户数:{len(over_index)},未完成用户数:{len(error_index) + len(wait_index)}", 10, ) - if self.if_send_mail: + if self.if_send_mail and ( + not self.if_send_error_only + or (self.if_send_error_only and len(error_index) + len(wait_index) != 0) + ): self.send_mail.emit( f"{self.mode[:4]}任务报告", f"{end_log}\n\nAUTO_MAA 敬上\n\n我们根据您在 AUTO_MAA 中的设置发送了这封电子邮件,本邮件无需回复\n", @@ -1199,6 +1205,9 @@ class Main(QWidget): self.mail_address = self.ui.findChild(QLineEdit, "lineEdit_mailaddress") self.mail_address.textChanged.connect(self.change_config) + self.if_send_error_only = self.ui.findChild(QCheckBox, "checkBox_ifonlyerror") + self.if_send_error_only.stateChanged.connect(self.change_config) + self.if_to_tray = self.ui.findChild(QCheckBox, "checkBox_iftotray") self.if_to_tray.stateChanged.connect(self.change_config) @@ -1339,6 +1348,7 @@ class Main(QWidget): ["SelfSet.IfProxyDirectly", "False"], ["SelfSet.IfSendMail", "False"], ["SelfSet.MailAddress", ""], + ["SelfSet.IfSendMail.OnlyError", "False"], ["SelfSet.IfToTray", "False"], ["SelfSet.UIsize", "1200x700"], ["SelfSet.UIlocation", "100x100"], @@ -1825,6 +1835,14 @@ class Main(QWidget): bool(self.config["Default"]["SelfSet.IfSendMail"] == "True") ) + self.if_send_error_only.setChecked( + bool(self.config["Default"]["SelfSet.IfSendMail.OnlyError"] == "True") + ) + + self.if_send_error_only.setVisible( + bool(self.config["Default"]["SelfSet.IfSendMail"] == "True") + ) + self.if_to_tray.setChecked( bool(self.config["Default"]["SelfSet.IfToTray"] == "True") ) @@ -2272,6 +2290,11 @@ class Main(QWidget): else: self.config["Default"]["SelfSet.IfSendMail"] = "False" + if self.if_send_error_only.isChecked(): + self.config["Default"]["SelfSet.IfSendMail.OnlyError"] = "True" + else: + self.config["Default"]["SelfSet.IfSendMail.OnlyError"] = "False" + if self.if_to_tray.isChecked(): self.config["Default"]["SelfSet.IfToTray"] = "True" else: @@ -2546,18 +2569,24 @@ class Main(QWidget): updater_version_current = [0, 0, 0, 0] # 从远程服务器获取最新版本信息 - try: - response = requests.get( - "https://ghp.ci/https://github.com/DLmaster361/AUTO_MAA/blob/main/res/version.json" - ) - except Exception as e: + for _ in range(3): + try: + response = requests.get( + "https://gitee.com/DLmaster_361/AUTO_MAA/raw/main/res/version.json" + ) + version_remote = response.json() + break + except Exception as e: + err = e + time.sleep(0.1) + else: QMessageBox.critical( self.ui, "错误", - f"获取版本信息时出错:\n{e}", + f"获取版本信息时出错:\n{err}", ) return None - version_remote = response.json() + main_version_remote = list(map(int, version_remote["main_version"].split("."))) updater_version_remote = list( map(int, version_remote["updater_version"].split(".")) @@ -2567,11 +2596,26 @@ class Main(QWidget): if (main_version_remote > main_version_current) or ( updater_version_remote > updater_version_current ): + + # 生成版本更新信息 + if main_version_remote > main_version_current: + main_version_info = f" 主程序:{self.version_text(main_version_current)} --> {self.version_text(main_version_remote)}\n" + else: + main_version_info = ( + f" 主程序:{self.version_text(main_version_current)}\n" + ) + if updater_version_remote > updater_version_current: + updater_version_info = f" 更新器:{self.version_text(updater_version_current)} --> {self.version_text(updater_version_remote)}\n" + else: + updater_version_info = ( + f" 更新器:{self.version_text(updater_version_current)}\n" + ) + # 询问是否开始版本更新 choice = QMessageBox.question( self.ui, "版本更新", - f"发现新版本:\n 主程序:{self.version_text(main_version_current)} --> {self.version_text(main_version_remote)}\n 更新器:{self.version_text(updater_version_current)} --> {self.version_text(updater_version_remote)}\n 更新说明:\n{version_remote['announcement'].replace("\n","\n ")}\n\n是否开始更新?\n\n 注意:主程序更新时AUTO_MAA将自动关闭", + f"发现新版本:\n{main_version_info}{updater_version_info} 更新说明:\n{version_remote['announcement'].replace("\n# ","\n !").replace("\n## ","\n - ").replace("\n- ","\n · ")}\n\n是否开始更新?\n\n 注意:主程序更新时AUTO_MAA将自动关闭", ) if choice == QMessageBox.No: return None @@ -2582,8 +2626,8 @@ class Main(QWidget): self.updater = Updater.Updater( self.app_path, "AUTO_MAA更新器", - version_remote["updater_download_url"], - version_remote["updater_version"], + main_version_remote, + updater_version_remote, ) # 完成更新器的更新后更新主程序 if main_version_remote > main_version_current: @@ -2616,7 +2660,9 @@ class Main(QWidget): if version_numb[3] == 0: version = f"v{'.'.join(str(_) for _ in version_numb[0:3])}" else: - version = f"v{'.'.join(str(_) for _ in version_numb[0:3])}_beta" + version = ( + f"v{'.'.join(str(_) for _ in version_numb[0:3])}_beta{version_numb[3]}" + ) return version def push_notification(self, title, message, ticker, t): diff --git a/Updater.py b/Updater.py index 65a9445..e9ca8d6 100644 --- a/Updater.py +++ b/Updater.py @@ -31,6 +31,7 @@ import json import zipfile import requests import subprocess +import time from PySide6.QtWidgets import ( QApplication, @@ -42,6 +43,8 @@ from PySide6.QtWidgets import ( from PySide6.QtGui import QIcon from PySide6.QtCore import QObject, QThread, Signal +from package import version_text + class UpdateProcess(QThread): @@ -49,13 +52,13 @@ class UpdateProcess(QThread): progress = Signal(int, int, int) accomplish = Signal() - def __init__(self, app_path, name, download_url, version): + def __init__(self, app_path, name, main_version, updater_version): super(UpdateProcess, self).__init__() self.app_path = app_path self.name = name - self.download_url = download_url - self.version = version + self.main_version = main_version + self.updater_version = updater_version self.download_path = f"{app_path}/AUTO_MAA_Update.zip" # 临时下载文件的路径 self.version_path = f"{app_path}/res/version.json" @@ -67,23 +70,71 @@ class UpdateProcess(QThread): except FileNotFoundError: pass + url_list = self.get_download_url() + # 下载 try: - response = requests.get(self.download_url, stream=True) - file_size = response.headers.get("Content-Length") + # 验证下载地址并获取文件大小 + for i in range(len(url_list)): + try: + response = requests.get(url_list[i], stream=True) + if response.status_code != 200: + self.info.emit( + f"连接失败,错误代码 {response.status_code} ,正在切换代理({i+1}/{len(url_list)})" + ) + time.sleep(1) + continue + print(url_list[i]) + file_size = response.headers.get("Content-Length") + break + except requests.RequestException: + self.info.emit(f"请求超时,正在切换代理({i+1}/{len(url_list)})") + time.sleep(1) + print(i) + else: + self.info.emit(f"服务器连接失败,已尝试所有{len(url_list)}个代理") + return None + if file_size is None: file_size = 1 else: file_size = int(file_size) + + # 下载文件 with open(self.download_path, "wb") as f: + downloaded_size = 0 + last_download_size = 0 + speed = 0 + last_time = time.time() + for chunk in response.iter_content(chunk_size=8192): + + # 写入已下载数据 f.write(chunk) downloaded_size += len(chunk) - self.info.emit( - f"正在下载:{self.name} 已下载: {downloaded_size / 1048576:.2f}/{file_size / 1048576:.2f} MB ({downloaded_size / file_size * 100:.2f}%)" - ) + + # 计算下载速度 + if time.time() - last_time >= 1.0: + speed = ( + (downloaded_size - last_download_size) + / (time.time() - last_time) + / 1024 + ) + last_download_size = downloaded_size + last_time = time.time() + + # 更新下载进度 + if speed >= 1024: + self.info.emit( + f"正在下载:{self.name} 已下载: {downloaded_size / 1048576:.2f}/{file_size / 1048576:.2f} MB ({downloaded_size / file_size * 100:.2f}%) 下载速度 {speed / 1024:.2f} MB/s", + ) + else: + self.info.emit( + f"正在下载:{self.name} 已下载: {downloaded_size / 1048576:.2f}/{file_size / 1048576:.2f} MB ({downloaded_size / file_size * 100:.2f}%) 下载速度 {speed:.2f} KB/s", + ) self.progress.emit(0, 100, int(downloaded_size / file_size * 100)) + except Exception as e: e = str(e) e = "\n".join([e[_ : _ + 75] for _ in range(0, len(e), 75)]) @@ -92,10 +143,17 @@ class UpdateProcess(QThread): # 解压 try: - self.info.emit("正在解压更新文件") - self.progress.emit(0, 0, 0) - with zipfile.ZipFile(self.download_path, "r") as zip_ref: - zip_ref.extractall(self.app_path) + + while True: + try: + self.info.emit("正在解压更新文件") + self.progress.emit(0, 0, 0) + with zipfile.ZipFile(self.download_path, "r") as zip_ref: + zip_ref.extractall(self.app_path) + break + except PermissionError: + self.info.emit("解压失败:AUTO_MAA正在运行,正在等待其关闭") + time.sleep(1) self.info.emit("正在删除临时文件") self.progress.emit(0, 0, 0) @@ -103,7 +161,9 @@ class UpdateProcess(QThread): self.info.emit(f"{self.name}更新成功!") self.progress.emit(0, 100, 100) + except Exception as e: + e = str(e) e = "\n".join([e[_ : _ + 75] for _ in range(0, len(e), 75)]) self.info.emit(f"解压更新时出错:\n{e}") @@ -113,9 +173,9 @@ class UpdateProcess(QThread): with open(self.version_path, "r", encoding="utf-8") as f: version_info = json.load(f) if self.name == "AUTO_MAA更新器": - version_info["updater_version"] = self.version + version_info["updater_version"] = self.updater_version elif self.name == "AUTO_MAA主程序": - version_info["main_version"] = self.version + version_info["main_version"] = self.main_version with open(self.version_path, "w", encoding="utf-8") as f: json.dump(version_info, f, indent=4) @@ -129,6 +189,35 @@ class UpdateProcess(QThread): self.accomplish.emit() + def get_download_url(self): + """计算下载链接""" + PROXY_list = [ + "", + "https://gitproxy.click/", + "https://cdn.moran233.xyz/", + "https://gh.llkk.cc/", + "https://github.akams.cn/", + "https://www.ghproxy.cn/", + ] + url_list = [] + if self.name == "AUTO_MAA主程序": + url_list.append( + f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip" + ) + for i in range(len(PROXY_list)): + url_list.append( + f"{PROXY_list[i]}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip" + ) + elif self.name == "AUTO_MAA更新器": + url_list.append( + f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/Updater_{version_text(self.updater_version)}.zip" + ) + for i in range(len(PROXY_list)): + url_list.append( + f"{PROXY_list[i]}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/Updater_{version_text(self.updater_version)}.zip" + ) + return url_list + class Updater(QObject): @@ -190,7 +279,7 @@ if __name__ == "__main__": # 从远程服务器获取最新版本信息 response = requests.get( - "https://ghp.ci/https://github.com/DLmaster361/AUTO_MAA/blob/main/res/version.json" + "https://gitee.com/DLmaster_361/AUTO_MAA/raw/main/res/version.json" ) version_remote = response.json() main_version_remote = list(map(int, version_remote["main_version"].split("."))) @@ -200,7 +289,7 @@ if __name__ == "__main__": app = AUTO_MAA_Updater( app_path, "AUTO_MAA主程序", - version_remote["main_download_url"], - version_remote["main_version"], + main_version_remote, + "", ) sys.exit(app.exec()) diff --git a/gui/ui/main.ui b/gui/ui/main.ui index f6b7192..b24c9c6 100644 --- a/gui/ui/main.ui +++ b/gui/ui/main.ui @@ -182,7 +182,7 @@ 0 0 - 98 + 88 74 @@ -1192,7 +1192,7 @@ QFrame::Shadow::Raised - + @@ -1216,6 +1216,13 @@ + + + + 仅推送异常信息 + + + diff --git a/package.py b/package.py index b63e6f7..6d64965 100644 --- a/package.py +++ b/package.py @@ -38,29 +38,31 @@ def version_text(version_numb): return version -with open("res/version.json", "r", encoding="utf-8") as f: - version = json.load(f) +if __name__ == "__main__": -main_version_numb = list(map(int, version["main_version"].split("."))) -updater_version_numb = list(map(int, version["updater_version"].split("."))) + with open("res/version.json", "r", encoding="utf-8") as f: + version = json.load(f) -main_info = f"# UTF-8\n#\nVSVersionInfo(\n ffi=FixedFileInfo(\n # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)\n # Set not needed items to zero 0.\n filevers=({', '.join(str(_) for _ in main_version_numb)}),\n prodvers=(0, 0, 0, 0),\n # Contains a bitmask that specifies the valid bits 'flags'r\n mask=0x3f,\n # Contains a bitmask that specifies the Boolean attributes of the file.\n flags=0x0,\n # The operating system for which this file was designed.\n # 0x4 - NT and there is no need to change it.\n OS=0x4,\n # The general type of file.\n # 0x1 - the file is an application.\n fileType=0x1,\n # The function of the file.\n # 0x0 - the function is not defined for this fileType\n subtype=0x0,\n # Creation date and time stamp.\n date=(0, 0)\n ),\n kids=[\n VarFileInfo([VarStruct('Translation', [0, 1200])]), \n StringFileInfo(\n [\n StringTable(\n '000004b0',\n [StringStruct('Comments', 'https://github.com/DLmaster361/AUTO_MAA/'),\n StringStruct('CompanyName', 'AUTO_MAA Team'),\n StringStruct('FileDescription', 'AUTO_MAA Component'),\n StringStruct('FileVersion', '{version["main_version"]}'),\n StringStruct('InternalName', 'AUTO_MAA'),\n StringStruct('LegalCopyright', 'Copyright © 2024 DLmaster361'),\n StringStruct('OriginalFilename', 'AUTO_MAA.py'),\n StringStruct('ProductName', 'AUTO_MAA'),\n StringStruct('ProductVersion', 'v{version["main_version"]}'),\n StringStruct('Assembly Version', 'v{version["main_version"]}')])\n ])\n ]\n)" -updater_info = f"# UTF-8\n#\nVSVersionInfo(\n ffi=FixedFileInfo(\n # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)\n # Set not needed items to zero 0.\n filevers=({', '.join(str(_) for _ in updater_version_numb)}),\n prodvers=(0, 0, 0, 0),\n # Contains a bitmask that specifies the valid bits 'flags'r\n mask=0x3f,\n # Contains a bitmask that specifies the Boolean attributes of the file.\n flags=0x0,\n # The operating system for which this file was designed.\n # 0x4 - NT and there is no need to change it.\n OS=0x4,\n # The general type of file.\n # 0x1 - the file is an application.\n fileType=0x1,\n # The function of the file.\n # 0x0 - the function is not defined for this fileType\n subtype=0x0,\n # Creation date and time stamp.\n date=(0, 0)\n ),\n kids=[\n VarFileInfo([VarStruct('Translation', [0, 1200])]), \n StringFileInfo(\n [\n StringTable(\n '000004b0',\n [StringStruct('Comments', 'https://github.com/DLmaster361/AUTO_MAA/'),\n StringStruct('CompanyName', 'AUTO_MAA Team'),\n StringStruct('FileDescription', 'AUTO_MAA Component'),\n StringStruct('FileVersion', '{version["updater_version"]}'),\n StringStruct('InternalName', 'AUTO_MAA_Updater'),\n StringStruct('LegalCopyright', 'Copyright © 2024 DLmaster361'),\n StringStruct('OriginalFilename', 'Updater.py'),\n StringStruct('ProductName', 'AUTO_MAA_Updater'),\n StringStruct('ProductVersion', 'v{version["updater_version"]}'),\n StringStruct('Assembly Version', 'v{version["updater_version"]}')])\n ])\n ]\n)" + main_version_numb = list(map(int, version["main_version"].split("."))) + updater_version_numb = list(map(int, version["updater_version"].split("."))) -with open("AUTO_MAA_info.txt", "w", encoding="utf-8") as f: - print(main_info, end="", file=f) -with open("Updater_info.txt", "w", encoding="utf-8") as f: - print(updater_info, end="", file=f) + main_info = f"# UTF-8\n#\nVSVersionInfo(\n ffi=FixedFileInfo(\n # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)\n # Set not needed items to zero 0.\n filevers=({', '.join(str(_) for _ in main_version_numb)}),\n prodvers=(0, 0, 0, 0),\n # Contains a bitmask that specifies the valid bits 'flags'r\n mask=0x3f,\n # Contains a bitmask that specifies the Boolean attributes of the file.\n flags=0x0,\n # The operating system for which this file was designed.\n # 0x4 - NT and there is no need to change it.\n OS=0x4,\n # The general type of file.\n # 0x1 - the file is an application.\n fileType=0x1,\n # The function of the file.\n # 0x0 - the function is not defined for this fileType\n subtype=0x0,\n # Creation date and time stamp.\n date=(0, 0)\n ),\n kids=[\n VarFileInfo([VarStruct('Translation', [0, 1200])]), \n StringFileInfo(\n [\n StringTable(\n '000004b0',\n [StringStruct('Comments', 'https://github.com/DLmaster361/AUTO_MAA/'),\n StringStruct('CompanyName', 'AUTO_MAA Team'),\n StringStruct('FileDescription', 'AUTO_MAA Component'),\n StringStruct('FileVersion', '{version["main_version"]}'),\n StringStruct('InternalName', 'AUTO_MAA'),\n StringStruct('LegalCopyright', 'Copyright © 2024 DLmaster361'),\n StringStruct('OriginalFilename', 'AUTO_MAA.py'),\n StringStruct('ProductName', 'AUTO_MAA'),\n StringStruct('ProductVersion', 'v{version["main_version"]}'),\n StringStruct('Assembly Version', 'v{version["main_version"]}')])\n ])\n ]\n)" + updater_info = f"# UTF-8\n#\nVSVersionInfo(\n ffi=FixedFileInfo(\n # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)\n # Set not needed items to zero 0.\n filevers=({', '.join(str(_) for _ in updater_version_numb)}),\n prodvers=(0, 0, 0, 0),\n # Contains a bitmask that specifies the valid bits 'flags'r\n mask=0x3f,\n # Contains a bitmask that specifies the Boolean attributes of the file.\n flags=0x0,\n # The operating system for which this file was designed.\n # 0x4 - NT and there is no need to change it.\n OS=0x4,\n # The general type of file.\n # 0x1 - the file is an application.\n fileType=0x1,\n # The function of the file.\n # 0x0 - the function is not defined for this fileType\n subtype=0x0,\n # Creation date and time stamp.\n date=(0, 0)\n ),\n kids=[\n VarFileInfo([VarStruct('Translation', [0, 1200])]), \n StringFileInfo(\n [\n StringTable(\n '000004b0',\n [StringStruct('Comments', 'https://github.com/DLmaster361/AUTO_MAA/'),\n StringStruct('CompanyName', 'AUTO_MAA Team'),\n StringStruct('FileDescription', 'AUTO_MAA Component'),\n StringStruct('FileVersion', '{version["updater_version"]}'),\n StringStruct('InternalName', 'AUTO_MAA_Updater'),\n StringStruct('LegalCopyright', 'Copyright © 2024 DLmaster361'),\n StringStruct('OriginalFilename', 'Updater.py'),\n StringStruct('ProductName', 'AUTO_MAA_Updater'),\n StringStruct('ProductVersion', 'v{version["updater_version"]}'),\n StringStruct('Assembly Version', 'v{version["updater_version"]}')])\n ])\n ]\n)" -os.system( - "pyinstaller -F --version-file AUTO_MAA_info.txt -w --icon=gui/ico/AUTO_MAA.ico AUTO_MAA.py --hidden-import plyer.platforms.win.notification" -) -os.system( - "pyinstaller -F --version-file Updater_info.txt -w --icon=gui/ico/AUTO_MAA_Updater.ico Updater.py" -) + with open("AUTO_MAA_info.txt", "w", encoding="utf-8") as f: + print(main_info, end="", file=f) + with open("Updater_info.txt", "w", encoding="utf-8") as f: + print(updater_info, end="", file=f) -with open("update_info.txt", "w", encoding="utf-8") as f: - print( - f"{version_text(main_version_numb)}\n{version_text(updater_version_numb)}{version["announcement"]}", - file=f, + os.system( + "pyinstaller -F --version-file AUTO_MAA_info.txt -w --icon=gui/ico/AUTO_MAA.ico AUTO_MAA.py --hidden-import plyer.platforms.win.notification" ) + os.system( + "pyinstaller -F --version-file Updater_info.txt -w --icon=gui/ico/AUTO_MAA_Updater.ico Updater.py" + ) + + with open("update_info.txt", "w", encoding="utf-8") as f: + print( + f"{version_text(main_version_numb)}\n{version_text(updater_version_numb)}{version["announcement"]}", + file=f, + ) diff --git a/res/version.json b/res/version.json index cac2f50..1fc590f 100644 --- a/res/version.json +++ b/res/version.json @@ -1,7 +1,5 @@ { - "main_version": "4.1.3.5", - "main_download_url": "https://ghp.ci/https://github.com/DLmaster361/AUTO_MAA/releases/download/v4.1.3_beta/AUTO_MAA_v4.1.3_beta.zip", - "updater_version": "1.0.5.0", - "updater_download_url": "https://ghp.ci/https://github.com/DLmaster361/AUTO_MAA/releases/download/v4.1.3_beta/Updater_v1.0.5.zip", - "announcement": "\n# 测试版本,遇BUG速速上报,谢谢~\n## 新增功能\n- 添加托盘中止当前任务选项\n## 修复BUG\n- 修复深色模式下UI异常 #10\n## 程序优化\n- MainTimer逻辑实现优化\n- 修改配置方法优化\n- 代理逻辑优化" + "main_version": "4.1.3.6", + "updater_version": "1.0.5.1", + "announcement": "\n# 测试版本,遇BUG速速上报,谢谢~\n## 新增功能\n- 添加托盘中止当前任务选项\n- 添加邮件仅推送异常信息选项\n## 修复BUG\n- 修复深色模式下UI异常 #10\n## 程序优化\n- MainTimer逻辑实现优化\n- 修改配置方法优化\n- 代理逻辑优化\n- 更新器添加更多代理地址,更新流程优化" } \ No newline at end of file