Compare commits

...

42 Commits

Author SHA1 Message Date
DLmaster
c99707ecb4 Merge branch 'dev' 2025-02-17 18:09:42 +08:00
DLmaster
2b8e648fe6 Merge branch 'DLMS_dev' into dev 2025-02-17 18:08:45 +08:00
DLmaster
fcf61fd39a feat(core): 接入镜像源 2025-02-17 18:06:05 +08:00
DLmaster
8e3026f91e ci(build): 激活测试流程 2025-02-17 14:27:06 +08:00
DLmaster
8e00676faf ci(build): 测试服务器上传功能 2025-02-17 14:20:23 +08:00
DLmaster
ae293c4c20 Merge pull request #28 from ClozyA:dev
ci(build): 增加预发布版本服务器上传
2025-02-17 13:28:53 +08:00
df4a5f3318 ci(build): 增加预发布版本服务器上传 2025-02-17 12:02:03 +08:00
DLmaster
1da96c4d1d fix(ui): 矫正老板键文案 2025-02-17 11:06:07 +08:00
DLmaster
144c7f5db7 feat(modles): 配置MAA前关闭可能未正常退出的MAA进程 2025-02-16 18:44:46 +08:00
DLmaster
b3a3ccfea3 feat(core): 恢复启动后直接运行主任务功能以及相关托盘菜单 2025-02-16 14:56:25 +08:00
DLmaster
c3212f0ca1 Merge branch 'DLMS_dev' into dev 2025-02-15 17:32:47 +08:00
DLmaster
a946999782 fix: 修正版本号 2025-02-15 17:32:33 +08:00
DLmaster
284c41feb7 fix(ui): 适配深色模式 2025-02-15 17:28:54 +08:00
DLmaster
ddf905cb13 docs: 文档站试运行 2025-02-15 15:55:25 +08:00
DLmaster
d5082d18ef fix: 更正版本描述 2025-02-14 23:21:38 +08:00
DLmaster
af51831062 Merge branch 'DLMS_dev' into dev 2025-02-14 16:51:04 +08:00
DLmaster
fe4707df84 feat(ui): 优化主调度台默认选项 2025-02-14 16:50:45 +08:00
DLmaster
292e7f51e0 chore(core): 升级日志监看方法 2025-02-14 16:27:18 +08:00
DLmaster
8936b1c41d fix(core): 修复部分异常;添加高级代理文件校验过程 2025-02-13 13:03:44 +08:00
DLmaster
d45da439bd chore(models): 优化MAA关闭方法 2025-02-12 22:15:17 +08:00
DLmaster
7dc057e30f feat(models): 简洁用户列表下相邻两个任务间的切换方式 2025-02-12 22:06:04 +08:00
DLmaster
eb2f9d2cea fix(models): 修复静默代理标记移除异常情况 2025-02-11 13:47:12 +08:00
DLmaster
fb1895c4eb Merge branch 'dev' 2025-02-10 19:26:25 +08:00
DLmaster
90d3dad8c8 fix(services): 修复邮箱发信人信息错误 2025-02-10 11:29:27 +08:00
DLmaster
de12339c3e Merge branch 'DLMS_dev' into dev 2025-02-09 21:36:57 +08:00
DLmaster
f07cd2b44a feat(core): 邮箱推送功能调整,改由用户提供发信邮箱 2025-02-09 21:36:33 +08:00
DLmaster
c7fbbf6f50 Merge branch 'DLMS_dev' into dev 2025-02-08 16:58:23 +08:00
DLmaster
de262ee6bd fix(models): 修复设置MAA时异常调用B服任务设置 2025-02-08 16:58:11 +08:00
DLmaster
0c123e9389 feat(services): 通知标题添加脚本实例信息 2025-02-08 12:25:48 +08:00
DLmaster
d13fbb063d feat(core): 初步完成托管bilibili游戏隐私政策功能 2025-02-08 12:05:28 +08:00
DLmaster
5c24eb7d56 docs: 改用腾讯文档展示使用方法 2025-02-07 20:15:12 +08:00
DLmaster
6c2f19a884 Revert "新增用户字段today_stauts"
This reverts commit 4ff632ed2a.
2025-02-07 19:56:41 +08:00
heziziziscool
4ff632ed2a 新增用户字段today_stauts
新增用户字段`today_status`(位于user_db),用于记录用户的代理是否代理成功,便于后续的衍生项目与二次开发。
2025-02-07 18:50:32 +08:00
DLmaster
d445c0054f Merge branch 'DLMS_dev' into dev 2025-02-07 18:27:29 +08:00
DLmaster
748fa7a004 fix(gui): 修复窗口记忆功能失效问题 2025-02-07 18:27:01 +08:00
DLmaster
c3e710b5cf chore(gui): 调整MAA设置目录时打开当前已配置的目录位置 2025-02-07 15:53:37 +08:00
DLmaster
a93a60d125 chore(core): 优化静默判定逻辑 2025-02-07 15:37:07 +08:00
DLmaster
07f24c6168 Merge branch 'DLMS_dev' into dev 2025-02-06 23:33:16 +08:00
DLmaster
7f5478b098 feat(core): 添加调度队列完成任务后行为选项 2025-02-06 23:33:00 +08:00
DLmaster
fb7a429ff2 Merge pull request #22 from ClozyA:auto_shutdown
feat(core): 添加运行完成后自动关机功能
2025-02-06 18:33:12 +08:00
3307793a3d feat: 添加运行完成后自动关机功能 2025-02-06 15:55:55 +08:00
DLmaster
0da9f4b7ab Merge branch 'main' into dev 2025-02-04 22:48:09 +08:00
22 changed files with 1104 additions and 724 deletions

