diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml index d9d28d7..b2f4174 100644 --- a/.github/workflows/build-app.yml +++ b/.github/workflows/build-app.yml @@ -150,4 +150,13 @@ jobs: gh release delete "$TAGNAME" --yes gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" artifacts/* env: - GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} + - name: Setup SSH Key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts + - name: Upload Release to Server + run: | + scp -r artifacts/* ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }}:/home/user/files/AUTO_MAA/ diff --git a/.github/workflows/build-pre.yml b/.github/workflows/build-pre.yml index a68d3ed..7c5be84 100644 --- a/.github/workflows/build-pre.yml +++ b/.github/workflows/build-pre.yml @@ -151,14 +151,12 @@ jobs: gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" --prerelease artifacts/* env: GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} - - name: Setup SSH Key run: | mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts - - name: Upload Release to Server run: | scp -r artifacts/* ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }}:/home/user/files/AUTO_MAA/ diff --git a/README.md b/README.md index b19e238..77f4a99 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,9 @@ MAA多账号管理与自动化软件 # 使用方法 -本项目已改用腾讯文档展示使用方法 +访问AUTO_MAA官方文档站以获取使用指南和项目相关信息 -- [《AUTO_MAA用户指南》](https://docs.qq.com/aio/DQ2NwUHRiWGtMWHBy) +- [AUTO_MAA官方文档站](https://clozya.github.io/AUTOMAA_docs) --- @@ -82,6 +82,8 @@ MAA多账号管理与自动化软件 ![Alt](https://repobeats.axiom.co/api/embed/6c2f834141eff1ac297db70d12bd11c6236a58a5.svg "Repobeats analytics image") +感谢 @ClozyA 为本项目提供的下载服务器 + ## Star History [![Star History Chart](https://api.star-history.com/svg?repos=DLmaster361/AUTO_MAA&type=Date)](https://star-history.com/#DLmaster361/AUTO_MAA&Date) diff --git a/app/__init__.py b/app/__init__.py index 0c70453..1bf7e18 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -29,7 +29,7 @@ __version__ = "4.2.0" __author__ = "DLmaster361 " __license__ = "GPL-3.0 license" -from .core import AppConfig, QueueConfig, MaaConfig, Task, Task_manager, Main_timer +from .core import AppConfig, QueueConfig, MaaConfig, Task, TaskManager, MainTimer from .models import MaaManager from .services import Notify, Crypto, System from .ui import AUTO_MAA @@ -40,8 +40,8 @@ __all__ = [ "QueueConfig", "MaaConfig", "Task", - "Task_manager", - "Main_timer", + "TaskManager", + "MainTimer", "MaaManager", "Notify", "Crypto", diff --git a/app/core/__init__.py b/app/core/__init__.py index 8990b48..3b8b5a3 100644 --- a/app/core/__init__.py +++ b/app/core/__init__.py @@ -31,8 +31,8 @@ __license__ = "GPL-3.0 license" from .config import AppConfig, QueueConfig, MaaConfig, Config from .main_info_bar import MainInfoBar -from .task_manager import Task, Task_manager -from .timer import Main_timer +from .task_manager import Task, TaskManager +from .timer import MainTimer __all__ = [ "AppConfig", @@ -41,6 +41,6 @@ __all__ = [ "MaaConfig", "MainInfoBar", "Task", - "Task_manager", - "Main_timer", + "TaskManager", + "MainTimer", ] diff --git a/app/core/config.py b/app/core/config.py index f0bee3f..02febe3 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -488,6 +488,7 @@ class AppConfig: self.maa_config.set(self.maa_config.MaaSet_Name, "") self.maa_config.set(self.maa_config.MaaSet_Path, ".") + self.maa_config.set(self.maa_config.RunSet_TaskTransitionMethod, "ExitEmulator") self.maa_config.set(self.maa_config.RunSet_ProxyTimesLimit, 0) self.maa_config.set(self.maa_config.RunSet_AnnihilationTimeLimit, 40) self.maa_config.set(self.maa_config.RunSet_RoutineTimeLimit, 10) @@ -643,6 +644,12 @@ class MaaConfig(QConfig): MaaSet_Name = ConfigItem("MaaSet", "Name", "") MaaSet_Path = ConfigItem("MaaSet", "Path", ".", FolderValidator()) + RunSet_TaskTransitionMethod = OptionsConfigItem( + "RunSet", + "TaskTransitionMethod", + "ExitEmulator", + OptionsValidator(["NoAction", "ExitGame", "ExitEmulator"]), + ) RunSet_ProxyTimesLimit = RangeConfigItem( "RunSet", "ProxyTimesLimit", 0, RangeValidator(0, 1024) ) diff --git a/app/core/task_manager.py b/app/core/task_manager.py index c2217e7..afe43fa 100644 --- a/app/core/task_manager.py +++ b/app/core/task_manager.py @@ -147,7 +147,7 @@ class Task(QThread): ) ) self.task.accomplish.connect( - lambda log: self.save_log(self.task_dict[i][0], log) + lambda log: self.task_accomplish(self.task_dict[i][0], log) ) self.task.run() @@ -173,13 +173,14 @@ class Task(QThread): return member_dict - def save_log(self, name: str, log: dict): + def task_accomplish(self, name: str, log: dict): """保存保存任务结果""" self.logs.append([name, log]) + self.task.deleteLater() -class TaskManager(QObject): +class _TaskManager(QObject): """业务调度器""" create_gui = Signal(Task) @@ -187,7 +188,7 @@ class TaskManager(QObject): push_info_bar = Signal(str, str, str, int) def __init__(self): - super(TaskManager, self).__init__() + super(_TaskManager, self).__init__() self.task_dict: Dict[str, Task] = {} @@ -252,6 +253,8 @@ class TaskManager(QObject): logger.info(f"任务结束:{name}") MainInfoBar.push_info_bar("info", "任务结束", name, 3000) + self.task_dict[name].deleteLater() + if len(logs) > 0: time = logs[0][1]["Time"] history = "" @@ -290,4 +293,4 @@ class TaskManager(QObject): self.task_dict[name].question_response.emit(bool(choice.exec_())) -Task_manager = TaskManager() +TaskManager = _TaskManager() diff --git a/app/core/timer.py b/app/core/timer.py index 6664a4b..97357b2 100644 --- a/app/core/timer.py +++ b/app/core/timer.py @@ -33,11 +33,11 @@ from datetime import datetime import pyautogui from .config import Config -from .task_manager import Task_manager +from .task_manager import TaskManager from app.services import System -class MainTimer(QWidget): +class _MainTimer(QWidget): def __init__( self, @@ -81,7 +81,7 @@ class MainTimer(QWidget): ): logger.info(f"定时任务:{name}") - Task_manager.add_task("自动代理_新调度台", name, info) + TaskManager.add_task("自动代理_新调度台", name, info) def set_silence(self): """设置静默模式""" @@ -125,4 +125,4 @@ class MainTimer(QWidget): return queue_list -Main_timer = MainTimer() +MainTimer = _MainTimer() diff --git a/app/models/MAA.py b/app/models/MAA.py index 02f5509..0b65cae 100644 --- a/app/models/MAA.py +++ b/app/models/MAA.py @@ -26,7 +26,7 @@ v4.2 """ from loguru import logger -from PySide6.QtCore import QObject, Signal, QEventLoop +from PySide6.QtCore import QObject, Signal, QEventLoop, QFileSystemWatcher, QTimer import json import sqlite3 from datetime import datetime, timedelta @@ -37,7 +37,7 @@ from pathlib import Path from typing import List from app.core import Config -from app.services import Notify +from app.services import Notify, System class MaaManager(QObject): @@ -50,6 +50,7 @@ class MaaManager(QObject): create_user_list = Signal(list) update_user_list = Signal(list) update_log_text = Signal(str) + interrupt = Signal() accomplish = Signal(dict) isInterruptionRequested = False @@ -65,6 +66,12 @@ class MaaManager(QObject): self.mode = mode self.config_path = config_path self.user_config_path = user_config_path + self.log_monitor = QFileSystemWatcher() + self.log_monitor_timer = QTimer() + self.log_monitor_timer.timeout.connect(self.refresh_maa_log) + self.monitor_loop = QEventLoop() + + self.interrupt.connect(self.quit_monitor) with (self.config_path / "config.json").open("r", encoding="utf-8") as f: self.set = json.load(f) @@ -85,6 +92,7 @@ class MaaManager(QObject): def configure(self): """提取配置信息""" + self.name = self.set["MaaSet"]["Name"] self.maa_root_path = Path(self.set["MaaSet"]["Path"]) self.maa_set_path = self.maa_root_path / "config/gui.json" self.maa_log_path = self.maa_root_path / "debug/gui.log" @@ -117,25 +125,27 @@ class MaaManager(QObject): if "设置MAA" not in self.mode: self.data = sorted(self.data, key=lambda x: (-len(x[15]), x[16])) - user_list: List[List[str, str, int]] = [ + self.user_list: List[List[str, str, int]] = [ [_[0], "等待", index] for index, _ in enumerate(self.data) if (_[3] != 0 and _[4] == "y") ] - self.create_user_list.emit(user_list) + self.create_user_list.emit(self.user_list) # 自动代理模式 if self.mode == "自动代理": + # 标记是否需要重启模拟器 + self.if_open_emulator = True # 执行情况预处理 - for _ in user_list: + for _ in self.user_list: if self.data[_[2]][5] != curdate: self.data[_[2]][5] = curdate self.data[_[2]][14] = 0 _[0] += f" - 第{self.data[_[2]][14] + 1}次代理" # 开始代理 - for user in user_list: + for user in self.user_list: if self.isInterruptionRequested: break @@ -145,12 +155,14 @@ class MaaManager(QObject): or self.data[user[2]][14] < self.set["RunSet"]["ProxyTimesLimit"] ): user[1] = "运行" - self.update_user_list.emit(user_list) + self.update_user_list.emit(self.user_list) else: user[1] = "跳过" - self.update_user_list.emit(user_list) + self.update_user_list.emit(self.user_list) continue + logger.info(f"{self.name} | 开始代理用户: {user[0]}") + # 初始化代理情况记录和模式替换记录 run_book = [False for _ in range(2)] mode_book = ["自动代理_剿灭", "自动代理_日常"] @@ -158,6 +170,11 @@ class MaaManager(QObject): # 简洁模式用户默认开启日常选项 if self.data[user[2]][15] == "simple": self.data[user[2]][9] = "y" + elif self.data[user[2]][15] == "beta": + check_book = [ + [True, "annihilation", "剿灭"], + [True, "routine", "日常"], + ] # 尝试次数循环 for i in range(self.set["RunSet"]["RunTimesLimit"]): @@ -165,6 +182,10 @@ class MaaManager(QObject): if self.isInterruptionRequested: break + logger.info( + f"{self.name} | 用户: {user[0]} - 尝试次数: {i + 1}/{self.set["RunSet"]["RunTimesLimit"]}" + ) + # 剿灭-日常模式循环 for j in range(2): @@ -177,6 +198,35 @@ class MaaManager(QObject): if run_book[j]: continue + logger.info( + f"{self.name} | 用户: {user[0]} - 模式: {mode_book[j]}" + ) + + if self.data[user[2]][15] == "beta": + + self.if_open_emulator = True + + if ( + check_book[j][0] + and not ( + self.config_path + / f"beta/{self.data[user[2]][16]}/{check_book[j][1]}/gui.json" + ).exists() + ): + logger.error( + f"{self.name} | 用户: {user[0]} - 未找到{check_book[j][2]}配置文件" + ) + self.push_info_bar.emit( + "error", + "启动MAA代理进程失败", + f"未找到{user[0]}的{check_book[j][2]}配置文件!", + -1, + ) + check_book[j][0] = False + continue + elif not check_book[j][0]: + continue + # 配置MAA self.set_maa(mode_book[j], user[2]) # 记录当前时间 @@ -194,95 +244,48 @@ class MaaManager(QObject): set["Configurations"]["Default"]["Start.EmulatorPath"] ) Config.silence_list.append(self.emulator_path) - # 记录是否超时的标记 - self.if_time_out = False # 监测MAA运行状态 - while not self.isInterruptionRequested: + self.start_monitor(start_time, mode_book[j]) - # 获取MAA日志 - logs = self.get_maa_log(start_time) - - # 判断是否超时 - if len(logs) > 0: - latest_time = datetime.now() - for _ in range(-1, 0 - len(logs) - 1, -1): - try: - latest_time = datetime.strptime( - logs[_][1:20], "%Y-%m-%d %H:%M:%S" - ) - break - except ValueError: - pass - now_time = datetime.now() - if ( - j == 0 - and now_time - latest_time - > timedelta( - minutes=self.set["RunSet"][ - "AnnihilationTimeLimit" - ] - ) - ) or ( - j == 1 - and now_time - latest_time - > timedelta( - minutes=self.set["RunSet"]["RoutineTimeLimit"] - ) - ): - self.if_time_out = True - - # 合并日志 - log = "".join(logs) - - # 更新MAA日志 - if len(logs) > 100: - self.update_log_text.emit("".join(logs[-100:])) - else: - self.update_log_text.emit("".join(logs)) - - # 判断MAA程序运行状态 - result = self.if_maa_success(log, mode_book[j]) - if result == "Success!": - run_book[j] = True - self.update_log_text.emit( - "检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s" - ) - # 移除静默进程标记 - Config.silence_list.remove(self.emulator_path) - for _ in range(10): - if self.isInterruptionRequested: - break - time.sleep(1) - break - elif result == "Wait": - # 检测时间间隔 + if self.maa_result == "Success!": + logger.info( + f"{self.name} | 用户: {user[0]} - MAA进程完成代理任务" + ) + run_book[j] = True + self.update_log_text.emit( + "检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s" + ) + for _ in range(10): + if self.isInterruptionRequested: + break time.sleep(1) - else: - # 打印中止信息 - # 此时,log变量内存储的就是出现异常的日志信息,可以保存或发送用于问题排查 - self.update_log_text.emit(result) - # 无命令行中止MAA与其子程序 - killprocess = subprocess.Popen( - f"taskkill /F /T /PID {maa.pid}", - shell=True, - creationflags=subprocess.CREATE_NO_WINDOW, - ) - killprocess.wait() - # 移除静默进程标记 - Config.silence_list.remove(self.emulator_path) - # 推送异常通知 - Notify.push_notification( - "用户自动代理出现异常!", - f"用户 {user[0].replace("_", " 今天的")}的{mode_book[j][5:7]}部分出现一次异常", - f"{user[0].replace("_", " ")}的{mode_book[j][5:7]}出现异常", - 1, - ) - for _ in range(10): - if self.isInterruptionRequested: - break - time.sleep(1) - break + else: + logger.error( + f"{self.name} | 用户: {user[0]} - 代理任务异常: {self.maa_result}" + ) + # 打印中止信息 + # 此时,log变量内存储的就是出现异常的日志信息,可以保存或发送用于问题排查 + self.update_log_text.emit( + f"{self.maa_result}\n正在中止相关程序\n请等待10s" + ) + # 无命令行中止MAA与其子程序 + System.kill_process(self.maa_exe_path) + self.if_open_emulator = True + # 推送异常通知 + Notify.push_notification( + "用户自动代理出现异常!", + f"用户 {user[0].replace("_", " 今天的")}的{mode_book[j][5:7]}部分出现一次异常", + f"{user[0].replace("_", " ")}的{mode_book[j][5:7]}出现异常", + 1, + ) + for _ in range(10): + if self.isInterruptionRequested: + break + time.sleep(1) + + # 移除静默进程标记 + Config.silence_list.remove(self.emulator_path) # 成功完成代理的用户修改相关参数 if run_book[0] and run_book[1]: @@ -302,28 +305,30 @@ class MaaManager(QObject): if not (run_book[0] and run_book[1]): user[1] = "异常" - self.update_user_list.emit(user_list) + self.update_user_list.emit(self.user_list) # 人工排查模式 elif self.mode == "人工排查": # 标记是否需要启动模拟器 - if_strat_app = True + self.if_open_emulator = True # 标识排查模式 - for _ in user_list: + for _ in self.user_list: _[0] += "_排查模式" # 开始排查 - for user in user_list: + for user in self.user_list: if self.isInterruptionRequested: break + logger.info(f"{self.name} | 开始排查用户: {user[0]}") + user[1] = "运行" - self.update_user_list.emit(user_list) + self.update_user_list.emit(self.user_list) if self.data[user[2]][15] == "beta": - if_strat_app = True + self.if_open_emulator = True run_book = [False for _ in range(2)] @@ -331,11 +336,7 @@ class MaaManager(QObject): while not self.isInterruptionRequested: # 配置MAA - if if_strat_app: - self.set_maa("人工排查_启动模拟器", user[2]) - if_strat_app = False - else: - self.set_maa("人工排查_仅切换账号", user[2]) + self.set_maa("人工排查", user[2]) # 记录当前时间 start_time = datetime.now() @@ -347,43 +348,28 @@ class MaaManager(QObject): ) # 监测MAA运行状态 - while not self.isInterruptionRequested: + self.start_monitor(start_time, "人工排查") - # 获取MAA日志 - logs = self.get_maa_log(start_time) - # 合并日志 - log = "".join(logs) - - # 更新MAA日志 - if len(logs) > 100: - self.update_log_text.emit("".join(logs[-100:])) - else: - self.update_log_text.emit("".join(logs)) - - # 判断MAA程序运行状态 - result = self.if_maa_success(log, "人工排查") - if result == "Success!": - run_book[0] = True - self.update_log_text.emit("检测到MAA进程成功登录PRTS") - break - elif result == "Wait": - # 检测时间间隔 + if self.maa_result == "Success!": + logger.info( + f"{self.name} | 用户: {user[0]} - MAA进程成功登录PRTS" + ) + run_book[0] = True + self.update_log_text.emit("检测到MAA进程成功登录PRTS") + else: + logger.error( + f"{self.name} | 用户: {user[0]} - MAA未能正确登录到PRTS: {self.maa_result}" + ) + self.update_log_text.emit( + f"{self.maa_result}\n正在中止相关程序\n请等待10s" + ) + # 无命令行中止MAA与其子程序 + System.kill_process(self.maa_exe_path) + self.if_open_emulator = True + for _ in range(10): + if self.isInterruptionRequested: + break time.sleep(1) - else: - self.update_log_text.emit(result) - # 无命令行中止MAA与其子程序 - killprocess = subprocess.Popen( - f"taskkill /F /T /PID {maa.pid}", - shell=True, - creationflags=subprocess.CREATE_NO_WINDOW, - ) - killprocess.wait() - if_strat_app = True - for _ in range(10): - if self.isInterruptionRequested: - break - time.sleep(1) - break # 登录成功,结束循环 if run_book[0]: @@ -406,19 +392,21 @@ class MaaManager(QObject): # 结果录入用户备注栏 if run_book[0] and run_book[1]: + logger.info(f"{self.name} | 用户 {user[0]} 通过人工排查") if "未通过人工排查" in self.data[user[2]][13]: self.data[user[2]][13] = self.data[user[2]][13].replace( "未通过人工排查|", "" ) user[1] = "完成" - elif not (run_book[0] and run_book[1]): + else: + logger.info(f"{self.name} | 用户 {user[0]} 未通过人工排查") if not "未通过人工排查" in self.data[user[2]][13]: self.data[user[2]][ 13 ] = f"未通过人工排查|{self.data[user[2]][13]}" user[1] = "异常" - self.update_user_list.emit(user_list) + self.update_user_list.emit(self.user_list) # 设置MAA模式 elif "设置MAA" in self.mode: @@ -435,20 +423,7 @@ class MaaManager(QObject): start_time = datetime.now() # 监测MAA运行状态 - while not self.isInterruptionRequested: - - # 获取MAA日志 - logs = self.get_maa_log(start_time) - # 合并日志 - log = "".join(logs) - - # 判断MAA程序运行状态 - result = self.if_maa_success(log, "设置MAA") - if result == "Success!": - break - elif result == "Wait": - # 检测时间间隔 - time.sleep(1) + self.start_monitor(start_time, "设置MAA") if "全局" in self.mode: (self.config_path / "Default").mkdir(parents=True, exist_ok=True) @@ -465,25 +440,20 @@ class MaaManager(QObject): # 关闭可能未正常退出的MAA进程 if self.isInterruptionRequested: - killprocess = subprocess.Popen( - f"taskkill /F /T /PID {maa.pid}", - shell=True, - creationflags=subprocess.CREATE_NO_WINDOW, - ) - killprocess.wait() + System.kill_process(self.maa_exe_path) # 更新用户数据 - modes = [self.data[_[2]][15] for _ in user_list] - uids = [self.data[_[2]][16] for _ in user_list] - days = [self.data[_[2]][3] for _ in user_list] - lasts = [self.data[_[2]][5] for _ in user_list] - notes = [self.data[_[2]][13] for _ in user_list] - numbs = [self.data[_[2]][14] for _ in user_list] + modes = [self.data[_[2]][15] for _ in self.user_list] + uids = [self.data[_[2]][16] for _ in self.user_list] + days = [self.data[_[2]][3] for _ in self.user_list] + lasts = [self.data[_[2]][5] for _ in self.user_list] + notes = [self.data[_[2]][13] for _ in self.user_list] + numbs = [self.data[_[2]][14] for _ in self.user_list] self.update_user_info.emit(modes, uids, days, lasts, notes, numbs) - error_index = [_[2] for _ in user_list if _[1] == "异常"] - over_index = [_[2] for _ in user_list if _[1] == "完成"] - wait_index = [_[2] for _ in user_list if _[1] == "等待"] + error_index = [_[2] for _ in self.user_list if _[1] == "异常"] + over_index = [_[2] for _ in self.user_list if _[1] == "完成"] + wait_index = [_[2] for _ in self.user_list if _[1] == "等待"] # 保存运行日志 end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") @@ -529,11 +499,17 @@ class MaaManager(QObject): Notify.CompanyWebHookBotPush(title, f"{end_log}AUTO_MAA 敬上") self.agree_bilibili(False) + self.log_monitor.deleteLater() + self.log_monitor_timer.deleteLater() self.accomplish.emit({"Time": begin_time, "History": end_log}) def requestInterruption(self) -> None: + logger.info(f"{self.name} | 收到任务中止申请") - logger.info("申请中止本次任务") + if len(self.log_monitor.files()) != 0: + self.interrupt.emit() + + self.maa_result = "您中止了本次任务" self.isInterruptionRequested = True def push_question(self, title: str, message: str) -> bool: @@ -548,9 +524,16 @@ class MaaManager(QObject): def _capture_response(self, response: bool) -> None: self.response = response - def get_maa_log(self, start_time): - """获取MAA日志""" + def refresh_maa_log(self) -> None: + """刷新MAA日志""" + with self.maa_log_path.open(mode="r", encoding="utf-8") as f: + pass + + def check_maa_log(self, start_time: datetime, mode: str) -> None: + """检查MAA日志以判断MAA程序运行状态""" + + # 获取日志 logs = [] if_log_start = False with self.maa_log_path.open(mode="r", encoding="utf-8") as f: @@ -565,53 +548,99 @@ class MaaManager(QObject): pass else: logs.append(entry) - return logs + log = "".join(logs) - def if_maa_success(self, log, mode): - """判断MAA程序运行状态""" + # 更新MAA日志 + if len(logs) > 100: + self.update_log_text.emit("".join(logs[-100:])) + else: + self.update_log_text.emit("".join(logs)) if "自动代理" in mode: + + # 获取最近一条日志的时间 + latest_time = start_time + for _ in logs[::-1]: + try: + latest_time = datetime.strptime(_[1:20], "%Y-%m-%d %H:%M:%S") + break + except ValueError: + pass + + time_book = { + "自动代理_剿灭": "AnnihilationTimeLimit", + "自动代理_日常": "RoutineTimeLimit", + } + if mode == "自动代理_日常" and "任务出错: Fight" in log: - return "检测到MAA未能实际执行任务\n正在中止相关程序\n请等待10s" + self.maa_result = "检测到MAA未能实际执行任务" if "任务出错: StartUp" in log: - return "检测到MAA未能正确登录PRTS\n正在中止相关程序\n请等待10s" + self.maa_result = "检测到MAA未能正确登录PRTS" elif "任务已全部完成!" in log: - return "Success!" + self.maa_result = "Success!" elif ( ("请「检查连接设置」或「尝试重启模拟器与 ADB」或「重启电脑」" in log) or ("已停止" in log) or ("MaaAssistantArknights GUI exited" in log) ): - return "检测到MAA进程异常\n正在中止相关程序\n请等待10s" - elif self.if_time_out: - return "检测到MAA进程超时\n正在中止相关程序\n请等待10s" - elif self.isInterruptionRequested: - return "您中止了本次任务\n正在中止相关程序\n请等待" + self.maa_result = "检测到MAA进程异常" + elif datetime.now() - latest_time > timedelta( + minutes=self.set["RunSet"][time_book[mode]] + ): + self.maa_result = "检测到MAA进程超时" else: - return "Wait" + self.maa_result = "Wait" elif mode == "人工排查": if "完成任务: StartUp" in log: - return "Success!" + self.maa_result = "Success!" elif ( ("请「检查连接设置」或「尝试重启模拟器与 ADB」或「重启电脑」" in log) or ("已停止" in log) or ("MaaAssistantArknights GUI exited" in log) ): - return "检测到MAA进程异常\n正在中止相关程序\n请等待10s" - elif self.isInterruptionRequested: - return "您中止了本次任务\n正在中止相关程序\n请等待" + self.maa_result = "检测到MAA进程异常" else: - return "Wait" + self.maa_result = "Wait" elif mode == "设置MAA": if "MaaAssistantArknights GUI exited" in log: - return "Success!" + self.maa_result = "Success!" else: - return "Wait" + self.maa_result = "Wait" + + if self.maa_result != "Wait": + + self.quit_monitor() + + def start_monitor(self, start_time: datetime, mode: str) -> None: + """开始监视MAA日志""" + + logger.info(f"{self.name} | 开始监视MAA日志") + self.log_monitor.addPath(str(self.maa_log_path)) + self.log_monitor.fileChanged.connect( + lambda: self.check_maa_log(start_time, mode) + ) + self.log_monitor_timer.start(1000) + self.monitor_loop.exec() + + def quit_monitor(self) -> None: + """退出MAA日志监视进程""" + + if len(self.log_monitor.files()) != 0: + + logger.info(f"{self.name} | 退出MAA日志监视") + self.log_monitor.removePath(str(self.maa_log_path)) + self.log_monitor.fileChanged.disconnect() + self.log_monitor_timer.stop() + self.monitor_loop.quit() def set_maa(self, mode, index): """配置MAA运行参数""" + logger.info(f"{self.name} | 配置MAA运行参数: {mode}/{index}") + + # 配置MAA前关闭可能未正常退出的MAA进程 + System.kill_process(self.maa_exe_path) # 预导入MAA配置文件 if mode == "设置MAA_用户": @@ -667,15 +696,34 @@ class MaaManager(QObject): data["Current"] = "Default" # 切换配置 for i in range(1, 9): data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置 - data["Configurations"]["Default"][ - "MainFunction.PostActions" - ] = "12" # 完成后退出MAA和模拟器 + + if ( + [i for i, _ in enumerate(self.user_list) if _[2] == index][0] + == len(self.user_list) - 1 + ) or ( + self.data[ + self.user_list[ + [i for i, _ in enumerate(self.user_list) if _[2] == index][0] + + 1 + ][2] + ][15] + == "beta" + ): + data["Configurations"]["Default"][ + "MainFunction.PostActions" + ] = "12" # 完成后退出MAA和模拟器 + else: + method_dict = {"NoAction": "8", "ExitGame": "9", "ExitEmulator": "12"} + data["Configurations"]["Default"]["MainFunction.PostActions"] = ( + method_dict[self.set["RunSet"]["TaskTransitionMethod"]] + ) # 完成后行为 + data["Configurations"]["Default"][ "Start.RunDirectly" ] = "True" # 启动MAA后直接运行 - data["Configurations"]["Default"][ - "Start.OpenEmulatorAfterLaunch" - ] = "True" # 启动MAA后自动开启模拟器 + data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = ( + "True" if self.if_open_emulator else "False" + ) # 启动MAA后自动开启模拟器 if Config.global_config.get(Config.global_config.function_IfSilence): data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化 @@ -888,7 +936,6 @@ class MaaManager(QObject): "Start.RunDirectly" ] = "True" # 启动MAA后直接运行 data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化 - # v5.1.12版本对以下字段处理 # 启动MAA后直接运行 data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = "True" # 启动MAA后自动开启模拟器 @@ -896,15 +943,9 @@ class MaaManager(QObject): data["Global"]["GUI.UseTray"] = "True" # 显示托盘图标 data["Global"]["GUI.MinimizeToTray"] = "True" # 最小化时隐藏至托盘 - # 启动MAA后自动开启模拟器 - if "启动模拟器" in mode: - data["Configurations"]["Default"][ - "Start.OpenEmulatorAfterLaunch" - ] = "True" - elif "仅切换账号" in mode: - data["Configurations"]["Default"][ - "Start.OpenEmulatorAfterLaunch" - ] = "False" + data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = ( + "True" if self.if_open_emulator else "False" + ) # 启动MAA后自动开启模拟器 if self.data[index][15] == "simple": @@ -1016,6 +1057,14 @@ class MaaManager(QObject): "TaskQueue.Reclamation.IsChecked" ] = "False" # 生息演算 + # 启动模拟器仅生效一次 + if ( + "设置MAA" not in mode + and self.if_open_emulator + and self.set["RunSet"]["TaskTransitionMethod"] != "ExitEmulator" + ): + self.if_open_emulator = False + # 覆写配置文件 with self.maa_set_path.open(mode="w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=4) @@ -1024,6 +1073,9 @@ class MaaManager(QObject): def agree_bilibili(self, if_agree): """向MAA写入Bilibili协议相关任务""" + logger.info( + f"{self.name} | Bilibili协议相关任务状态: {"启用" if if_agree else "禁用"}" + ) with self.maa_tasks_path.open(mode="r", encoding="utf-8") as f: data = json.load(f) diff --git a/app/services/system.py b/app/services/system.py index b2939d2..88cfecf 100644 --- a/app/services/system.py +++ b/app/services/system.py @@ -34,6 +34,7 @@ import win32process import winreg import psutil import subprocess +from pathlib import Path from app.core import Config @@ -50,7 +51,7 @@ class _SystemHandler: self.set_Sleep() self.set_SelfStart() - def set_Sleep(self): + def set_Sleep(self) -> None: """同步系统休眠状态""" if Config.global_config.get(Config.global_config.function_IfAllowSleep): @@ -62,7 +63,7 @@ class _SystemHandler: # 恢复系统电源状态 ctypes.windll.kernel32.SetThreadExecutionState(self.ES_CONTINUOUS) - def set_SelfStart(self): + def set_SelfStart(self) -> None: """同步开机自启""" if ( @@ -90,7 +91,7 @@ class _SystemHandler: winreg.DeleteValue(key, "AUTO_MAA") winreg.CloseKey(key) - def set_power(self, mode): + def set_power(self, mode) -> None: if sys.platform.startswith("win"): @@ -144,7 +145,7 @@ class _SystemHandler: self.main_window.close() - def is_startup(self): + def is_startup(self) -> bool: """判断程序是否已经开机自启""" key = winreg.OpenKey( @@ -162,7 +163,7 @@ class _SystemHandler: winreg.CloseKey(key) return False - def get_window_info(self): + def get_window_info(self) -> list: """获取当前窗口信息""" def callback(hwnd, window_info): @@ -176,5 +177,29 @@ class _SystemHandler: win32gui.EnumWindows(callback, window_info) return window_info + def kill_process(self, path: Path) -> None: + """根据路径中止进程""" + + for pid in self.search_pids(path): + killprocess = subprocess.Popen( + f"taskkill /F /T /PID {pid}", + shell=True, + creationflags=subprocess.CREATE_NO_WINDOW, + ) + killprocess.wait() + + def search_pids(self, path: Path) -> list: + """根据路径查找进程PID""" + + pids = [] + for proc in psutil.process_iter(["pid", "exe"]): + try: + if proc.info["exe"] and proc.info["exe"].lower() == str(path).lower(): + pids.append(proc.info["pid"]) + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + # 进程可能在此期间已结束或无法访问,忽略这些异常 + pass + return pids + System = _SystemHandler() diff --git a/app/ui/dispatch_center.py b/app/ui/dispatch_center.py index af073e0..fd9b5ec 100644 --- a/app/ui/dispatch_center.py +++ b/app/ui/dispatch_center.py @@ -52,7 +52,7 @@ from typing import List, Dict import json -from app.core import Config, Task_manager, Task, MainInfoBar +from app.core import Config, TaskManager, Task, MainInfoBar class DispatchCenter(QWidget): @@ -92,7 +92,7 @@ class DispatchCenter(QWidget): dispatch_box = DispatchBox(task.name, self) dispatch_box.top_bar.button.clicked.connect( - lambda: Task_manager.stop_task(task.name) + lambda: TaskManager.stop_task(task.name) ) task.create_task_list.connect(dispatch_box.info.task.create_task) @@ -128,7 +128,7 @@ class DispatchCenter(QWidget): self.script_list["主调度台"].top_bar.button.clicked.disconnect() self.script_list["主调度台"].top_bar.button.setText("中止任务") self.script_list["主调度台"].top_bar.button.clicked.connect( - lambda: Task_manager.stop_task(task.name) + lambda: TaskManager.stop_task(task.name) ) task.create_task_list.connect( self.script_list["主调度台"].info.task.create_task @@ -166,20 +166,31 @@ class DispatchCenter(QWidget): """更新顶栏""" list = [] + queue_numb, member_numb = 0, 0 if (Config.app_path / "config/QueueConfig").exists(): for json_file in (Config.app_path / "config/QueueConfig").glob("*.json"): list.append(f"队列 - {json_file.stem}") + queue_numb += 1 if (Config.app_path / "config/MaaConfig").exists(): for subdir in (Config.app_path / "config/MaaConfig").iterdir(): if subdir.is_dir(): list.append(f"实例 - Maa - {subdir.name}") + member_numb += 1 self.script_list["主调度台"].top_bar.object.clear() self.script_list["主调度台"].top_bar.object.addItems(list) - self.script_list["主调度台"].top_bar.object.setCurrentIndex(-1) - self.script_list["主调度台"].top_bar.mode.setCurrentIndex(-1) + self.script_list["主调度台"].top_bar.mode.clear() + self.script_list["主调度台"].top_bar.mode.addItems(["自动代理", "人工排查"]) + + if queue_numb == 1: + self.script_list["主调度台"].top_bar.object.setCurrentIndex(0) + elif member_numb == 1: + self.script_list["主调度台"].top_bar.object.setCurrentIndex(queue_numb) + else: + self.script_list["主调度台"].top_bar.object.setCurrentIndex(-1) + self.script_list["主调度台"].top_bar.mode.setCurrentIndex(0) class DispatchBox(QWidget): @@ -223,7 +234,6 @@ class DispatchBox(QWidget): self.object = ComboBox() self.object.setPlaceholderText("请选择调度对象") self.mode = ComboBox() - self.mode.addItems(["自动代理", "人工排查"]) self.mode.setPlaceholderText("请选择调度模式") self.button = PushButton("开始任务") @@ -276,7 +286,7 @@ class DispatchBox(QWidget): info = json.load(f) logger.info(f"用户添加任务:{name}") - Task_manager.add_task(f"{self.mode.currentText()}_主调度台", name, info) + TaskManager.add_task(f"{self.mode.currentText()}_主调度台", name, info) elif self.object.currentText().split(" - ")[0] == "实例": @@ -285,7 +295,7 @@ class DispatchBox(QWidget): info = {"Queue": {"Member_1": name}} logger.info(f"用户添加任务:{name}") - Task_manager.add_task( + TaskManager.add_task( f"{self.mode.currentText()}_主调度台", "自定义队列", info ) diff --git a/app/ui/main_window.py b/app/ui/main_window.py index 28e99e9..227db62 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -26,10 +26,7 @@ v4.2 """ from loguru import logger -from PySide6.QtWidgets import ( - QApplication, - QSystemTrayIcon, -) +from PySide6.QtWidgets import QSystemTrayIcon from qfluentwidgets import ( Action, PushButton, @@ -39,15 +36,18 @@ from qfluentwidgets import ( InfoBar, InfoBarPosition, setTheme, + isDarkTheme, + SystemThemeListener, Theme, MSFluentWindow, NavigationItemPosition, qconfig, ) from PySide6.QtGui import QIcon, QCloseEvent -from PySide6.QtCore import Qt +from PySide6.QtCore import Qt, QTimer +import json -from app.core import Config, Task_manager, Main_timer, MainInfoBar +from app.core import Config, TaskManager, MainTimer, MainInfoBar from app.services import Notify, Crypto, System from .setting import Setting from .member_manager import MemberManager @@ -63,7 +63,7 @@ class AUTO_MAA(MSFluentWindow): self.setWindowIcon(QIcon(str(Config.app_path / "resources/icons/AUTO_MAA.ico"))) self.setWindowTitle("AUTO_MAA") - setTheme(Theme.AUTO) + setTheme(Theme.AUTO, lazy=True) self.splashScreen = SplashScreen(self.windowIcon(), self) self.show_ui("显示主窗口", if_quick=True) @@ -143,22 +143,17 @@ class AUTO_MAA(MSFluentWindow): self.tray_menu.addSeparator() # 开始任务菜单项 - # self.tray_menu.addActions( - # [ - # Action( - # FluentIcon.PLAY, - # "运行自动代理", - # triggered=lambda: self.start_task("自动代理"), - # ), - # Action( - # FluentIcon.PLAY, - # "运行人工排查", - # triggered=lambda: self.start_task("人工排查"), - # ), - # Action(FluentIcon.PAUSE, "中止当前任务", triggered=self.stop_task), - # ] - # ) - # self.tray_menu.addSeparator() + self.tray_menu.addActions( + [ + Action(FluentIcon.PLAY, "运行自动代理", triggered=self.start_main_task), + Action( + FluentIcon.PAUSE, + "中止所有任务", + triggered=lambda: TaskManager.stop_task("ALL"), + ), + ] + ) + self.tray_menu.addSeparator() # 退出主程序菜单项 self.tray_menu.addAction( @@ -169,8 +164,8 @@ class AUTO_MAA(MSFluentWindow): self.tray.setContextMenu(self.tray_menu) self.tray.activated.connect(self.on_tray_activated) - Task_manager.create_gui.connect(self.dispatch_center.add_board) - Task_manager.connect_gui.connect(self.dispatch_center.connect_main_board) + TaskManager.create_gui.connect(self.dispatch_center.add_board) + TaskManager.connect_gui.connect(self.dispatch_center.connect_main_board) self.setting.ui.card_IfShowTray.checkedChanged.connect( lambda: self.show_ui("配置托盘") ) @@ -178,6 +173,23 @@ class AUTO_MAA(MSFluentWindow): self.splashScreen.finish() + self.themeListener = SystemThemeListener(self) + self.themeListener.systemThemeChanged.connect(self.switch_theme) + self.themeListener.start() + + def switch_theme(self): + """切换主题""" + + setTheme(Theme.AUTO, lazy=True) + QTimer.singleShot(100, lambda: setTheme(Theme.AUTO, lazy=True)) + + # 云母特效启用时需要增加重试机制 + if self.isMicaEffectEnabled(): + QTimer.singleShot( + 100, + lambda: self.windowEffect.setMicaEffect(self.winId(), isDarkTheme()), + ) + def start_up_task(self) -> None: """启动时任务""" @@ -212,6 +224,11 @@ class AUTO_MAA(MSFluentWindow): info.addWidget(Up) info.show() + # 直接运行主任务 + if Config.global_config.get(Config.global_config.start_IfRunDirectly): + + self.start_main_task() + def set_min_method(self) -> None: """设置最小化方法""" @@ -230,40 +247,32 @@ class AUTO_MAA(MSFluentWindow): if reason == QSystemTrayIcon.DoubleClick: self.show_ui("显示主窗口") - # def start_task(self, mode): - # """调起对应任务""" - # if self.main.MaaManager.isRunning(): - # Notify.push_notification( - # f"无法运行{mode}!", - # "当前已有任务正在运行,请在该任务结束后重试", - # "当前已有任务正在运行,请在该任务结束后重试", - # 3, - # ) - # else: - # self.main.maa_starter(mode) + def start_main_task(self) -> None: + """启动主任务""" - # def stop_task(self): - # """中止当前任务""" - # if self.main.MaaManager.isRunning(): - # if ( - # self.main.MaaManager.mode == "自动代理" - # or self.main.MaaManager.mode == "人工排查" - # ): - # self.main.maa_ender(f"{self.main.MaaManager.mode}_结束") - # elif "设置MAA" in self.main.MaaManager.mode: - # Notify.push_notification( - # "正在设置MAA!", - # "正在运行设置MAA任务,无法中止", - # "正在运行设置MAA任务,无法中止", - # 3, - # ) - # else: - # Notify.push_notification( - # "无任务运行!", - # "当前无任务正在运行,无需中止", - # "当前无任务正在运行,无需中止", - # 3, - # ) + if (Config.app_path / "config/QueueConfig/调度队列_1.json").exists(): + + with (Config.app_path / "config/QueueConfig/调度队列_1.json").open( + mode="r", encoding="utf-8" + ) as f: + info = json.load(f) + + logger.info("自动添加任务:调度队列_1") + TaskManager.add_task("自动代理_主调度台", "主任务队列", info) + + elif (Config.app_path / "config/MaaConfig/脚本_1").exists(): + + info = {"Queue": {"Member_1": "脚本_1"}} + + logger.info("自动添加任务:脚本_1") + TaskManager.add_task("自动代理_主调度台", "主任务队列", info) + + else: + + logger.worning("启动主任务失败:未找到有效的主任务配置文件") + MainInfoBar.push_info_bar( + "warning", "启动主任务失败", "“调度队列_1”与“脚本_1”均不存在", -1 + ) def show_ui(self, mode: str, if_quick: bool = False) -> None: """配置窗口状态""" @@ -330,13 +339,17 @@ class AUTO_MAA(MSFluentWindow): self.show_ui("隐藏到托盘", if_quick=True) # 清理各功能线程 - Main_timer.Timer.stop() - Main_timer.Timer.deleteLater() - Task_manager.stop_task("ALL") + MainTimer.Timer.stop() + MainTimer.Timer.deleteLater() + TaskManager.stop_task("ALL") # 关闭数据库连接 Config.close_database() + # 关闭主题监听 + self.themeListener.terminate() + self.themeListener.deleteLater() + logger.info("AUTO_MAA主程序关闭") logger.info("----------------END----------------") diff --git a/app/ui/member_manager.py b/app/ui/member_manager.py index ec7bfe6..42049bf 100644 --- a/app/ui/member_manager.py +++ b/app/ui/member_manager.py @@ -46,9 +46,12 @@ from qfluentwidgets import ( HeaderCardWidget, CommandBar, ExpandGroupSettingCard, + ComboBoxSettingCard, PushSettingCard, ) from PySide6.QtCore import Qt +import requests +import time from functools import partial from pathlib import Path from typing import List @@ -56,8 +59,9 @@ from datetime import datetime, timedelta import json import shutil -from app.core import Config, MainInfoBar, Task_manager +from app.core import Config, MainInfoBar, TaskManager from app.services import Crypto +from app.utils import Updater from .Widget import ( LineEditMessageBox, LineEditSettingCard, @@ -107,14 +111,21 @@ class MemberManager(QWidget): ] ) self.tools.addSeparator() - self.key = Action( - FluentIcon.HIDE, - "显示/隐藏密码", - checkable=True, - triggered=self.show_password, - ) self.tools.addAction( - self.key, + Action( + FluentIcon.DOWNLOAD, + "脚本下载器", + triggered=self.member_downloader, + ) + ) + self.tools.addSeparator() + self.tools.addAction( + Action( + FluentIcon.HIDE, + "显示/隐藏密码", + checkable=True, + triggered=self.show_password, + ) ) layout.addWidget(self.tools) @@ -291,6 +302,64 @@ class MemberManager(QWidget): self.member_manager.show_SettingBox(index + 1) + def member_downloader(self): + """脚本下载器""" + + choice = ComboBoxMessageBox( + self.window(), + "选择一个脚本类型以下载相应脚本", + ["选择脚本类型"], + [["MAA"]], + ) + if choice.exec() and choice.input[0].currentIndex() != -1: + + if choice.input[0].currentText() == "MAA": + + folder = QFileDialog.getExistingDirectory( + self, "选择MAA下载目录", str(Config.app_path) + ) + if not folder: + logger.warning("选择MAA下载目录时未选择文件夹") + MainInfoBar.push_info_bar( + "warning", "警告", "未选择MAA下载目录", 5000 + ) + return None + + # 从mirrorc服务器获取最新版本信息 + for _ in range(3): + try: + response = requests.get( + "https://mirrorc.top/api/resources/MAA/latest?user_agent=MaaWpfGui&os=win&arch=x64&channel=beta" + ) + maa_info = response.json() + break + except Exception as e: + err = e + time.sleep(0.1) + else: + choice = MessageBox( + "错误", + f"获取版本信息时出错:\n{err}", + self.window(), + ) + choice.cancelButton.hide() + choice.buttonLayout.insertStretch(1) + if choice.exec(): + return None + maa_version = list( + map( + int, + maa_info["data"]["version_name"][1:] + .replace("-beta", "") + .split("."), + ) + ) + while len(maa_version) < 4: + maa_version.append(0) + + self.downloader = Updater(Path(folder), "MAA", maa_version, []) + self.downloader.ui.show() + def show_password(self): if Config.PASSWORD == "": @@ -522,7 +591,7 @@ class MaaSettingBox(QWidget): ) ) self.card_Set.clicked.connect( - lambda: Task_manager.add_task("设置MAA_全局", self.name, None) + lambda: TaskManager.add_task("设置MAA_全局", self.name, None) ) Layout.addWidget(self.card_Name) @@ -578,9 +647,13 @@ class MaaSettingBox(QWidget): parent, ) - widget = QWidget() - Layout = QVBoxLayout(widget) - + self.card_TaskTransitionMethod = ComboBoxSettingCard( + configItem=Config.maa_config.RunSet_TaskTransitionMethod, + icon=FluentIcon.PAGE_RIGHT, + title="任务切换方式", + content="简洁用户列表下相邻两个任务间的切换方式", + texts=["直接切换账号", "重启明日方舟", "重启模拟器"], + ) self.ProxyTimesLimit = SpinBoxSettingCard( (0, 1024), FluentIcon.PAGE_RIGHT, @@ -588,7 +661,6 @@ class MaaSettingBox(QWidget): "当用户本日代理成功次数超过该阈值时跳过代理,阈值为“0”时视为无代理次数上限", Config.maa_config.RunSet_ProxyTimesLimit, ) - self.AnnihilationTimeLimit = SpinBoxSettingCard( (1, 1024), FluentIcon.PAGE_RIGHT, @@ -596,7 +668,6 @@ class MaaSettingBox(QWidget): "MAA日志无变化时间超过该阈值视为超时,单位为分钟", Config.maa_config.RunSet_AnnihilationTimeLimit, ) - self.RoutineTimeLimit = SpinBoxSettingCard( (1, 1024), FluentIcon.PAGE_RIGHT, @@ -604,7 +675,6 @@ class MaaSettingBox(QWidget): "MAA日志无变化时间超过该阈值视为超时,单位为分钟", Config.maa_config.RunSet_RoutineTimeLimit, ) - self.RunTimesLimit = SpinBoxSettingCard( (1, 1024), FluentIcon.PAGE_RIGHT, @@ -613,14 +683,15 @@ class MaaSettingBox(QWidget): Config.maa_config.RunSet_RunTimesLimit, ) + widget = QWidget() + Layout = QVBoxLayout(widget) + Layout.addWidget(self.card_TaskTransitionMethod) Layout.addWidget(self.ProxyTimesLimit) Layout.addWidget(self.AnnihilationTimeLimit) Layout.addWidget(self.RoutineTimeLimit) Layout.addWidget(self.RunTimesLimit) - self.viewLayout.setContentsMargins(0, 0, 0, 0) self.viewLayout.setSpacing(0) - self.addGroupWidget(widget) class UserSettingCard(HeaderCardWidget): @@ -746,7 +817,7 @@ class MaaSettingBox(QWidget): ): set_book = ["routine", "annihilation"] - Task_manager.add_task( + TaskManager.add_task( "设置MAA_用户", self.name, { diff --git a/app/ui/setting.py b/app/ui/setting.py index 3c4c91f..d77e775 100644 --- a/app/ui/setting.py +++ b/app/ui/setting.py @@ -363,7 +363,7 @@ class Setting(QWidget): def show_notice(self, if_show: bool = True): """显示公告""" - # 从远程服务器获取最新版本信息 + # 从远程服务器获取最新公告 for _ in range(3): try: response = requests.get( @@ -454,10 +454,10 @@ class FunctionSettingCard(HeaderCardWidget): configItem=Config.global_config.function_IfSilence, ) self.card_BossKey = LineEditSettingCard( - text="请输入安卓模拟器老版键", + text="请输入安卓模拟器老板键", icon=FluentIcon.PAGE_RIGHT, - title="模拟器老版键", - content="输入模拟器老版快捷键,以“+”分隔", + title="模拟器老板键", + content="输入模拟器老板快捷键,以“+”分隔", configItem=Config.global_config.function_BossKey, ) @@ -484,8 +484,8 @@ class StartSettingCard(HeaderCardWidget): ) self.card_IfRunDirectly = SwitchSettingCard( icon=FluentIcon.PAGE_RIGHT, - title="启动后直接运行", - content="启动AUTO_MAA后自动运行任务(暂不可用)", + title="启动后直接运行主任务", + content="启动AUTO_MAA后自动运行自动代理任务,优先级:调度队列 1 > 脚本 1", configItem=Config.global_config.start_IfRunDirectly, ) @@ -750,11 +750,11 @@ class OtherSettingCard(HeaderCardWidget): content="查看AUTO_MAA的最新公告", ) self.card_UserDocs = HyperlinkCard( - url="https://docs.qq.com/aio/DQ2NwUHRiWGtMWHBy", - text="查看使用指南", + url="https://clozya.github.io/AUTOMAA_docs", + text="访问", icon=FluentIcon.PAGE_RIGHT, - title="使用指南", - content="查看AUTO_MAA的使用教程和文档", + title="AUTO_MAA官方文档站", + content="访问AUTO_MAA的官方文档站,获取使用指南和项目相关信息", ) self.card_Association = self.AssociationSettingCard() diff --git a/app/utils/Updater.py b/app/utils/Updater.py index 87a9ca0..aa83d4a 100644 --- a/app/utils/Updater.py +++ b/app/utils/Updater.py @@ -70,7 +70,7 @@ class UpdateProcess(QThread): self.name = name self.main_version = main_version self.updater_version = updater_version - self.download_path = app_path / "AUTO_MAA_Update.zip" # 临时下载文件的路径 + self.download_path = app_path / "DOWNLOAD_TEMP.zip" # 临时下载文件的路径 self.version_path = app_path / "resources/version.json" def run(self) -> None: @@ -160,7 +160,7 @@ class UpdateProcess(QThread): zip_ref.extractall(self.app_path) break except PermissionError: - self.info.emit("解压出错:AUTO_MAA正在运行,正在等待其关闭") + self.info.emit(f"解压出错:{self.name}正在运行,正在等待其关闭") time.sleep(1) self.info.emit("正在删除临时文件") @@ -178,14 +178,17 @@ class UpdateProcess(QThread): return None # 更新version文件 - 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"] = ".".join(map(str, self.updater_version)) - elif self.name == "AUTO_MAA主程序": - version_info["main_version"] = ".".join(map(str, self.main_version)) - with open(self.version_path, "w", encoding="utf-8") as f: - json.dump(version_info, f, ensure_ascii=False, indent=4) + if self.name in ["AUTO_MAA主程序", "AUTO_MAA更新器"]: + with open(self.version_path, "r", encoding="utf-8") as f: + version_info = json.load(f) + if self.name == "AUTO_MAA主程序": + version_info["main_version"] = ".".join(map(str, self.main_version)) + elif self.name == "AUTO_MAA更新器": + version_info["updater_version"] = ".".join( + map(str, self.updater_version) + ) + with open(self.version_path, "w", encoding="utf-8") as f: + json.dump(version_info, f, ensure_ascii=False, indent=4) # 主程序更新完成后打开AUTO_MAA if self.name == "AUTO_MAA主程序": @@ -194,6 +197,12 @@ class UpdateProcess(QThread): shell=True, creationflags=subprocess.CREATE_NO_WINDOW, ) + elif self.name == "MAA": + subprocess.Popen( + str(self.app_path / "MAA.exe"), + shell=True, + creationflags=subprocess.CREATE_NO_WINDOW, + ) self.accomplish.emit() @@ -239,6 +248,9 @@ class UpdateProcess(QThread): 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" ) + url_list.append( + f"https://jp-download.fearr.xyz/AUTO_MAA/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" @@ -247,10 +259,21 @@ class UpdateProcess(QThread): 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" ) + url_list.append( + f"https://jp-download.fearr.xyz/AUTO_MAA/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" ) + elif self.name == "MAA": + url_list.append( + f"https://jp-download.fearr.xyz/MAA/MAA-{version_text(self.main_version)}-win-x64.zip" + ) + for i in range(len(PROXY_list)): + url_list.append( + f"{PROXY_list[i]}https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases/download/{version_text(self.main_version)}/MAA-{version_text(self.main_version)}-win-x64.zip" + ) return url_list @@ -265,7 +288,12 @@ class Updater(QObject): self.ui.setWindowTitle("AUTO_MAA更新器") self.ui.resize(700, 70) self.ui.setWindowIcon( - QIcon(str(app_path / "resources/icons/AUTO_MAA_Updater.ico")) + QIcon( + str( + Path(sys.argv[0]).resolve().parent + / "resources/icons/AUTO_MAA_Updater.ico" + ) + ) ) # 创建垂直布局 diff --git a/resources/docs/MAA_config_info.txt b/resources/docs/MAA_config_info.txt index 7e813bb..d5e5a1c 100644 --- a/resources/docs/MAA_config_info.txt +++ b/resources/docs/MAA_config_info.txt @@ -1,5 +1,7 @@ #主界面 -"MainFunction.PostActions": "12" #完成后 +"MainFunction.PostActions": "8" #完成后退出MAA +"MainFunction.PostActions": "9" #完成后退出MAA和游戏 +"MainFunction.PostActions": "12" #完成后退出MAA和模拟器 "TaskQueue.WakeUp.IsChecked": "True" #开始唤醒 "TaskQueue.Recruiting.IsChecked": "True" #自动公招 "TaskQueue.Base.IsChecked": "True" #基建换班 diff --git a/resources/version.json b/resources/version.json index 9603433..9cede48 100644 --- a/resources/version.json +++ b/resources/version.json @@ -1,7 +1,7 @@ { - "main_version": "4.2.3.0", - "updater_version": "1.1.1.3", - "announcement": "\n## 新增功能\n- 添加用户每日代理次数上限功能 #15\n- 新增代理成功消息推送渠道Server酱与企业微信群机器人推送\n- 添加更新类别可选项\n- 添加调度队列完成任务后行为选项\n- 初步完成`托管bilibili游戏隐私政策`功能\n## 修复BUG\n- 修复自定义基建无法正常使用的问题\n- 修正人工排查文案\n- 修复高级MAA配置序号错位\n- 修复高级用户列表无法配置问题\n- 修复主调度台选项乱动问题\n- 修复更新器文件夹定位问题\n- 修复窗口记忆功能失效问题\n## 程序优化\n- 优化弹窗逻辑\n- 优化静默判定逻辑\n- 调整MAA设置目录时打开当前已配置的目录位置\n- 邮箱推送功能调整,改由用户提供发信邮箱", + "main_version": "4.2.4.0", + "updater_version": "1.1.2.0", + "announcement": "\n## 新增功能\n- 添加`简洁用户列表下相邻两个任务间的切换方式`可选项\n- 恢复启动后直接运行主任务功能以及相关托盘菜单\n## 修复BUG\n- 修复静默代理标记移除异常情况\n- 适配深色模式 #18\n## 程序优化\n- 优化MAA关闭方法\n- 添加高级代理文件校验过程\n- 升级日志监看方法\n- 优化主调度台默认选项\n- 配置MAA前关闭可能未正常退出的MAA进程\n- 接入镜像源", "proxy_list": [ "", "https://gitproxy.click/",