Compare commits

...

52 Commits

Author SHA1 Message Date
DLmaster361
5e46040db6 feat: 日志分析忽略MAA超时提示 2025-05-12 14:53:57 +08:00
DLmaster361
f2b04dd0f6 Merge branch 'dev' 2025-05-11 23:28:18 +08:00
DLmaster361
2177c1b40e fix(models): 适配MAAv5.16.3的ADB报错信息更改 2025-05-11 23:26:42 +08:00
DLmaster361
d1f4cffe8f feat(ui): 主调度台添加仅一次电源任务,UI样式优化 2025-05-10 23:15:54 +08:00
DLmaster361
74ce441b90 feat(core): 电源相关选项改为所有任务完成后生效 2025-05-09 21:52:18 +08:00
DLmaster361
5893aa2426 feat(ui): 自定义基建显示配置名称 2025-05-09 19:54:07 +08:00
DLmaster361
cb7e7bf9d4 Merge branch 'dev' 2025-05-09 14:54:29 +08:00
DLmaster361
fbfdc6aa12 feat: 优化更新方式 2025-05-09 14:54:09 +08:00
DLmaster361
e7b6743e10 test: 移除测试ci 2025-05-06 17:43:23 +08:00
DLmaster361
ff4283e917 Merge branch 'dev' 2025-05-06 17:42:42 +08:00
DLmaster361
890886d62d fix(ci): 适配新下载站模式 2025-05-06 17:42:24 +08:00
DLmaster361
fd75dda2b1 feat(ui): 适配MAA连战次数AUTO模式 2025-05-06 13:57:49 +08:00
DLmaster361
f22c1aeae3 test(ci): 补充步骤 2025-05-06 13:07:56 +08:00
DLmaster361
d68d49a469 test(ci): 更正环境 2025-05-06 13:00:04 +08:00
DLmaster361
1900d4eaf5 test(ci): 测试ssh部分 2025-05-06 12:57:55 +08:00
DLmaster361
02833209d5 Merge branch 'dev' 2025-05-06 00:18:44 +08:00
2058c0218c ci: 更新下载服务器配置 2025-05-06 00:09:14 +08:00
DLmaster361
8896e723eb fix: 补充版本号 2025-05-05 20:14:25 +08:00
DLmaster361
edcc614833 Merge commit '23fe1ff0beb234eb7ebbc07b189782b7fb84d5e5' 2025-05-05 20:13:22 +08:00
DLmaster361
23fe1ff0be feat: release中添加安装程序 2025-05-05 20:12:59 +08:00
DLmaster361
19d1dc9f28 fix: 修复详细模式下非定时自定义基建无法正常换班的问题 2025-05-05 15:59:07 +08:00
DLmaster361
24b93cfcad feat: 下载器支持调用Mirror酱 2025-05-04 23:43:04 +08:00
DLmaster361
d3298fac8a fix: 移除serverchan_sdk 2025-05-04 17:01:36 +08:00
DLmaster361
fba5395bf0 Merge branch 'dev' 2025-05-04 16:44:46 +08:00
DLmaster361
2c4508ee16 fix: 修复版本号错误 2025-05-04 16:43:49 +08:00
d239443555 chore(release): 发版 4.3.6.2 2025-05-04 16:36:36 +08:00
e45ad08fab refactor(notification): 重构 Server酱 推送服务 2025-05-04 16:25:46 +08:00
ddf5d26c4b refactor(notification): 重构 Server酱 推送服务 2025-05-04 16:12:45 +08:00
DLmaster361
ce74dcf912 Merge branch 'dev' of github.com:DLmaster361/AUTO_MAA into dev 2025-05-04 15:18:31 +08:00
DLmaster361
41412e1ef4 feat(ui): 新增无人值守模式 2025-05-04 15:18:26 +08:00
雪影
1395d48cd0 头部信息居中 (#43)
Co-authored-by: DLmaster361 <DLmaster_361@163.com>
2025-05-04 13:57:56 +08:00
DLmaster361
418c3d4742 fix(ui): 修复隐藏到托盘时,托盘无法退出主程序的问题 2025-05-04 11:12:34 +08:00
DLmaster361
17ec962a22 fix(ui): 修复软件窗口相关问题
- 修复软件窗口最大化异常问题
- 修复异常操作导致窗口离开屏幕后难以复原的问题
- 修正剩余理智关卡文案
- 主窗口显示版本号
2025-05-04 03:14:14 +08:00
DLmaster361
989ee73549 feat(maa): 单次自动代理任务中,已完成的子任务在重复执行时不再启用 2025-05-02 11:36:40 +08:00
DLmaster361
7e452e1253 Merge branch 'dev' 2025-05-02 00:40:55 +08:00
DLmaster361
5bdb5c8025 fix: 详细配置模式下更新也由AUTO统筹配置 2025-05-02 00:39:58 +08:00
DLmaster361
924a5fea0b feat: 适配6周年游戏变动的优化
- 用户设置中新增连战次数与剩余理智关卡两项配置项
- 支持自动代理时更新MAA
- 移除增效任务
2025-05-01 21:31:09 +08:00
DLmaster361
b51a57a6ee fix: 修复无法建立网络连接时软件卡死问题 2025-04-29 09:40:11 +08:00
DLmaster361
4079188881 fix: 脚本实例任务完成后即时更新状态信息 2025-04-28 20:41:08 +08:00
DLmaster361
174163e305 feat: 模拟器路径适配快捷方式 2025-04-28 18:35:56 +08:00
DLmaster361
0886439685 Merge branch 'dev' 2025-04-27 01:00:18 +08:00
DLmaster361
34bf5a4fe8 fix: 同步到MAAv5.15.4的tasks目录结构变化 2025-04-27 00:59:46 +08:00
DLmaster361
e6a97f2b17 feat(ui): 主调度台支持直接启动多开调度台 2025-04-26 22:40:47 +08:00
DLmaster361
fecff625a3 feat: 新增MAA稳定版更新提醒 2025-04-26 11:28:14 +08:00
DLmaster361
6f540036a0 feat(ui): 历史记录功能升级 2025-04-25 22:47:00 +08:00
DLmaster361
86d72aec39 feat: 历史记录页面添加选中最近一月选中最近一周选项 2025-04-23 15:04:36 +08:00
DLmaster361
39876832f3 fix: 历史记录加载方法优化 2025-04-21 19:21:27 +08:00
DLmaster361
f3af6ddbbc fix: 修复更新时主程序退出不彻底与ADB路径与模拟器路径相关问题 2025-04-20 16:40:14 +08:00
DLmaster361
ba7299e20c fix: 同步核心版本号 2025-04-20 11:14:21 +08:00
DLmaster361
5db9d934b2 fix: 修复更新器使用mirrorc下载时删除已弃用的文件卡死 2025-04-20 11:07:41 +08:00
DLmaster361
5c8eebf12c fix: 清理debug时的print 2025-04-20 00:18:31 +08:00
DLmaster361
e725f6d2b2 Merge branch 'main' into dev 2025-04-20 00:05:17 +08:00
34 changed files with 2743 additions and 1298 deletions

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
name: Build AUTO_MAA
@@ -55,6 +55,8 @@ jobs:
python -m pip install --upgrade pip
pip install flake8 pytest
pip install -r requirements.txt
choco install innosetup
echo "C:\Program Files (x86)\Inno Setup 6" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
@@ -71,14 +73,11 @@ jobs:
run: |
$MAIN_VERSION=(Get-Content -Path "version_info.txt" -TotalCount 1).Trim()
"AUTO_MAA_version=$MAIN_VERSION" | Out-File -FilePath $env:GITHUB_ENV -Append
$UPDATER_VERSION=(Get-Content -Path "version_info.txt" -TotalCount 2 | Select-Object -Index 1).Trim()
"updater_version=$UPDATER_VERSION" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: AUTO_MAA_${{ env.AUTO_MAA_version }}
path: |
AUTO_MAA_${{ env.AUTO_MAA_version }}.zip
path: AUTO_MAA_${{ env.AUTO_MAA_version }}.zip
- name: Upload Version_Info Artifact
uses: actions/upload-artifact@v4
with:
@@ -110,8 +109,11 @@ jobs:
NAME="$(sed 's/\r$//g' <(head -n 1 version_info.txt))"
TAGNAME="$(sed 's/\r$//g' <(head -n 1 version_info.txt))"
NOTES_MAIN="$(sed 's/\r$//g' <(tail -n +3 version_info.txt))"
NOTES_TAIL="\`\`\`本release通过GitHub Actions自动构建\`\`\`"
NOTES="$NOTES_MAIN<br><br>$NOTES_TAIL"
NOTES="$NOTES_MAIN
[已有 Mirror酱 CDK ?前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA)
\`\`\`本release通过GitHub Actions自动构建\`\`\`"
if [ "${{ github.ref_name }}" == "main" ]; then
PRERELEASE_FLAG=""
else
@@ -126,27 +128,3 @@ jobs:
gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan_release_note
env:
GH_TOKEN: ${{ secrets.GITHUB_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/
- name: Install obsutil
run: |
wget https://obs-community.obs.cn-north-1.myhuaweicloud.com/obsutil/current/obsutil_linux_amd64.tar.gz
tar -xzvf obsutil_linux_amd64.tar.gz --strip-components=1
chmod 755 obsutil
./obsutil version
- name: Upload Release to Huawei OBS
env:
OBS_AK: ${{ secrets.OBS_AK }}
OBS_SK: ${{ secrets.OBS_SK }}
OBS_ENDPOINT: ${{ secrets.OBS_ENDPOINT }}
OBS_BUCKET: ${{ secrets.OBS_BUCKET }}
run: |
./obsutil config -i $OBS_AK -k $OBS_SK -e $OBS_ENDPOINT
./obsutil cp artifacts/ obs://$OBS_BUCKET/releases/ -r -f

View File

@@ -1,19 +1,20 @@
# AUTO_MAA
MAA多账号管理与自动化软件
!["软件图标"](https://github.com/DLmaster361/AUTO_MAA/blob/main/resources/images/AUTO_MAA.png "软件图标")
<h1 align="center">AUTO_MAA</h1>
<p align="center">
MAA多账号管理与自动化软件<br><br>
<img alt="软件图标" src="https://github.com/DLmaster361/AUTO_MAA/blob/main/resources/images/AUTO_MAA.png">
</p>
---
</h1>
[![GitHub Stars](https://img.shields.io/github/stars/DLmaster361/AUTO_MAA?style=flat-square)](https://github.com/DLmaster361/AUTO_MAA/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/DLmaster361/AUTO_MAA?style=flat-square)](https://github.com/DLmaster361/AUTO_MAA/network)
[![GitHub Issues](https://img.shields.io/github/issues/DLmaster361/AUTO_MAA?style=flat-square)](https://github.com/DLmaster361/AUTO_MAA/issues)
[![GitHub Contributors](https://img.shields.io/github/contributors/DLmaster361/AUTO_MAA?style=flat-square)](https://github.com/DLmaster361/AUTO_MAA/graphs/contributors)
[![GitHub License](https://img.shields.io/github/license/DLmaster361/AUTO_MAA?style=flat-square)](https://github.com/DLmaster361/AUTO_MAA/blob/main/LICENSE)
</div>
<p align="center">
<a href="https://github.com/DLmaster361/AUTO_MAA/stargazers"><img alt="GitHub Stars" src="https://img.shields.io/github/stars/DLmaster361/AUTO_MAA?style=flat-square"></a>
<a href="https://github.com/DLmaster361/AUTO_MAA/network"><img alt="GitHub Forks" src="https://img.shields.io/github/forks/DLmaster361/AUTO_MAA?style=flat-square"></a>
<a href="https://github.com/DLmaster361/AUTO_MAA/releases/latest"><img alt="GitHub Downloads" src="https://img.shields.io/github/downloads/DLmaster361/AUTO_MAA/total?style=flat-square"></a>
<a href="https://github.com/DLmaster361/AUTO_MAA/issues"><img alt="GitHub Issues" src="https://img.shields.io/github/issues/DLmaster361/AUTO_MAA?style=flat-square"></a>
<a href="https://github.com/DLmaster361/AUTO_MAA/graphs/contributors"><img alt="GitHub Contributors" src="https://img.shields.io/github/contributors/DLmaster361/AUTO_MAA?style=flat-square"></a>
<a href="https://github.com/DLmaster361/AUTO_MAA/blob/main/LICENSE"><img alt="GitHub License" src="https://img.shields.io/github/license/DLmaster361/AUTO_MAA?style=flat-square"></a>
<a href="https://mirrorchyan.com/zh/projects?rid=AUTO_MAA"><img alt="mirrorc" src="https://img.shields.io/badge/Mirror%E9%85%B1-%239af3f6?logo=countingworkspro&logoColor=4f46e5"></a>
</p>
## 软件介绍

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -33,7 +33,6 @@ from .core import QueueConfig, MaaConfig, MaaUserConfig, Task, TaskManager, Main
from .models import MaaManager
from .services import Notify, Crypto, System
from .ui import AUTO_MAA
from .utils import DownloadManager
__all__ = [
"QueueConfig",
@@ -47,5 +46,4 @@ __all__ = [
"Crypto",
"System",
"AUTO_MAA",
"DownloadManager",
]

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -33,7 +33,7 @@ import sys
import shutil
import re
import base64
from datetime import datetime, timedelta
from datetime import datetime, timedelta, date
from collections import defaultdict
from pathlib import Path
from qfluentwidgets import (
@@ -91,7 +91,10 @@ class GlobalConfig(QConfig):
OptionsValidator(["默认", "自定义", "主题图像"]),
)
self.function_HistoryRetentionTime = OptionsConfigItem(
"Function", "HistoryRetentionTime", 0, OptionsValidator([7, 15, 30, 60, 0])
"Function",
"HistoryRetentionTime",
0,
OptionsValidator([7, 15, 30, 60, 90, 180, 365, 0]),
)
self.function_IfAllowSleep = ConfigItem(
"Function", "IfAllowSleep", False, BoolValidator()
@@ -100,6 +103,9 @@ class GlobalConfig(QConfig):
"Function", "IfSilence", False, BoolValidator()
)
self.function_BossKey = ConfigItem("Function", "BossKey", "")
self.function_UnattendedMode = ConfigItem(
"Function", "UnattendedMode", False, BoolValidator()
)
self.function_IfAgreeBilibili = ConfigItem(
"Function", "IfAgreeBilibili", False, BoolValidator()
)
@@ -254,8 +260,10 @@ class QueueConfig(QConfig):
self.queueSet_AfterAccomplish = OptionsConfigItem(
"QueueSet",
"AfterAccomplish",
"None",
OptionsValidator(["None", "KillSelf", "Sleep", "Hibernate", "Shutdown"]),
"NoAction",
OptionsValidator(
["NoAction", "KillSelf", "Sleep", "Hibernate", "Shutdown"]
),
)
self.time_TimeEnabled_0 = ConfigItem(
@@ -406,27 +414,24 @@ class MaaConfig(QConfig):
"ExitEmulator",
OptionsValidator(["NoAction", "ExitGame", "ExitEmulator"]),
)
self.RunSet_EnhanceTask = OptionsConfigItem(
"RunSet",
"EnhanceTask",
"None",
OptionsValidator(["None", "KillADB", "KillEmulator", "KillADB&Emulator"]),
)
self.RunSet_ProxyTimesLimit = RangeConfigItem(
"RunSet", "ProxyTimesLimit", 0, RangeValidator(0, 1024)
)
self.RunSet_RunTimesLimit = RangeConfigItem(
"RunSet", "RunTimesLimit", 3, RangeValidator(1, 1024)
)
self.RunSet_AnnihilationTimeLimit = RangeConfigItem(
"RunSet", "AnnihilationTimeLimit", 40, RangeValidator(1, 1024)
)
self.RunSet_RoutineTimeLimit = RangeConfigItem(
"RunSet", "RoutineTimeLimit", 10, RangeValidator(1, 1024)
)
self.RunSet_RunTimesLimit = RangeConfigItem(
"RunSet", "RunTimesLimit", 3, RangeValidator(1, 1024)
)
self.RunSet_AnnihilationWeeklyLimit = ConfigItem(
"RunSet", "AnnihilationWeeklyLimit", False, BoolValidator()
)
self.RunSet_AutoUpdateMaa = ConfigItem(
"RunSet", "AutoUpdateMaa", False, BoolValidator()
)
def toDict(self, serialize=True):
"""convert config items to `dict`"""
@@ -518,17 +523,27 @@ class MaaUserConfig(QConfig):
"Info", "Annihilation", False, BoolValidator()
)
self.Info_Routine = ConfigItem("Info", "Routine", False, BoolValidator())
self.Info_Infrastructure = ConfigItem(
"Info", "Infrastructure", False, BoolValidator()
self.Info_InfrastMode = OptionsConfigItem(
"Info",
"InfrastMode",
"Normal",
OptionsValidator(["Normal", "Rotation", "Custom"]),
)
self.Info_Password = ConfigItem("Info", "Password", "")
self.Info_Notes = ConfigItem("Info", "Notes", "")
self.Info_MedicineNumb = ConfigItem(
"Info", "MedicineNumb", 0, RangeValidator(0, 1024)
)
self.Info_SeriesNumb = OptionsConfigItem(
"Info",
"SeriesNumb",
"1",
OptionsValidator(["1000", "6", "5", "4", "3", "2", "1", "-1"]),
)
self.Info_GameId = ConfigItem("Info", "GameId", "-")
self.Info_GameId_1 = ConfigItem("Info", "GameId_1", "-")
self.Info_GameId_2 = ConfigItem("Info", "GameId_2", "-")
self.Info_GameId_Remain = ConfigItem("Info", "GameId_Remain", "-")
self.Data_LastProxyDate = ConfigItem("Data", "LastProxyDate", "2000-01-01")
self.Data_LastAnnihilationDate = ConfigItem(
@@ -538,6 +553,9 @@ class MaaUserConfig(QConfig):
"Data", "ProxyTimes", 0, RangeValidator(0, 1024)
)
self.Data_IfPassCheck = ConfigItem("Data", "IfPassCheck", True, BoolValidator())
self.Data_CustomInfrastPlanIndex = ConfigItem(
"Data", "CustomInfrastPlanIndex", "0"
)
def toDict(self, serialize=True):
"""convert config items to `dict`"""
@@ -606,11 +624,12 @@ class MaaUserConfig(QConfig):
class AppConfig(GlobalConfig):
VERSION = "4.3.4.2"
VERSION = "4.3.8.2"
gameid_refreshed = Signal()
PASSWORD_refreshed = Signal()
user_info_changed = Signal()
power_sign_changed = Signal()
def __init__(self) -> None:
super().__init__()
@@ -625,6 +644,7 @@ class AppConfig(GlobalConfig):
self.gameid_path = self.app_path / "data/gameid.txt"
self.version_path = self.app_path / "resources/version.json"
self.main_window = None
self.PASSWORD = ""
self.running_list = []
self.silence_list = []
@@ -632,6 +652,7 @@ class AppConfig(GlobalConfig):
"ALL": {"value": [], "text": []},
"Today": {"value": [], "text": []},
}
self.power_sign = "NoAction"
self.if_ignore_silence = False
self.if_database_opened = False
@@ -752,7 +773,7 @@ class AppConfig(GlobalConfig):
]
# 生成本日关卡信息
days = datetime.strptime(self.server_date(), "%Y-%m-%d").isoweekday()
days = self.server_date().isoweekday()
gameid_list = [
{"value": "-", "text": "当前/上次", "days": [1, 2, 3, 4, 5, 6, 7]},
@@ -787,13 +808,13 @@ class AppConfig(GlobalConfig):
self.gameid_refreshed.emit()
def server_date(self) -> str:
def server_date(self) -> date:
"""获取当前的服务器日期"""
dt = datetime.now()
if dt.time() < datetime.min.time().replace(hour=4):
dt = dt - timedelta(days=1)
return dt.strftime("%Y-%m-%d")
return dt.date()
def check_data(self) -> None:
"""检查用户数据文件并处理数据文件版本更新"""
@@ -1286,11 +1307,34 @@ class AppConfig(GlobalConfig):
user_config.set(
user_config.Data_IfPassCheck, info["Config"]["Data"]["IfPassCheck"]
)
user_config.set(
user_config.Data_CustomInfrastPlanIndex,
info["Config"]["Data"]["CustomInfrastPlanIndex"],
)
self.user_info_changed.emit()
def set_power_sign(self, sign: str) -> None:
"""设置当前电源状态"""
self.power_sign = sign
self.power_sign_changed.emit()
def save_history(self, key: str, content: dict) -> None:
"""保存历史记录"""
if key in self.queue_dict:
self.queue_dict[key]["Config"].set(
self.queue_dict[key]["Config"].Data_LastProxyTime, content["Time"]
)
self.queue_dict[key]["Config"].set(
self.queue_dict[key]["Config"].Data_LastProxyHistory, content["History"]
)
else:
logger.warning(f"保存历史记录时未找到调度队列: {key}")
def save_maa_log(self, log_path: Path, logs: list, maa_result: str) -> bool:
"""保存MAA日志"""
"""保存MAA日志并生成初步统计数据"""
data: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = {
"recruit_statistics": defaultdict(int),
@@ -1453,9 +1497,9 @@ class AppConfig(GlobalConfig):
data["drop_statistics"][stage][item] = count
# 合并MAA结果
data["maa_result"][
json_file.name.replace(".json", "").replace("-", ":")
] = single_data["maa_result"]
data["maa_result"][json_file.stem.replace("-", ":")] = single_data[
"maa_result"
]
# 生成汇总 JSON 文件
if mode == "所有项":
@@ -1478,9 +1522,21 @@ class AppConfig(GlobalConfig):
info: Dict[str, Dict[str, Union[int, dict]]] = json.load(f)
data = {}
# 4点前的记录放在当日最后
sorted_maa_result = sorted(
info["maa_result"].items(),
key=lambda x: (
(
1
if datetime.strptime(x[0], "%H:%M:%S").time()
< datetime.min.time().replace(hour=4)
else 0
),
datetime.strptime(x[0], "%H:%M:%S"),
),
)
data["条目索引"] = [
[k, "完成" if v == "Success!" else "异常"]
for k, v in info["maa_result"].items()
[k, "完成" if v == "Success!" else "异常"] for k, v in sorted_maa_result
]
data["条目索引"].insert(0, ["数据总览", "运行"])
data["统计数据"] = {"公招统计": list(info["recruit_statistics"].items())}
@@ -1511,7 +1567,9 @@ class AppConfig(GlobalConfig):
return data
def search_history(self) -> dict:
def search_history(
self, mode: str, start_date: datetime, end_date: datetime
) -> dict:
"""搜索所有历史记录"""
history_dict = {}
@@ -1524,34 +1582,55 @@ class AppConfig(GlobalConfig):
date = datetime.strptime(date_folder.name, "%Y-%m-%d")
history_dict[date.strftime("%Y年 %m月 %d")] = list(
date_folder.glob("*.json")
)
if not (start_date <= date <= end_date):
continue # 只统计在范围内的日期
if mode == "按日合并":
history_dict[date.strftime("%Y年 %m月 %d")] = list(
date_folder.glob("*.json")
)
elif mode == "按周合并":
year, week, _ = date.isocalendar()
if f"{year}年 第{week}" not in history_dict:
history_dict[f"{year}年 第{week}"] = {}
for user in date_folder.glob("*.json"):
if user.stem not in history_dict[f"{year}年 第{week}"]:
history_dict[f"{year}年 第{week}"][user.stem] = list(
user.with_suffix("").glob("*.json")
)
else:
history_dict[f"{year}年 第{week}"][user.stem] += list(
user.with_suffix("").glob("*.json")
)
elif mode == "按月合并":
if date.strftime("%Y年 %m月") not in history_dict:
history_dict[date.strftime("%Y年 %m月")] = {}
for user in date_folder.glob("*.json"):
if user.stem not in history_dict[date.strftime("%Y年 %m月")]:
history_dict[date.strftime("%Y年 %m月")][user.stem] = list(
user.with_suffix("").glob("*.json")
)
else:
history_dict[date.strftime("%Y年 %m月")][user.stem] += list(
user.with_suffix("").glob("*.json")
)
except ValueError:
logger.warning(f"非日期格式的目录: {date_folder}")
return {
k: v
for k, v in sorted(
history_dict.items(),
key=lambda x: datetime.strptime(x[0], "%Y年 %m月 %d"),
reverse=True,
)
for k, v in sorted(history_dict.items(), key=lambda x: x[0], reverse=True)
}
def save_history(self, key: str, content: dict) -> None:
"""保存历史记录"""
if key in self.queue_dict:
self.queue_dict[key]["Config"].set(
self.queue_dict[key]["Config"].Data_LastProxyTime, content["Time"]
)
self.queue_dict[key]["Config"].set(
self.queue_dict[key]["Config"].Data_LastProxyHistory, content["History"]
)
else:
logger.warning(f"保存历史记录时未找到调度队列: {key}")
Config = AppConfig()

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -29,17 +29,15 @@ from loguru import logger
from PySide6.QtCore import Qt
from qfluentwidgets import InfoBar, InfoBarPosition
from .config import Config
class _MainInfoBar:
"""信息通知栏"""
def __init__(self, main_window=None):
self.main_window = main_window
def push_info_bar(self, mode: str, title: str, content: str, time: int):
"""推送到信息通知栏"""
if self.main_window is None:
if Config.main_window is None:
logger.error("信息通知栏未设置父窗口")
return None
@@ -61,7 +59,7 @@ class _MainInfoBar:
isClosable=True,
position=InfoBarPosition.TOP_RIGHT,
duration=time,
parent=self.main_window,
parent=Config.main_window,
)
else:
logger.error(f"未知的通知栏模式: {mode}")

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -53,8 +53,6 @@ class _Network(QThread):
self.if_running = True
print(self.url)
if self.mode == "get":
self.get_json(self.url)
elif self.mode == "get_file":
@@ -80,6 +78,8 @@ class _Network(QThread):
def get_json(self, url: str) -> None:
"""通过get方法获取json数据"""
response = None
for _ in range(self.max_retries):
try:
response = requests.get(url, timeout=self.timeout)
@@ -94,10 +94,11 @@ class _Network(QThread):
time.sleep(self.backoff_factor)
self.loop.quit()
print("quited")
def get_file(self, url: str, path: Path) -> None:
"""通过get方法获取json数据"""
"""通过get方法下载文件"""
response = None
try:
response = requests.get(url, timeout=10)
@@ -114,7 +115,6 @@ class _Network(QThread):
self.error_message = str(e)
self.loop.quit()
print("quited-----")
Network = _Network()

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -29,10 +29,12 @@ from loguru import logger
from PySide6.QtCore import QThread, QObject, Signal
from qfluentwidgets import MessageBox
from datetime import datetime
from packaging import version
from typing import Dict, Union
from .config import Config
from .main_info_bar import MainInfoBar
from .network import Network
from app.models import MaaManager
from app.services import System
@@ -40,6 +42,7 @@ from app.services import System
class Task(QThread):
"""业务线程"""
check_maa_version = Signal(str)
push_info_bar = Signal(str, str, str, int)
question = Signal(str, str)
question_response = Signal(bool)
@@ -77,6 +80,7 @@ class Task(QThread):
Config.member_dict[self.name],
(None if "全局" in self.mode else self.info["SetMaaInfo"]["Path"]),
)
self.task.check_maa_version.connect(self.check_maa_version.emit)
self.task.push_info_bar.connect(self.push_info_bar.emit)
self.task.accomplish.connect(lambda: self.accomplish.emit([]))
@@ -132,6 +136,7 @@ class Task(QThread):
Config.member_dict[task[2]],
)
self.task.check_maa_version.connect(self.check_maa_version.emit)
self.task.question.connect(self.question.emit)
self.question_response.disconnect()
self.question_response.connect(self.task.question_response.emit)
@@ -149,6 +154,7 @@ class Task(QThread):
Config.running_list.remove(task[2])
task[1] = "完成"
self.update_task_list.emit(self.task_list)
logger.info(f"任务完成:{task[0]}")
self.push_info_bar.emit("info", "任务完成", task[0], 3000)
@@ -166,12 +172,10 @@ class _TaskManager(QObject):
create_gui = Signal(Task)
connect_gui = Signal(Task)
push_info_bar = Signal(str, str, str, int)
def __init__(self, main_window=None):
def __init__(self):
super(_TaskManager, self).__init__()
self.main_window = main_window
self.task_dict: Dict[str, Task] = {}
def add_task(
@@ -190,6 +194,7 @@ class _TaskManager(QObject):
Config.running_list.append(name)
self.task_dict[name] = Task(mode, name, info)
self.task_dict[name].check_maa_version.connect(self.check_maa_version)
self.task_dict[name].question.connect(
lambda title, content: self.push_dialog(name, title, content)
)
@@ -260,33 +265,52 @@ class _TaskManager(QObject):
Config.queue_dict[name]["Config"].get(
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
)
!= "None"
!= "NoAction"
and Config.power_sign == "NoAction"
):
from app.ui import ProgressRingMessageBox
mode_book = {
"Shutdown": "关机",
"Hibernate": "休眠",
"Sleep": "睡眠",
"KillSelf": "关闭AUTO_MAA",
}
choice = ProgressRingMessageBox(
self.main_window,
f"{mode_book[Config.queue_dict[name]["Config"].get(Config.queue_dict[name]["Config"].queueSet_AfterAccomplish)]}倒计时",
)
if choice.exec():
System.set_power(
Config.queue_dict[name]["Config"].get(
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
)
Config.set_power_sign(
Config.queue_dict[name]["Config"].get(
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
)
)
def check_maa_version(self, v: str):
"""检查MAA版本"""
Network.set_info(
mode="get",
url="https://mirrorchyan.com/api/resources/MAA/latest?user_agent=AutoMaaGui&os=win&arch=x64&channel=stable",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
maa_info = Network.response_json
else:
logger.warning(f"获取MAA版本信息时出错{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取MAA版本信息时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
if version.parse(maa_info["data"]["version_name"]) > version.parse(v):
logger.info(
f"检测到MAA版本过低{v},最新版本:{maa_info['data']['version_name']}"
)
MainInfoBar.push_info_bar(
"info",
"MAA版本过低",
f"当前版本:{v},最新稳定版:{maa_info['data']['version_name']}",
-1,
)
def push_dialog(self, name: str, title: str, content: str):
"""推送对话框"""
choice = MessageBox(title, content, self.main_window)
choice = MessageBox(title, content, Config.main_window)
choice.yesButton.setText("")
choice.cancelButton.setText("")

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -46,6 +46,7 @@ class _MainTimer(QWidget):
self.Timer = QTimer()
self.Timer.timeout.connect(self.timed_start)
self.Timer.timeout.connect(self.set_silence)
self.Timer.timeout.connect(self.check_power)
self.Timer.start(1000)
self.LongTimer = QTimer()
self.LongTimer.timeout.connect(self.long_timed_task)
@@ -55,6 +56,9 @@ class _MainTimer(QWidget):
"""长时间定期检定任务"""
Config.get_gameid()
Config.main_window.setting.show_notice()
if Config.get(Config.update_IfAutoUpdate):
Config.main_window.setting.check_update()
def timed_start(self):
"""定时启动代理任务"""
@@ -110,5 +114,27 @@ class _MainTimer(QWidget):
logger.warning(f"FailSafeException: {e}")
self.if_FailSafeException = True
def check_power(self):
if Config.power_sign != "NoAction" and not Config.running_list:
from app.ui import ProgressRingMessageBox
mode_book = {
"KillSelf": "退出软件",
"Sleep": "睡眠",
"Hibernate": "休眠",
"Shutdown": "关机",
}
choice = ProgressRingMessageBox(
Config.main_window, f"{mode_book[Config.power_sign]}倒计时"
)
if choice.exec():
System.set_power(Config.power_sign)
Config.set_power_sign("NoAction")
else:
Config.set_power_sign("NoAction")
MainTimer = _MainTimer()

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -28,10 +28,12 @@ v4.3
from loguru import logger
from PySide6.QtCore import QObject, Signal, QEventLoop, QFileSystemWatcher, QTimer
import json
from datetime import datetime, timedelta
import subprocess
import shutil
import time
import re
import win32com.client
from datetime import datetime, timedelta
from pathlib import Path
from jinja2 import Environment, FileSystemLoader
from typing import Union, List, Dict
@@ -43,6 +45,7 @@ from app.services import Notify, System
class MaaManager(QObject):
"""MAA控制器"""
check_maa_version = Signal(str)
question = Signal(str, str)
question_response = Signal(bool)
update_user_info = Signal(str, dict)
@@ -87,6 +90,9 @@ class MaaManager(QObject):
self.interrupt.connect(self.quit_monitor)
self.maa_version = None
self.maa_update_package = ""
self.task_dict = {}
self.set = config["Config"].toDict()
self.data = {}
@@ -107,12 +113,12 @@ 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"
self.maa_tasks_path = self.maa_root_path / "resource/tasks/tasks.json"
def run(self):
"""主进程运行MAA代理进程"""
curdate = Config.server_date()
curdate = Config.server_date().strftime("%Y-%m-%d")
begin_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.configure()
@@ -259,6 +265,67 @@ class MaaManager(QObject):
]
)
# 解析任务构成
if user_data["Info"]["Mode"] == "简洁":
if mode == "Annihilation":
self.task_dict = {
"WakeUp": "True",
"Recruiting": "False",
"Base": "False",
"Combat": "True",
"Mission": "False",
"Mall": "False",
"AutoRoguelike": "False",
"Reclamation": "False",
}
elif mode == "Routine":
self.task_dict = {
"WakeUp": "True",
"Recruiting": "True",
"Base": "True",
"Combat": "True",
"Mission": "True",
"Mall": "True",
"AutoRoguelike": "False",
"Reclamation": "False",
}
elif user_data["Info"]["Mode"] == "详细":
with (self.data[user[2]]["Path"] / f"{mode}/gui.json").open(
mode="r", encoding="utf-8"
) as f:
data = json.load(f)
self.task_dict = {
"WakeUp": data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
],
"Recruiting": data["Configurations"]["Default"][
"TaskQueue.Recruiting.IsChecked"
],
"Base": data["Configurations"]["Default"][
"TaskQueue.Base.IsChecked"
],
"Combat": data["Configurations"]["Default"][
"TaskQueue.Combat.IsChecked"
],
"Mission": data["Configurations"]["Default"][
"TaskQueue.Mission.IsChecked"
],
"Mall": data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked"
],
"AutoRoguelike": data["Configurations"]["Default"][
"TaskQueue.AutoRoguelike.IsChecked"
],
"Reclamation": data["Configurations"]["Default"][
"TaskQueue.Reclamation.IsChecked"
],
}
# 尝试次数循环
for i in range(self.set["RunSet"]["RunTimesLimit"]):
@@ -284,9 +351,49 @@ class MaaManager(QObject):
self.emulator_arguments = set["Configurations"]["Default"][
"Start.EmulatorAddCommand"
].split()
# 如果是快捷方式,进行解析
if (
self.emulator_path.suffix == ".lnk"
and self.emulator_path.exists()
):
try:
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortcut(str(self.emulator_path))
self.emulator_path = Path(shortcut.TargetPath)
self.emulator_arguments = shortcut.Arguments.split()
except Exception as e:
logger.error(
f"{self.name} | 解析快捷方式时出现异常:{e}"
)
self.push_info_bar.emit(
"error",
"解析快捷方式时出现异常",
"请检查快捷方式",
-1,
)
self.if_open_emulator = True
break
elif not self.emulator_path.exists():
logger.error(
f"{self.name} | 模拟器快捷方式不存在:{self.emulator_path}"
)
self.push_info_bar.emit(
"error",
"启动模拟器时出现异常",
"模拟器快捷方式不存在",
-1,
)
self.if_open_emulator = True
break
self.ADB_path = Path(
set["Configurations"]["Default"]["Connect.AdbPath"]
)
self.ADB_path = (
self.ADB_path
if self.ADB_path.is_absolute()
else self.maa_root_path / self.ADB_path
)
self.ADB_address = set["Configurations"]["Default"][
"Connect.Address"
]
@@ -300,27 +407,44 @@ class MaaManager(QObject):
]
== "True"
)
# 添加静默进程标记
Config.silence_list.append(self.emulator_path)
# 增强任务:任务开始前强杀ADB
if "ADB" in self.set["RunSet"]["EnhanceTask"]:
System.kill_process(self.ADB_path)
else:
try:
subprocess.run(
[self.ADB_path, "disconnect", self.ADB_address],
creationflags=subprocess.CREATE_NO_WINDOW,
)
except subprocess.CalledProcessError as e:
# 忽略错误,因为可能本来就没有连接
logger.warning(f"{self.name} | 释放ADB时出现异常{e}")
if self.if_open_emulator_process:
self.emulator_process = subprocess.Popen(
[self.emulator_path, *self.emulator_arguments],
# 任务开始前释放ADB
try:
subprocess.run(
[self.ADB_path, "disconnect", self.ADB_address],
creationflags=subprocess.CREATE_NO_WINDOW,
)
except subprocess.CalledProcessError as e:
# 忽略错误,因为可能本来就没有连接
logger.warning(f"{self.name} | 释放ADB时出现异常{e}")
except Exception as e:
logger.error(f"{self.name} | 释放ADB时出现异常{e}")
self.push_info_bar.emit(
"error",
"释放ADB时出现异常",
"请检查MAA中ADB路径设置",
-1,
)
if self.if_open_emulator_process:
try:
self.emulator_process = subprocess.Popen(
[self.emulator_path, *self.emulator_arguments],
creationflags=subprocess.CREATE_NO_WINDOW,
)
except Exception as e:
logger.error(f"{self.name} | 启动模拟器时出现异常:{e}")
self.push_info_bar.emit(
"error",
"启动模拟器时出现异常",
"请检查MAA中模拟器路径设置",
-1,
)
self.if_open_emulator = True
break
# 添加静默进程标记
Config.silence_list.append(self.emulator_path)
# 创建MAA任务
maa = subprocess.Popen(
@@ -331,13 +455,40 @@ class MaaManager(QObject):
self.start_monitor(start_time, mode_book[mode])
if self.maa_result == "Success!":
# 标记任务完成
run_book[mode] = True
# 从配置文件中解析所需信息
with self.maa_set_path.open(
mode="r", encoding="utf-8"
) as f:
data = json.load(f)
# 记录自定义基建索引
user_data["Data"]["CustomInfrastPlanIndex"] = data[
"Configurations"
]["Default"]["Infrast.CustomInfrastPlanIndex"]
# 记录更新包路径
if (
data["Global"]["VersionUpdate.package"]
and (
self.maa_root_path
/ data["Global"]["VersionUpdate.package"]
).exists()
):
self.maa_update_package = data["Global"][
"VersionUpdate.package"
]
logger.info(
f"{self.name} | 用户: {user[0]} - MAA进程完成代理任务"
)
run_book[mode] = True
self.update_log_text.emit(
"检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s"
)
for _ in range(10):
if self.isInterruptionRequested:
break
@@ -354,13 +505,30 @@ class MaaManager(QObject):
# 无命令行中止MAA与其子程序
System.kill_process(self.maa_exe_path)
if "Emulator" in self.set["RunSet"]["EnhanceTask"]:
System.kill_process(self.emulator_path)
else:
self.emulator_process.terminate()
self.emulator_process.wait()
# 中止模拟器进程
self.emulator_process.terminate()
self.emulator_process.wait()
self.if_open_emulator = True
# 从配置文件中解析所需信息
with self.maa_set_path.open(
mode="r", encoding="utf-8"
) as f:
data = json.load(f)
# 记录更新包路径
if (
data["Global"]["VersionUpdate.package"]
and (
self.maa_root_path
/ data["Global"]["VersionUpdate.package"]
).exists()
):
self.maa_update_package = data["Global"][
"VersionUpdate.package"
]
# 推送异常通知
Notify.push_plyer(
"用户自动代理出现异常!",
@@ -376,26 +544,52 @@ class MaaManager(QObject):
# 移除静默进程标记
Config.silence_list.remove(self.emulator_path)
# 增强任务:任务结束后强杀ADB和模拟器或释放进程
if "ADB" in self.set["RunSet"]["EnhanceTask"]:
System.kill_process(self.ADB_path)
else:
try:
subprocess.run(
[self.ADB_path, "disconnect", self.ADB_address],
creationflags=subprocess.CREATE_NO_WINDOW,
)
except subprocess.CalledProcessError as e:
# 忽略错误,因为可能本来就没有连接
logger.warning(f"{self.name} | 释放ADB时出现异常{e}")
# 任务结束后释放ADB
try:
subprocess.run(
[self.ADB_path, "disconnect", self.ADB_address],
creationflags=subprocess.CREATE_NO_WINDOW,
)
except subprocess.CalledProcessError as e:
# 忽略错误,因为可能本来就没有连接
logger.warning(f"{self.name} | 释放ADB时出现异常{e}")
except Exception as e:
logger.error(f"{self.name} | 释放ADB时出现异常{e}")
self.push_info_bar.emit(
"error",
"释放ADB时出现异常",
"请检查MAA中ADB路径设置",
-1,
)
# 任务结束后再次手动中止模拟器进程,防止退出不彻底
if self.if_kill_emulator:
if "Emulator" in self.set["RunSet"]["EnhanceTask"]:
System.kill_process(self.emulator_path)
else:
self.emulator_process.terminate()
self.emulator_process.wait()
self.emulator_process.terminate()
self.emulator_process.wait()
self.if_open_emulator = True
# 执行MAA解压更新动作
if self.maa_update_package:
logger.info(
f"{self.name} | 检测到MAA更新正在执行更新动作"
)
self.update_log_text.emit(
f"检测到MAA存在更新\nMAA正在执行更新动作\n请等待10s"
)
self.set_maa("更新MAA", None)
subprocess.Popen(
[self.maa_exe_path],
creationflags=subprocess.CREATE_NO_WINDOW,
)
for _ in range(10):
if self.isInterruptionRequested:
break
time.sleep(1)
System.kill_process(self.maa_exe_path)
logger.info(f"{self.name} | 更新动作结束")
# 记录剿灭情况
if (
mode == "Annihilation"
@@ -710,12 +904,27 @@ class MaaManager(QObject):
else:
self.update_log_text.emit("".join(logs))
# 获取MAA版本号
if not self.set["RunSet"]["AutoUpdateMaa"] and not self.maa_version:
section_match = re.search(r"={35}(.*?)={35}", log, re.DOTALL)
if section_match:
version_match = re.search(
r"Version\s+v(\d+\.\d+\.\d+(?:-\w+\.\d+)?)", section_match.group(1)
)
if version_match:
self.maa_version = f"v{version_match.group(1)}"
self.check_maa_version.emit(self.maa_version)
if "自动代理" in mode:
# 获取最近一条日志的时间
latest_time = start_time
for _ in logs[::-1]:
try:
if "如果长时间无进一步日志更新,可能需要手动干预。" in _:
continue
latest_time = datetime.strptime(_[1:20], "%Y-%m-%d %H:%M:%S")
break
except ValueError:
@@ -731,38 +940,67 @@ class MaaManager(QObject):
else:
self.weekly_annihilation_limit_reached = False
if mode == "自动代理_日常" and "任务出错: Fight" in log:
self.maa_result = "检测到MAA未能实际执行任务"
elif "任务出错: StartUp" in log:
self.maa_result = "检测到MAA未能正确登录PRTS"
if "任务出错: StartUp" in log:
self.maa_result = "MAA未能正确登录PRTS"
elif "任务已全部完成!" in log:
self.maa_result = "Success!"
elif (
("请「检查连接设置」或「尝试重启模拟器与 ADB」或「重启电脑」" in log)
or ("未检测到任何模拟器" in log)
or ("已停止" in log)
or ("MaaAssistantArknights GUI exited" in log)
):
self.maa_result = "检测到MAA进程异常"
if "完成任务: StartUp" in log:
self.task_dict["WakeUp"] = "False"
if "完成任务: Recruit" in log:
self.task_dict["Recruiting"] = "False"
if "完成任务: Infrast" in log:
self.task_dict["Base"] = "False"
if "完成任务: Fight" in log or "剿灭任务失败" in log:
self.task_dict["Combat"] = "False"
if "完成任务: Mall" in log:
self.task_dict["Mall"] = "False"
if "完成任务: Award" in log:
self.task_dict["Mission"] = "False"
if "完成任务: Roguelike" in log:
self.task_dict["AutoRoguelike"] = "False"
if "完成任务: Reclamation" in log:
self.task_dict["Reclamation"] = "False"
if all(v == "False" for v in self.task_dict.values()):
self.maa_result = "Success!"
else:
self.maa_result = "MAA部分任务执行失败"
elif "请 「检查连接设置」 → 「尝试重启模拟器与 ADB」 → 「重启电脑」" in log:
self.maa_result = "MAA的ADB连接异常"
elif "未检测到任何模拟器" in log:
self.maa_result = "MAA未检测到任何模拟器"
elif "已停止" in log:
self.maa_result = "MAA在完成任务前中止"
elif "MaaAssistantArknights GUI exited" in log:
self.maa_result = "MAA在完成任务前退出"
elif datetime.now() - latest_time > timedelta(
minutes=self.set["RunSet"][time_book[mode]]
):
self.maa_result = "检测到MAA进程超时"
self.maa_result = "MAA进程超时"
elif self.isInterruptionRequested:
self.maa_result = "任务被手动中止"
else:
self.maa_result = "Wait"
elif mode == "人工排查":
if "完成任务: StartUp" in log:
self.maa_result = "Success!"
elif (
("请「检查连接设置」或「尝试重启模拟器与 ADB」或「重启电脑」" in log)
or ("未检测到任何模拟器" in log)
or ("已停止" in log)
or ("MaaAssistantArknights GUI exited" in log)
):
self.maa_result = "检测到MAA进程异常"
elif "请 「检查连接设置」 → 「尝试重启模拟器与 ADB」 → 「重启电脑」" in log:
self.maa_result = "MAA的ADB连接异常"
elif "未检测到任何模拟器" in log:
self.maa_result = "MAA未检测到任何模拟器"
elif "已停止" in log:
self.maa_result = "MAA在完成任务前中止"
elif "MaaAssistantArknights GUI exited" in log:
self.maa_result = "MAA在完成任务前退出"
elif self.isInterruptionRequested:
self.maa_result = "任务被手动中止"
else:
@@ -808,7 +1046,7 @@ class MaaManager(QObject):
"""配置MAA运行参数"""
logger.info(f"{self.name} | 配置MAA运行参数: {mode}/{index}")
if "设置MAA" not in self.mode:
if "设置MAA" not in self.mode and "更新MAA" not in mode:
user_data = self.data[index]["Config"]
# 配置MAA前关闭可能未正常退出的MAA进程
@@ -823,7 +1061,7 @@ class MaaManager(QObject):
self.config_path / "Default/gui.json",
self.maa_set_path,
)
elif (mode == "设置MAA_全局") or (
elif (mode in ["设置MAA_全局", "更新MAA"]) or (
("自动代理" in mode or "人工排查" in mode)
and user_data["Info"]["Mode"] == "简洁"
):
@@ -850,7 +1088,7 @@ 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 (
if ("设置MAA" not in self.mode and "更新MAA" not in mode) and (
(
user_data["Info"]["Mode"] == "简洁"
and user_data["Info"]["Server"] == "Bilibili"
@@ -898,10 +1136,20 @@ class MaaManager(QObject):
data["Configurations"]["Default"][
"Start.RunDirectly"
] = "True" # 启动MAA后直接运行
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = (
"True" if self.if_open_emulator else "False"
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = str(
self.if_open_emulator
) # 启动MAA后自动开启模拟器
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"]["VersionUpdate.AutoDownloadUpdatePackage"] = str(
self.set["RunSet"]["AutoUpdateMaa"]
) # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "False" # 自动安装更新包
if Config.get(Config.function_IfSilence):
data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化
data["Global"]["GUI.UseTray"] = "True" # 显示托盘图标
@@ -919,49 +1167,53 @@ class MaaManager(QObject):
"Info"
]["Id"]
# 按预设设定任务
data["Configurations"]["Default"]["TaskQueue.Recruiting.IsChecked"] = (
self.task_dict["Recruiting"]
) # 自动公招
data["Configurations"]["Default"]["TaskQueue.Base.IsChecked"] = (
self.task_dict["Base"]
) # 基建换班
data["Configurations"]["Default"]["TaskQueue.Combat.IsChecked"] = (
self.task_dict["Combat"]
) # 刷理智
data["Configurations"]["Default"]["TaskQueue.Mission.IsChecked"] = (
self.task_dict["Mission"]
) # 领取奖励
data["Configurations"]["Default"]["TaskQueue.Mall.IsChecked"] = (
self.task_dict["Mall"]
) # 获取信用及购物
data["Configurations"]["Default"]["TaskQueue.AutoRoguelike.IsChecked"] = (
self.task_dict["AutoRoguelike"]
) # 自动肉鸽
data["Configurations"]["Default"]["TaskQueue.Reclamation.IsChecked"] = (
self.task_dict["Reclamation"]
) # 生息演算
if user_data["Info"]["Mode"] == "简洁":
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"][
"VersionUpdate.AutoDownloadUpdatePackage"
] = "False" # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "False" # 自动安装更新包
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "True" # 开始唤醒
data["Configurations"]["Default"]["Start.ClientType"] = user_data[
"Info"
][
"Server"
] # 客户端类型
# 整理任务顺序
data["Configurations"]["Default"]["TaskQueue.Order.WakeUp"] = "0"
data["Configurations"]["Default"]["TaskQueue.Order.Recruiting"] = "1"
data["Configurations"]["Default"]["TaskQueue.Order.Base"] = "2"
data["Configurations"]["Default"]["TaskQueue.Order.Combat"] = "3"
data["Configurations"]["Default"]["TaskQueue.Order.Mall"] = "4"
data["Configurations"]["Default"]["TaskQueue.Order.Mission"] = "5"
data["Configurations"]["Default"]["TaskQueue.Order.AutoRoguelike"] = "6"
data["Configurations"]["Default"]["TaskQueue.Order.Reclamation"] = "7"
if "剿灭" in mode:
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "True" # 开始唤醒
data["Configurations"]["Default"][
"TaskQueue.Recruiting.IsChecked"
] = "False" # 自动公招
data["Configurations"]["Default"][
"TaskQueue.Base.IsChecked"
] = "False" # 基建换班
data["Configurations"]["Default"][
"TaskQueue.Combat.IsChecked"
] = "True" # 刷理智
data["Configurations"]["Default"][
"TaskQueue.Mission.IsChecked"
] = "False" # 领取奖励
data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked"
] = "False" # 获取信用及购物
data["Configurations"]["Default"][
"TaskQueue.AutoRoguelike.IsChecked"
] = "False" # 自动肉鸽
data["Configurations"]["Default"][
"TaskQueue.Reclamation.IsChecked"
] = "False" # 生息演算
data["Configurations"]["Default"][
"MainFunction.Stage1"
] = "Annihilation" # 主关卡
@@ -998,31 +1250,6 @@ class MaaManager(QObject):
elif "日常" in mode:
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "True" # 开始唤醒
data["Configurations"]["Default"][
"TaskQueue.Recruiting.IsChecked"
] = "True" # 自动公招
data["Configurations"]["Default"][
"TaskQueue.Base.IsChecked"
] = "True" # 基建换班
data["Configurations"]["Default"][
"TaskQueue.Combat.IsChecked"
] = "True" # 刷理智
data["Configurations"]["Default"][
"TaskQueue.Mission.IsChecked"
] = "True" # 领取奖励
data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked"
] = "True" # 获取信用及购物
data["Configurations"]["Default"][
"TaskQueue.AutoRoguelike.IsChecked"
] = "False" # 自动肉鸽
data["Configurations"]["Default"][
"TaskQueue.Reclamation.IsChecked"
] = "False" # 生息演算
data["Configurations"]["Default"]["MainFunction.UseMedicine"] = (
"False" if user_data["Info"]["MedicineNumb"] == 0 else "True"
) # 吃理智药
@@ -1046,55 +1273,47 @@ class MaaManager(QObject):
if user_data["Info"]["GameId_2"] != "-"
else ""
) # 备选关卡2
data["Configurations"]["Default"]["Fight.RemainingSanityStage"] = (
user_data["Info"]["GameId_Remain"]
if user_data["Info"]["GameId_Remain"] != "-"
else ""
) # 剩余理智关卡
data["Configurations"]["Default"][
"Fight.RemainingSanityStage"
] = "" # 剩余理智关卡
# 连战次数
if user_data["Info"]["GameId"] == "1-7":
data["Configurations"]["Default"][
"MainFunction.Series.Quantity"
] = "6"
else:
data["Configurations"]["Default"][
"MainFunction.Series.Quantity"
] = "1"
"MainFunction.Series.Quantity"
] = user_data["Info"][
"SeriesNumb"
] # 连战次数
data["Configurations"]["Default"][
"Penguin.IsDrGrandet"
] = "False" # 博朗台模式
data["Configurations"]["Default"][
"GUI.CustomStageCode"
] = "True" # 手动输入关卡名
# 备选关卡
if (
user_data["Info"]["GameId_1"] == "-"
and user_data["Info"]["GameId_2"] == "-"
):
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "False"
else:
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "True"
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "True" # 备选关卡
data["Configurations"]["Default"][
"Fight.UseRemainingSanityStage"
] = "False" # 使用剩余理智
] = "True" # 使用剩余理智
data["Configurations"]["Default"][
"Fight.UseExpiringMedicine"
] = "True" # 无限吃48小时内过期的理智药
# 自定义基建配置
if user_data["Info"]["Infrastructure"]:
if user_data["Info"]["InfrastMode"] == "Custom":
if (
self.data[index]["Path"]
/ "Infrastructure/infrastructure.json"
).exists():
data["Configurations"]["Default"][
"Infrast.CustomInfrastEnabled"
] = "True" # 启用自定义基建配置
"Infrast.InfrastMode"
] = "Custom" # 基建模式
data["Configurations"]["Default"][
"Infrast.CustomInfrastPlanIndex"
] = "1" # 自定义基建配置索引
] = user_data["Data"][
"CustomInfrastPlanIndex"
] # 自定义基建配置索引
data["Configurations"]["Default"][
"Infrast.DefaultInfrast"
] = "user_defined" # 内置配置
@@ -1119,11 +1338,13 @@ class MaaManager(QObject):
)
data["Configurations"]["Default"][
"Infrast.CustomInfrastEnabled"
] = "False" # 禁用自定义基建配置
] = "Normal" # 基建模式
else:
data["Configurations"]["Default"][
"Infrast.CustomInfrastEnabled"
] = "False" # 禁用自定义基建配置
"Infrast.InfrastMode"
] = user_data["Info"][
"InfrastMode"
] # 基建模式
elif user_data["Info"]["Mode"] == "详细":
@@ -1156,19 +1377,33 @@ class MaaManager(QObject):
if user_data["Info"]["GameId_2"] != "-"
else ""
) # 备选关卡2
data["Configurations"]["Default"]["Fight.RemainingSanityStage"] = (
user_data["Info"]["GameId_Remain"]
if user_data["Info"]["GameId_Remain"] != "-"
else ""
) # 剩余理智关卡
data["Configurations"]["Default"][
"MainFunction.Series.Quantity"
] = user_data["Info"][
"SeriesNumb"
] # 连战次数
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "True" # 备选关卡
data["Configurations"]["Default"][
"Fight.UseRemainingSanityStage"
] = "True" # 使用剩余理智
# 备选关卡
# 基建模式
if (
user_data["Info"]["GameId_1"] == "-"
and user_data["Info"]["GameId_2"] == "-"
data["Configurations"]["Default"]["Infrast.InfrastMode"]
== "Custom"
):
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "False"
else:
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "True"
"Infrast.CustomInfrastPlanIndex"
] = user_data["Data"][
"CustomInfrastPlanIndex"
] # 自定义基建配置索引
# 人工排查配置
elif "人工排查" in mode:
@@ -1186,8 +1421,8 @@ class MaaManager(QObject):
data["Global"]["GUI.UseTray"] = "True" # 显示托盘图标
data["Global"]["GUI.MinimizeToTray"] = "True" # 最小化时隐藏至托盘
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = (
"True" if self.if_open_emulator else "False"
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = str(
self.if_open_emulator
) # 启动MAA后自动开启模拟器
# 账号切换
@@ -1301,8 +1536,63 @@ class MaaManager(QObject):
"TaskQueue.Reclamation.IsChecked"
] = "False" # 生息演算
elif mode == "更新MAA":
data["Current"] = "Default" # 切换配置
for i in range(1, 9):
data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置
data["Configurations"]["Default"][
"MainFunction.PostActions"
] = "0" # 完成后无动作
data["Configurations"]["Default"][
"Start.RunDirectly"
] = "False" # 启动MAA后直接运行
data["Configurations"]["Default"][
"Start.OpenEmulatorAfterLaunch"
] = "False" # 启动MAA后自动开启模拟器
data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化
data["Global"]["GUI.UseTray"] = "True" # 显示托盘图标
data["Global"]["GUI.MinimizeToTray"] = "True" # 最小化时隐藏至托盘
data["Global"][
"VersionUpdate.package"
] = self.maa_update_package # 更新包路径
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"][
"VersionUpdate.AutoDownloadUpdatePackage"
] = "False" # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "True" # 自动安装更新包
data["Configurations"]["Default"][
"TaskQueue.WakeUp.IsChecked"
] = "False" # 开始唤醒
data["Configurations"]["Default"][
"TaskQueue.Recruiting.IsChecked"
] = "False" # 自动公招
data["Configurations"]["Default"][
"TaskQueue.Base.IsChecked"
] = "False" # 基建换班
data["Configurations"]["Default"][
"TaskQueue.Combat.IsChecked"
] = "False" # 刷理智
data["Configurations"]["Default"][
"TaskQueue.Mission.IsChecked"
] = "False" # 领取奖励
data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked"
] = "False" # 获取信用及购物
data["Configurations"]["Default"][
"TaskQueue.AutoRoguelike.IsChecked"
] = "False" # 自动肉鸽
data["Configurations"]["Default"][
"TaskQueue.Reclamation.IsChecked"
] = "False" # 生息演算
# 启动模拟器仅生效一次
if "设置MAA" not in mode and self.if_open_emulator:
if "设置MAA" not in mode and "更新MAA" not in mode and self.if_open_emulator:
self.if_open_emulator = False
# 覆写配置文件

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -37,9 +37,6 @@ from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.utils import formataddr
from serverchan_sdk import sc_send
from app.core import Config
from app.services.security import Crypto
@@ -143,76 +140,91 @@ class Notification(QWidget):
self.push_info_bar.emit("error", "发送邮件时出错", f"{e}", -1)
def ServerChanPush(self, title, content):
"""使用Server酱推送通知"""
"""使用Server酱推送通知(支持 tag 和 channel避免使用SDK"""
if Config.get(Config.notify_IfServerChan):
send_key = Config.get(Config.notify_ServerChanKey)
if Config.get(Config.notify_ServerChanKey) == "":
if not send_key:
logger.error("请正确设置Server酱的SendKey")
self.push_info_bar.emit(
"error",
"Server酱通知推送异常",
"请正确设置Server酱的SendKey",
-1,
"error", "Server酱通知推送异常", "请正确设置Server酱的SendKey", -1
)
return None
else:
send_key = Config.get(Config.notify_ServerChanKey)
option = {}
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 = "|".join(
_.strip() for _ in Config.get(Config.notify_ServerChanTag).split("|")
)
send_channel = "|".join(
_.strip()
for _ in Config.get(Config.notify_ServerChanChannel).split("|")
)
try:
# 构造 URL
if send_key.startswith("sctp"):
match = re.match(r"^sctp(\d+)t", send_key)
if match:
url = f"https://{match.group(1)}.push.ft07.com/send/{send_key}.send"
else:
raise ValueError("SendKey 格式错误sctp")
else:
url = f"https://sctapi.ftqq.com/{send_key}.send"
if is_valid(send_tag):
option["tags"] = send_tag
else:
option["tags"] = ""
logger.warning("请正确设置Auto_MAA中ServerChan的Tag。")
self.push_info_bar.emit(
"warning",
"Server酱通知推送异常",
"请正确设置Auto_MAA中ServerChan的Tag。",
-1,
# 构建 tags 和 channel
def is_valid(s):
return s == "" or (
s == "|".join(s.split("|"))
and (s.count("|") == 0 or all(s.split("|")))
)
tags = "|".join(
_.strip()
for _ in Config.get(Config.notify_ServerChanTag).split("|")
)
channels = "|".join(
_.strip()
for _ in Config.get(Config.notify_ServerChanChannel).split("|")
)
if is_valid(send_channel):
option["channel"] = send_channel
else:
option["channel"] = ""
logger.warning("请正确设置Auto_MAA中ServerChan的Channel。")
self.push_info_bar.emit(
"warning",
"Server酱通知推送异常",
"请正确设置Auto_MAA中ServerChan的Channel。",
-1,
)
options = {}
if is_valid(tags):
options["tags"] = tags
else:
logger.warning("Server酱 Tag 配置不正确,将被忽略")
self.push_info_bar.emit(
"warning",
"Server酱通知推送异常",
"请正确设置 ServerChan 的 Tag",
-1,
)
response = sc_send(send_key, title, content, option)
if response["code"] == 0:
logger.info("Server酱推送通知成功")
return True
else:
logger.info("Server酱推送通知失败")
logger.error(response)
if is_valid(channels):
options["channel"] = channels
else:
logger.warning("Server酱 Channel 配置不正确,将被忽略")
self.push_info_bar.emit(
"warning",
"Server酱通知推送异常",
"请正确设置 ServerChan 的 Channel",
-1,
)
# 请求发送
params = {"title": title, "desp": content, **options}
headers = {"Content-Type": "application/json;charset=utf-8"}
response = requests.post(url, json=params, headers=headers, timeout=10)
result = response.json()
if result.get("code") == 0:
logger.info("Server酱推送通知成功")
return True
else:
error_code = result.get("code", "-1")
logger.error(f"Server酱通知推送失败响应码{error_code}")
self.push_info_bar.emit(
"error", "Server酱通知推送失败", f"响应码:{error_code}", -1
)
return f"Server酱通知推送失败{error_code}"
except Exception as e:
logger.exception("Server酱通知推送异常")
self.push_info_bar.emit(
"error",
"Server酱通知推送失败",
f'使用Server酱推送通知时出错\n{response["data"]['error']}',
-1,
"error", "Server酱通知推送异常", f"请检查相关设置,如还有问题可联系开发者", -1
)
return f'使用Server酱推送通知时出错:\n{response["data"]['error']}'
return f"Server酱通知推送异常:{str(e)}"
def CompanyWebHookBotPush(self, title, content):
"""使用企业微信群机器人推送通知"""

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -26,7 +26,7 @@ v4.3
"""
from loguru import logger
from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QApplication
import sys
import ctypes
import win32gui
@@ -44,9 +44,7 @@ class _SystemHandler:
ES_CONTINUOUS = 0x80000000
ES_SYSTEM_REQUIRED = 0x00000001
def __init__(self, main_window: QWidget = None):
self.main_window = main_window
def __init__(self):
self.set_Sleep()
self.set_SelfStart()
@@ -89,7 +87,7 @@ class _SystemHandler:
if sys.platform.startswith("win"):
if mode == "None":
if mode == "NoAction":
logger.info("不执行系统电源操作")
@@ -112,11 +110,12 @@ class _SystemHandler:
elif mode == "KillSelf":
self.main_window.close()
Config.main_window.close()
QApplication.quit()
elif sys.platform.startswith("linux"):
if mode == "None":
if mode == "NoAction":
logger.info("不执行系统电源操作")
@@ -137,7 +136,8 @@ class _SystemHandler:
elif mode == "KillSelf":
self.main_window.close()
Config.main_window.close()
QApplication.quit()
def is_startup(self) -> bool:
"""判断程序是否已经开机自启"""

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -26,6 +26,7 @@ v4.3
"""
from PySide6.QtWidgets import (
QApplication,
QWidget,
QWidget,
QLabel,
@@ -60,6 +61,7 @@ from qfluentwidgets import (
TransparentToolButton,
TeachingTipTailPosition,
ExpandSettingCard,
ExpandGroupSettingCard,
ToolButton,
PushButton,
PrimaryPushButton,
@@ -69,6 +71,9 @@ from qfluentwidgets import (
SwitchButton,
IndicatorPosition,
Slider,
ScrollArea,
Pivot,
PivotItem,
)
from qfluentwidgets.common.overload import singledispatchmethod
import os
@@ -107,7 +112,14 @@ class LineEditMessageBox(MessageBoxBase):
class ComboBoxMessageBox(MessageBoxBase):
"""选择对话框"""
def __init__(self, parent, title: str, content: List[str], list: List[List[str]]):
def __init__(
self,
parent,
title: str,
content: List[str],
text_list: List[List[str]],
data_list: List[List[str]] = None,
):
super().__init__(parent)
self.title = SubtitleLabel(title)
@@ -119,7 +131,11 @@ class ComboBoxMessageBox(MessageBoxBase):
for i in range(len(content)):
self.input.append(ComboBox())
self.input[i].addItems(list[i])
if data_list:
for j in range(len(text_list[i])):
self.input[i].addItem(text_list[i][j], userData=data_list[i][j])
else:
self.input[i].addItems(text_list[i])
self.input[i].setCurrentIndex(-1)
self.input[i].setPlaceholderText(content[i])
Layout.addWidget(self.input[i])
@@ -543,7 +559,7 @@ class UserLableSettingCard(SettingCard):
text_list.append("未通过人工排查")
text_list.append(
f"今日已代理{self.qconfig.get(self.configItems["ProxyTimes"])}"
if Config.server_date()
if Config.server_date().strftime("%Y-%m-%d")
== self.qconfig.get(self.configItems["LastProxyDate"])
else "今日未进行代理"
)
@@ -553,7 +569,7 @@ class UserLableSettingCard(SettingCard):
self.qconfig.get(self.configItems["LastAnnihilationDate"]),
"%Y-%m-%d",
).isocalendar()[:2]
== datetime.strptime(Config.server_date(), "%Y-%m-%d").isocalendar()[:2]
== Config.server_date().isocalendar()[:2]
else "本周剿灭未完成"
)
@@ -608,6 +624,53 @@ class PushAndSwitchButtonSettingCard(SettingCard):
self.switchButton.setText("" if isChecked else "")
class PushAndComboBoxSettingCard(SettingCard):
"""Setting card with push & combo box"""
clicked = Signal()
def __init__(
self,
icon: Union[str, QIcon, FluentIconBase],
title: str,
content: Union[str, None],
text: str,
texts: List[str],
qconfig: QConfig,
configItem: OptionsConfigItem,
parent=None,
):
super().__init__(icon, title, content, parent)
self.qconfig = qconfig
self.configItem = configItem
self.comboBox = ComboBox(self)
self.button = PushButton(text, self)
self.hBoxLayout.addWidget(self.button, 0, Qt.AlignRight)
self.hBoxLayout.addWidget(self.comboBox, 0, Qt.AlignRight)
self.hBoxLayout.addSpacing(16)
self.button.clicked.connect(self.clicked)
self.optionToText = {o: t for o, t in zip(configItem.options, texts)}
for text, option in zip(texts, configItem.options):
self.comboBox.addItem(text, userData=option)
self.comboBox.setCurrentText(self.optionToText[self.qconfig.get(configItem)])
self.comboBox.currentIndexChanged.connect(self._onCurrentIndexChanged)
configItem.valueChanged.connect(self.setValue)
def _onCurrentIndexChanged(self, index: int):
self.qconfig.set(self.configItem, self.comboBox.itemData(index))
def setValue(self, value):
if value not in self.optionToText:
return
self.comboBox.setCurrentText(self.optionToText[value])
self.qconfig.set(self.configItem, value)
class SpinBoxSettingCard(SettingCard):
"""Setting card with SpinBox"""
@@ -1068,6 +1131,119 @@ class QuantifiedItemCard(CardWidget):
self.Layout.addWidget(self.Numb)
class PivotArea(ScrollArea):
def __init__(self, parent=None):
super().__init__(parent)
# 创建中间容器并设置布局
self.center_container = QWidget()
self.center_layout = QHBoxLayout(self.center_container)
self.center_layout.setContentsMargins(0, 0, 0, 0)
self.center_layout.setSpacing(0)
self.center_container.setStyleSheet("background: transparent; border: none;")
self.center_container.setFixedHeight(45)
self.pivot = self._Pivot(self)
self.pivot.ItemNumbChanged.connect(
lambda: QTimer.singleShot(
100,
lambda: (
self.center_container.setFixedWidth(
max(self.width() - 2, self.pivot.width())
)
),
)
)
self.center_layout.addStretch(1)
self.center_layout.addWidget(self.pivot)
self.center_layout.addStretch(1)
self.setWidgetResizable(False)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.viewport().setCursor(Qt.ArrowCursor)
self.setStyleSheet("background: transparent; border: none;")
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.setWidget(self.center_container)
def wheelEvent(self, event):
scroll_bar = self.horizontalScrollBar()
if scroll_bar.maximum() > 0:
delta = event.angleDelta().y()
scroll_bar.setValue(scroll_bar.value() - delta // 15)
event.ignore()
def resizeEvent(self, event):
super().resizeEvent(event)
self.center_container.setFixedWidth(max(self.width() - 2, self.pivot.width()))
QTimer.singleShot(
100,
lambda: (
self.center_container.setFixedWidth(
max(self.width() - 2, self.pivot.width())
)
),
)
class _Pivot(Pivot):
ItemNumbChanged = Signal()
def __init__(self, parent=None):
super().__init__(parent)
def insertWidget(
self, index: int, routeKey: str, widget: PivotItem, onClick=None
):
super().insertWidget(index, routeKey, widget, onClick)
self.ItemNumbChanged.emit()
def removeWidget(self, routeKey: str):
super().removeWidget(routeKey)
self.ItemNumbChanged.emit()
def clear(self):
super().clear()
self.ItemNumbChanged.emit()
class QuickExpandGroupCard(ExpandGroupSettingCard):
"""全局配置"""
def __init__(
self,
icon: Union[str, QIcon, FluentIcon],
title: str,
content: str = None,
parent=None,
):
super().__init__(icon, title, content, parent)
def setExpand(self, isExpand: bool):
"""set the expand status of card"""
if self.isExpand == isExpand:
return
# update style sheet
self.isExpand = isExpand
self.setProperty("isExpand", isExpand)
self.setStyle(QApplication.style())
# start expand animation
if isExpand:
h = self.viewLayout.sizeHint().height()
self.verticalScrollBar().setValue(h)
self.expandAni.setStartValue(h)
self.expandAni.setEndValue(0)
self.expandAni.start()
else:
self.setFixedHeight(self.viewportMargins().top())
self.card.expandButton.setExpand(isExpand)
class IconButton(TransparentToolButton):
"""包含下拉框的自定义设置卡片类。"""

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -33,8 +33,8 @@ from PySide6.QtWidgets import (
QHBoxLayout,
)
from qfluentwidgets import (
BodyLabel,
CardWidget,
Pivot,
ScrollArea,
FluentIcon,
HeaderCardWidget,
@@ -44,13 +44,12 @@ from qfluentwidgets import (
SubtitleLabel,
PushButton,
)
from PySide6.QtCore import Qt
from PySide6.QtGui import QTextCursor
from typing import List, Dict
from app.core import Config, TaskManager, Task, MainInfoBar
from .Widget import StatefulItemCard
from .Widget import StatefulItemCard, ComboBoxMessageBox, PivotArea
class DispatchCenter(QWidget):
@@ -60,13 +59,29 @@ class DispatchCenter(QWidget):
self.setObjectName("调度中枢")
self.pivot = Pivot(self)
self.multi_button = PushButton(FluentIcon.ADD, "添加任务", self)
self.multi_button.setToolTip("添加任务")
self.multi_button.clicked.connect(self.start_multi_task)
self.power_combox = ComboBox()
self.power_combox.addItem("无动作", userData="NoAction")
self.power_combox.addItem("退出软件", userData="KillSelf")
self.power_combox.addItem("睡眠", userData="Sleep")
self.power_combox.addItem("休眠", userData="Hibernate")
self.power_combox.addItem("关机", userData="Shutdown")
self.power_combox.setCurrentText("无动作")
self.power_combox.currentIndexChanged.connect(self.set_power_sign)
self.pivotArea = PivotArea(self)
self.pivot = self.pivotArea.pivot
self.stackedWidget = QStackedWidget(self)
self.Layout = QVBoxLayout(self)
self.stackedWidget.setContentsMargins(0, 0, 0, 0)
self.stackedWidget.setStyleSheet("background: transparent; border: none;")
self.script_list: Dict[str, DispatchBox] = {}
self.script_list: Dict[str, DispatchCenter.DispatchBox] = {}
dispatch_box = DispatchBox("主调度台", self)
dispatch_box = self.DispatchBox("主调度台", self)
self.script_list["主调度台"] = dispatch_box
self.stackedWidget.addWidget(self.script_list["主调度台"])
self.pivot.addItem(
@@ -76,7 +91,15 @@ class DispatchCenter(QWidget):
icon=FluentIcon.CAFE,
)
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
h_layout = QHBoxLayout()
h_layout.addWidget(self.multi_button)
h_layout.addWidget(self.pivotArea)
h_layout.addWidget(BodyLabel("全部完成后", self))
h_layout.addWidget(self.power_combox)
h_layout.setContentsMargins(11, 5, 11, 0)
self.Layout = QVBoxLayout(self)
self.Layout.addLayout(h_layout)
self.Layout.addWidget(self.stackedWidget)
self.Layout.setContentsMargins(0, 0, 0, 0)
@@ -87,9 +110,9 @@ class DispatchCenter(QWidget):
def add_board(self, task: Task) -> None:
"""添加一个调度台界面"""
dispatch_box = DispatchBox(task.name, self)
dispatch_box = self.DispatchBox(task.name, self)
dispatch_box.top_bar.button.clicked.connect(
dispatch_box.top_bar.main_button.clicked.connect(
lambda: TaskManager.stop_task(task.name)
)
@@ -123,9 +146,9 @@ class DispatchCenter(QWidget):
self.script_list["主调度台"].top_bar.Lable.show()
self.script_list["主调度台"].top_bar.object.hide()
self.script_list["主调度台"].top_bar.mode.hide()
self.script_list["主调度台"].top_bar.button.clicked.disconnect()
self.script_list["主调度台"].top_bar.button.setText("中止任务")
self.script_list["主调度台"].top_bar.button.clicked.connect(
self.script_list["主调度台"].top_bar.main_button.clicked.disconnect()
self.script_list["主调度台"].top_bar.main_button.setText("中止任务")
self.script_list["主调度台"].top_bar.main_button.clicked.connect(
lambda: TaskManager.stop_task(task.name)
)
task.create_task_list.connect(
@@ -153,10 +176,10 @@ class DispatchCenter(QWidget):
self.script_list["主调度台"].top_bar.Lable.hide()
self.script_list["主调度台"].top_bar.object.show()
self.script_list["主调度台"].top_bar.mode.show()
self.script_list["主调度台"].top_bar.button.clicked.disconnect()
self.script_list["主调度台"].top_bar.button.setText("开始任务")
self.script_list["主调度台"].top_bar.button.clicked.connect(
self.script_list["主调度台"].top_bar.start_task
self.script_list["主调度台"].top_bar.main_button.clicked.disconnect()
self.script_list["主调度台"].top_bar.main_button.setText("开始任务")
self.script_list["主调度台"].top_bar.main_button.clicked.connect(
self.script_list["主调度台"].top_bar.start_main_task
)
if len(logs) > 0:
history = ""
@@ -206,229 +229,327 @@ class DispatchCenter(QWidget):
self.script_list["主调度台"].top_bar.mode.addItems(["自动代理", "人工排查"])
self.script_list["主调度台"].top_bar.mode.setCurrentIndex(0)
def update_power_sign(self) -> None:
"""更新电源设置"""
class DispatchBox(QWidget):
mode_book = {
"NoAction": "无动作",
"KillSelf": "退出软件",
"Sleep": "睡眠",
"Hibernate": "休眠",
"Shutdown": "关机",
}
self.power_combox.currentIndexChanged.disconnect()
self.power_combox.setCurrentText(mode_book[Config.power_sign])
self.power_combox.currentIndexChanged.connect(self.set_power_sign)
def __init__(self, name: str, parent=None):
super().__init__(parent)
def set_power_sign(self) -> None:
"""设置所有任务完成后动作"""
self.setObjectName(name)
if not Config.running_list:
layout = QVBoxLayout()
self.power_combox.currentIndexChanged.disconnect()
self.power_combox.setCurrentText("无动作")
self.power_combox.currentIndexChanged.connect(self.set_power_sign)
logger.warning("没有正在运行的任务,无法设置任务完成后动作")
MainInfoBar.push_info_bar(
"warning",
"没有正在运行的任务",
"无法设置任务完成后动作",
5000,
)
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
else:
content_widget = QWidget()
content_layout = QVBoxLayout(content_widget)
Config.set_power_sign(self.power_combox.currentData())
self.top_bar = self.DispatchTopBar(self, name)
self.info = self.DispatchInfoCard(self)
def start_multi_task(self) -> None:
"""开始任务"""
content_layout.addWidget(self.top_bar)
content_layout.addWidget(self.info)
# 获取所有可用的队列和实例
text_list = []
data_list = []
for name, info in Config.queue_dict.items():
if name in Config.running_list:
continue
text_list.append(
"队列"
if info["Config"].get(info["Config"].queueSet_Name) == ""
else f"队列 - {info["Config"].get(info["Config"].queueSet_Name)}"
)
data_list.append(name)
scrollArea.setWidget(content_widget)
for name, info in Config.member_dict.items():
if name in Config.running_list:
continue
text_list.append(
f"实例 - {info['Type']}"
if info["Config"].get(info["Config"].MaaSet_Name) == ""
else f"实例 - {info['Type']} - {info["Config"].get(info["Config"].MaaSet_Name)}"
)
data_list.append(name)
layout.addWidget(scrollArea)
choice = ComboBoxMessageBox(
self.window(),
"选择一个对象以添加相应多开任务",
["选择调度对象"],
[text_list],
[data_list],
)
self.setLayout(layout)
if choice.exec() and choice.input[0].currentIndex() != -1:
class DispatchTopBar(CardWidget):
if choice.input[0].currentData() in Config.running_list:
logger.warning(f"任务已存在:{choice.input[0].currentData()}")
MainInfoBar.push_info_bar(
"warning", "任务已存在", choice.input[0].currentData(), 5000
)
return None
def __init__(self, parent=None, name: str = None):
if "调度队列" in choice.input[0].currentData():
logger.info(f"用户添加任务:{choice.input[0].currentData()}")
TaskManager.add_task(
"自动代理_新调度台",
choice.input[0].currentData(),
Config.queue_dict[choice.input[0].currentData()]["Config"].toDict(),
)
elif "脚本" in choice.input[0].currentData():
if Config.member_dict[choice.input[0].currentData()]["Type"] == "Maa":
logger.info(f"用户添加任务:{choice.input[0].currentData()}")
TaskManager.add_task(
"自动代理_新调度台",
f"自定义队列 - {choice.input[0].currentData()}",
{"Queue": {"Member_1": choice.input[0].currentData()}},
)
class DispatchBox(QWidget):
def __init__(self, name: str, parent=None):
super().__init__(parent)
Layout = QHBoxLayout(self)
self.setObjectName(name)
if name == "主调度台":
layout = QVBoxLayout()
self.Lable = SubtitleLabel("", self)
self.Lable.hide()
self.object = ComboBox()
self.object.setPlaceholderText("请选择调度对象")
self.mode = ComboBox()
self.mode.setPlaceholderText("请选择调度模式")
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setContentsMargins(0, 0, 0, 0)
scrollArea.setStyleSheet("background: transparent; border: none;")
self.button = PushButton("开始任务")
self.button.clicked.connect(self.start_task)
content_widget = QWidget()
content_layout = QVBoxLayout(content_widget)
Layout.addWidget(self.Lable)
Layout.addWidget(self.object)
Layout.addWidget(self.mode)
Layout.addStretch(1)
Layout.addWidget(self.button)
self.top_bar = self.DispatchTopBar(self, name)
self.info = self.DispatchInfoCard(self)
else:
content_layout.addWidget(self.top_bar)
content_layout.addWidget(self.info)
self.Lable = SubtitleLabel(name, self)
self.button = PushButton("中止任务")
scrollArea.setWidget(content_widget)
Layout.addWidget(self.Lable)
Layout.addStretch(1)
Layout.addWidget(self.button)
layout.addWidget(scrollArea)
def start_task(self):
"""开始任务"""
self.setLayout(layout)
if self.object.currentIndex() == -1:
logger.warning("未选择调度对象")
MainInfoBar.push_info_bar(
"warning", "未选择调度对象", "请选择后再开始任务", 5000
)
return None
class DispatchTopBar(CardWidget):
if self.mode.currentIndex() == -1:
logger.warning("未选择调度模式")
MainInfoBar.push_info_bar(
"warning", "未选择调度模式", "请选择后再开始任务", 5000
)
return None
def __init__(self, parent=None, name: str = None):
super().__init__(parent)
if self.object.currentData() in Config.running_list:
logger.warning(f"任务已存在:{self.object.currentData()}")
MainInfoBar.push_info_bar(
"warning", "任务已存在", self.object.currentData(), 5000
)
return None
Layout = QHBoxLayout(self)
if "调度队列" in self.object.currentData():
if name == "调度":
logger.info(f"用户添加任务:{self.object.currentData()}")
TaskManager.add_task(
f"{self.mode.currentText()}_主调度台",
self.object.currentData(),
Config.queue_dict[self.object.currentData()]["Config"].toDict(),
)
self.Lable = SubtitleLabel("", self)
self.Lable.hide()
self.object = ComboBox()
self.object.setPlaceholderText("请选择调度对象")
self.mode = ComboBox()
self.mode.setPlaceholderText("请选择调度模式")
elif "脚本" in self.object.currentData():
self.main_button = PushButton("开始任务")
self.main_button.clicked.connect(self.start_main_task)
if Config.member_dict[self.object.currentData()]["Type"] == "Maa":
Layout.addWidget(self.Lable)
Layout.addWidget(self.object)
Layout.addWidget(self.mode)
Layout.addStretch(1)
Layout.addWidget(self.main_button)
else:
self.Lable = SubtitleLabel(name, self)
self.main_button = PushButton("中止任务")
Layout.addWidget(self.Lable)
Layout.addStretch(1)
Layout.addWidget(self.main_button)
def start_main_task(self):
"""开始任务"""
if self.object.currentIndex() == -1:
logger.warning("未选择调度对象")
MainInfoBar.push_info_bar(
"warning", "未选择调度对象", "请选择后再开始任务", 5000
)
return None
if self.mode.currentIndex() == -1:
logger.warning("未选择调度模式")
MainInfoBar.push_info_bar(
"warning", "未选择调度模式", "请选择后再开始任务", 5000
)
return None
if self.object.currentData() in Config.running_list:
logger.warning(f"任务已存在:{self.object.currentData()}")
MainInfoBar.push_info_bar(
"warning", "任务已存在", self.object.currentData(), 5000
)
return None
if "调度队列" in self.object.currentData():
logger.info(f"用户添加任务:{self.object.currentData()}")
TaskManager.add_task(
f"{self.mode.currentText()}_主调度台",
"自定义队列",
{"Queue": {"Member_1": self.object.currentData()}},
self.object.currentData(),
Config.queue_dict[self.object.currentData()]["Config"].toDict(),
)
class DispatchInfoCard(HeaderCardWidget):
elif "脚本" in self.object.currentData():
def __init__(self, parent=None):
super().__init__(parent)
if Config.member_dict[self.object.currentData()]["Type"] == "Maa":
self.setTitle("调度信息")
logger.info(f"用户添加任务:{self.object.currentData()}")
TaskManager.add_task(
f"{self.mode.currentText()}_主调度台",
"自定义队列",
{"Queue": {"Member_1": self.object.currentData()}},
)
self.task = self.TaskInfoCard(self)
self.user = self.UserInfoCard(self)
self.log_text = self.LogCard(self)
self.viewLayout.addWidget(self.task)
self.viewLayout.addWidget(self.user)
self.viewLayout.addWidget(self.log_text)
self.viewLayout.setStretch(0, 1)
self.viewLayout.setStretch(1, 1)
self.viewLayout.setStretch(2, 5)
def update_board(self, task_list: list, user_list: list, log: str):
"""更新调度信息"""
self.task.update_task(task_list)
self.user.update_user(user_list)
self.log_text.text.setText(log)
class TaskInfoCard(HeaderCardWidget):
class DispatchInfoCard(HeaderCardWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("任务队列")
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.setTitle("调度信息")
self.task_cards: List[StatefulItemCard] = []
self.task = self.TaskInfoCard(self)
self.user = self.UserInfoCard(self)
self.log_text = self.LogCard(self)
def create_task(self, task_list: list):
"""创建任务队列"""
self.viewLayout.addWidget(self.task)
self.viewLayout.addWidget(self.user)
self.viewLayout.addWidget(self.log_text)
while self.Layout.count() > 0:
item = self.Layout.takeAt(0)
if item.spacerItem():
self.Layout.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
self.viewLayout.setStretch(0, 1)
self.viewLayout.setStretch(1, 1)
self.viewLayout.setStretch(2, 5)
self.task_cards = []
def update_board(self, task_list: list, user_list: list, log: str):
"""更新调度信息"""
for task in task_list:
self.task.update_task(task_list)
self.user.update_user(user_list)
self.log_text.text.setText(log)
self.task_cards.append(StatefulItemCard(task))
self.Layout.addWidget(self.task_cards[-1])
class TaskInfoCard(HeaderCardWidget):
self.Layout.addStretch(1)
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("任务队列")
def update_task(self, task_list: list):
"""更新任务队列"""
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
for i in range(len(task_list)):
self.task_cards: List[StatefulItemCard] = []
self.task_cards[i].update_status(task_list[i][1])
def create_task(self, task_list: list):
"""创建任务队列"""
class UserInfoCard(HeaderCardWidget):
while self.Layout.count() > 0:
item = self.Layout.takeAt(0)
if item.spacerItem():
self.Layout.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("用户队列")
self.task_cards = []
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
for task in task_list:
self.user_cards: List[StatefulItemCard] = []
self.task_cards.append(StatefulItemCard(task))
self.Layout.addWidget(self.task_cards[-1])
def create_user(self, user_list: list):
"""创建用户队列"""
self.Layout.addStretch(1)
while self.Layout.count() > 0:
item = self.Layout.takeAt(0)
if item.spacerItem():
self.Layout.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
def update_task(self, task_list: list):
"""更新任务队列"""
self.user_cards = []
for i in range(len(task_list)):
for user in user_list:
self.task_cards[i].update_status(task_list[i][1])
self.user_cards.append(StatefulItemCard(user))
self.Layout.addWidget(self.user_cards[-1])
class UserInfoCard(HeaderCardWidget):
self.Layout.addStretch(1)
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("用户队列")
def update_user(self, user_list: list):
"""更新用户队列"""
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
for i in range(len(user_list)):
self.user_cards: List[StatefulItemCard] = []
self.user_cards[i].Label.setText(user_list[i][0])
self.user_cards[i].update_status(user_list[i][1])
def create_user(self, user_list: list):
"""创建用户队列"""
class LogCard(HeaderCardWidget):
while self.Layout.count() > 0:
item = self.Layout.takeAt(0)
if item.spacerItem():
self.Layout.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("日志")
self.user_cards = []
self.text = TextBrowser()
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.viewLayout.addWidget(self.text)
for user in user_list:
self.text.textChanged.connect(self.to_end)
self.user_cards.append(StatefulItemCard(user))
self.Layout.addWidget(self.user_cards[-1])
def to_end(self):
"""滚动到底部"""
self.Layout.addStretch(1)
self.text.moveCursor(QTextCursor.End)
self.text.ensureCursorVisible()
def update_user(self, user_list: list):
"""更新用户队列"""
for i in range(len(user_list)):
self.user_cards[i].Label.setText(user_list[i][0])
self.user_cards[i].update_status(user_list[i][1])
class LogCard(HeaderCardWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("日志")
self.text = TextBrowser()
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.viewLayout.addWidget(self.text)
self.text.textChanged.connect(self.to_end)
def to_end(self):
"""滚动到底部"""
self.text.moveCursor(QTextCursor.End)
self.text.ensureCursorVisible()

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,28 +16,24 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
AUTO_MAA更新器
v1.2
v4.3
作者DLmaster_361
"""
import sys
import json
import zipfile
import requests
import subprocess
import time
import win32crypt
import base64
from packaging import version
import psutil
from functools import partial
from pathlib import Path
from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout
from PySide6.QtWidgets import QDialog, QVBoxLayout
from qfluentwidgets import (
ProgressBar,
IndeterminateProgressBar,
@@ -45,7 +41,7 @@ from qfluentwidgets import (
setTheme,
Theme,
)
from PySide6.QtGui import QIcon, QCloseEvent
from PySide6.QtGui import QCloseEvent
from PySide6.QtCore import QThread, Signal, QTimer, QEventLoop
from typing import List, Dict, Union
@@ -180,7 +176,11 @@ class ZipExtractProcess(QThread):
self.accomplish.emit()
break
except PermissionError:
self.info.emit(f"解压出错:{self.name}正在运行,正在等待其关闭")
if self.name == "AUTO_MAA":
self.info.emit(f"解压出错AUTO_MAA正在运行正在尝试将其关闭")
self.kill_process(self.app_path / "AUTO_MAA.exe")
else:
self.info.emit(f"解压出错:{self.name}正在运行,正在等待其关闭")
time.sleep(1)
except Exception as e:
@@ -190,6 +190,30 @@ class ZipExtractProcess(QThread):
self.info.emit(f"解压更新时出错:\n{e}")
return None
def kill_process(self, path: Path) -> None:
"""根据路径中止进程"""
for pid in self.search_pids(path):
killprocess = subprocess.Popen(
f"taskkill /F /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
class DownloadManager(QDialog):
"""下载管理器"""
@@ -200,28 +224,17 @@ class DownloadManager(QDialog):
isInterruptionRequested = False
def __init__(
self,
app_path: Path,
name: str,
main_version: list,
config: dict,
) -> None:
def __init__(self, app_path: Path, name: str, version: list, config: dict) -> None:
super().__init__()
self.app_path = app_path
self.name = name
self.main_version = main_version
self.version = version
self.config = config
self.download_path = app_path / "DOWNLOAD_TEMP.zip" # 临时下载文件的路径
self.version_path = app_path / "resources/version.json"
self.download_process_dict: Dict[str, DownloadProcess] = {}
self.timer_dict: Dict[str, QTimer] = {}
self.setWindowTitle("AUTO_MAA更新器")
self.setWindowIcon(
QIcon(str(app_path / "resources/icons/AUTO_MAA_Updater.ico"))
)
self.resize(700, 70)
setTheme(Theme.AUTO, lazy=True)
@@ -243,14 +256,14 @@ class DownloadManager(QDialog):
def run(self) -> None:
if self.name == "MAA":
self.download_task1()
elif self.name == "AUTO_MAA":
if self.name == "AUTO_MAA":
if self.config["mode"] == "Proxy":
self.test_speed_task1()
self.speed_test_accomplish.connect(self.download_task1)
elif self.config["mode"] == "MirrorChyan":
self.download_task1()
elif self.config["mode"] == "MirrorChyan":
self.download_task1()
def get_download_url(self, mode: str) -> Union[str, Dict[str, str]]:
"""获取下载链接"""
@@ -260,26 +273,23 @@ class DownloadManager(QDialog):
if mode == "测速":
url_dict["GitHub站"] = (
f"https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
f"https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.version)}/AUTO_MAA_{version_text(self.version)}.zip"
)
url_dict["官方镜像站"] = (
f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.version)}/AUTO_MAA_{version_text(self.version)}.zip"
)
for name, download_url_head in self.config["download_dict"].items():
url_dict[name] = (
f"{download_url_head}AUTO_MAA_{version_text(self.main_version)}.zip"
f"{download_url_head}AUTO_MAA_{version_text(self.version)}.zip"
)
for proxy_url in self.config["proxy_list"]:
url_dict[proxy_url] = (
f"{proxy_url}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
f"{proxy_url}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.version)}/AUTO_MAA_{version_text(self.version)}.zip"
)
return url_dict
elif mode == "下载":
if self.name == "MAA":
return f"https://jp-download.fearr.xyz/MAA/MAA-{version_text(self.main_version)}-win-x64.zip"
if self.name == "AUTO_MAA":
if self.config["mode"] == "Proxy":
@@ -293,15 +303,16 @@ class DownloadManager(QDialog):
)
if selected_url == "GitHub站":
return f"https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
return f"https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.version)}/AUTO_MAA_{version_text(self.version)}.zip"
elif selected_url == "官方镜像站":
return f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
return f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.version)}/AUTO_MAA_{version_text(self.version)}.zip"
elif selected_url in self.config["download_dict"].keys():
return f"{self.config["download_dict"][selected_url]}AUTO_MAA_{version_text(self.main_version)}.zip"
return f"{self.config["download_dict"][selected_url]}AUTO_MAA_{version_text(self.version)}.zip"
else:
return f"{selected_url}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
return f"{selected_url}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.version)}/AUTO_MAA_{version_text(self.version)}.zip"
elif self.config["mode"] == "MirrorChyan":
with requests.get(
self.config["url"],
allow_redirects=True,
@@ -311,6 +322,14 @@ class DownloadManager(QDialog):
if response.status_code == 200:
return response.url
elif self.config["mode"] == "MirrorChyan":
with requests.get(
self.config["url"], allow_redirects=True, timeout=10, stream=True
) as response:
if response.status_code == 200:
return response.url
def test_speed_task1(self) -> None:
if self.isInterruptionRequested:
@@ -510,36 +529,25 @@ class DownloadManager(QDialog):
self.zip_extract.start()
self.zip_loop.exec()
self.update_info("正在删除已弃用的文件")
if (self.app_path / "changes.json").exists():
with (self.app_path / "changes.json").open(mode="r", encoding="utf-8") as f:
info: Dict[str, List[str]] = json.load(f)
if "deleted" in info:
for file_path in info:
(self.app_path / file_path).unlink()
(self.app_path / "changes.json").unlink()
self.update_info("正在删除临时文件")
self.update_progress(0, 0, 0)
if (self.app_path / "changes.json").exists():
(self.app_path / "changes.json").unlink()
if self.download_path.exists():
self.download_path.unlink()
# 主程序更新完成后打开对应程序
if not self.isInterruptionRequested and self.name == "AUTO_MAA":
subprocess.Popen(
[self.app_path / "AUTO_MAA.exe"],
creationflags=subprocess.CREATE_NO_WINDOW,
)
elif not self.isInterruptionRequested and self.name == "MAA":
# 下载完成后打开对应程序
if not self.isInterruptionRequested and self.name == "MAA":
subprocess.Popen(
[self.app_path / "MAA.exe"],
creationflags=subprocess.CREATE_NO_WINDOW,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
self.update_info(f"{self.name}更新成功!")
if self.name == "AUTO_MAA":
self.update_info(f"即将安装{self.name}")
else:
self.update_info(f"{self.name}下载成功!")
self.update_progress(0, 100, 100)
self.download_accomplish.emit()
@@ -581,151 +589,3 @@ class DownloadManager(QDialog):
self.requestInterruption()
event.accept()
class AUTO_MAA_Downloader(QApplication):
def __init__(
self,
app_path: Path,
name: str,
main_version: list,
config: dict,
) -> None:
super().__init__()
self.main = DownloadManager(app_path, name, main_version, config)
self.main.show()
self.main.run()
if __name__ == "__main__":
# 获取软件自身的路径
app_path = Path(sys.argv[0]).resolve().parent
# 从本地版本信息文件获取当前版本信息
if (app_path / "resources/version.json").exists():
with (app_path / "resources/version.json").open(
mode="r", encoding="utf-8"
) as f:
current_version_info = json.load(f)
current_version = list(
map(int, current_version_info["main_version"].split("."))
)
else:
current_version = [0, 0, 0, 0]
# 从本地配置文件获取更新信息
if (app_path / "config/config.json").exists():
with (app_path / "config/config.json").open(mode="r", encoding="utf-8") as f:
config = json.load(f)
if "Update" in config:
if "UpdateType" in config["Update"]:
update_type = config["Update"]["UpdateType"]
else:
update_type = "stable"
if "ProxyUrlList" in config["Update"]:
proxy_list = config["Update"]["ProxyUrlList"]
else:
proxy_list = []
if "ThreadNumb" in config["Update"]:
thread_numb = config["Update"]["ThreadNumb"]
else:
thread_numb = 8
if "MirrorChyanCDK" in config["Update"]:
mirrorchyan_CDK = (
win32crypt.CryptUnprotectData(
base64.b64decode(config["Update"]["MirrorChyanCDK"]),
None,
None,
None,
0,
)[1].decode("utf-8")
if config["Update"]["MirrorChyanCDK"]
else ""
)
else:
mirrorchyan_CDK = ""
else:
update_type = "stable"
proxy_list = []
thread_numb = 8
mirrorchyan_CDK = ""
else:
update_type = "stable"
proxy_list = []
thread_numb = 8
mirrorchyan_CDK = ""
# 从远程服务器获取最新版本信息
for _ in range(3):
try:
response = requests.get(
f"https://mirrorchyan.com/api/resources/AUTO_MAA/latest?user_agent=AutoMaaDownloader&current_version={version_text(current_version)}&cdk={mirrorchyan_CDK}&channel={update_type}",
timeout=10,
)
version_info: Dict[str, Union[int, str, Dict[str, str]]] = response.json()
break
except Exception as e:
err = e
time.sleep(0.1)
else:
sys.exit(f"获取版本信息时出错:\n{err}")
if version_info["code"] == 0:
if "url" in version_info["data"]:
download_config = {
"mode": "MirrorChyan",
"thread_numb": 1,
"url": version_info["data"]["url"],
}
else:
download_config = {"mode": "Proxy", "thread_numb": thread_numb}
else:
sys.exit(f"获取版本信息时出错:{version_info["msg"]}")
remote_version = list(
map(
int,
version_info["data"]["version_name"][1:].replace("-beta", "").split("."),
)
)
if download_config["mode"] == "Proxy":
for _ in range(3):
try:
response = requests.get(
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json",
timeout=10,
)
download_info = response.json()
download_config["proxy_list"] = list(
set(proxy_list + download_info["proxy_list"])
)
download_config["download_dict"] = download_info["download_dict"]
break
except Exception as e:
err = e
time.sleep(0.1)
else:
sys.exit(f"获取代理信息时出错:{err}")
if (app_path / "changes.json").exists():
(app_path / "changes.json").unlink()
# 启动更新线程
if version.parse(version_text(remote_version)) > version.parse(
version_text(current_version)
):
app = AUTO_MAA_Downloader(
app_path,
"AUTO_MAA",
remote_version,
download_config,
)
sys.exit(app.exec())

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -36,18 +36,23 @@ from qfluentwidgets import (
FluentIcon,
HeaderCardWidget,
PushButton,
ExpandGroupSettingCard,
TextBrowser,
CardWidget,
ComboBox,
ZhDatePicker,
SubtitleLabel,
)
from PySide6.QtCore import Signal
from PySide6.QtCore import Signal, QDate
import os
import subprocess
from datetime import datetime, timedelta
from functools import partial
from pathlib import Path
from typing import List
from typing import Union, List, Dict
from app.core import Config
from .Widget import StatefulItemCard, QuantifiedItemCard
from .Widget import StatefulItemCard, QuantifiedItemCard, QuickExpandGroupCard
class History(QWidget):
@@ -58,20 +63,24 @@ class History(QWidget):
content_widget = QWidget()
self.content_layout = QVBoxLayout(content_widget)
self.history_top_bar = self.HistoryTopBar(self)
self.history_top_bar.search_history.connect(self.reload_history)
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setContentsMargins(0, 0, 0, 0)
scrollArea.setStyleSheet("background: transparent; border: none;")
scrollArea.setWidget(content_widget)
layout = QVBoxLayout()
layout.addWidget(self.history_top_bar)
layout.addWidget(scrollArea)
self.setLayout(layout)
self.history_card_list = []
self.refresh()
def refresh(self):
"""刷新脚本实例界面"""
def reload_history(self, mode: str, start_date: QDate, end_date: QDate) -> None:
"""加载历史记录界面"""
while self.content_layout.count() > 0:
item = self.content_layout.takeAt(0)
@@ -82,177 +91,300 @@ class History(QWidget):
self.history_card_list = []
history_dict = Config.search_history()
history_dict = Config.search_history(
mode,
datetime(start_date.year(), start_date.month(), start_date.day()),
datetime(end_date.year(), end_date.month(), end_date.day()),
)
for date, user_list in history_dict.items():
for date, user in history_dict.items():
self.history_card_list.append(HistoryCard(date, user_list, self))
self.history_card_list.append(self.HistoryCard(mode, date, user, self))
self.content_layout.addWidget(self.history_card_list[-1])
self.content_layout.addStretch(1)
class HistoryTopBar(CardWidget):
"""历史记录顶部工具栏"""
class HistoryCard(ExpandGroupSettingCard):
search_history = Signal(str, QDate, QDate)
def __init__(self, date: str, user_list: List[Path], parent=None):
super().__init__(
FluentIcon.HISTORY, date, f"{date}的历史运行记录与统计信息", parent
)
def __init__(self, parent=None):
super().__init__(parent)
widget = QWidget()
Layout = QVBoxLayout(widget)
self.viewLayout.setContentsMargins(0, 0, 0, 0)
self.viewLayout.setSpacing(0)
self.addGroupWidget(widget)
Layout = QHBoxLayout(self)
self.user_history_card_list = []
self.lable_1 = SubtitleLabel("查询范围:")
self.start_date = ZhDatePicker()
self.start_date.setDate(QDate(2019, 5, 1))
self.lable_2 = SubtitleLabel("")
self.end_date = ZhDatePicker()
server_date = Config.server_date()
self.end_date.setDate(
QDate(server_date.year, server_date.month, server_date.day)
)
self.mode = ComboBox()
self.mode.setPlaceholderText("请选择查询模式")
self.mode.addItems(["按日合并", "按周合并", "按月合并"])
for user_path in user_list:
self.select_month = PushButton(FluentIcon.TAG, "最近一月")
self.select_week = PushButton(FluentIcon.TAG, "最近一周")
self.search = PushButton(FluentIcon.SEARCH, "查询")
self.select_month.clicked.connect(lambda: self.select_date("month"))
self.select_week.clicked.connect(lambda: self.select_date("week"))
self.search.clicked.connect(
lambda: self.search_history.emit(
self.mode.currentText(),
self.start_date.getDate(),
self.end_date.getDate(),
)
)
self.user_history_card_list.append(self.UserHistoryCard(user_path, self))
Layout.addWidget(self.user_history_card_list[-1])
Layout.addWidget(self.lable_1)
Layout.addWidget(self.start_date)
Layout.addWidget(self.lable_2)
Layout.addWidget(self.end_date)
Layout.addWidget(self.mode)
Layout.addStretch(1)
Layout.addWidget(self.select_month)
Layout.addWidget(self.select_week)
Layout.addWidget(self.search)
class UserHistoryCard(HeaderCardWidget):
def select_date(self, date: str) -> None:
"""选中最近一段时间并启动查询"""
server_date = Config.server_date()
if date == "week":
begin_date = server_date - timedelta(weeks=1)
elif date == "month":
begin_date = server_date - timedelta(days=30)
self.start_date.setDate(
QDate(begin_date.year, begin_date.month, begin_date.day)
)
self.end_date.setDate(
QDate(server_date.year, server_date.month, server_date.day)
)
self.search.clicked.emit()
class HistoryCard(QuickExpandGroupCard):
def __init__(
self,
user_history_path: Path,
mode: str,
date: str,
user: Union[List[Path], Dict[str, List[Path]]],
parent=None,
):
super().__init__(parent)
super().__init__(
FluentIcon.HISTORY, date, f"{date}的历史运行记录与统计信息", parent
)
self.setTitle(user_history_path.name.replace(".json", ""))
self.user_history_path = user_history_path
self.main_history = Config.load_maa_logs("总览", user_history_path)
self.index_card = self.IndexCard(self.main_history["条目索引"], self)
self.statistics_card = QHBoxLayout()
self.log_card = self.LogCard(self)
self.index_card.index_changed.connect(self.update_info)
self.viewLayout.addWidget(self.index_card)
self.viewLayout.addLayout(self.statistics_card)
self.viewLayout.addWidget(self.log_card)
widget = QWidget()
Layout = QVBoxLayout(widget)
self.viewLayout.setContentsMargins(0, 0, 0, 0)
self.viewLayout.setSpacing(0)
self.viewLayout.setStretch(0, 1)
self.viewLayout.setStretch(2, 4)
self.addGroupWidget(widget)
self.update_info("数据总览")
self.user_history_card_list = []
def update_info(self, index: str) -> None:
"""更新信息"""
if mode == "按日合并":
if index == "数据总览":
while self.statistics_card.count() > 0:
item = self.statistics_card.takeAt(0)
if item.spacerItem():
self.statistics_card.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
for name, item_list in self.main_history["统计数据"].items():
statistics_card = self.StatisticsCard(name, item_list, self)
self.statistics_card.addWidget(statistics_card)
self.log_card.hide()
else:
single_history = Config.load_maa_logs(
"单项",
self.user_history_path.with_suffix("")
/ f"{index.replace(":","-")}.json",
)
while self.statistics_card.count() > 0:
item = self.statistics_card.takeAt(0)
if item.spacerItem():
self.statistics_card.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
for name, item_list in single_history["统计数据"].items():
statistics_card = self.StatisticsCard(name, item_list, self)
self.statistics_card.addWidget(statistics_card)
self.log_card.text.setText(single_history["日志信息"])
self.log_card.button.clicked.disconnect()
self.log_card.button.clicked.connect(
lambda: os.startfile(
self.user_history_path.with_suffix("")
/ f"{index.replace(":","-")}.log"
for user_path in user:
self.user_history_card_list.append(
self.UserHistoryCard(mode, user_path.stem, user_path, self)
)
)
self.log_card.show()
Layout.addWidget(self.user_history_card_list[-1])
self.viewLayout.setStretch(1, self.statistics_card.count())
elif mode in ["按周合并", "按月合并"]:
self.setMinimumHeight(300)
class IndexCard(HeaderCardWidget):
index_changed = Signal(str)
def __init__(self, index_list: list, parent=None):
super().__init__(parent)
self.setTitle("记录条目")
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.index_cards: List[StatefulItemCard] = []
for index in index_list:
self.index_cards.append(StatefulItemCard(index))
self.index_cards[-1].clicked.connect(
partial(self.index_changed.emit, index[0])
for user, info in user.items():
self.user_history_card_list.append(
self.UserHistoryCard(mode, user, info, self)
)
self.Layout.addWidget(self.index_cards[-1])
Layout.addWidget(self.user_history_card_list[-1])
self.Layout.addStretch(1)
class UserHistoryCard(HeaderCardWidget):
"""用户历史记录卡片"""
class StatisticsCard(HeaderCardWidget):
def __init__(self, name: str, item_list: list, parent=None):
def __init__(
self,
mode: str,
name: str,
user_history: Union[Path, List[Path]],
parent=None,
):
super().__init__(parent)
self.setTitle(name)
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
if mode == "按日合并":
self.item_cards: List[QuantifiedItemCard] = []
self.user_history_path = user_history
self.main_history = Config.load_maa_logs("总览", user_history)
for item in item_list:
self.index_card = self.IndexCard(
self.main_history["条目索引"], self
)
self.index_card.index_changed.connect(self.update_info)
self.viewLayout.addWidget(self.index_card)
self.item_cards.append(QuantifiedItemCard(item))
self.Layout.addWidget(self.item_cards[-1])
elif mode in ["按周合并", "按月合并"]:
if len(item_list) == 0:
self.Layout.addWidget(QuantifiedItemCard(["暂无记录", ""]))
history = Config.merge_maa_logs("指定项", user_history)
self.Layout.addStretch(1)
self.main_history = {}
self.main_history["统计数据"] = {
"公招统计": list(history["recruit_statistics"].items())
}
class LogCard(HeaderCardWidget):
for game_id, drops in history["drop_statistics"].items():
self.main_history["统计数据"][f"掉落统计:{game_id}"] = list(
drops.items()
)
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("日志")
self.statistics_card = QHBoxLayout()
self.log_card = self.LogCard(self)
self.text = TextBrowser(self)
self.button = PushButton("打开日志文件", self)
self.button.clicked.connect(lambda: print("打开日志文件"))
self.viewLayout.addLayout(self.statistics_card)
self.viewLayout.addWidget(self.log_card)
self.viewLayout.setContentsMargins(0, 0, 0, 0)
self.viewLayout.setSpacing(0)
self.viewLayout.setStretch(0, 1)
self.viewLayout.setStretch(2, 4)
Layout = QVBoxLayout()
Layout.addWidget(self.text)
Layout.addWidget(self.button)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.viewLayout.addLayout(Layout)
self.update_info("数据总览")
def update_info(self, index: str) -> None:
"""更新信息"""
if index == "数据总览":
while self.statistics_card.count() > 0:
item = self.statistics_card.takeAt(0)
if item.spacerItem():
self.statistics_card.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
for name, item_list in self.main_history["统计数据"].items():
statistics_card = self.StatisticsCard(name, item_list, self)
self.statistics_card.addWidget(statistics_card)
self.log_card.hide()
else:
single_history = Config.load_maa_logs(
"单项",
self.user_history_path.with_suffix("")
/ f"{index.replace(":","-")}.json",
)
while self.statistics_card.count() > 0:
item = self.statistics_card.takeAt(0)
if item.spacerItem():
self.statistics_card.removeItem(item.spacerItem())
elif item.widget():
item.widget().deleteLater()
for name, item_list in single_history["统计数据"].items():
statistics_card = self.StatisticsCard(name, item_list, self)
self.statistics_card.addWidget(statistics_card)
self.log_card.text.setText(single_history["日志信息"])
self.log_card.open_file.clicked.disconnect()
self.log_card.open_file.clicked.connect(
lambda: os.startfile(
self.user_history_path.with_suffix("")
/ f"{index.replace(":","-")}.log"
)
)
self.log_card.open_dir.clicked.disconnect()
self.log_card.open_dir.clicked.connect(
lambda: subprocess.Popen(
[
"explorer",
"/select,",
str(
self.user_history_path.with_suffix("")
/ f"{index.replace(":","-")}.log"
),
]
)
)
self.log_card.show()
self.viewLayout.setStretch(1, self.statistics_card.count())
self.setMinimumHeight(300)
class IndexCard(HeaderCardWidget):
index_changed = Signal(str)
def __init__(self, index_list: list, parent=None):
super().__init__(parent)
self.setTitle("记录条目")
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.index_cards: List[StatefulItemCard] = []
for index in index_list:
self.index_cards.append(StatefulItemCard(index))
self.index_cards[-1].clicked.connect(
partial(self.index_changed.emit, index[0])
)
self.Layout.addWidget(self.index_cards[-1])
self.Layout.addStretch(1)
class StatisticsCard(HeaderCardWidget):
def __init__(self, name: str, item_list: list, parent=None):
super().__init__(parent)
self.setTitle(name)
self.Layout = QVBoxLayout()
self.viewLayout.addLayout(self.Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.item_cards: List[QuantifiedItemCard] = []
for item in item_list:
self.item_cards.append(QuantifiedItemCard(item))
self.Layout.addWidget(self.item_cards[-1])
if len(item_list) == 0:
self.Layout.addWidget(QuantifiedItemCard(["暂无记录", ""]))
self.Layout.addStretch(1)
class LogCard(HeaderCardWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("日志")
self.text = TextBrowser(self)
self.open_file = PushButton("打开日志文件", self)
self.open_file.clicked.connect(lambda: print("打开日志文件"))
self.open_dir = PushButton("打开所在目录", self)
self.open_dir.clicked.connect(lambda: print("打开所在文件"))
Layout = QVBoxLayout()
h_layout = QHBoxLayout()
h_layout.addWidget(self.open_file)
h_layout.addWidget(self.open_dir)
Layout.addWidget(self.text)
Layout.addLayout(h_layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.viewLayout.addLayout(Layout)

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -149,6 +149,8 @@ class Home(QWidget):
layout = QVBoxLayout()
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setContentsMargins(0, 0, 0, 0)
scrollArea.setStyleSheet("background: transparent; border: none;")
scrollArea.setWidget(widget)
layout.addWidget(scrollArea)
self.setLayout(layout)

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -26,9 +26,8 @@ v4.3
"""
from loguru import logger
from PySide6.QtWidgets import QSystemTrayIcon
from PySide6.QtWidgets import QApplication, QSystemTrayIcon
from qfluentwidgets import (
qconfig,
Action,
SystemTrayMenu,
SplashScreen,
@@ -62,16 +61,22 @@ class AUTO_MAA(MSFluentWindow):
super().__init__()
self.setWindowIcon(QIcon(str(Config.app_path / "resources/icons/AUTO_MAA.ico")))
self.setWindowTitle("AUTO_MAA")
version_numb = list(map(int, Config.VERSION.split(".")))
version_text = (
f"v{'.'.join(str(_) for _ in version_numb[0:3])}"
if version_numb[3] == 0
else f"v{'.'.join(str(_) for _ in version_numb[0:3])}-beta.{version_numb[3]}"
)
self.setWindowTitle(f"AUTO_MAA - {version_text}")
self.switch_theme()
self.splashScreen = SplashScreen(self.windowIcon(), self)
self.show_ui("显示主窗口", if_quick=True)
TaskManager.main_window = self.window()
MainInfoBar.main_window = self.window()
System.main_window = self.window()
Config.main_window = self.window()
# 创建主窗口
self.home = Home(self)
@@ -140,9 +145,6 @@ class AUTO_MAA(MSFluentWindow):
self.dispatch_center.update_top_bar() if index == 3 else None
)
)
self.stackedWidget.currentChanged.connect(
lambda index: (self.history.refresh() if index == 4 else None)
)
# 创建系统托盘及其菜单
self.tray = QSystemTrayIcon(
@@ -176,14 +178,21 @@ class AUTO_MAA(MSFluentWindow):
# 退出主程序菜单项
self.tray_menu.addAction(
Action(FluentIcon.POWER_BUTTON, "退出主程序", triggered=self.window().close)
Action(
FluentIcon.POWER_BUTTON,
"退出主程序",
triggered=lambda: System.set_power("KillSelf"),
)
)
# 设置托盘菜单
self.tray.setContextMenu(self.tray_menu)
self.tray.activated.connect(self.on_tray_activated)
self.set_min_method()
Config.user_info_changed.connect(self.member_manager.refresh_dashboard)
Config.power_sign_changed.connect(self.dispatch_center.update_power_sign)
TaskManager.create_gui.connect(self.dispatch_center.add_board)
TaskManager.connect_gui.connect(self.dispatch_center.connect_main_board)
Notify.push_info_bar.connect(MainInfoBar.push_info_bar)
@@ -238,10 +247,10 @@ class AUTO_MAA(MSFluentWindow):
# 清理旧日志
self.clean_old_logs()
# 清理临时更新器
if (Config.app_path / "AUTO_Updater.active.exe").exists():
# 清理安装包
if (Config.app_path / "AUTO_MAA-Setup.exe").exists():
try:
(Config.app_path / "AUTO_Updater.active.exe").unlink()
(Config.app_path / "AUTO_MAA-Setup.exe").unlink()
except Exception:
pass
@@ -258,11 +267,11 @@ class AUTO_MAA(MSFluentWindow):
self.start_main_task()
# 获取公告
self.setting.show_notice(if_show=False)
self.setting.show_notice(if_first=True)
# 检查更新
if Config.get(Config.update_IfAutoUpdate):
self.setting.check_update()
self.setting.check_update(if_first=True)
# 直接最小化
if Config.get(Config.start_IfMinimizeDirectly):
@@ -324,7 +333,7 @@ class AUTO_MAA(MSFluentWindow):
logger.info("自动添加任务调度队列_1")
TaskManager.add_task(
"自动代理_主调度台",
"主任务队列",
"调度队列_1",
Config.queue_dict["调度队列_1"]["Config"].toDict(),
)
@@ -332,7 +341,7 @@ class AUTO_MAA(MSFluentWindow):
logger.info("自动添加任务脚本_1")
TaskManager.add_task(
"自动代理_主调度台", "主任务队列", {"Queue": {"Member_1": "脚本_1"}}
"自动代理_主调度台", "自定义队列", {"Queue": {"Member_1": "脚本_1"}}
)
else:
@@ -350,27 +359,37 @@ class AUTO_MAA(MSFluentWindow):
if mode == "显示主窗口":
# 配置主窗口
size = list(
map(
int,
Config.get(Config.ui_size).split("x"),
if not self.window().isVisible():
size = list(
map(
int,
Config.get(Config.ui_size).split("x"),
)
)
)
location = list(
map(
int,
Config.get(Config.ui_location).split("x"),
location = list(
map(
int,
Config.get(Config.ui_location).split("x"),
)
)
)
self.window().setGeometry(location[0], location[1], size[0], size[1])
self.window().show()
if self.window().isMaximized():
self.window().showNormal()
self.window().setGeometry(location[0], location[1], size[0], size[1])
self.window().show()
if not if_quick:
if Config.get(Config.ui_maximized):
self.titleBar.maxBtn.click()
self.show_ui("配置托盘")
if not any(
self.window().geometry().intersects(screen.availableGeometry())
for screen in QApplication.screens()
):
self.window().showNormal()
self.window().setGeometry(100, 100, 1200, 700)
self.window().raise_()
self.window().activateWindow()
if not if_quick:
if Config.get(Config.ui_maximized):
self.window().showMaximized()
self.set_min_method()
self.show_ui("配置托盘")
elif mode == "配置托盘":
@@ -392,6 +411,7 @@ class AUTO_MAA(MSFluentWindow):
Config.ui_location,
f"{self.geometry().x()}x{self.geometry().y()}",
)
Config.set(Config.ui_maximized, self.window().isMaximized())
Config.save()

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -35,9 +35,9 @@ from PySide6.QtWidgets import (
QTableWidgetItem,
QHeaderView,
)
from PySide6.QtGui import QIcon
from qfluentwidgets import (
Action,
Pivot,
ScrollArea,
FluentIcon,
MessageBox,
@@ -48,16 +48,17 @@ from qfluentwidgets import (
TableWidget,
PrimaryToolButton,
)
from PySide6.QtCore import Qt, Signal
from PySide6.QtCore import Signal
from datetime import datetime
from functools import partial
from pathlib import Path
from typing import List
import shutil
import json
from app.core import Config, MainInfoBar, TaskManager, MaaConfig, MaaUserConfig, Network
from app.services import Crypto
from app.utils import DownloadManager
from .downloader import DownloadManager
from .Widget import (
LineEditMessageBox,
LineEditSettingCard,
@@ -69,6 +70,8 @@ from .Widget import (
ComboBoxSettingCard,
SwitchSettingCard,
PushAndSwitchButtonSettingCard,
PushAndComboBoxSettingCard,
PivotArea,
)
@@ -313,68 +316,135 @@ class MemberManager(QWidget):
def member_downloader(self):
"""脚本下载器"""
if not Config.get(Config.update_MirrorChyanCDK):
logger.warning("脚本下载器未设置CDK")
MainInfoBar.push_info_bar(
"warning",
"未设置Mirror酱CDK",
"下载器依赖于Mirror酱未设置CDK时无法使用",
5000,
)
return None
# 从远程服务器获取应用列表
Network.set_info(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/apps_info.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
apps_info = Network.response_json
else:
logger.warning(f"获取应用列表时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取应用列表时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
choice = ComboBoxMessageBox(
self.window(),
"选择一个脚本类型以下载相应脚本",
["选择脚本类型"],
[["MAA"]],
[list(apps_info.keys())],
)
if choice.exec() and choice.input[0].currentIndex() != -1:
if choice.input[0].currentText() == "MAA":
app_name = choice.input[0].currentText()
app_rid = apps_info[app_name]["rid"]
(Config.app_path / "script/MAA").mkdir(parents=True, exist_ok=True)
folder = QFileDialog.getExistingDirectory(
self, "选择MAA下载目录", str(Config.app_path / "script/MAA")
(Config.app_path / f"script/{app_rid}").mkdir(parents=True, exist_ok=True)
folder = QFileDialog.getExistingDirectory(
self,
f"选择{app_name}下载目录",
str(Config.app_path / f"script/{app_rid}"),
)
if not folder:
logger.warning(f"选择{app_name}下载目录时未选择文件夹")
MainInfoBar.push_info_bar(
"warning", "警告", f"未选择{app_name}下载目录", 5000
)
if not folder:
logger.warning("选择MAA下载目录时未选择文件夹")
MainInfoBar.push_info_bar(
"warning", "警告", "未选择MAA下载目录", 5000
)
return None
return None
# 从mirrorc服务器获取最新版本信息
Network.set_info(
mode="get",
url=f"https://mirrorchyan.com/api/resources/{app_rid}/latest?user_agent=AutoMaaGui&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&os={apps_info[app_name]["os"]}&arch={apps_info[app_name]["arch"]}&channel=stable",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
app_info = Network.response_json
else:
if Network.response_json:
app_info = Network.response_json
if app_info["code"] != 0:
logger.error(f"获取版本信息时出错:{app_info["msg"]}")
error_remark_dict = {
1001: "获取版本信息的URL参数不正确",
7001: "填入的 CDK 已过期",
7002: "填入的 CDK 错误",
7003: "填入的 CDK 今日下载次数已达上限",
7004: "填入的 CDK 类型和待下载的资源不匹配",
7005: "填入的 CDK 已被封禁",
8001: "对应架构和系统下的资源不存在",
8002: "错误的系统参数",
8003: "错误的架构参数",
8004: "错误的更新通道参数",
1: app_info["msg"],
}
if app_info["code"] in error_remark_dict:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
error_remark_dict[app_info["code"]],
-1,
)
else:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
"意料之外的错误,请及时联系项目组以获取来自 Mirror 酱的技术支持",
-1,
)
# 从mirrorc服务器获取最新版本信息
Network.set_info(
mode="get",
url="https://mirrorchyan.com/api/resources/MAA/latest?user_agent=AutoMaaGui&os=win&arch=x64&channel=stable",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
maa_info = Network.response_json
else:
choice = MessageBox(
"错误",
f"获取版本信息时出错:\n{Network.error_message}",
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 = DownloadManager(
Path(folder),
"MAA",
maa_version,
{
"mode": "Proxy",
"thread_numb": Config.get(Config.update_ThreadNumb),
},
logger.warning(f"获取版本信息时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取版本信息时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
self.downloader.show()
self.downloader.run()
return None
self.downloader = DownloadManager(
Path(folder),
app_rid,
None,
{
"mode": "MirrorChyan",
"thread_numb": 1,
"url": app_info["data"]["url"],
},
)
self.downloader.setWindowTitle("AUTO_MAA下载器 - Mirror酱渠道")
self.downloader.setWindowIcon(
QIcon(str(Config.app_path / "resources/icons/MirrorChyan.ico"))
)
self.downloader.show()
self.downloader.run()
def show_password(self):
@@ -415,13 +485,17 @@ class MemberManager(QWidget):
self.setObjectName("脚本管理页面组")
self.pivot = Pivot(self)
self.pivotArea = PivotArea(self)
self.pivot = self.pivotArea.pivot
self.stackedWidget = QStackedWidget(self)
self.Layout = QVBoxLayout(self)
self.stackedWidget.setContentsMargins(0, 0, 0, 0)
self.stackedWidget.setStyleSheet("background: transparent; border: none;")
self.script_list: List[MemberManager.MemberSettingBox.MaaSettingBox] = []
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
self.Layout = QVBoxLayout(self)
self.Layout.addWidget(self.pivotArea)
self.Layout.addWidget(self.stackedWidget)
self.Layout.setContentsMargins(0, 0, 0, 0)
@@ -493,6 +567,8 @@ class MemberManager(QWidget):
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setContentsMargins(0, 0, 0, 0)
scrollArea.setStyleSheet("background: transparent; border: none;")
content_widget = QWidget()
content_layout = QVBoxLayout(content_widget)
@@ -613,21 +689,7 @@ class MemberManager(QWidget):
configItem=self.config.RunSet_TaskTransitionMethod,
parent=self,
)
self.card_EnhanceTask = ComboBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="自动代理增效任务",
content="自动代理时的额外操作,此操作无法区分多开模拟器,可能会干扰其他任务,也可能关闭您正在使用的模拟器",
texts=[
"禁用",
"强制关闭ADB",
"强制关闭所有模拟器",
"强制关闭ADB和所有模拟器",
],
qconfig=self.config,
configItem=self.config.RunSet_EnhanceTask,
parent=self,
)
self.ProxyTimesLimit = SpinBoxSettingCard(
self.card_ProxyTimesLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="用户单日代理次数上限",
content="当用户本日代理成功次数达到该阈值时跳过代理阈值为“0”时视为无代理次数上限",
@@ -636,25 +698,7 @@ class MemberManager(QWidget):
configItem=self.config.RunSet_ProxyTimesLimit,
parent=self,
)
self.AnnihilationTimeLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="剿灭代理超时限制",
content="MAA日志无变化时间超过该阈值视为超时单位为分钟",
range=(1, 1024),
qconfig=self.config,
configItem=self.config.RunSet_AnnihilationTimeLimit,
parent=self,
)
self.RoutineTimeLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="自动代理超时限制",
content="MAA日志无变化时间超过该阈值视为超时单位为分钟",
range=(1, 1024),
qconfig=self.config,
configItem=self.config.RunSet_RoutineTimeLimit,
parent=self,
)
self.RunTimesLimit = SpinBoxSettingCard(
self.card_RunTimesLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="代理重试次数限制",
content="若超过该次数限制仍未完成代理,视为代理失败",
@@ -663,7 +707,25 @@ class MemberManager(QWidget):
configItem=self.config.RunSet_RunTimesLimit,
parent=self,
)
self.AnnihilationWeeklyLimit = SwitchSettingCard(
self.card_AnnihilationTimeLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="剿灭代理超时限制",
content="MAA日志无变化时间超过该阈值视为超时单位为分钟",
range=(1, 1024),
qconfig=self.config,
configItem=self.config.RunSet_AnnihilationTimeLimit,
parent=self,
)
self.card_RoutineTimeLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="自动代理超时限制",
content="MAA日志无变化时间超过该阈值视为超时单位为分钟",
range=(1, 1024),
qconfig=self.config,
configItem=self.config.RunSet_RoutineTimeLimit,
parent=self,
)
self.card_AnnihilationWeeklyLimit = SwitchSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="每周剿灭仅执行到上限",
content="每周剿灭模式执行到上限,本周剩下时间不再执行剿灭任务",
@@ -671,16 +733,24 @@ class MemberManager(QWidget):
configItem=self.config.RunSet_AnnihilationWeeklyLimit,
parent=self,
)
self.card_AutoUpdateMaa = SwitchSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="自动代理时自动更新MAA",
content="执行自动代理任务时自动更新MAA关闭后仍会进行MAA版本检查",
qconfig=self.config,
configItem=self.config.RunSet_AutoUpdateMaa,
parent=self,
)
widget = QWidget()
Layout = QVBoxLayout(widget)
Layout.addWidget(self.card_TaskTransitionMethod)
Layout.addWidget(self.card_EnhanceTask)
Layout.addWidget(self.ProxyTimesLimit)
Layout.addWidget(self.AnnihilationTimeLimit)
Layout.addWidget(self.RoutineTimeLimit)
Layout.addWidget(self.RunTimesLimit)
Layout.addWidget(self.AnnihilationWeeklyLimit)
Layout.addWidget(self.card_ProxyTimesLimit)
Layout.addWidget(self.card_RunTimesLimit)
Layout.addWidget(self.card_AnnihilationTimeLimit)
Layout.addWidget(self.card_RoutineTimeLimit)
Layout.addWidget(self.card_AnnihilationWeeklyLimit)
Layout.addWidget(self.card_AutoUpdateMaa)
self.viewLayout.setContentsMargins(0, 0, 0, 0)
self.viewLayout.setSpacing(0)
self.addGroupWidget(widget)
@@ -947,9 +1017,14 @@ class MemberManager(QWidget):
self.setObjectName("用户管理")
self.name = name
self.pivot = Pivot(self)
self.pivotArea = PivotArea(self)
self.pivot = self.pivotArea.pivot
self.stackedWidget = QStackedWidget(self)
self.Layout = QVBoxLayout(self)
self.stackedWidget.setContentsMargins(0, 0, 0, 0)
self.stackedWidget.setStyleSheet(
"background: transparent; border: none;"
)
self.script_list: List[
MemberManager.MemberSettingBox.MaaSettingBox.UserManager.UserSettingBox.UserMemberSettingBox
@@ -960,7 +1035,8 @@ class MemberManager(QWidget):
self.stackedWidget.addWidget(self.user_dashboard)
self.pivot.addItem(routeKey="用户仪表盘", text="用户仪表盘")
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
self.Layout = QVBoxLayout(self)
self.Layout.addWidget(self.pivotArea)
self.Layout.addWidget(self.stackedWidget)
self.Layout.setContentsMargins(0, 0, 0, 0)
@@ -1049,7 +1125,7 @@ class MemberManager(QWidget):
self.name = name
self.dashboard = TableWidget(self)
self.dashboard.setColumnCount(10)
self.dashboard.setColumnCount(11)
self.dashboard.setHorizontalHeaderLabels(
[
"用户名",
@@ -1059,8 +1135,9 @@ class MemberManager(QWidget):
"代理情况",
"给药量",
"关卡选择",
"备选关卡-1",
"备选关卡-2",
"备选 - 1",
"备选 - 2",
"剩余理智",
"",
]
)
@@ -1070,14 +1147,14 @@ class MemberManager(QWidget):
self.dashboard.horizontalHeader().setSectionResizeMode(
col, QHeaderView.ResizeMode.ResizeToContents
)
for col in range(6, 9):
for col in range(6, 10):
self.dashboard.horizontalHeader().setSectionResizeMode(
col, QHeaderView.ResizeMode.Stretch
)
self.dashboard.horizontalHeader().setSectionResizeMode(
9, QHeaderView.ResizeMode.Fixed
10, QHeaderView.ResizeMode.Fixed
)
self.dashboard.setColumnWidth(9, 32)
self.dashboard.setColumnWidth(10, 32)
self.viewLayout.addWidget(self.dashboard)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
@@ -1099,7 +1176,7 @@ class MemberManager(QWidget):
text_list.append("未通过人工排查")
text_list.append(
f"今日已代理{config.get(config.Data_ProxyTimes)}"
if Config.server_date()
if Config.server_date().strftime("%Y-%m-%d")
== config.get(config.Data_LastProxyDate)
else "今日未进行代理"
)
@@ -1109,9 +1186,7 @@ class MemberManager(QWidget):
config.get(config.Data_LastAnnihilationDate),
"%Y-%m-%d",
).isocalendar()[:2]
== datetime.strptime(
Config.server_date(), "%Y-%m-%d"
).isocalendar()[:2]
== Config.server_date().isocalendar()[:2]
else "本周剿灭未完成"
)
@@ -1209,8 +1284,32 @@ class MemberManager(QWidget):
else config.get(config.Info_GameId_2)
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
9,
QTableWidgetItem(
"不使用"
if config.get(config.Info_GameId_Remain) == "-"
else (
(
Config.gameid_dict["ALL"]["text"][
Config.gameid_dict["ALL"][
"value"
].index(
config.get(
config.Info_GameId_Remain
)
)
]
)
if config.get(config.Info_GameId_Remain)
in Config.gameid_dict["ALL"]["value"]
else config.get(config.Info_GameId_Remain)
)
),
)
self.dashboard.setCellWidget(
int(name[3:]) - 1, 9, button
int(name[3:]) - 1, 10, button
)
class UserMemberSettingBox(HeaderCardWidget):
@@ -1309,13 +1408,18 @@ class MemberManager(QWidget):
configItem=self.config.Info_Routine,
parent=self,
)
self.card_Infrastructure = PushAndSwitchButtonSettingCard(
self.card_InfrastMode = PushAndComboBoxSettingCard(
icon=FluentIcon.CAFE,
title="自定义基建",
content="自定义基建相关设置项",
title="基建模式",
content="自定义基建配置文件未生效",
text="选择配置文件",
texts=[
"常规模式",
"一键轮休",
"自定义基建",
],
qconfig=self.config,
configItem=self.config.Info_Infrastructure,
configItem=self.config.Info_InfrastMode,
parent=self,
)
self.card_Password = PasswordLineEditSettingCard(
@@ -1346,6 +1450,16 @@ class MemberManager(QWidget):
configItem=self.config.Info_MedicineNumb,
parent=self,
)
self.card_SeriesNumb = ComboBoxSettingCard(
icon=FluentIcon.GAME,
title="连战次数",
content="连战次数较大时建议搭配剩余理智关卡使用",
texts=["AUTO", "6", "5", "4", "3", "2", "1", "不选择"],
qconfig=self.config,
configItem=self.config.Info_SeriesNumb,
parent=self,
)
self.card_SeriesNumb.comboBox.setMinimumWidth(150)
self.card_GameId = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="关卡选择",
@@ -1358,7 +1472,7 @@ class MemberManager(QWidget):
)
self.card_GameId_1 = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="备选关卡-1",
title="备选关卡 - 1",
content="按下回车以添加自定义关卡号",
value=Config.gameid_dict["ALL"]["value"],
texts=Config.gameid_dict["ALL"]["text"],
@@ -1368,7 +1482,7 @@ class MemberManager(QWidget):
)
self.card_GameId_2 = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="备选关卡-2",
title="备选关卡 - 2",
content="按下回车以添加自定义关卡号",
value=Config.gameid_dict["ALL"]["value"],
texts=Config.gameid_dict["ALL"]["text"],
@@ -1376,6 +1490,19 @@ class MemberManager(QWidget):
configItem=self.config.Info_GameId_2,
parent=self,
)
self.card_GameId_Remain = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="剩余理智关卡",
content="按下回车以添加自定义关卡号",
value=Config.gameid_dict["ALL"]["value"],
texts=[
"不使用" if _ == "当前/上次" else _
for _ in Config.gameid_dict["ALL"]["text"]
],
qconfig=self.config,
configItem=self.config.Info_GameId_Remain,
parent=self,
)
self.card_UserLable = UserLableSettingCard(
icon=FluentIcon.INFO,
@@ -1404,16 +1531,19 @@ class MemberManager(QWidget):
h4_layout = QHBoxLayout()
h4_layout.addWidget(self.card_Annihilation)
h4_layout.addWidget(self.card_Routine)
h4_layout.addWidget(self.card_Infrastructure)
h4_layout.addWidget(self.card_InfrastMode)
h5_layout = QHBoxLayout()
h5_layout.addWidget(self.card_Password)
h5_layout.addWidget(self.card_Notes)
h6_layout = QHBoxLayout()
h6_layout.addWidget(self.card_MedicineNumb)
h6_layout.addWidget(self.card_GameId)
h6_layout.addWidget(self.card_SeriesNumb)
h7_layout = QHBoxLayout()
h7_layout.addWidget(self.card_GameId)
h7_layout.addWidget(self.card_GameId_1)
h7_layout.addWidget(self.card_GameId_2)
h8_layout = QHBoxLayout()
h8_layout.addWidget(self.card_GameId_2)
h8_layout.addWidget(self.card_GameId_Remain)
Layout = QVBoxLayout()
Layout.addLayout(h1_layout)
@@ -1424,6 +1554,7 @@ class MemberManager(QWidget):
Layout.addLayout(h5_layout)
Layout.addLayout(h6_layout)
Layout.addLayout(h7_layout)
Layout.addLayout(h8_layout)
self.viewLayout.addLayout(Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
@@ -1431,19 +1562,23 @@ class MemberManager(QWidget):
self.card_Mode.comboBox.currentIndexChanged.connect(
self.switch_mode
)
self.card_InfrastMode.comboBox.currentIndexChanged.connect(
self.switch_infrastructure
)
self.card_Annihilation.clicked.connect(
lambda: self.set_maa("Annihilation")
)
self.card_Routine.clicked.connect(
lambda: self.set_maa("Routine")
)
self.card_Infrastructure.clicked.connect(
self.card_InfrastMode.clicked.connect(
self.set_infrastructure
)
Config.gameid_refreshed.connect(self.refresh_gameid)
Config.PASSWORD_refreshed.connect(self.refresh_password)
self.switch_mode()
self.switch_infrastructure()
def switch_mode(self) -> None:
@@ -1452,15 +1587,36 @@ class MemberManager(QWidget):
self.card_Routine.setVisible(False)
self.card_Server.setVisible(True)
self.card_Annihilation.button.setVisible(False)
self.card_Infrastructure.setVisible(True)
self.card_InfrastMode.setVisible(True)
elif self.config.get(self.config.Info_Mode) == "详细":
self.card_Server.setVisible(False)
self.card_Infrastructure.setVisible(False)
self.card_InfrastMode.setVisible(False)
self.card_Annihilation.button.setVisible(True)
self.card_Routine.setVisible(True)
def switch_infrastructure(self) -> None:
if (
self.config.get(self.config.Info_InfrastMode)
== "Custom"
):
self.card_InfrastMode.button.setVisible(True)
with (
self.user_path
/ "Infrastructure/infrastructure.json"
).open(mode="r", encoding="utf-8") as f:
infrastructure = json.load(f)
self.card_InfrastMode.setContent(
f"当前基建配置:{infrastructure.get("title","未命名")}"
)
else:
self.card_InfrastMode.button.setVisible(False)
self.card_InfrastMode.setContent(
"自定义基建配置文件未生效"
)
def refresh_gameid(self):
self.card_GameId.reLoadOptions(
@@ -1475,6 +1631,13 @@ class MemberManager(QWidget):
Config.gameid_dict["ALL"]["value"],
Config.gameid_dict["ALL"]["text"],
)
self.card_GameId_Remain.reLoadOptions(
Config.gameid_dict["ALL"]["value"],
[
"不使用" if _ == "当前/上次" else _
for _ in Config.gameid_dict["ALL"]["text"]
],
)
def refresh_password(self):
@@ -1509,6 +1672,7 @@ class MemberManager(QWidget):
self.user_path
/ "Infrastructure/infrastructure.json",
)
self.switch_infrastructure()
else:
logger.warning("未选择自定义基建文件")
MainInfoBar.push_info_bar(

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -34,14 +34,12 @@ from PySide6.QtWidgets import (
)
from qfluentwidgets import (
Action,
Pivot,
ScrollArea,
FluentIcon,
MessageBox,
HeaderCardWidget,
CommandBar,
)
from PySide6.QtCore import Qt
from typing import List
from app.core import QueueConfig, Config, MainInfoBar
@@ -52,6 +50,7 @@ from .Widget import (
TimeEditSettingCard,
NoOptionComboBoxSettingCard,
HistoryCard,
PivotArea,
)
@@ -305,15 +304,19 @@ class QueueManager(QWidget):
self.setObjectName("调度队列管理")
self.pivot = Pivot(self)
self.pivotArea = PivotArea()
self.pivot = self.pivotArea.pivot
self.stackedWidget = QStackedWidget(self)
self.Layout = QVBoxLayout(self)
self.stackedWidget.setContentsMargins(0, 0, 0, 0)
self.stackedWidget.setStyleSheet("background: transparent; border: none;")
self.script_list: List[
QueueManager.QueueSettingBox.QueueMemberSettingBox
] = []
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
self.Layout = QVBoxLayout(self)
self.Layout.addWidget(self.pivotArea)
self.Layout.addWidget(self.stackedWidget)
self.Layout.setContentsMargins(0, 0, 0, 0)
@@ -380,6 +383,8 @@ class QueueManager(QWidget):
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setContentsMargins(0, 0, 0, 0)
scrollArea.setStyleSheet("background: transparent; border: none;")
content_widget = QWidget()
content_layout = QVBoxLayout(content_widget)

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -27,12 +27,12 @@ v4.3
from loguru import logger
from PySide6.QtWidgets import QWidget, QVBoxLayout
from PySide6.QtGui import QIcon
from PySide6.QtCore import Qt
from qfluentwidgets import (
ScrollArea,
FluentIcon,
MessageBox,
Dialog,
HyperlinkCard,
HeaderCardWidget,
ExpandGroupSettingCard,
@@ -42,16 +42,16 @@ from qfluentwidgets import (
import os
import re
import json
import time
import shutil
import subprocess
from datetime import datetime
from packaging import version
from pathlib import Path
from typing import Dict, List, Union
from typing import Dict, Union
from app.core import Config, MainInfoBar, Network
from app.services import Crypto, System, Notify
from .downloader import DownloadManager
from .Widget import (
SwitchSettingCard,
RangeSettingCard,
@@ -89,9 +89,9 @@ class Setting(QWidget):
self.start.card_IfSelfStart.checkedChanged.connect(System.set_SelfStart)
self.security.card_changePASSWORD.clicked.connect(self.change_PASSWORD)
self.updater.card_CheckUpdate.clicked.connect(
lambda: self.check_update(if_click=True)
lambda: self.check_update(if_show=True)
)
self.other.card_Notice.clicked.connect(self.show_notice)
self.other.card_Notice.clicked.connect(lambda: self.show_notice(if_show=True))
content_layout.addWidget(self.function)
content_layout.addWidget(self.start)
@@ -103,6 +103,8 @@ class Setting(QWidget):
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setContentsMargins(0, 0, 0, 0)
scrollArea.setStyleSheet("background: transparent; border: none;")
scrollArea.setWidget(content_widget)
layout = QVBoxLayout()
layout.addWidget(scrollArea)
@@ -179,10 +181,7 @@ class Setting(QWidget):
while True:
choice = LineEditMessageBox(
self.window(),
"未检测到管理密钥,请设置您的管理密钥",
"管理密钥",
"密码",
self.window(), "请设置您的管理密钥", "管理密钥", "密码"
)
if choice.exec() and choice.input.text() != "":
Crypto.get_PASSWORD(choice.input.text())
@@ -260,12 +259,12 @@ class Setting(QWidget):
if choice.exec():
break
def check_update(self, if_click: bool = False) -> None:
def check_update(self, if_show: bool = False, if_first: bool = False) -> None:
"""检查版本更新,调起文件下载进程"""
current_version = list(map(int, Config.VERSION.split(".")))
if Network.if_running and if_click:
if Network.if_running and if_show:
MainInfoBar.push_info_bar(
"warning", "请求速度过快", "上个网络请求还未结束,请稍等片刻", 5000
)
@@ -282,6 +281,46 @@ class Setting(QWidget):
Network.response_json
)
else:
if Network.response_json:
version_info = Network.response_json
if version_info["code"] != 0:
logger.error(f"获取版本信息时出错:{version_info["msg"]}")
error_remark_dict = {
1001: "获取版本信息的URL参数不正确",
7001: "填入的 CDK 已过期",
7002: "填入的 CDK 错误",
7003: "填入的 CDK 今日下载次数已达上限",
7004: "填入的 CDK 类型和待下载的资源不匹配",
7005: "填入的 CDK 已被封禁",
8001: "对应架构和系统下的资源不存在",
8002: "错误的系统参数",
8003: "错误的架构参数",
8004: "错误的更新通道参数",
1: version_info["msg"],
}
if version_info["code"] in error_remark_dict:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
error_remark_dict[version_info["code"]],
-1,
)
else:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
"意料之外的错误,请及时联系项目组以获取来自 Mirror 酱的技术支持",
-1,
)
return None
logger.warning(f"获取版本信息时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
@@ -291,41 +330,6 @@ class Setting(QWidget):
)
return None
if version_info["code"] != 0:
logger.error(f"获取版本信息时出错:{version_info["msg"]}")
error_remark_dict = {
1001: "获取版本信息的URL参数不正确",
7001: "填入的 CDK 已过期",
7002: "填入的 CDK 错误",
7003: "填入的 CDK 今日下载次数已达上限",
7004: "填入的 CDK 类型和待下载的资源不匹配",
7005: "填入的 CDK 不合法",
8001: "对应架构和系统下的资源不存在",
8002: "错误的系统参数",
8003: "错误的架构参数",
8004: "错误的更新通道参数",
1: version_info["msg"],
}
if version_info["code"] in error_remark_dict:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
error_remark_dict[version_info["code"]],
-1,
)
else:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
"意料之外的错误,请及时联系项目组以获取来自 Mirror 酱的技术支持",
-1,
)
return None
remote_version = list(
map(
int,
@@ -335,8 +339,14 @@ class Setting(QWidget):
)
)
# 有版本更新
if version.parse(version_text(remote_version)) > version.parse(
if (
if_show
or (
not if_show
and if_first
and not Config.get(Config.function_UnattendedMode)
)
) and version.parse(version_text(remote_version)) > version.parse(
version_text(current_version)
):
@@ -372,48 +382,110 @@ class Setting(QWidget):
else:
all_version_info[key] = value.copy()
version_info = {
"更新总览": f"{main_version_info}\n\n{version_info_markdown(update_version_info)}",
"ALL~版本信息": version_info_markdown(all_version_info),
**{
version_text(list(map(int, k.split(".")))): version_info_markdown(v)
for k, v in version_info_json.items()
},
}
# 询问是否开始版本更新
choice = NoticeMessageBox(self.window(), "版本更新", version_info)
choice = NoticeMessageBox(
self.window(),
"版本更新",
{
"更新总览": f"{main_version_info}\n\n{version_info_markdown(update_version_info)}",
"ALL~版本信息": version_info_markdown(all_version_info),
**{
version_text(
list(map(int, k.split(".")))
): version_info_markdown(v)
for k, v in version_info_json.items()
},
},
)
if choice.exec():
with Config.version_path.open(mode="r", encoding="utf-8") as f:
version_info = json.load(f)
version_info["main_version"] = Config.VERSION
with Config.version_path.open(mode="w", encoding="utf-8") as f:
json.dump(version_info, f, ensure_ascii=False, indent=4)
if (Config.app_path / "AUTO_Updater.exe").exists():
shutil.copy(
Config.app_path / "AUTO_Updater.exe",
Config.app_path / "AUTO_Updater.active.exe",
)
if "url" in version_info["data"]:
download_config = {
"mode": "MirrorChyan",
"thread_numb": 1,
"url": version_info["data"]["url"],
}
else:
logger.error("更新器文件不存在")
MainInfoBar.push_info_bar(
"error", "更新器不存在", "请手动前往 GitHub 获取最新版本", -1
# 从远程服务器获取代理信息
Network.set_info(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json",
)
return None
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
download_info = Network.response_json
else:
logger.warning(f"获取应用列表时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取应用列表时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
subprocess.Popen(
[Config.app_path / "AUTO_Updater.active.exe"],
creationflags=subprocess.CREATE_NO_WINDOW,
download_config = {
"mode": "Proxy",
"thread_numb": Config.get(Config.update_ThreadNumb),
"proxy_list": list(
set(
Config.get(Config.update_ProxyUrlList)
+ download_info["proxy_list"]
)
),
"download_dict": download_info["download_dict"],
}
self.downloader = DownloadManager(
Config.app_path, "AUTO_MAA", remote_version, download_config
)
self.window().close()
self.downloader.setWindowTitle("AUTO_MAA更新器")
self.downloader.setWindowIcon(
QIcon(str(Config.app_path / "resources/icons/AUTO_MAA_Updater.ico"))
)
self.downloader.download_accomplish.connect(self.start_setup)
self.downloader.show()
self.downloader.run()
# 无版本更新
else:
MainInfoBar.push_info_bar("success", "更新检查", "已是最新版本~", 3000)
elif (
if_show
or if_first
or version.parse(version_text(remote_version))
> version.parse(version_text(current_version))
):
def show_notice(self, if_show: bool = True) -> None:
if version.parse(version_text(remote_version)) > version.parse(
version_text(current_version)
):
MainInfoBar.push_info_bar(
"info",
"发现新版本",
f"{version_text(current_version)} --> {version_text(remote_version)}",
3600000,
)
else:
MainInfoBar.push_info_bar("success", "更新检查", "已是最新版本~", 3000)
def start_setup(self) -> None:
subprocess.Popen(
[
Config.app_path / "AUTO_MAA-Setup.exe",
"/SP-",
"/SILENT",
"/NOCANCEL",
"/FORCECLOSEAPPLICATIONS",
"/LANG=Chinese",
f"/DIR={Config.app_path}",
],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
System.set_power("KillSelf")
def show_notice(self, if_show: bool = False, if_first: bool = False) -> None:
"""显示公告"""
# 从远程服务器获取最新公告
@@ -452,9 +524,11 @@ class Setting(QWidget):
}
if if_show or (
datetime.now()
if_first
and datetime.now()
> datetime.strptime(notice["time"], "%Y-%m-%d %H:%M")
> time_local
and not Config.get(Config.function_UnattendedMode)
):
choice = NoticeMessageBox(self.window(), "公告", notice["notice_dict"])
@@ -466,6 +540,17 @@ class Setting(QWidget):
) as f:
json.dump(notice, f, ensure_ascii=False, indent=4)
elif (
datetime.now()
> datetime.strptime(notice["time"], "%Y-%m-%d %H:%M")
> time_local
):
MainInfoBar.push_info_bar(
"info", "有新公告", "请前往设置界面查看公告", 3600000
)
return None
class FunctionSettingCard(HeaderCardWidget):
@@ -486,7 +571,7 @@ class FunctionSettingCard(HeaderCardWidget):
icon=FluentIcon.PAGE_RIGHT,
title="历史记录保留时间",
content="选择历史记录的保留时间,超期自动清理",
texts=["7 天", "15 天", "30 天", "60 天", "永久"],
texts=["7 天", "15 天", "30 天", "60 天", "90 天", "半年", "一年", "永久"],
qconfig=Config,
configItem=Config.function_HistoryRetentionTime,
parent=self,
@@ -500,6 +585,14 @@ class FunctionSettingCard(HeaderCardWidget):
parent=self,
)
self.card_IfSilence = self.SilenceSettingCard(self)
self.card_UnattendedMode = SwitchSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="无人值守模式",
content="开启后AUTO_MAA不再主动弹出对话框以免影响代理任务运行",
qconfig=Config,
configItem=Config.function_UnattendedMode,
parent=self,
)
self.card_IfAgreeBilibili = SwitchSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="托管bilibili游戏隐私政策",
@@ -522,6 +615,7 @@ class FunctionSettingCard(HeaderCardWidget):
Layout.addWidget(self.card_HistoryRetentionTime)
Layout.addWidget(self.card_IfAllowSleep)
Layout.addWidget(self.card_IfSilence)
Layout.addWidget(self.card_UnattendedMode)
Layout.addWidget(self.card_IfAgreeBilibili)
Layout.addWidget(self.card_IfSkipMumuSplashAds)
self.viewLayout.addLayout(Layout)
@@ -997,9 +1091,9 @@ class OtherSettingCard(HeaderCardWidget):
)
self.card_UserDocs = HyperlinkCard(
url="https://clozya.github.io/AUTOMAA_docs",
text="访问",
text="查看指南",
icon=FluentIcon.PAGE_RIGHT,
title="AUTO_MAA官方文档站",
title="用户指南",
content="访问AUTO_MAA的官方文档站获取使用指南和项目相关信息",
parent=self,
)

88
app/utils/AUTO_MAA.iss Normal file
View File

@@ -0,0 +1,88 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "AUTO_MAA"
#define MyAppVersion ""
#define MyAppPublisher "AUTO_MAA Team"
#define MyAppURL "https://doc.automaa.xyz/"
#define MyAppExeName "AUTO_MAA.exe"
#define MyAppPath ""
#define OutputDir ""
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{D116A92A-E174-4699-B777-61C5FD837B19}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName=D:\{#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName}
; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run
; on anything but x64 and Windows 11 on Arm.
ArchitecturesAllowed=x64compatible
; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the
; install be done in "64-bit mode" on x64 or Windows 11 on Arm,
; meaning it should use the native 64-bit Program Files directory and
; the 64-bit view of the registry.
ArchitecturesInstallIn64BitMode=x64compatible
DisableProgramGroupPage=yes
LicenseFile={#MyAppPath}\LICENSE
; Remove the following line to run in administrative install mode (install for all users).
PrivilegesRequired=lowest
OutputDir={#OutputDir}
OutputBaseFilename=AUTO_MAA-Setup
SetupIconFile={#MyAppPath}\resources\icons\AUTO_MAA.ico
SolidCompression=yes
WizardStyle=modern
[Languages]
Name: "Chinese"; MessagesFile: "{#MyAppPath}\resources\docs\ChineseSimplified.isl"
Name: "English"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "{#MyAppPath}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\app\*"; DestDir: "{app}\app"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#MyAppPath}\resources\*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#MyAppPath}\main.py"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\requirements.txt"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\README.md"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppPath}\LICENSE"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall
[Code]
var
DeleteDataQuestion: Boolean;
function InitializeUninstall: Boolean;
begin
DeleteDataQuestion := MsgBox('您确认要完全移除 AUTO_MAA 的所有用户数据文件与子组件吗?', mbConfirmation, MB_YESNO) = IDYES;
Result := True;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall then
begin
DelTree(ExpandConstant('{app}\app'), True, True, True);
DelTree(ExpandConstant('{app}\resources'), True, True, True);
if DeleteDataQuestion then
begin
DelTree(ExpandConstant('{app}'), True, True, True);
end;
end;
end;

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -29,6 +29,4 @@ __version__ = "4.2.0"
__author__ = "DLmaster361 <DLmaster_361@163.com>"
__license__ = "GPL-3.0 license"
from .downloader import DownloadManager
__all__ = ["DownloadManager"]
__all__ = []

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -66,7 +66,6 @@ if __name__ == "__main__":
version = json.load(f)
main_version_numb = list(map(int, version["main_version"].split(".")))
updater_version_numb = list(map(int, version["updater_version"].split(".")))
print("Packaging AUTO_MAA main program ...")
@@ -79,42 +78,17 @@ if __name__ == "__main__":
f" --file-version={version["main_version"]}"
f" --product-version={version["main_version"]}"
" --file-description='AUTO_MAA Component'"
" --copyright='Copyright © 2024 DLmaster361'"
" --copyright='Copyright © 2024-2025 DLmaster361'"
" --assume-yes-for-downloads --output-filename=AUTO_MAA"
" --remove-output main.py"
)
print("AUTO_MAA main program packaging completed !")
print("Packaging AUTO_MAA update program ...")
shutil.copy(root_path / "app/utils/downloader.py", root_path)
os.system(
"powershell -Command python -m nuitka --standalone --onefile --mingw64"
" --enable-plugins=pyside6 --windows-console-mode=disable"
" --onefile-tempdir-spec='{TEMP}\\AUTO_MAA_Updater'"
" --windows-icon-from-ico=resources\\icons\\AUTO_MAA_Updater.ico"
" --company-name='AUTO_MAA Team' --product-name=AUTO_MAA"
f" --file-version={version["updater_version"]}"
f" --product-version={version["main_version"]}"
" --file-description='AUTO_MAA Component'"
" --copyright='Copyright © 2024 DLmaster361'"
" --assume-yes-for-downloads --output-filename=AUTO_Updater"
" --remove-output downloader.py"
)
(root_path / "downloader.py").unlink()
print("AUTO_MAA update program packaging completed !")
print("start to create setup program ...")
(root_path / "AUTO_MAA").mkdir(parents=True, exist_ok=True)
print("Start to move AUTO_MAA program ...")
shutil.move(root_path / "AUTO_MAA.exe", root_path / "AUTO_MAA/")
shutil.move(root_path / "AUTO_Updater.exe", root_path / "AUTO_MAA/")
print("Start to copy rescourses ...")
shutil.copytree(root_path / "app", root_path / "AUTO_MAA/app")
shutil.copytree(root_path / "resources", root_path / "AUTO_MAA/resources")
shutil.copy(root_path / "main.py", root_path / "AUTO_MAA/")
@@ -122,17 +96,38 @@ if __name__ == "__main__":
shutil.copy(root_path / "README.md", root_path / "AUTO_MAA/")
shutil.copy(root_path / "LICENSE", root_path / "AUTO_MAA/")
print("Start to compress ...")
with (root_path / "app/utils/AUTO_MAA.iss").open(mode="r", encoding="utf-8") as f:
iss = f.read()
iss = (
iss.replace(
'#define MyAppVersion ""',
f'#define MyAppVersion "{version["main_version"]}"',
)
.replace(
'#define MyAppPath ""', f'#define MyAppPath "{root_path / "AUTO_MAA"}"'
)
.replace('#define OutputDir ""', f'#define OutputDir "{root_path}"')
)
with (root_path / "AUTO_MAA.iss").open(mode="w", encoding="utf-8") as f:
f.write(iss)
os.system(f'ISCC "{root_path / "AUTO_MAA.iss"}"')
(root_path / "AUTO_MAA_Setup").mkdir(parents=True, exist_ok=True)
shutil.move(root_path / "AUTO_MAA-Setup.exe", root_path / "AUTO_MAA_Setup")
shutil.make_archive(
base_name=root_path / f"AUTO_MAA_{version_text(main_version_numb)}",
format="zip",
root_dir=root_path / "AUTO_MAA",
root_dir=root_path / "AUTO_MAA_Setup",
base_dir=".",
)
shutil.rmtree(root_path / "AUTO_MAA")
print("compress completed !")
print("setup program created !")
(root_path / "AUTO_MAA.iss").unlink(missing_ok=True)
shutil.rmtree(root_path / "AUTO_MAA")
shutil.rmtree(root_path / "AUTO_MAA_Setup")
all_version_info = {}
for v_i in version["version_info"].values():
@@ -143,6 +138,6 @@ if __name__ == "__main__":
all_version_info[key] = value.copy()
(root_path / "version_info.txt").write_text(
f"{version_text(main_version_numb)}\n{version_text(updater_version_numb)}\n<!--{json.dumps(version["version_info"], ensure_ascii=False)}-->\n{version_info_markdown(all_version_info)}",
f"{version_text(main_version_numb)}\n\n<!--{json.dumps(version["version_info"], ensure_ascii=False)}-->\n{version_info_markdown(all_version_info)}",
encoding="utf-8",
)

View File

@@ -1,5 +1,5 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# DLmaster_361@163.com
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
@@ -27,7 +27,6 @@ v4.3
from loguru import logger
from PySide6.QtWidgets import QApplication
from PySide6.QtCore import Qt
from qfluentwidgets import FluentTranslator
import sys
@@ -36,7 +35,6 @@ import sys
def main():
application = QApplication(sys.argv)
QApplication.setAttribute(Qt.AA_DontCreateNativeWidgetSiblings)
translator = FluentTranslator()
application.installTranslator(translator)
@@ -45,6 +43,7 @@ def main():
window = AUTO_MAA()
window.show_ui("显示主窗口")
window.show_ui("配置托盘")
window.start_up_task()
sys.exit(application.exec())

View File

@@ -10,5 +10,4 @@ pycryptodome
requests
markdown
Jinja2
serverchan_sdk
nuitka

View File

@@ -0,0 +1,403 @@
; *** Inno Setup version 6.4.0+ Chinese Simplified messages ***
;
; To download user-contributed translations of this file, go to:
; https://jrsoftware.org/files/istrans/
;
; Note: When translating this text, do not add periods (.) to the end of
; messages that didn't have them already, because on those messages Inno
; Setup adds the periods automatically (appending a period would result in
; two periods being displayed).
;
; Maintained by Zhenghan Yang
; Email: 847320916@QQ.com
; Translation based on network resource
; The latest Translation is on https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation
;
[LangOptions]
; The following three entries are very important. Be sure to read and
; understand the '[LangOptions] section' topic in the help file.
LanguageName=简体中文
; If Language Name display incorrect, uncomment next line
; LanguageName=<7B80><4F53><4E2D><6587>
; About LanguageID, to reference link:
; https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c
LanguageID=$0804
; About CodePage, to reference link:
; https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
LanguageCodePage=936
; If the language you are translating to requires special font faces or
; sizes, uncomment any of the following entries and change them accordingly.
;DialogFontName=
;DialogFontSize=8
;WelcomeFontName=Verdana
;WelcomeFontSize=12
;TitleFontName=Arial
;TitleFontSize=29
;CopyrightFontName=Arial
;CopyrightFontSize=8
[Messages]
; *** 应用程序标题
SetupAppTitle=安装
SetupWindowTitle=安装 - %1
UninstallAppTitle=卸载
UninstallAppFullTitle=%1 卸载
; *** Misc. common
InformationTitle=信息
ConfirmTitle=确认
ErrorTitle=错误
; *** SetupLdr messages
SetupLdrStartupMessage=现在将安装 %1。您想要继续吗
LdrCannotCreateTemp=无法创建临时文件。安装程序已中止
LdrCannotExecTemp=无法执行临时目录中的文件。安装程序已中止
HelpTextNote=
; *** 启动错误消息
LastErrorMessage=%1。%n%n错误 %2: %3
SetupFileMissing=安装目录中缺少文件 %1。请修正这个问题或者获取程序的新副本。
SetupFileCorrupt=安装文件已损坏。请获取程序的新副本。
SetupFileCorruptOrWrongVer=安装文件已损坏,或是与这个安装程序的版本不兼容。请修正这个问题或获取新的程序副本。
InvalidParameter=无效的命令行参数:%n%n%1
SetupAlreadyRunning=安装程序正在运行。
WindowsVersionNotSupported=此程序不支持当前计算机运行的 Windows 版本。
WindowsServicePackRequired=此程序需要 %1 服务包 %2 或更高版本。
NotOnThisPlatform=此程序不能在 %1 上运行。
OnlyOnThisPlatform=此程序只能在 %1 上运行。
OnlyOnTheseArchitectures=此程序只能安装到为下列处理器架构设计的 Windows 版本中:%n%n%1
WinVersionTooLowError=此程序需要 %1 版本 %2 或更高。
WinVersionTooHighError=此程序不能安装于 %1 版本 %2 或更高。
AdminPrivilegesRequired=在安装此程序时您必须以管理员身份登录。
PowerUserPrivilegesRequired=在安装此程序时您必须以管理员身份或有权限的用户组身份登录。
SetupAppRunningError=安装程序发现 %1 当前正在运行。%n%n请先关闭正在运行的程序然后点击“确定”继续或点击“取消”退出。
UninstallAppRunningError=卸载程序发现 %1 当前正在运行。%n%n请先关闭正在运行的程序然后点击“确定”继续或点击“取消”退出。
; *** 启动问题
PrivilegesRequiredOverrideTitle=选择安装程序模式
PrivilegesRequiredOverrideInstruction=选择安装模式
PrivilegesRequiredOverrideText1=%1 可以为所有用户安装(需要管理员权限),或仅为您安装。
PrivilegesRequiredOverrideText2=%1 只能为您安装,或为所有用户安装(需要管理员权限)。
PrivilegesRequiredOverrideAllUsers=为所有用户安装(&A)
PrivilegesRequiredOverrideAllUsersRecommended=为所有用户安装(&A) (建议选项)
PrivilegesRequiredOverrideCurrentUser=只为我安装(&M)
PrivilegesRequiredOverrideCurrentUserRecommended=只为我安装(&M) (建议选项)
; *** 其他错误
ErrorCreatingDir=安装程序无法创建目录“%1”
ErrorTooManyFilesInDir=无法在目录“%1”中创建文件因为里面包含太多文件
; *** 安装程序公共消息
ExitSetupTitle=退出安装程序
ExitSetupMessage=安装程序尚未完成。如果现在退出,将不会安装该程序。%n%n您之后可以再次运行安装程序完成安装。%n%n现在退出安装程序吗
AboutSetupMenuItem=关于安装程序(&A)...
AboutSetupTitle=关于安装程序
AboutSetupMessage=%1 版本 %2%n%3%n%n%1 主页:%n%4
AboutSetupNote=
TranslatorNote=简体中文翻译由Kira(847320916@qq.com)维护。项目地址https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation
; *** 按钮
ButtonBack=< 上一步(&B)
ButtonNext=下一步(&N) >
ButtonInstall=安装(&I)
ButtonOK=确定
ButtonCancel=取消
ButtonYes=是(&Y)
ButtonYesToAll=全是(&A)
ButtonNo=否(&N)
ButtonNoToAll=全否(&O)
ButtonFinish=完成(&F)
ButtonBrowse=浏览(&B)...
ButtonWizardBrowse=浏览(&R)...
ButtonNewFolder=新建文件夹(&M)
; *** “选择语言”对话框消息
SelectLanguageTitle=选择安装语言
SelectLanguageLabel=选择安装时使用的语言。
; *** 公共向导文字
ClickNext=点击“下一步”继续,或点击“取消”退出安装程序。
BeveledLabel=
BrowseDialogTitle=浏览文件夹
BrowseDialogLabel=在下面的列表中选择一个文件夹,然后点击“确定”。
NewFolderName=新建文件夹
; *** “欢迎”向导页
WelcomeLabel1=欢迎使用 [name] 安装向导
WelcomeLabel2=现在将安装 [name/ver] 到您的电脑中。%n%n建议您在继续安装前关闭所有其他应用程序。
; *** “密码”向导页
WizardPassword=密码
PasswordLabel1=这个安装程序有密码保护。
PasswordLabel3=请输入密码,然后点击“下一步”继续。密码区分大小写。
PasswordEditLabel=密码(&P)
IncorrectPassword=您输入的密码不正确,请重新输入。
; *** “许可协议”向导页
WizardLicense=许可协议
LicenseLabel=请在继续安装前阅读以下重要信息。
LicenseLabel3=请仔细阅读下列许可协议。在继续安装前您必须同意这些协议条款。
LicenseAccepted=我同意此协议(&A)
LicenseNotAccepted=我不同意此协议(&D)
; *** “信息”向导页
WizardInfoBefore=信息
InfoBeforeLabel=请在继续安装前阅读以下重要信息。
InfoBeforeClickLabel=准备好继续安装后,点击“下一步”。
WizardInfoAfter=信息
InfoAfterLabel=请在继续安装前阅读以下重要信息。
InfoAfterClickLabel=准备好继续安装后,点击“下一步”。
; *** “用户信息”向导页
WizardUserInfo=用户信息
UserInfoDesc=请输入您的信息。
UserInfoName=用户名(&U)
UserInfoOrg=组织(&O)
UserInfoSerial=序列号(&S)
UserInfoNameRequired=您必须输入用户名。
; *** “选择目标目录”向导页
WizardSelectDir=选择目标位置
SelectDirDesc=您想将 [name] 安装在哪里?
SelectDirLabel3=安装程序将安装 [name] 到下面的文件夹中。
SelectDirBrowseLabel=点击“下一步”继续。如果您想选择其他文件夹,点击“浏览”。
DiskSpaceGBLabel=至少需要有 [gb] GB 的可用磁盘空间。
DiskSpaceMBLabel=至少需要有 [mb] MB 的可用磁盘空间。
CannotInstallToNetworkDrive=安装程序无法安装到一个网络驱动器。
CannotInstallToUNCPath=安装程序无法安装到一个 UNC 路径。
InvalidPath=您必须输入一个带驱动器卷标的完整路径,例如:%n%nC:\APP%n%n或UNC路径%n%n\\server\share
InvalidDrive=您选定的驱动器或 UNC 共享不存在或不能访问。请选择其他位置。
DiskSpaceWarningTitle=磁盘空间不足
DiskSpaceWarning=安装程序至少需要 %1 KB 的可用空间才能安装,但选定驱动器只有 %2 KB 的可用空间。%n%n您一定要继续吗
DirNameTooLong=文件夹名称或路径太长。
InvalidDirName=文件夹名称无效。
BadDirName32=文件夹名称不能包含下列任何字符:%n%n%1
DirExistsTitle=文件夹已存在
DirExists=文件夹:%n%n%1%n%n已经存在。您一定要安装到这个文件夹中吗
DirDoesntExistTitle=文件夹不存在
DirDoesntExist=文件夹:%n%n%1%n%n不存在。您想要创建此文件夹吗
; *** “选择组件”向导页
WizardSelectComponents=选择组件
SelectComponentsDesc=您想安装哪些程序组件?
SelectComponentsLabel2=选中您想安装的组件;取消您不想安装的组件。然后点击“下一步”继续。
FullInstallation=完全安装
; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
CompactInstallation=简洁安装
CustomInstallation=自定义安装
NoUninstallWarningTitle=组件已存在
NoUninstallWarning=安装程序检测到下列组件已安装在您的电脑中:%n%n%1%n%n取消选中这些组件不会卸载它们。%n%n确定要继续吗
ComponentSize1=%1 KB
ComponentSize2=%1 MB
ComponentsDiskSpaceGBLabel=当前选择的组件需要至少 [gb] GB 的磁盘空间。
ComponentsDiskSpaceMBLabel=当前选择的组件需要至少 [mb] MB 的磁盘空间。
; *** “选择附加任务”向导页
WizardSelectTasks=选择附加任务
SelectTasksDesc=您想要安装程序执行哪些附加任务?
SelectTasksLabel2=选择您想要安装程序在安装 [name] 时执行的附加任务,然后点击“下一步”。
; *** “选择开始菜单文件夹”向导页
WizardSelectProgramGroup=选择开始菜单文件夹
SelectStartMenuFolderDesc=安装程序应该在哪里放置程序的快捷方式?
SelectStartMenuFolderLabel3=安装程序将在下列“开始”菜单文件夹中创建程序的快捷方式。
SelectStartMenuFolderBrowseLabel=点击“下一步”继续。如果您想选择其他文件夹,点击“浏览”。
MustEnterGroupName=您必须输入一个文件夹名。
GroupNameTooLong=文件夹名或路径太长。
InvalidGroupName=无效的文件夹名字。
BadGroupName=文件夹名不能包含下列任何字符:%n%n%1
NoProgramGroupCheck2=不创建开始菜单文件夹(&D)
; *** “准备安装”向导页
WizardReady=准备安装
ReadyLabel1=安装程序准备就绪,现在可以开始安装 [name] 到您的电脑。
ReadyLabel2a=点击“安装”继续此安装程序。如果您想重新考虑或修改任何设置,点击“上一步”。
ReadyLabel2b=点击“安装”继续此安装程序。
ReadyMemoUserInfo=用户信息:
ReadyMemoDir=目标位置:
ReadyMemoType=安装类型:
ReadyMemoComponents=已选择组件:
ReadyMemoGroup=开始菜单文件夹:
ReadyMemoTasks=附加任务:
; *** TExtractionWizardPage wizard page and Extract7ZipArchive
ExtractionLabel=正在提取附加文件...
ButtonStopExtraction=停止提取(&S)
StopExtraction=您确定要停止提取吗?
ErrorExtractionAborted=提取已中止
ErrorExtractionFailed=提取失败:%1
; *** TDownloadWizardPage wizard page and DownloadTemporaryFile
DownloadingLabel=正在下载附加文件...
ButtonStopDownload=停止下载(&S)
StopDownload=您确定要停止下载吗?
ErrorDownloadAborted=下载已中止
ErrorDownloadFailed=下载失败:%1 %2
ErrorDownloadSizeFailed=获取下载大小失败:%1 %2
ErrorFileHash1=校验文件哈希失败:%1
ErrorFileHash2=无效的文件哈希:预期 %1实际 %2
ErrorProgress=无效的进度:%1 / %2
ErrorFileSize=文件大小错误:预期 %1实际 %2
; *** “正在准备安装”向导页
WizardPreparing=正在准备安装
PreparingDesc=安装程序正在准备安装 [name] 到您的电脑。
PreviousInstallNotCompleted=先前的程序安装或卸载未完成,您需要重启您的电脑以完成。%n%n在重启电脑后再次运行安装程序以完成 [name] 的安装。
CannotContinue=安装程序不能继续。请点击“取消”退出。
ApplicationsFound=以下应用程序正在使用将由安装程序更新的文件。建议您允许安装程序自动关闭这些应用程序。
ApplicationsFound2=以下应用程序正在使用将由安装程序更新的文件。建议您允许安装程序自动关闭这些应用程序。安装完成后,安装程序将尝试重新启动这些应用程序。
CloseApplications=自动关闭应用程序(&A)
DontCloseApplications=不要关闭应用程序(&D)
ErrorCloseApplications=安装程序无法自动关闭所有应用程序。建议您在继续之前,关闭所有在使用需要由安装程序更新的文件的应用程序。
PrepareToInstallNeedsRestart=安装程序必须重启您的计算机。计算机重启后,请再次运行安装程序以完成 [name] 的安装。%n%n是否立即重新启动
; *** “正在安装”向导页
WizardInstalling=正在安装
InstallingLabel=安装程序正在安装 [name] 到您的电脑,请稍候。
; *** “安装完成”向导页
FinishedHeadingLabel=[name] 安装完成
FinishedLabelNoIcons=安装程序已在您的电脑中安装了 [name]。
FinishedLabel=安装程序已在您的电脑中安装了 [name]。您可以通过已安装的快捷方式运行此应用程序。
ClickFinish=点击“完成”退出安装程序。
FinishedRestartLabel=为完成 [name] 的安装,安装程序必须重新启动您的电脑。要立即重启吗?
FinishedRestartMessage=为完成 [name] 的安装,安装程序必须重新启动您的电脑。%n%n要立即重启吗
ShowReadmeCheck=是,我想查阅自述文件
YesRadio=是,立即重启电脑(&Y)
NoRadio=否,稍后重启电脑(&N)
; used for example as 'Run MyProg.exe'
RunEntryExec=运行 %1
; used for example as 'View Readme.txt'
RunEntryShellExec=查阅 %1
; *** “安装程序需要下一张磁盘”提示
ChangeDiskTitle=安装程序需要下一张磁盘
SelectDiskLabel2=请插入磁盘 %1 并点击“确定”。%n%n如果这个磁盘中的文件可以在下列文件夹之外的文件夹中找到请输入正确的路径或点击“浏览”。
PathLabel=路径(&P)
FileNotInDir2=“%2”中找不到文件“%1”。请插入正确的磁盘或选择其他文件夹。
SelectDirectoryLabel=请指定下一张磁盘的位置。
; *** 安装状态消息
SetupAborted=安装程序未完成安装。%n%n请修正这个问题并重新运行安装程序。
AbortRetryIgnoreSelectAction=选择操作
AbortRetryIgnoreRetry=重试(&T)
AbortRetryIgnoreIgnore=忽略错误并继续(&I)
AbortRetryIgnoreCancel=关闭安装程序
; *** 安装状态消息
StatusClosingApplications=正在关闭应用程序...
StatusCreateDirs=正在创建目录...
StatusExtractFiles=正在解压缩文件...
StatusCreateIcons=正在创建快捷方式...
StatusCreateIniEntries=正在创建 INI 条目...
StatusCreateRegistryEntries=正在创建注册表条目...
StatusRegisterFiles=正在注册文件...
StatusSavingUninstall=正在保存卸载信息...
StatusRunProgram=正在完成安装...
StatusRestartingApplications=正在重启应用程序...
StatusRollback=正在撤销更改...
; *** 其他错误
ErrorInternal2=内部错误:%1
ErrorFunctionFailedNoCode=%1 失败
ErrorFunctionFailed=%1 失败;错误代码 %2
ErrorFunctionFailedWithMessage=%1 失败;错误代码 %2.%n%3
ErrorExecutingProgram=无法执行文件:%n%1
; *** 注册表错误
ErrorRegOpenKey=打开注册表项时出错:%n%1\%2
ErrorRegCreateKey=创建注册表项时出错:%n%1\%2
ErrorRegWriteKey=写入注册表项时出错:%n%1\%2
; *** INI 错误
ErrorIniEntry=在文件“%1”中创建 INI 条目时出错。
; *** 文件复制错误
FileAbortRetryIgnoreSkipNotRecommended=跳过此文件(&S) (不推荐)
FileAbortRetryIgnoreIgnoreNotRecommended=忽略错误并继续(&I) (不推荐)
SourceIsCorrupted=源文件已损坏
SourceDoesntExist=源文件“%1”不存在
ExistingFileReadOnly2=无法替换现有文件,它是只读的。
ExistingFileReadOnlyRetry=移除只读属性并重试(&R)
ExistingFileReadOnlyKeepExisting=保留现有文件(&K)
ErrorReadingExistingDest=尝试读取现有文件时出错:
FileExistsSelectAction=选择操作
FileExists2=文件已经存在。
FileExistsOverwriteExisting=覆盖已存在的文件(&O)
FileExistsKeepExisting=保留现有的文件(&K)
FileExistsOverwriteOrKeepAll=为所有冲突文件执行此操作(&D)
ExistingFileNewerSelectAction=选择操作
ExistingFileNewer2=现有的文件比安装程序将要安装的文件还要新。
ExistingFileNewerOverwriteExisting=覆盖已存在的文件(&O)
ExistingFileNewerKeepExisting=保留现有的文件(&K) (推荐)
ExistingFileNewerOverwriteOrKeepAll=为所有冲突文件执行此操作(&D)
ErrorChangingAttr=尝试更改下列现有文件的属性时出错:
ErrorCreatingTemp=尝试在目标目录创建文件时出错:
ErrorReadingSource=尝试读取下列源文件时出错:
ErrorCopying=尝试复制下列文件时出错:
ErrorReplacingExistingFile=尝试替换现有文件时出错:
ErrorRestartReplace=重启并替换失败:
ErrorRenamingTemp=尝试重命名下列目标目录中的一个文件时出错:
ErrorRegisterServer=无法注册 DLL/OCX%1
ErrorRegSvr32Failed=RegSvr32 失败;退出代码 %1
ErrorRegisterTypeLib=无法注册类库:%1
; *** 卸载显示名字标记
; used for example as 'My Program (32-bit)'
UninstallDisplayNameMark=%1 (%2)
; used for example as 'My Program (32-bit, All users)'
UninstallDisplayNameMarks=%1 (%2, %3)
UninstallDisplayNameMark32Bit=32 位
UninstallDisplayNameMark64Bit=64 位
UninstallDisplayNameMarkAllUsers=所有用户
UninstallDisplayNameMarkCurrentUser=当前用户
; *** 安装后错误
ErrorOpeningReadme=尝试打开自述文件时出错。
ErrorRestartingComputer=安装程序无法重启电脑,请手动重启。
; *** 卸载消息
UninstallNotFound=文件“%1”不存在。无法卸载。
UninstallOpenError=文件“%1”不能被打开。无法卸载。
UninstallUnsupportedVer=此版本的卸载程序无法识别卸载日志文件“%1”的格式。无法卸载
UninstallUnknownEntry=卸载日志中遇到一个未知条目 (%1)
ConfirmUninstall=您确认要完全移除 %1 及其所有组件吗?
UninstallOnlyOnWin64=仅允许在 64 位 Windows 中卸载此程序。
OnlyAdminCanUninstall=仅使用管理员权限的用户能完成此卸载。
UninstallStatusLabel=正在从您的电脑中移除 %1请稍候。
UninstalledAll=已顺利从您的电脑中移除 %1。
UninstalledMost=%1 卸载完成。%n%n有部分内容未能被删除但您可以手动删除它们。
UninstalledAndNeedsRestart=为完成 %1 的卸载,需要重启您的电脑。%n%n立即重启电脑吗
UninstallDataCorrupted=文件“%1”已损坏。无法卸载
; *** 卸载状态消息
ConfirmDeleteSharedFileTitle=删除共享的文件吗?
ConfirmDeleteSharedFile2=系统表示下列共享的文件已不有其他程序使用。您希望卸载程序删除这些共享的文件吗?%n%n如果删除这些文件但仍有程序在使用这些文件则这些程序可能出现异常。如果您不能确定请选择“否”在系统中保留这些文件以免引发问题。
SharedFileNameLabel=文件名:
SharedFileLocationLabel=位置:
WizardUninstalling=卸载状态
StatusUninstalling=正在卸载 %1...
; *** Shutdown block reasons
ShutdownBlockReasonInstallingApp=正在安装 %1。
ShutdownBlockReasonUninstallingApp=正在卸载 %1。
; The custom messages below aren't used by Setup itself, but if you make
; use of them in your scripts, you'll want to translate them.
[CustomMessages]
NameAndVersion=%1 版本 %2
AdditionalIcons=附加快捷方式:
CreateDesktopIcon=创建桌面快捷方式(&D)
CreateQuickLaunchIcon=创建快速启动栏快捷方式(&Q)
ProgramOnTheWeb=%1 网站
UninstallProgram=卸载 %1
LaunchProgram=运行 %1
AssocFileExtension=将 %2 文件扩展名与 %1 建立关联(&A)
AssocingFileExtension=正在将 %2 文件扩展名与 %1 建立关联...
AutoStartProgramGroupDescription=启动:
AutoStartProgram=自动启动 %1
AddonHostProgramNotFound=您选择的文件夹中无法找到 %1。%n%n您要继续吗

View File

@@ -6,10 +6,18 @@
"TaskQueue.Recruiting.IsChecked": "True" #自动公招
"TaskQueue.Base.IsChecked": "True" #基建换班
"TaskQueue.Combat.IsChecked": "True" #刷理智
"TaskQueue.Mission.IsChecked": "True" #领取奖励
"TaskQueue.Mall.IsChecked": "True" #获取信用及购物
"TaskQueue.Mission.IsChecked": "True" #领取奖励
"TaskQueue.AutoRoguelike.IsChecked": "False" #自动肉鸽
"TaskQueue.Reclamation.IsChecked": "False" #生息演算
"TaskQueue.Order.WakeUp": "0"
"TaskQueue.Order.Recruiting": "1"
"TaskQueue.Order.Base": "2"
"TaskQueue.Order.Combat": "3"
"TaskQueue.Order.Mall": "4"
"TaskQueue.Order.Mission": "5"
"TaskQueue.Order.AutoRoguelike": "6"
"TaskQueue.Order.Reclamation": "7"
#刷理智
"MainFunction.UseMedicine": "True" #吃理智药
"MainFunction.UseMedicine.Quantity": "999" #吃理智药数量
@@ -30,8 +38,8 @@
"Penguin.EnablePenguin": "True" #上报企鹅物流
"Yituliu.EnableYituliu": "True" #上报一图流
#基建换班
"Infrast.CustomInfrastEnabled": "True" #启用自定义基建配置
"Infrast.CustomInfrastPlanIndex": "1" #自定义基建配置索引
"Infrast.InfrastMode": "Normal"、"Rotation"、"Custom" #基建模式
"Infrast.CustomInfrastPlanIndex": "1" #自定义基建配置索引
"Infrast.DefaultInfrast": "user_defined" #内置配置
"Infrast.IsCustomInfrastFileReadOnly": "False" #自定义基建配置文件只读
"Infrast.CustomInfrastFile": "" #自定义基建配置文件地址
@@ -49,4 +57,5 @@ G"Start.MinimizeDirectly": "True" #启动MAA后直接最小化
G"GUI.UseTray": "True" #显示托盘图标
G"GUI.MinimizeToTray": "False" #最小化时隐藏至托盘
"Start.EmulatorPath" #模拟器路径
"Start.EmulatorAddCommand": "-v 2" #附加命令
"Start.EmulatorAddCommand": "-v 2" #附加命令
G"VersionUpdate.package": "MirrorChyanAppv5.15.6.zip" #更新包标识

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,57 +1,31 @@
{
"main_version": "4.3.4.2",
"updater_version": "1.0.0.0",
"announcement": "\n## 新增功能\n- 屏蔽MuMu模拟器开屏广告功能上线\n- 更新器支持多线程下载\n- 添加强制关闭ADB与模拟器等增强任务项\n## 修复BUG\n- 修复统计信息HTML模板公招匹配错误\n- 修复密码显示按钮动画异常\n- 修复`检测到MAA未能实际执行任务`报错被异常屏蔽\n- 修复MAA超时判定异常失效\n## 程序优化\n- 关机等电源操作添加100s倒计时\n- 人工排查弹窗方法优化\n- 人工排查时自动屏蔽静默操作\n- 公告样式优化",
"main_version": "4.3.8.2",
"version_info": {
"4.3.4.2": {
"程序优化": [
"调度队列历史记录归入配置类管理",
"添加.gitignore",
"工作流删除冗余部分",
"自动代理与人工排查结束后MAA恢复到全局配置 #40",
"网络相关操作由子线程执行"
"4.3.8.2": {
"修复bug": [
"日志分析忽略MAA超时提示"
]
},
"4.3.4.1": {
"4.3.8.1": {
"新增功能": [
"开始任务前自动释放ADB端口"
"自定义基建显示配置名称 #46",
"主调度台添加仅一次电源任务"
],
"修复bug": [
"电源相关选项改为所有任务完成后生效",
"适配MAAv5.16.3的ADB报错信息更改"
],
"程序优化": [
"request 添加超时限制",
"用户任务运行流程改进",
"自动代理中模拟器改由AUTO_MAA控制",
"修正部分配置项文案"
"UI样式优化进一步适配win10主题"
]
},
"4.3.3.0": {
"修复BUG": [
"修复更新器无法下载MAA的异常"
"4.3.7.0": {
"新增功能": [
"下载器支持完整mirrorc列表"
],
"程序优化": [
"自动发版改为手动触发"
]
},
"4.3.2.0": {
"修复BUG": [
"修复更新器无法启动的异常"
]
},
"4.3.1.0": {
"修复BUG": [
"覆盖规避v4.3.0错误包"
"重构更新逻辑,去除独立更新器"
]
}
},
"proxy_list": [
"https://gitproxy.click/",
"https://cdn.moran233.xyz/",
"https://gh.llkk.cc/",
"https://github.akams.cn/",
"https://www.ghproxy.cn/",
"https://ghfast.top/"
],
"download_dict": {
"官方下载站-jp": "https://jp-download.fearr.xyz/AUTO_MAA/",
"官方下载站-hw": "http://hwobs.fearr.xyz/releases/artifacts/"
}
}