View File

@@ -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 }}
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/

View File

@@ -150,4 +150,13 @@ jobs:
gh release delete "$TAGNAME" --yes
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" --prerelease artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
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/

131
README.md
View File

@@ -58,132 +58,9 @@ MAA多账号管理与自动化软件
# 使用方法
## 安装软件
访问AUTO_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://clozya.github.io/AUTOMAA_docs)
---
@@ -205,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)
@@ -213,7 +92,7 @@ MAA多账号管理与自动化软件
欢迎加入AUTO_MAA项目组欢迎反馈bug
- QQ群[957750551](https://qm.qq.com/q/bd9fISNoME)
- QQ交流群:[957750551](https://qm.qq.com/q/bd9fISNoME)
---

View File

@@ -29,7 +29,7 @@ __version__ = "4.2.0"
__author__ = "DLmaster361 <DLmaster_361@163.com>"
__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",

View File

@@ -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",
]

View File

@@ -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":
@@ -486,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)
@@ -499,6 +502,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 +545,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 +563,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 +589,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")
@@ -628,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)
)

View File

@@ -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,
)

View File

@@ -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.task_accomplish(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)
@@ -174,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)
@@ -188,16 +188,16 @@ class TaskManager(QObject):
push_info_bar = Signal(str, str, str, int)
def __init__(self):
super(TaskManager, self).__init__()
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 +207,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,26 +233,28 @@ 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)
self.task_dict[name].deleteLater()
if len(logs) > 0:
time = logs[0][1]["Time"]
history = ""
@@ -271,9 +273,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 +290,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()
TaskManager = _TaskManager()

View File

@@ -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,30 +81,35 @@ class MainTimer(QWidget):
):
logger.info(f"定时任务:{name}")
Task_manager.add_task("自动代理_新调度台", name, info)
TaskManager.add_task("自动代理_新调度台", name, info)
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:
"""搜索所有调度队列实例"""
@@ -120,4 +125,4 @@ class MainTimer(QWidget):
return queue_list
Main_timer = MainTimer()
MainTimer = _MainTimer()

