diff --git a/README.md b/README.md index 2c5c299..b19e238 100644 --- a/README.md +++ b/README.md @@ -58,132 +58,9 @@ MAA多账号管理与自动化软件 # 使用方法 -## 安装软件 +本项目已改用腾讯文档展示使用方法 -``` -本软件是MAA的外部工具,需要安装MAA后才能使用。 -``` - -### 下载MAA - -- 什么是MAA? [官网](https://maa.plus/)/[GitHub](https://github.com/MaaAssistantArknights/MaaAssistantArknights) -- MAA下载地址 [GitHub下载](https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases) - -### 安装MAA - -- 将MAA压缩包解压至任意普通文件夹即可。 -- 若为首次安装MAA,请双击`MAA.exe`启动MAA程序以生成MAA配置文件。 - -### 下载AUTO_MAA [![](https://img.shields.io/github/downloads/DLmaster361/AUTO_MAA/total?color=66ccff)](https://github.com/DLmaster361/AUTO_MAA/releases) - -- GitHub下载地址 [GitHub下载](https://github.com/DLmaster361/AUTO_MAA/releases) - -### 安装AUTO_MAA - -- 将AUTO_MAA压缩包解压至任意普通文件夹即可。 - -## 配置AUTO_MAA - -### 启动AUTO_MAA - -- 双击`AUTO_MAA.exe`以启动软件。 - -``` -注意: - - 首次启动时会要求设置管理密钥。 - - 管理密钥是解密用户密码的唯一凭证,与用户数据库绑定。 - 密钥丢失或data/key/目录下任一文件损坏都将导致解密无法正常进行。 - - 本项目采用自主开发的混合加密模式,项目组也无法找回您的管理密钥或修复data/key/目录下的文件。 - 如果不幸的事发生,建议您删除data/key目录与config目录后重新录入信息。 -``` - -### 配置信息 - -#### 设置脚本实例 - -1. 单击`+`并选择`MAA`以添加MAA脚本实例。 -2. 在`MAA目录`选项卡中通过`选择文件夹`打开MAA软件目录以绑定MAA。 -3. 在`MAA全局配置`选项卡中通过`设置`进行MAA全局设置。在打开的MAA界面完成`性能设置`、`游戏设置`、`连接设置`、`启动设置`、`界面设置`、`软件更新`等基本配置以及代理任务的详细配置。 -4. 完成基本配置后,关闭MAA页面,AUTO_MAA会自动保存您的配置。 - -- 注意:在MAA的设置过程中,若MAA要求`立刻重启应用更改`,请选择`稍后`。否则,MAA重启后的一切更改都不会被程序记录。 - -- 特别的,在设置MAA过程中,您需要确保自己: - - 在`切换配置`选项卡中选择了`Default`项。 - - 取消勾选`开机自启动MAA`。 - - 配置自己模拟器所在的位置并根据实际情况填写`等待模拟器启动时间`(建议预留10s以防意外)。 - - 如果是模拟器多开用户,还需要填写`附加命令`,具体填写值参见多开模拟器对应快捷方式路径(如`-v 1`)。 - -![MAA配置](https://github.com/DLmaster361/AUTO_MAA/blob/main/resources/images/README/MAA配置.png "MAA配置") - - -#### 设置用户配置 - -每一个脚本实例都有独立的用户数据库,您可以直接在`用户列表`选项卡配置用户相关信息,页面简介如下: -- `新建用户`、`删除用户`:新建一个用户到当前用户配置列表、删除当前所选第一行所对应的用户。 -- `向上移动`、`向下移动`:移动用户位置,用户位置即代理顺序。 -- `模式转换`:将当前所选第一行所对应的用户转为高级/简洁配置模式。 -- `用户选项配置`:选择用户与对应配置项目,执行对应配置流程。 - - `自定义基建`:选择自定义基建配置文件。 - - `日常`、`剿灭`:打开MAA界面进行设置,设置方法与MAA全局配置相同。 -- `显示密码`:输入管理密钥以显示用户密码,仅当管理密钥正确时能够修改`密码栏目`。 -- `简洁用户配置列表`:仅支持核心代理选项的设置,其它设置选项沿用MAA的全局设置,部分代理核心功能选项由程序托管。 -- `高级用户配置列表`:支持几乎所有代理选项的设置,通过`用户选项配置`进行MAA自定义,仅部分代理核心功能选项由程序托管。 -- `用户配置列表栏目`详解如下: - - `用户名`:展示在执行界面的用户名,用于区分不同用户。 - - `账号ID`:MAA进行账号切换所需的凭据,官服用户请输入手机号码、B服请输入B站ID。 - - `服务器`:当前支持官服、B服。 - - `代理天数`:剩余需要进行代理的天数,输入`任意负数`可设置为无限代理天数,当剩余天数为0时不再代理或排查。 - - `状态`:用户的状态,禁用时将不再对其进行代理或排查。 - - `执行情况`:当日执行情况,不可编辑。 - - `关卡`、`备选关卡-1`、`备选关卡-2`:关卡号。 - - `日常`:单独设定是否进行自动代理的日常部分,可进一步配置MAA的具体代理任务,该配置与全局MAA配置相互独立。 - - `剿灭`:单独设定是否进行自动代理的剿灭部分,高级配置模式下可进一步配置MAA的具体代理任务,该配置与全局MAA配置相互独立。 - - `自定义基建`:是否启用自定义基建功能,需要进一步配置自定义基建文件,该配置与其他用户相互独立。 - - `密码`:仅用于登记用户的密码,可留空。 - - `备注`:用于备注用户信息。 - -- 特别的: - - 对于`简洁用户配置列表的关卡、备选关卡-1、备选关卡-2栏目`您可以自定义关卡号替换方案。 - - 程序会读取`data/gameid.txt`中的数据,依据此进行关卡号的替换,便于常用关卡的使用。 - - `gameid.txt`会在程序首次运行时生成,其中将预置一些常用资源本的替换方案。 - -![gameid](https://github.com/DLmaster361/AUTO_MAA/blob/main/resources/images/README/gameid.png "gameid") - -#### 设置调度队列 - -- 单个调度队列可包含至多10个定时与至多10个任务实例。 -- 调度队列状态为关闭时,将不会定时启动该调度队列,但仍能在主调度台直接运行该调度队列。 -- 同一调度队列内任务实例被依次挨个调起运行,非同一调度队列内的不同任务实例可被同时调起。 -- 同一时间内,任何脚本实例或调度队列都不会被重复调起,若某一任务运行时发现同一任务已在运行,将自动跳过。 - -#### 设置AUTO_MAA - -- 详见软件中对应选项卡的注解。 - -## 运行代理任务 - -### 直接运行 - -- 在`调度中心`的`主调度台`选择对应任务与`自动代理`模式,单击`开始任务`即可开始代理。 - -### 定时运行 - -- 将调度队列状态设为开启,并在`定时`选项卡设置定时启动时间。 -- 保持软件开启,软件会在设定的时间自动运行。 - -## 人工排查代理结果 - -### 直接开始人工排查 - -- 在`调度中心`的`主调度台`选择对应任务与`人工排查`模式,单击`开始任务`即可开始人工排查。 -- 软件将调起MAA,依次登录各用户的账号。 -- 完成PRTS登录后,请人工检查代理情况,可以手动完成未代理的任务。 -- 在对话框中单击对应账号的代理情况。 -- 结束人工排查后,相应排查情况将被写入用户管理页的`备注栏目`。 +- [《AUTO_MAA用户指南》](https://docs.qq.com/aio/DQ2NwUHRiWGtMWHBy) --- @@ -213,7 +90,7 @@ MAA多账号管理与自动化软件 欢迎加入AUTO_MAA项目组,欢迎反馈bug -- QQ群:[957750551](https://qm.qq.com/q/bd9fISNoME) +- QQ交流群:[957750551](https://qm.qq.com/q/bd9fISNoME) --- diff --git a/app/core/config.py b/app/core/config.py index 1496a6e..f0bee3f 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -127,6 +127,8 @@ class AppConfig: self.queue_config = QueueConfig() self.maa_config = MaaConfig() + qconfig.load(self.config_path, self.global_config) + config_list = self.search_config() for config in config_list: if config[0] == "Maa": @@ -499,6 +501,7 @@ class AppConfig: self.queue_config.set(self.queue_config.queueSet_Name, "") self.queue_config.set(self.queue_config.queueSet_Enabled, False) + self.queue_config.set(self.queue_config.queueSet_AfterAccomplish, "None") self.queue_config.set(self.queue_config.time_TimeEnabled_0, False) self.queue_config.set(self.queue_config.time_TimeSet_0, "00:00") @@ -541,6 +544,9 @@ class GlobalConfig(QConfig): ) function_IfSilence = ConfigItem("Function", "IfSilence", False, BoolValidator()) function_BossKey = ConfigItem("Function", "BossKey", "") + function_IfAgreeBilibili = ConfigItem( + "Function", "IfAgreeBilibili", False, BoolValidator() + ) start_IfSelfStart = ConfigItem("Start", "IfSelfStart", False, BoolValidator()) start_IfRunDirectly = ConfigItem("Start", "IfRunDirectly", False, BoolValidator()) @@ -556,7 +562,10 @@ class GlobalConfig(QConfig): notify_IfSendErrorOnly = ConfigItem( "Notify", "IfSendErrorOnly", False, BoolValidator() ) - notify_MailAddress = ConfigItem("Notify", "MailAddress", "") + notify_SMTPServerAddress = ConfigItem("Notify", "SMTPServerAddress", "") + notify_AuthorizationCode = ConfigItem("Notify", "AuthorizationCode", "") + notify_FromAddress = ConfigItem("Notify", "FromAddress", "") + notify_ToAddress = ConfigItem("Notify", "ToAddress", "") notify_IfServerChan = ConfigItem("Notify", "IfServerChan", False, BoolValidator()) notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "") notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "") @@ -579,6 +588,12 @@ class QueueConfig(QConfig): queueSet_Name = ConfigItem("QueueSet", "Name", "") queueSet_Enabled = ConfigItem("QueueSet", "Enabled", False, BoolValidator()) + queueSet_AfterAccomplish = OptionsConfigItem( + "QueueSet", + "AfterAccomplish", + "None", + OptionsValidator(["None", "KillSelf", "Sleep", "Hibernate", "Shutdown"]), + ) time_TimeEnabled_0 = ConfigItem("Time", "TimeEnabled_0", False, BoolValidator()) time_TimeSet_0 = ConfigItem("Time", "TimeSet_0", "00:00") diff --git a/app/core/main_info_bar.py b/app/core/main_info_bar.py index 349df7e..c83f1ca 100644 --- a/app/core/main_info_bar.py +++ b/app/core/main_info_bar.py @@ -36,14 +36,14 @@ from qfluentwidgets import ( class _MainInfoBar: """信息通知栏""" - def __init__(self, parent=None): + def __init__(self, main_window=None): - self.parent = parent + self.main_window = main_window def push_info_bar(self, mode: str, title: str, content: str, time: int): """推送到信息通知栏""" - if self.parent is None: + if self.main_window is None: logger.error("信息通知栏未设置父窗口") return None @@ -55,7 +55,7 @@ class _MainInfoBar: isClosable=True, position=InfoBarPosition.TOP_RIGHT, duration=time, - parent=self.parent, + parent=self.main_window, ) elif mode == "warning": InfoBar.warning( @@ -65,7 +65,7 @@ class _MainInfoBar: isClosable=True, position=InfoBarPosition.TOP_RIGHT, duration=time, - parent=self.parent, + parent=self.main_window, ) elif mode == "error": InfoBar.error( @@ -75,7 +75,7 @@ class _MainInfoBar: isClosable=True, position=InfoBarPosition.TOP_RIGHT, duration=time, - parent=self.parent, + parent=self.main_window, ) elif mode == "info": InfoBar.info( @@ -85,7 +85,7 @@ class _MainInfoBar: isClosable=True, position=InfoBarPosition.TOP_RIGHT, duration=time, - parent=self.parent, + parent=self.main_window, ) diff --git a/app/core/task_manager.py b/app/core/task_manager.py index 70ae791..c2217e7 100644 --- a/app/core/task_manager.py +++ b/app/core/task_manager.py @@ -28,6 +28,7 @@ v4.2 from loguru import logger from PySide6.QtCore import QThread, QObject, Signal from qfluentwidgets import Dialog +import json from pathlib import Path from datetime import datetime from typing import Dict, Union @@ -35,6 +36,7 @@ from typing import Dict, Union from .config import Config from .main_info_bar import MainInfoBar from app.models import MaaManager +from app.services import System class Task(QThread): @@ -52,10 +54,7 @@ class Task(QThread): accomplish = Signal(list) def __init__( - self, - mode: str, - name: str, - info: Dict[str, Dict[str, Union[str, int, bool]]], + self, mode: str, name: str, info: Dict[str, Dict[str, Union[str, int, bool]]] ): super(Task, self).__init__() @@ -92,41 +91,41 @@ class Task(QThread): else: self.member_dict = self.search_member() - self.task_list = [ + self.task_dict = [ [value, "等待"] for _, value in self.info["Queue"].items() if value != "禁用" ] - self.create_task_list.emit(self.task_list) + self.create_task_list.emit(self.task_dict) - for i in range(len(self.task_list)): + for i in range(len(self.task_dict)): if self.isInterruptionRequested(): break - self.task_list[i][1] = "运行" - self.update_task_list.emit(self.task_list) + self.task_dict[i][1] = "运行" + self.update_task_list.emit(self.task_dict) - if self.task_list[i][0] in Config.running_list: + if self.task_dict[i][0] in Config.running_list: - self.task_list[i][1] = "跳过" - self.update_task_list.emit(self.task_list) - logger.info(f"跳过任务:{self.task_list[i][0]}") + self.task_dict[i][1] = "跳过" + self.update_task_list.emit(self.task_dict) + logger.info(f"跳过任务:{self.task_dict[i][0]}") self.push_info_bar.emit( - "info", "跳过任务", self.task_list[i][0], 3000 + "info", "跳过任务", self.task_dict[i][0], 3000 ) continue - Config.running_list.append(self.task_list[i][0]) - logger.info(f"任务开始:{self.task_list[i][0]}") - self.push_info_bar.emit("info", "任务开始", self.task_list[i][0], 3000) + Config.running_list.append(self.task_dict[i][0]) + logger.info(f"任务开始:{self.task_dict[i][0]}") + self.push_info_bar.emit("info", "任务开始", self.task_dict[i][0], 3000) - if self.member_dict[self.task_list[i][0]][0] == "Maa": + if self.member_dict[self.task_dict[i][0]][0] == "Maa": self.task = MaaManager( self.mode[0:4], - self.member_dict[self.task_list[i][0]][1], + self.member_dict[self.task_dict[i][0]][1], ) self.task.question.connect(self.question.emit) @@ -138,7 +137,7 @@ class Task(QThread): self.task.update_log_text.connect(self.update_log_text.emit) self.task.update_user_info.connect( lambda modes, uids, days, lasts, notes, numbs: self.update_user_info.emit( - self.member_dict[self.task_list[i][0]][1], + self.member_dict[self.task_dict[i][0]][1], modes, uids, days, @@ -148,16 +147,16 @@ class Task(QThread): ) ) self.task.accomplish.connect( - lambda log: self.save_log(self.task_list[i][0], log) + lambda log: self.save_log(self.task_dict[i][0], log) ) self.task.run() - Config.running_list.remove(self.task_list[i][0]) + Config.running_list.remove(self.task_dict[i][0]) - self.task_list[i][1] = "完成" - logger.info(f"任务完成:{self.task_list[i][0]}") - self.push_info_bar.emit("info", "任务完成", self.task_list[i][0], 3000) + self.task_dict[i][1] = "完成" + logger.info(f"任务完成:{self.task_dict[i][0]}") + self.push_info_bar.emit("info", "任务完成", self.task_dict[i][0], 3000) self.accomplish.emit(self.logs) @@ -190,14 +189,14 @@ class TaskManager(QObject): def __init__(self): super(TaskManager, self).__init__() - self.task_list: Dict[str, Task] = {} + self.task_dict: Dict[str, Task] = {} def add_task( self, mode: str, name: str, info: Dict[str, Dict[str, Union[str, int, bool]]] ): """添加任务""" - if name in Config.running_list or name in self.task_list: + if name in Config.running_list or name in self.task_dict: logger.warning(f"任务已存在:{name}") MainInfoBar.push_info_bar("warning", "任务已存在", name, 5000) @@ -207,23 +206,23 @@ class TaskManager(QObject): MainInfoBar.push_info_bar("info", "任务开始", name, 3000) Config.running_list.append(name) - self.task_list[name] = Task(mode, name, info) - self.task_list[name].question.connect( + self.task_dict[name] = Task(mode, name, info) + self.task_dict[name].question.connect( lambda title, content: self.push_dialog(name, title, content) ) - self.task_list[name].push_info_bar.connect(MainInfoBar.push_info_bar) - self.task_list[name].update_user_info.connect(Config.change_user_info) - self.task_list[name].accomplish.connect( - lambda logs: self.remove_task(name, logs) + self.task_dict[name].push_info_bar.connect(MainInfoBar.push_info_bar) + self.task_dict[name].update_user_info.connect(Config.change_user_info) + self.task_dict[name].accomplish.connect( + lambda logs: self.remove_task(mode, name, logs) ) if "新调度台" in mode: - self.create_gui.emit(self.task_list[name]) + self.create_gui.emit(self.task_dict[name]) elif "主调度台" in mode: - self.connect_gui.emit(self.task_list[name]) + self.connect_gui.emit(self.task_dict[name]) - self.task_list[name].start() + self.task_dict[name].start() def stop_task(self, name: str): """中止任务""" @@ -233,22 +232,22 @@ class TaskManager(QObject): if name == "ALL": - for name in self.task_list: + for name in self.task_dict: - self.task_list[name].task.requestInterruption() - self.task_list[name].requestInterruption() - self.task_list[name].quit() - self.task_list[name].wait() + self.task_dict[name].task.requestInterruption() + self.task_dict[name].requestInterruption() + self.task_dict[name].quit() + self.task_dict[name].wait() - elif name in self.task_list: + elif name in self.task_dict: - self.task_list[name].task.requestInterruption() - self.task_list[name].requestInterruption() - self.task_list[name].quit() - self.task_list[name].wait() + self.task_dict[name].task.requestInterruption() + self.task_dict[name].requestInterruption() + self.task_dict[name].quit() + self.task_dict[name].wait() - def remove_task(self, name: str, logs: str): - """移除任务标记""" + def remove_task(self, mode: str, name: str, logs: str): + """任务结束后的处理""" logger.info(f"任务结束:{name}") MainInfoBar.push_info_bar("info", "任务结束", name, 3000) @@ -271,9 +270,16 @@ class TaskManager(QObject): }, ) - self.task_list.pop(name) + self.task_dict.pop(name) Config.running_list.remove(name) + if "调度队列" in name and "人工排查" not in mode: + with (Config.app_path / f"config/QueueConfig/{name}.json").open( + "r", encoding="utf-8" + ) as f: + info = json.load(f) + System.set_power(info["QueueSet"]["AfterAccomplish"]) + def push_dialog(self, name: str, title: str, content: str): """推送对话框""" @@ -281,7 +287,7 @@ class TaskManager(QObject): choice.yesButton.setText("是") choice.cancelButton.setText("否") - self.task_list[name].question_response.emit(bool(choice.exec_())) + self.task_dict[name].question_response.emit(bool(choice.exec_())) Task_manager = TaskManager() diff --git a/app/core/timer.py b/app/core/timer.py index bcf5909..6664a4b 100644 --- a/app/core/timer.py +++ b/app/core/timer.py @@ -86,25 +86,30 @@ class MainTimer(QWidget): def set_silence(self): """设置静默模式""" - windows = System.get_window_info() - if any( - str(emulator_path) in window - for window in windows - for emulator_path in Config.silence_list + if ( + Config.global_config.get(Config.global_config.function_IfSilence) + and Config.global_config.get(Config.global_config.function_BossKey) != "" ): - try: - pyautogui.hotkey( - *[ - _.strip().lower() - for _ in Config.global_config.get( - Config.global_config.function_BossKey - ).split("+") - ] - ) - except pyautogui.FailSafeException as e: - if not self.if_FailSafeException: - logger.warning(f"FailSafeException: {e}") - self.if_FailSafeException = True + + windows = System.get_window_info() + if any( + str(emulator_path) in window + for window in windows + for emulator_path in Config.silence_list + ): + try: + pyautogui.hotkey( + *[ + _.strip().lower() + for _ in Config.global_config.get( + Config.global_config.function_BossKey + ).split("+") + ] + ) + except pyautogui.FailSafeException as e: + if not self.if_FailSafeException: + logger.warning(f"FailSafeException: {e}") + self.if_FailSafeException = True def search_queue(self) -> list: """搜索所有调度队列实例""" diff --git a/app/models/MAA.py b/app/models/MAA.py index 27b8fcb..02f5509 100644 --- a/app/models/MAA.py +++ b/app/models/MAA.py @@ -89,6 +89,7 @@ class MaaManager(QObject): self.maa_set_path = self.maa_root_path / "config/gui.json" self.maa_log_path = self.maa_root_path / "debug/gui.log" self.maa_exe_path = self.maa_root_path / "MAA.exe" + self.maa_tasks_path = self.maa_root_path / "resource/tasks.json" def run(self): """主进程,运行MAA代理进程""" @@ -187,17 +188,12 @@ class MaaManager(QObject): creationflags=subprocess.CREATE_NO_WINDOW, ) # 添加静默进程标记 - if Config.global_config.get( - Config.global_config.function_IfSilence - ): - with self.maa_set_path.open( - mode="r", encoding="utf-8" - ) as f: - set = json.load(f) - self.emulator_path = Path( - set["Configurations"]["Default"]["Start.EmulatorPath"] - ) - Config.silence_list.append(self.emulator_path) + with self.maa_set_path.open(mode="r", encoding="utf-8") as f: + set = json.load(f) + self.emulator_path = Path( + set["Configurations"]["Default"]["Start.EmulatorPath"] + ) + Config.silence_list.append(self.emulator_path) # 记录是否超时的标记 self.if_time_out = False @@ -253,10 +249,7 @@ class MaaManager(QObject): "检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s" ) # 移除静默进程标记 - if Config.global_config.get( - Config.global_config.function_IfSilence - ): - Config.silence_list.remove(self.emulator_path) + Config.silence_list.remove(self.emulator_path) for _ in range(10): if self.isInterruptionRequested: break @@ -277,10 +270,7 @@ class MaaManager(QObject): ) killprocess.wait() # 移除静默进程标记 - if Config.global_config.get( - Config.global_config.function_IfSilence - ): - Config.silence_list.remove(self.emulator_path) + Config.silence_list.remove(self.emulator_path) # 推送异常通知 Notify.push_notification( "用户自动代理出现异常!", @@ -513,9 +503,14 @@ class MaaManager(QObject): f"{"\n".join([self.data[_][0] for _ in wait_index])}\n" ) + title = ( + f"{self.set["MaaSet"]["Name"]}的{self.mode[:4]}任务报告" + if self.set["MaaSet"]["Name"] != "" + else f"{self.mode[:4]}任务报告" + ) # 推送代理结果通知 Notify.push_notification( - f"{self.mode[2:4]}任务已完成!", + title.replace("报告", "已完成!"), f"已完成用户数:{len(over_index)},未完成用户数:{len(error_index) + len(wait_index)}", f"已完成用户数:{len(over_index)},未完成用户数:{len(error_index) + len(wait_index)}", 10, @@ -527,18 +522,13 @@ class MaaManager(QObject): and len(error_index) + len(wait_index) != 0 ): Notify.send_mail( - f"{self.mode[:4]}任务报告", + title, f"{end_log}\n\nAUTO_MAA 敬上\n\n我们根据您在 AUTO_MAA 中的设置发送了这封电子邮件,本邮件无需回复\n", ) - Notify.ServerChanPush( - f"{self.mode[:4]}任务报告", - f"{end_log}\n\nAUTO_MAA 敬上", - ) - Notify.CompanyWebHookBotPush( - f"{self.mode[:4]}任务报告", - f"{end_log}AUTO_MAA 敬上", - ) + Notify.ServerChanPush(title, f"{end_log}\n\nAUTO_MAA 敬上") + Notify.CompanyWebHookBotPush(title, f"{end_log}AUTO_MAA 敬上") + self.agree_bilibili(False) self.accomplish.emit({"Time": begin_time, "History": end_log}) def requestInterruption(self) -> None: @@ -660,6 +650,17 @@ class MaaManager(QObject): with self.maa_set_path.open(mode="r", encoding="utf-8") as f: data = json.load(f) + if "设置MAA" not in mode and ( + (self.data[index][15] == "simple" and self.data[index][2] == "Bilibili") + or ( + self.data[index][15] == "beta" + and data["Configurations"]["Default"]["Start.ClientType"] == "Bilibili" + ) + ): + self.agree_bilibili(True) + else: + self.agree_bilibili(False) + # 自动代理配置 if "自动代理" in mode: @@ -1021,6 +1022,34 @@ class MaaManager(QObject): return True + def agree_bilibili(self, if_agree): + """向MAA写入Bilibili协议相关任务""" + + with self.maa_tasks_path.open(mode="r", encoding="utf-8") as f: + data = json.load(f) + + if if_agree and Config.global_config.get( + Config.global_config.function_IfAgreeBilibili + ): + data["BilibiliAgreement_AUTO"] = { + "algorithm": "OcrDetect", + "action": "ClickSelf", + "text": ["同意"], + "maxTimes": 5, + "Doc": "关闭B服用户协议", + "next": ["StartUpThemes#next"], + } + if "BilibiliAgreement_AUTO" not in data["StartUpThemes"]["next"]: + data["StartUpThemes"]["next"].insert(0, "BilibiliAgreement_AUTO") + else: + if "BilibiliAgreement_AUTO" in data: + data.pop("BilibiliAgreement_AUTO") + if "BilibiliAgreement_AUTO" in data["StartUpThemes"]["next"]: + data["StartUpThemes"]["next"].remove("BilibiliAgreement_AUTO") + + with self.maa_tasks_path.open(mode="w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=4) + def get_emulator_path(self): """获取模拟器路径""" diff --git a/app/services/notification.py b/app/services/notification.py index c566cdd..4f21411 100644 --- a/app/services/notification.py +++ b/app/services/notification.py @@ -34,7 +34,8 @@ from email.utils import formataddr from serverchan_sdk import sc_send -from app.core import Config +from app.core import Config, MainInfoBar +from app.services.security import Crypto class Notification: @@ -57,73 +58,99 @@ class Notification: return True def send_mail(self, title, content): - """使用官方专用邮箱推送邮件通知""" - - # 声明:此邮箱为AUTO_MAA项目组资产,未经授权不得私自使用 - # 注意:此声明注释只有使用者更换发信邮箱时才能删除,本条规则优先级高于GPLv3 + """推送邮件通知""" if Config.global_config.get(Config.global_config.notify_IfSendMail): - # 第三方 SMTP 服务配置 - mail_host = "smtp.163.com" # 设置服务器 - mail_sender = "AUTO_MAA_server@163.com" # 用户名 - mail_key = "SYrq87nDLD4RNB5T" # 授权码 24/11/15 - - # 定义邮件正文 - message = MIMEText(content, "plain", "utf-8") - message["From"] = formataddr( - ( - Header("AUTO_MAA通知服务", "utf-8").encode(), - "AUTO_MAA_server@163.com", - ) - ) # 发件人显示的名字 - message["To"] = formataddr( - ( - Header("AUTO_MAA用户", "utf-8").encode(), - Config.global_config.get(Config.global_config.notify_MailAddress), - ) - ) # 收件人显示的名字 - message["Subject"] = Header(title, "utf-8") - try: - smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 465为SMTP_SSL默认端口 - smtpObj.login(mail_sender, mail_key) + # 定义邮件正文 + message = MIMEText(content, "plain", "utf-8") + message["From"] = formataddr( + ( + Header("AUTO_MAA通知服务", "utf-8").encode(), + Config.global_config.get( + Config.global_config.notify_FromAddress + ), + ) + ) # 发件人显示的名字 + message["To"] = formataddr( + ( + Header("AUTO_MAA用户", "utf-8").encode(), + Config.global_config.get(Config.global_config.notify_ToAddress), + ) + ) # 收件人显示的名字 + message["Subject"] = Header(title, "utf-8") + + smtpObj = smtplib.SMTP_SSL( + Config.global_config.get( + Config.global_config.notify_SMTPServerAddress + ), + 465, + ) + smtpObj.login( + Config.global_config.get(Config.global_config.notify_FromAddress), + Crypto.win_decryptor( + Config.global_config.get( + Config.global_config.notify_AuthorizationCode + ) + ), + ) smtpObj.sendmail( - mail_sender, - Config.global_config.get(Config.global_config.notify_MailAddress), + Config.global_config.get(Config.global_config.notify_FromAddress), + Config.global_config.get(Config.global_config.notify_ToAddress), message.as_string(), ) - return True - except smtplib.SMTPException as e: - return f"发送邮件时出错:\n{e}" - finally: smtpObj.quit() + logger.success("邮件发送成功") + except Exception as e: + logger.error(f"发送邮件时出错:\n{e}") + MainInfoBar.push_info_bar("error", "发送邮件时出错", f"{e}", -1) def ServerChanPush(self, title, content): """使用Server酱推送通知""" if Config.global_config.get(Config.global_config.notify_IfServerChan): - send_key = Config.global_config.get(Config.global_config.notify_ServerChanKey) + send_key = Config.global_config.get( + Config.global_config.notify_ServerChanKey + ) option = {} - is_valid = lambda s: s == "" or (s == '|'.join(s.split('|')) and (s.count('|') == 0 or all(s.split('|')))) + is_valid = lambda s: s == "" or ( + s == "|".join(s.split("|")) and (s.count("|") == 0 or all(s.split("|"))) + ) """ is_valid => True, 如果启用的话需要正确设置Tag和Channel。 允许空的Tag和Channel即不启用,但不允许例如a||b,|a|b,a|b|,|||| """ - send_tag = Config.global_config.get(Config.global_config.notify_ServerChanTag) - send_channel = Config.global_config.get(Config.global_config.notify_ServerChanChannel) + send_tag = Config.global_config.get( + Config.global_config.notify_ServerChanTag + ) + send_channel = Config.global_config.get( + Config.global_config.notify_ServerChanChannel + ) if is_valid(send_tag): - option['tags'] = send_tag + option["tags"] = send_tag else: - option['tags'] = '' - logger.warning('请正确设置Auto_MAA中ServerChan的Tag。') + option["tags"] = "" + logger.warning("请正确设置Auto_MAA中ServerChan的Tag。") + MainInfoBar.push_info_bar( + "warning", + "Server酱通知推送异常", + "请正确设置Auto_MAA中ServerChan的Tag。", + -1, + ) if is_valid(send_channel): - option['channel'] = send_channel + option["channel"] = send_channel else: - option['channel'] = '' - logger.warning('请正确设置Auto_MAA中ServerChan的Channel。') + option["channel"] = "" + logger.warning("请正确设置Auto_MAA中ServerChan的Channel。") + MainInfoBar.push_info_bar( + "warning", + "Server酱通知推送异常", + "请正确设置Auto_MAA中ServerChan的Channel。", + -1, + ) response = sc_send(send_key, title, content, option) if response["code"] == 0: @@ -132,21 +159,24 @@ class Notification: else: logger.info("Server酱推送通知失败") logger.error(response) + MainInfoBar.push_info_bar( + "error", + "Server酱通知推送失败", + f'使用Server酱推送通知时出错:\n{response["data"]['error']}', + -1, + ) return f'使用Server酱推送通知时出错:\n{response["data"]['error']}' def CompanyWebHookBotPush(self, title, content): """使用企业微信群机器人推送通知""" if Config.global_config.get(Config.global_config.notify_IfCompanyWebHookBot): - content = f'{title}\n{content}' - data = { - "msgtype": "text", - "text": { - "content": content - } - } + content = f"{title}\n{content}" + data = {"msgtype": "text", "text": {"content": content}} response = requests.post( - url=Config.global_config.get(Config.global_config.notify_CompanyWebHookBotUrl), - json=data + url=Config.global_config.get( + Config.global_config.notify_CompanyWebHookBotUrl + ), + json=data, ) if response.json()["errcode"] == 0: logger.info("企业微信群机器人推送通知成功") @@ -154,7 +184,15 @@ class Notification: else: logger.info("企业微信群机器人推送通知失败") logger.error(response.json()) - return f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}' + MainInfoBar.push_info_bar( + "error", + "企业微信群机器人通知推送失败", + f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}', + -1, + ) + return ( + f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}' + ) Notify = Notification() diff --git a/app/services/security.py b/app/services/security.py index 719c124..ed7c50e 100644 --- a/app/services/security.py +++ b/app/services/security.py @@ -30,6 +30,8 @@ import sqlite3 import hashlib import random import secrets +import base64 +import win32crypt from pathlib import Path from Crypto.Cipher import AES from Crypto.PublicKey import RSA @@ -83,8 +85,8 @@ class CryptoHandler: private_key_local = AES_key.encrypt(pad(private_key.exportKey(), 32)) (Config.app_path / "data/key/private_key.bin").write_bytes(private_key_local) - def encryptx(self, note: str) -> bytes: - """加密数据""" + def AUTO_encryptor(self, note: str) -> bytes: + """使用AUTO_MAA的算法加密数据""" # 读取RSA公钥 public_key_local = RSA.import_key( @@ -95,8 +97,8 @@ class CryptoHandler: encrypted = cipher.encrypt(note.encode("utf-8")) return encrypted - def decryptx(self, note: bytes, PASSWORD: str) -> str: - """解密数据""" + def AUTO_decryptor(self, note: bytes, PASSWORD: str) -> str: + """使用AUTO_MAA的算法解密数据""" # 读入RSA私钥密文、盐与校验哈希值 private_key_local = ( @@ -150,7 +152,9 @@ class CryptoHandler: # 使用旧管理密钥解密 user_data["Password"] = [] for i in range(len(data)): - user_data["Password"].append(self.decryptx(data[i][12], PASSWORD_old)) + user_data["Password"].append( + self.AUTO_decryptor(data[i][12], PASSWORD_old) + ) cur.close() db.close() @@ -169,7 +173,7 @@ class CryptoHandler: cur.execute( "UPDATE adminx SET password = ? WHERE mode = ? AND uid = ?", ( - self.encryptx(user_data["Password"][i]), + self.AUTO_encryptor(user_data["Password"][i]), data[i][15], data[i][16], ), @@ -181,6 +185,27 @@ class CryptoHandler: cur.close() db.close() + def win_encryptor( + self, note: str, description: str = None, entropy: bytes = None + ) -> str: + """使用Windows DPAPI加密数据""" + + encrypted = win32crypt.CryptProtectData( + note.encode("utf-8"), description, entropy, None, None, 0 + ) + return base64.b64encode(encrypted).decode("utf-8") + + def win_decryptor(self, note: str, entropy: bytes = None) -> str: + """使用Windows DPAPI解密数据""" + + if note == "": + return "" + + decrypted = win32crypt.CryptUnprotectData( + base64.b64decode(note), entropy, None, None, 0 + ) + return decrypted[1].decode("utf-8") + def search_member(self) -> List[Dict[str, Union[Path, list]]]: """搜索所有脚本实例及其用户数据库路径""" @@ -197,7 +222,9 @@ class CryptoHandler: def check_PASSWORD(self, PASSWORD: str) -> bool: """验证管理密钥""" - return bool(self.decryptx(self.encryptx(""), PASSWORD) != "管理密钥错误") + return bool( + self.AUTO_decryptor(self.AUTO_encryptor(""), PASSWORD) != "管理密钥错误" + ) Crypto = CryptoHandler() diff --git a/app/services/system.py b/app/services/system.py index 8099229..b2939d2 100644 --- a/app/services/system.py +++ b/app/services/system.py @@ -25,21 +25,27 @@ v4.2 作者:DLmaster_361 """ +from loguru import logger +from PySide6.QtWidgets import QWidget +import sys import ctypes import win32gui import win32process import winreg import psutil +import subprocess from app.core import Config -class SystemHandler: +class _SystemHandler: ES_CONTINUOUS = 0x80000000 ES_SYSTEM_REQUIRED = 0x00000001 - def __init__(self): + def __init__(self, main_window: QWidget = None): + + self.main_window = main_window self.set_Sleep() self.set_SelfStart() @@ -84,6 +90,60 @@ class SystemHandler: winreg.DeleteValue(key, "AUTO_MAA") winreg.CloseKey(key) + def set_power(self, mode): + + if sys.platform.startswith("win"): + + if mode == "None": + + logger.info("不执行系统电源操作") + + elif mode == "Shutdown": + + logger.info("执行关机操作") + subprocess.run(["shutdown", "/s", "/t", "0"]) + + elif mode == "Hibernate": + + logger.info("执行休眠操作") + subprocess.run(["shutdown", "/h"]) + + elif mode == "Sleep": + + logger.info("执行睡眠操作") + subprocess.run( + ["rundll32.exe", "powrprof.dll,SetSuspendState", "0,1,0"] + ) + + elif mode == "KillSelf": + + self.main_window.close() + + elif sys.platform.startswith("linux"): + + if mode == "None": + + logger.info("不执行系统电源操作") + + elif mode == "Shutdown": + + logger.info("执行关机操作") + subprocess.run(["shutdown", "-h", "now"]) + + elif mode == "Hibernate": + + logger.info("执行休眠操作") + subprocess.run(["systemctl", "hibernate"]) + + elif mode == "Sleep": + + logger.info("执行睡眠操作") + subprocess.run(["systemctl", "suspend"]) + + elif mode == "KillSelf": + + self.main_window.close() + def is_startup(self): """判断程序是否已经开机自启""" @@ -117,4 +177,4 @@ class SystemHandler: return window_info -System = SystemHandler() +System = _SystemHandler() diff --git a/app/ui/Widget.py b/app/ui/Widget.py index 1ec5a8f..6ac1d6b 100644 --- a/app/ui/Widget.py +++ b/app/ui/Widget.py @@ -44,14 +44,15 @@ from qfluentwidgets import ( TimeEdit, OptionsConfigItem, ) - from typing import Union, List +from app.services import Crypto -class InputMessageBox(MessageBoxBase): + +class LineEditMessageBox(MessageBoxBase): """输入对话框""" - def __init__(self, parent, title: str, content: str, mode: str, list: list = None): + def __init__(self, parent, title: str, content: str, mode: str): super().__init__(parent) self.title = SubtitleLabel(title) @@ -60,10 +61,6 @@ class InputMessageBox(MessageBoxBase): self.input.setClearButtonEnabled(True) elif mode == "密码": self.input = PasswordLineEdit() - elif mode == "选择": - self.input = ComboBox() - self.input.addItems(list) - self.input.setCurrentIndex(-1) self.input.setPlaceholderText(content) @@ -72,8 +69,8 @@ class InputMessageBox(MessageBoxBase): self.viewLayout.addWidget(self.input) -class SetMessageBox(MessageBoxBase): - """输入对话框""" +class ComboBoxMessageBox(MessageBoxBase): + """选择对话框""" def __init__(self, parent, title: str, content: List[str], list: List[List[str]]): super().__init__(parent) @@ -98,7 +95,7 @@ class SetMessageBox(MessageBoxBase): class LineEditSettingCard(SettingCard): - """Setting card with switch button""" + """Setting card with LineEdit""" textChanged = Signal(str) @@ -138,7 +135,49 @@ class LineEditSettingCard(SettingCard): self.LineEdit.setText(content) +class PasswordLineEditSettingCard(SettingCard): + """Setting card with PasswordLineEdit""" + + textChanged = Signal(str) + + def __init__( + self, + text, + icon: Union[str, QIcon, FluentIconBase], + title, + content=None, + configItem: ConfigItem = None, + parent=None, + ): + + super().__init__(icon, title, content, parent) + self.configItem = configItem + self.LineEdit = PasswordLineEdit(self) + self.LineEdit.setMinimumWidth(250) + self.LineEdit.setPlaceholderText(text) + + if configItem: + self.setValue(qconfig.get(configItem)) + configItem.valueChanged.connect(self.setValue) + + self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight) + self.hBoxLayout.addSpacing(16) + + self.LineEdit.textChanged.connect(self.__textChanged) + + def __textChanged(self, content: str): + self.setValue(Crypto.win_encryptor(content)) + self.textChanged.emit(content) + + def setValue(self, content: str): + if self.configItem: + qconfig.set(self.configItem, content) + + self.LineEdit.setText(Crypto.win_decryptor(content)) + + class SpinBoxSettingCard(SettingCard): + """Setting card with SpinBox""" textChanged = Signal(int) diff --git a/app/ui/dispatch_center.py b/app/ui/dispatch_center.py index d8161e9..af073e0 100644 --- a/app/ui/dispatch_center.py +++ b/app/ui/dispatch_center.py @@ -286,7 +286,7 @@ class DispatchBox(QWidget): logger.info(f"用户添加任务:{name}") Task_manager.add_task( - f"{self.mode.currentText()}_主调度台", "用户自定义队列", info + f"{self.mode.currentText()}_主调度台", "自定义队列", info ) class DispatchInfoCard(HeaderCardWidget): diff --git a/app/ui/main_window.py b/app/ui/main_window.py index e5de864..28e99e9 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -68,7 +68,8 @@ class AUTO_MAA(MSFluentWindow): self.splashScreen = SplashScreen(self.windowIcon(), self) self.show_ui("显示主窗口", if_quick=True) - MainInfoBar.parent = self + MainInfoBar.main_window = self.window() + System.main_window = self.window() # 创建主窗口 self.setting = Setting(self) @@ -161,7 +162,7 @@ class AUTO_MAA(MSFluentWindow): # 退出主程序菜单项 self.tray_menu.addAction( - Action(FluentIcon.POWER_BUTTON, "退出主程序", triggered=self.kill_main) + Action(FluentIcon.POWER_BUTTON, "退出主程序", triggered=self.window().close) ) # 设置托盘菜单 @@ -222,7 +223,7 @@ class AUTO_MAA(MSFluentWindow): else: self.titleBar.minBtn.clicked.disconnect() - self.titleBar.minBtn.clicked.connect(self.showMinimized) + self.titleBar.minBtn.clicked.connect(self.window().showMinimized) def on_tray_activated(self, reason): """双击返回主界面""" @@ -264,11 +265,6 @@ class AUTO_MAA(MSFluentWindow): # 3, # ) - def kill_main(self) -> None: - """退出主程序""" - self.close() - QApplication.quit() - def show_ui(self, mode: str, if_quick: bool = False) -> None: """配置窗口状态""" @@ -289,11 +285,11 @@ class AUTO_MAA(MSFluentWindow): ), ) ) - self.setGeometry(location[0], location[1], size[0], size[1]) - self.show() + self.window().setGeometry(location[0], location[1], size[0], size[1]) + self.window().show() if not if_quick: if Config.global_config.get(Config.global_config.ui_maximized): - self.showMaximized() + self.window().showMaximized() self.set_min_method() self.show_ui("配置托盘") @@ -307,7 +303,7 @@ class AUTO_MAA(MSFluentWindow): elif mode == "隐藏到托盘": # 保存窗口相关属性 - if not self.isMaximized(): + if not self.window().isMaximized(): Config.global_config.set( Config.global_config.ui_size, @@ -318,14 +314,14 @@ class AUTO_MAA(MSFluentWindow): f"{self.geometry().x()}x{self.geometry().y()}", ) Config.global_config.set( - Config.global_config.ui_maximized, self.isMaximized() + Config.global_config.ui_maximized, self.window().isMaximized() ) Config.global_config.save() # 隐藏主窗口 if not if_quick: - self.hide() + self.window().hide() self.tray.show() def closeEvent(self, event: QCloseEvent): @@ -342,6 +338,6 @@ class AUTO_MAA(MSFluentWindow): Config.close_database() logger.info("AUTO_MAA主程序关闭") - logger.info("===================================") + logger.info("----------------END----------------") event.accept() diff --git a/app/ui/member_manager.py b/app/ui/member_manager.py index ed60c90..ec7bfe6 100644 --- a/app/ui/member_manager.py +++ b/app/ui/member_manager.py @@ -59,10 +59,10 @@ import shutil from app.core import Config, MainInfoBar, Task_manager from app.services import Crypto from .Widget import ( - InputMessageBox, + LineEditMessageBox, LineEditSettingCard, SpinBoxSettingCard, - SetMessageBox, + ComboBoxMessageBox, ) @@ -123,16 +123,15 @@ class MemberManager(QWidget): def add_setting_box(self): """添加一个脚本实例""" - choice = InputMessageBox( - self, - "选择一个脚本类型并添加相应脚本实例", - "选择脚本类型", - "选择", - ["MAA"], + choice = ComboBoxMessageBox( + self.window(), + "选择一个脚本类型以添加相应脚本实例", + ["选择脚本类型"], + [["MAA"]], ) - if choice.exec() and choice.input.currentIndex() != -1: + if choice.exec() and choice.input[0].currentIndex() != -1: - if choice.input.currentText() == "MAA": + if choice.input[0].currentText() == "MAA": index = len(self.member_manager.search_member()) + 1 @@ -170,7 +169,7 @@ class MemberManager(QWidget): choice = MessageBox( "确认", f"确定要删除 {name} 实例吗?", - self, + self.window(), ) if choice.exec(): @@ -295,8 +294,8 @@ class MemberManager(QWidget): def show_password(self): if Config.PASSWORD == "": - choice = InputMessageBox( - self, + choice = LineEditMessageBox( + self.window(), "请输入管理密钥", "管理密钥", "密码", @@ -535,7 +534,11 @@ class MaaSettingBox(QWidget): def PathClicked(self): - folder = QFileDialog.getExistingDirectory(self, "选择MAA目录", "./") + folder = QFileDialog.getExistingDirectory( + self, + "选择MAA目录", + Config.maa_config.get(Config.maa_config.MaaSet_Path), + ) if ( not folder or Config.maa_config.get(Config.maa_config.MaaSet_Path) == folder @@ -690,8 +693,8 @@ class MaaSettingBox(QWidget): user_list = [_[0] for _ in data if _[15] == "simple"] set_list = ["自定义基建"] - choice = SetMessageBox( - self.parent().parent().parent().parent().parent().parent().parent(), + choice = ComboBoxMessageBox( + self.window(), "用户选项配置", ["选择要配置的用户", "选择要配置的选项"], [user_list, set_list], @@ -730,8 +733,8 @@ class MaaSettingBox(QWidget): user_list = [_[0] for _ in data if _[15] == "beta"] set_list = ["MAA日常配置", "MAA剿灭配置"] - choice = SetMessageBox( - self.parent().parent().parent().parent().parent().parent().parent(), + choice = ComboBoxMessageBox( + self.window(), "用户选项配置", ["选择要配置的用户", "选择要配置的选项"], [user_list, set_list], @@ -985,7 +988,7 @@ class MaaSettingBox(QWidget): item = QTableWidgetItem("******") item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) else: - result = Crypto.decryptx(value, Config.PASSWORD) + result = Crypto.AUTO_decryptor(value, Config.PASSWORD) item = QTableWidgetItem(result) if result == "管理密钥错误": item.setFlags( @@ -1052,7 +1055,7 @@ class MaaSettingBox(QWidget): item = QTableWidgetItem("******") item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) else: - result = Crypto.decryptx(value, Config.PASSWORD) + result = Crypto.AUTO_decryptor(value, Config.PASSWORD) item = QTableWidgetItem(result) if result == "管理密钥错误": item.setFlags( @@ -1123,7 +1126,7 @@ class MaaSettingBox(QWidget): games[game_in.strip()] = game_out.strip() text = games.get(text, text) if item.column() == 11: # 密码 - text = Crypto.encryptx(text) + text = Crypto.AUTO_encryptor(text) # 保存至本地数据库 if text != "": @@ -1141,7 +1144,7 @@ class MaaSettingBox(QWidget): self.update_user_info("normal") return None if item.column() == 6: # 密码 - text = Crypto.encryptx(text) + text = Crypto.AUTO_encryptor(text) # 保存至本地数据库 if text != "": @@ -1223,7 +1226,7 @@ class MaaSettingBox(QWidget): Config.cur.execute( "INSERT INTO adminx VALUES('新用户','手机号码(官服)/B站ID(B服)','Official',-1,'y','2000-01-01','1-7','-','-','n','n','n',?,'无',0,?,?)", ( - Crypto.encryptx("未设置"), + Crypto.AUTO_encryptor("未设置"), set_book[0], set_book[1], ), @@ -1273,15 +1276,7 @@ class MaaSettingBox(QWidget): choice = MessageBox( "确认", f"确定要删除用户 {data[0][0]} 吗?", - self.parent() - .parent() - .parent() - .parent() - .parent() - .parent() - .parent() - .parent() - .parent(), + self.window(), ) # 删除用户 @@ -1570,15 +1565,7 @@ class MaaSettingBox(QWidget): choice = MessageBox( "确认", f"确定要将用户 {data[0][0]} 转为{mode_list[1 - mode]}配置模式吗?", - self.parent() - .parent() - .parent() - .parent() - .parent() - .parent() - .parent() - .parent() - .parent(), + self.window(), ) # 切换用户 diff --git a/app/ui/queue_manager.py b/app/ui/queue_manager.py index 8ae4f5b..4004220 100644 --- a/app/ui/queue_manager.py +++ b/app/ui/queue_manager.py @@ -43,6 +43,7 @@ from qfluentwidgets import ( TextBrowser, CommandBar, SwitchSettingCard, + ComboBoxSettingCard, ) from PySide6.QtCore import Qt from typing import List @@ -138,7 +139,7 @@ class QueueManager(QWidget): choice = MessageBox( "确认", f"确定要删除 {name} 吗?", - self, + self.window(), ) if choice.exec(): @@ -412,9 +413,23 @@ class QueueMemberSettingBox(QWidget): "调度队列状态", Config.queue_config.queueSet_Enabled, ) + self.card_AfterAccomplish = ComboBoxSettingCard( + configItem=Config.queue_config.queueSet_AfterAccomplish, + icon=FluentIcon.POWER_BUTTON, + title="调度队列结束后", + content="选择调度队列结束后的操作", + texts=[ + "无动作", + "退出AUTO_MAA", + "睡眠(win系统需禁用休眠)", + "休眠", + "关机", + ], + ) Layout.addWidget(self.card_Name) Layout.addWidget(self.card_Enable) + Layout.addWidget(self.card_AfterAccomplish) self.viewLayout.addLayout(Layout) diff --git a/app/ui/setting.py b/app/ui/setting.py index 60d44b4..3c4c91f 100644 --- a/app/ui/setting.py +++ b/app/ui/setting.py @@ -53,7 +53,7 @@ import requests from app.core import Config, MainInfoBar from app.services import Crypto, System from app.utils import Updater -from .Widget import InputMessageBox, LineEditSettingCard +from .Widget import LineEditMessageBox, LineEditSettingCard, PasswordLineEditSettingCard class Setting(QWidget): @@ -83,6 +83,7 @@ class Setting(QWidget): self.other = OtherSettingCard(self) self.function.card_IfAllowSleep.checkedChanged.connect(System.set_Sleep) + self.function.card_IfAgreeBilibili.checkedChanged.connect(self.agree_bilibili) self.start.card_IfSelfStart.checkedChanged.connect(System.set_SelfStart) self.security.card_changePASSWORD.clicked.connect(self.change_PASSWORD) self.updater.card_CheckUpdate.clicked.connect(self.get_update) @@ -102,6 +103,31 @@ class Setting(QWidget): self.setLayout(layout) + def agree_bilibili(self) -> None: + """授权bilibili游戏隐私政策""" + + if not Config.global_config.get(Config.global_config.function_IfAgreeBilibili): + logger.info("取消授权bilibili游戏隐私政策") + MainInfoBar.push_info_bar( + "info", "操作成功", "已取消授权bilibili游戏隐私政策", 3000 + ) + return None + + choice = MessageBox( + "授权声明", + "开启“托管bilibili游戏隐私政策”功能,即代表您已完整阅读并同意《哔哩哔哩弹幕网用户使用协议》、《哔哩哔哩隐私政策》和《哔哩哔哩游戏中心用户协议》,并授权AUTO_MAA在其认定需要时以其认定合适的方法替您处理相关弹窗\n\n是否同意授权?", + self.window(), + ) + if choice.exec(): + logger.success("确认授权bilibili游戏隐私政策") + MainInfoBar.push_info_bar( + "success", "操作成功", "已确认授权bilibili游戏隐私政策", 3000 + ) + else: + Config.global_config.set( + Config.global_config.function_IfAgreeBilibili, False + ) + def check_PASSWORD(self) -> None: """检查并配置管理密钥""" @@ -110,8 +136,8 @@ class Setting(QWidget): while True: - choice = InputMessageBox( - self.parent().parent().parent(), + choice = LineEditMessageBox( + self.window(), "未检测到管理密钥,请设置您的管理密钥", "管理密钥", "密码", @@ -123,12 +149,11 @@ class Setting(QWidget): choice = MessageBox( "警告", "您没有设置管理密钥,无法使用本软件,请先设置管理密钥", - self.parent().parent().parent(), + self.window(), ) choice.cancelButton.hide() choice.buttonLayout.insertStretch(1) - if choice.exec(): - pass + choice.exec() def change_PASSWORD(self) -> None: """修改管理密钥""" @@ -137,8 +162,8 @@ class Setting(QWidget): while if_change: - choice = InputMessageBox( - self, + choice = LineEditMessageBox( + self.window(), "请输入旧的管理密钥", "旧管理密钥", "密码", @@ -152,8 +177,8 @@ class Setting(QWidget): # 获取新的管理密钥 while True: - choice = InputMessageBox( - self, + choice = LineEditMessageBox( + self.window(), "请输入新的管理密钥", "新管理密钥", "密码", @@ -173,23 +198,22 @@ class Setting(QWidget): choice = MessageBox( "确认", "您没有输入新的管理密钥,是否取消修改管理密钥?", - self, + self.window(), ) if choice.exec(): if_change = False break else: - choice = MessageBox("错误", "管理密钥错误", self) + choice = MessageBox("错误", "管理密钥错误", self.window()) choice.cancelButton.hide() choice.buttonLayout.insertStretch(1) - if choice.exec(): - pass + choice.exec() else: choice = MessageBox( "确认", "您没有输入管理密钥,是否取消修改管理密钥?", - self, + self.window(), ) if choice.exec(): break @@ -261,7 +285,7 @@ class Setting(QWidget): choice = MessageBox( "错误", f"获取版本信息时出错:\n{err}", - self, + self.window(), ) choice.cancelButton.hide() choice.buttonLayout.insertStretch(1) @@ -297,7 +321,7 @@ class Setting(QWidget): choice = MessageBox( "版本更新", 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将自动关闭", - self, + self.window(), ) if not choice.exec(): return None @@ -400,10 +424,17 @@ class FunctionSettingCard(HeaderCardWidget): configItem=Config.global_config.function_IfAllowSleep, ) self.card_IfSilence = self.SilenceSettingCard(self) + self.card_IfAgreeBilibili = SwitchSettingCard( + icon=FluentIcon.PAGE_RIGHT, + title="托管bilibili游戏隐私政策", + content="授权AUTO_MAA同意bilibili游戏隐私政策", + configItem=Config.global_config.function_IfAgreeBilibili, + ) Layout = QVBoxLayout() Layout.addWidget(self.card_IfAllowSleep) Layout.addWidget(self.card_IfSilence) + Layout.addWidget(self.card_IfAgreeBilibili) self.viewLayout.addLayout(Layout) class SilenceSettingCard(ExpandGroupSettingCard): @@ -526,7 +557,7 @@ class NotifySettingCard(HeaderCardWidget): super().__init__( FluentIcon.SETTING, "推送邮件通知", - "通过AUTO_MAA官方通知服务邮箱推送任务结果", + "通过电子邮箱推送任务结果", parent, ) @@ -536,18 +567,42 @@ class NotifySettingCard(HeaderCardWidget): content="是否启用邮件通知功能", configItem=Config.global_config.notify_IfSendMail, ) - self.card_MailAddress = LineEditSettingCard( - text="请输入邮箱地址", + self.card_SMTPServerAddress = LineEditSettingCard( + text="请输入SMTP服务器地址", icon=FluentIcon.PAGE_RIGHT, - title="邮箱地址", + title="SMTP服务器地址", + content="发信邮箱的SMTP服务器地址", + configItem=Config.global_config.notify_SMTPServerAddress, + ) + self.card_FromAddress = LineEditSettingCard( + text="请输入发信邮箱地址", + icon=FluentIcon.PAGE_RIGHT, + title="发信邮箱地址", + content="发送通知的邮箱地址", + configItem=Config.global_config.notify_FromAddress, + ) + self.card_AuthorizationCode = PasswordLineEditSettingCard( + text="请输入发信邮箱授权码", + icon=FluentIcon.PAGE_RIGHT, + title="发信邮箱授权码", + content="发送通知的邮箱授权码", + configItem=Config.global_config.notify_AuthorizationCode, + ) + self.card_ToAddress = LineEditSettingCard( + text="请输入收信邮箱地址", + icon=FluentIcon.PAGE_RIGHT, + title="收信邮箱地址", content="接收通知的邮箱地址", - configItem=Config.global_config.notify_MailAddress, + configItem=Config.global_config.notify_ToAddress, ) widget = QWidget() Layout = QVBoxLayout(widget) Layout.addWidget(self.card_IfSendMail) - Layout.addWidget(self.card_MailAddress) + Layout.addWidget(self.card_SMTPServerAddress) + Layout.addWidget(self.card_FromAddress) + Layout.addWidget(self.card_AuthorizationCode) + Layout.addWidget(self.card_ToAddress) self.viewLayout.setContentsMargins(0, 0, 0, 0) self.viewLayout.setSpacing(0) self.addGroupWidget(widget) diff --git a/resources/version.json b/resources/version.json index a957e16..9603433 100644 --- a/resources/version.json +++ b/resources/version.json @@ -1,7 +1,7 @@ { - "main_version": "4.2.2.2", + "main_version": "4.2.3.0", "updater_version": "1.1.1.3", - "announcement": "\n## 新增功能\n- 添加用户每日代理次数上限功能 #15\n- 新增代理成功消息推送渠道Server酱与企业微信群机器人推送\n- 添加更新类别可选项\n## 修复BUG\n- 修复自定义基建无法正常使用的问题\n- 修正人工排查文案\n- 修复高级MAA配置序号错位\n- 修复高级用户列表无法配置问题\n- 修复主调度台选项乱动问题\n- 修复更新器文件夹定位问题\n## 程序优化\n- 无", + "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- 邮箱推送功能调整,改由用户提供发信邮箱", "proxy_list": [ "", "https://gitproxy.click/",