View File

@@ -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,10 +92,12 @@ 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"
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代理进程"""
@@ -116,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
@@ -144,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 = ["自动代理_剿灭", "自动代理_日常"]
@@ -157,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"]):
@@ -164,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):
@@ -176,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])
# 记录当前时间
@@ -187,112 +238,54 @@ 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)
# 记录是否超时的标记
self.if_time_out = False
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)
# 监测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"
)
# 移除静默进程标记
if Config.global_config.get(
Config.global_config.function_IfSilence
):
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()
# 移除静默进程标记
if Config.global_config.get(
Config.global_config.function_IfSilence
):
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]:
@@ -312,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)]
@@ -341,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()
@@ -357,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]:
@@ -416,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:
@@ -445,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)
@@ -475,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")
@@ -513,9 +473,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,23 +492,24 @@ 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.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:
@@ -558,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:
@@ -575,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_用户":
@@ -660,21 +679,51 @@ 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:
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后直接最小化
@@ -887,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后自动开启模拟器
@@ -895,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":
@@ -1015,12 +1057,51 @@ 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)
return True
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)
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):
"""获取模拟器路径"""

View File

@@ -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|ba|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()

View File

@@ -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()

View File

@@ -25,26 +25,33 @@ 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 pathlib import Path
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()
def set_Sleep(self):
def set_Sleep(self) -> None:
"""同步系统休眠状态"""
if Config.global_config.get(Config.global_config.function_IfAllowSleep):
@@ -56,7 +63,7 @@ class SystemHandler:
# 恢复系统电源状态
ctypes.windll.kernel32.SetThreadExecutionState(self.ES_CONTINUOUS)
def set_SelfStart(self):
def set_SelfStart(self) -> None:
"""同步开机自启"""
if (
@@ -84,7 +91,61 @@ class SystemHandler:
winreg.DeleteValue(key, "AUTO_MAA")
winreg.CloseKey(key)
def is_startup(self):
def set_power(self, mode) -> None:
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) -> bool:
"""判断程序是否已经开机自启"""
key = winreg.OpenKey(
@@ -102,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):
@@ -116,5 +177,29 @@ class SystemHandler:
win32gui.EnumWindows(callback, window_info)
return window_info
def kill_process(self, path: Path) -> None:
"""根据路径中止进程"""
System = SystemHandler()
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()

View File

@@ -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)

View File

@@ -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,8 +295,8 @@ class DispatchBox(QWidget):
info = {"Queue": {"Member_1": name}}
logger.info(f"用户添加任务:{name}")
Task_manager.add_task(
f"{self.mode.currentText()}_主调度台", "用户自定义队列", info
TaskManager.add_task(
f"{self.mode.currentText()}_主调度台", "自定义队列", info
)
class DispatchInfoCard(HeaderCardWidget):

View File

@@ -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,12 +63,13 @@ 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)
MainInfoBar.parent = self
MainInfoBar.main_window = self.window()
System.main_window = self.window()
# 创建主窗口
self.setting = Setting(self)
@@ -142,34 +143,29 @@ 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(
Action(FluentIcon.POWER_BUTTON, "退出主程序", triggered=self.kill_main)
Action(FluentIcon.POWER_BUTTON, "退出主程序", triggered=self.window().close)
)
# 设置托盘菜单
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("配置托盘")
)
@@ -177,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:
"""启动时任务"""
@@ -211,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:
"""设置最小化方法"""
@@ -222,52 +240,39 @@ 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):
"""双击返回主界面"""
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():
def kill_main(self) -> None:
"""退出主程序"""
self.close()
QApplication.quit()
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:
"""配置窗口状态"""
@@ -289,11 +294,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 +312,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 +323,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):
@@ -334,14 +339,18 @@ 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("===================================")
logger.info("----------------END----------------")
event.accept()

View File

@@ -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,13 +59,14 @@ 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 (
InputMessageBox,
LineEditMessageBox,
LineEditSettingCard,
SpinBoxSettingCard,
SetMessageBox,
ComboBoxMessageBox,
)
@@ -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)
@@ -123,16 +134,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 +180,7 @@ class MemberManager(QWidget):
choice = MessageBox(
"确认",
f"确定要删除 {name} 实例吗?",
self,
self.window(),
)
if choice.exec():
@@ -292,11 +302,69 @@ 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 == "":
choice = InputMessageBox(
self,
choice = LineEditMessageBox(
self.window(),
"请输入管理密钥",
"管理密钥",
"密码",
@@ -523,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)
@@ -535,7 +603,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
@@ -575,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,
@@ -585,7 +661,6 @@ class MaaSettingBox(QWidget):
"当用户本日代理成功次数超过该阈值时跳过代理阈值为“0”时视为无代理次数上限",
Config.maa_config.RunSet_ProxyTimesLimit,
)
self.AnnihilationTimeLimit = SpinBoxSettingCard(
(1, 1024),
FluentIcon.PAGE_RIGHT,
@@ -593,7 +668,6 @@ class MaaSettingBox(QWidget):
"MAA日志无变化时间超过该阈值视为超时单位为分钟",
Config.maa_config.RunSet_AnnihilationTimeLimit,
)
self.RoutineTimeLimit = SpinBoxSettingCard(
(1, 1024),
FluentIcon.PAGE_RIGHT,
@@ -601,7 +675,6 @@ class MaaSettingBox(QWidget):
"MAA日志无变化时间超过该阈值视为超时单位为分钟",
Config.maa_config.RunSet_RoutineTimeLimit,
)
self.RunTimesLimit = SpinBoxSettingCard(
(1, 1024),
FluentIcon.PAGE_RIGHT,
@@ -610,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):
@@ -690,8 +764,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 +804,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],
@@ -743,7 +817,7 @@ class MaaSettingBox(QWidget):
):
set_book = ["routine", "annihilation"]
Task_manager.add_task(
TaskManager.add_task(
"设置MAA_用户",
self.name,
{
@@ -985,7 +1059,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 +1126,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 +1197,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 +1215,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 +1297,7 @@ class MaaSettingBox(QWidget):
Config.cur.execute(
"INSERT INTO adminx VALUES('新用户','手机号码(官服)/B站IDB服','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 +1347,7 @@ class MaaSettingBox(QWidget):
choice = MessageBox(
"确认",
f"确定要删除用户 {data[0][0]} 吗?",
self.parent()
.parent()
.parent()
.parent()
.parent()
.parent()
.parent()
.parent()
.parent(),
self.window(),
)
# 删除用户
@@ -1570,15 +1636,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(),
)
# 切换用户

View File

@@ -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)

View File

@@ -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
@@ -339,7 +363,7 @@ class Setting(QWidget):
def show_notice(self, if_show: bool = True):
"""显示公告"""
# 从远程服务器获取最新版本信息
# 从远程服务器获取最新公告
for _ in range(3):
try:
response = requests.get(
@@ -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):
@@ -423,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,
)
@@ -453,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,
)
@@ -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)
@@ -695,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()

View File

@@ -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"
)
)
)
# 创建垂直布局

View File

@@ -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" #基建换班

View File

@@ -1,7 +1,7 @@
{
"main_version": "4.2.2.2",
"updater_version": "1.1.1.3",
"announcement": "\n## 新增功能\n- 添加用户每日代理次数上限功能 #15\n- 新增代理成功消息推送渠道Server酱与企业微信群机器人推送\n- 添加更新类别可选项\n## 修复BUG\n- 修复自定义基建无法正常使用的问题\n- 修正人工排查文案\n- 修复高级MAA配置序号错位\n- 修复高级用户列表无法配置问题\n- 修复主调度台选项乱动问题\n- 修复更新器文件夹定位问题\n## 程序优化\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/",