Compare commits

...

28 Commits

Author SHA1 Message Date
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
DLmaster361
494b655156 Merge branch 'dev' 2025-04-20 00:02:42 +08:00
DLmaster361
2940f2557c Merge branch 'DLMS_dev' into dev 2025-04-20 00:02:25 +08:00
DLmaster361
5e4660670f chore: 性能优化
- 调度队列历史记录归入配置类管理
- 添加.gitignore
- 工作流删除冗余部分
- 自动代理与人工排查结束后MAA恢复到全局配置 #40
- 网络相关操作由子线程执行
2025-04-20 00:01:49 +08:00
e8d592ae76 refactor(app): 优化关卡掉落物品正则匹配 2025-04-16 16:58:22 +08:00
DLmaster361
97ea51df59 docs: README链接修复 2025-04-16 11:23:24 +08:00
DLmaster361
986061dc97 Merge branch 'DLMS_dev' into dev 2025-04-15 22:34:52 +08:00
DLmaster361
fe1910d16f fix: 修正部分配置项文案 2025-04-15 22:34:37 +08:00
DLmaster361
63cb1aaa74 feat: 自动代理流程优化 2025-04-15 22:15:59 +08:00
49ebd50077 refactor(ui): 优化模拟器老板键设置卡片的提示文本 2025-04-14 16:04:53 +08:00
DLmaster361
4a6f874210 fix: request 添加超时限制 2025-04-14 15:03:29 +08:00
34 changed files with 1668 additions and 996 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
@@ -102,20 +102,8 @@ jobs:
with:
name: version_info
path: ./
- name: Check if release exists
id: check_if_release_exists
run: |
release_id=$(gh release view $(sed 's/\r$//g' <(head -n 1 version_info.txt)) --json id --jq .id || true)
if [[ -z $release_id ]]; then
echo "release_exists=false" >> $GITHUB_OUTPUT
else
echo "release_exists=true" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
- name: Create release
id: create_release
if: steps.check_if_release_exists.outputs.release_exists == 'false'
run: |
set -xe
shopt -s nullglob
@@ -124,22 +112,12 @@ jobs:
NOTES_MAIN="$(sed 's/\r$//g' <(tail -n +3 version_info.txt))"
NOTES_TAIL="\`\`\`本release通过GitHub Actions自动构建\`\`\`"
NOTES="$NOTES_MAIN<br><br>$NOTES_TAIL"
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
- name: Update release
id: update_release
if: steps.check_if_release_exists.outputs.release_exists == 'true'
run: |
set -xe
shopt -s nullglob
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"
gh release delete "$TAGNAME" --yes
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" artifacts/*
if [ "${{ github.ref_name }}" == "main" ]; then
PRERELEASE_FLAG=""
else
PRERELEASE_FLAG="--prerelease"
fi
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" $PRERELEASE_FLAG artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
- name: Trigger MirrorChyanUploading

View File

@@ -1,174 +0,0 @@
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
# Copyright © <2024> <DLmaster361>
# This file is part of AUTO_MAA.
# AUTO_MAA is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
# AUTO_MAA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License for more details.
# 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
name: Build AUTO_MAA_Pre
on:
workflow_dispatch:
permissions:
contents: write
actions: write
jobs:
pre_check:
name: Pre Checks
runs-on: ubuntu-latest
steps:
- name: Repo Check
id: repo_check
run: |
if [[ "$GITHUB_REPOSITORY" != "DLmaster361/AUTO_MAA" ]]; then
echo "When forking this repository to make your own builds, you have to adjust this check."
exit 1
fi
exit 0
build_AUTO_MAA:
runs-on: windows-latest
needs: pre_check
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install -r requirements.txt
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Package
id: package
run: |
copy app\utils\package.py .\
python package.py
- name: Read version
id: read_version
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
- name: Upload Version_Info Artifact
uses: actions/upload-artifact@v4
with:
name: version_info
path: version_info.txt
publish_prerelease:
name: Publish prerelease
needs: build_AUTO_MAA
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
with:
pattern: AUTO_MAA_*
merge-multiple: true
path: artifacts
- name: Download Version_Info
uses: actions/download-artifact@v4
with:
name: version_info
path: ./
- name: Check if release exists
id: check_if_release_exists
run: |
release_id=$(gh release view $(sed 's/\r$//g' <(head -n 1 version_info.txt)) --json id --jq .id || true)
if [[ -z $release_id ]]; then
echo "release_exists=false" >> $GITHUB_OUTPUT
else
echo "release_exists=true" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
- name: Create prerelease
id: create_prerelease
if: steps.check_if_release_exists.outputs.release_exists == 'false'
run: |
set -xe
shopt -s nullglob
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"
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" --prerelease artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
- name: Update prerelease
id: update_prerelease
if: steps.check_if_release_exists.outputs.release_exists == 'true'
run: |
set -xe
shopt -s nullglob
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"
gh release delete "$TAGNAME" --yes
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" --prerelease artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
- name: Trigger MirrorChyanUploading
run: |
gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan
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

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
__pycache__/
config/
data/
debug/
history/
resources/notice.json
resources/theme_image.json
resources/images/Home/BannerTheme.jpg

View File

@@ -10,9 +10,11 @@ MAA多账号管理与自动化软件
[![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 Downloads](https://img.shields.io/github/downloads/DLmaster361/AUTO_MAA/total?style=flat-square)](https://github.com/DLmaster361/AUTO_MAA/releases/latest)
[![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)
[![mirrorc](https://img.shields.io/badge/Mirror%E9%85%B1-%239af3f6?logo=countingworkspro&logoColor=4f46e5)](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA)
</div>
## 软件介绍
@@ -98,4 +100,4 @@ MAA多账号管理与自动化软件
如果喜欢这个项目的话,给作者来杯咖啡吧!
![payid](https://github.com/DLmaster361/AUTO_MAA/blob/main/resources/README/payid.png "payid")
![payid](resources/images/README/payid.png "payid")

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
@@ -31,6 +31,7 @@ __license__ = "GPL-3.0 license"
from .config import QueueConfig, MaaConfig, MaaUserConfig, Config
from .main_info_bar import MainInfoBar
from .network import Network
from .task_manager import Task, TaskManager
from .timer import MainTimer
@@ -40,6 +41,7 @@ __all__ = [
"MaaConfig",
"MaaUserConfig",
"MainInfoBar",
"Network",
"Task",
"TaskManager",
"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
@@ -32,10 +32,8 @@ import json
import sys
import shutil
import re
import requests
import time
import base64
from datetime import datetime, timedelta
from datetime import datetime, timedelta, date
from collections import defaultdict
from pathlib import Path
from qfluentwidgets import (
@@ -53,6 +51,8 @@ from qfluentwidgets import (
from urllib.parse import urlparse
from typing import Union, Dict, List
from .network import Network
class UrlListValidator(ConfigValidator):
"""Url list validator"""
@@ -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()
@@ -319,6 +322,13 @@ class QueueConfig(QConfig):
self.queue_Member_9 = OptionsConfigItem("Queue", "Member_9", "禁用")
self.queue_Member_10 = OptionsConfigItem("Queue", "Member_10", "禁用")
self.Data_LastProxyTime = ConfigItem(
"Data", "LastProxyTime", "2000-01-01 00:00:00"
)
self.Data_LastProxyHistory = ConfigItem(
"Data", "LastProxyHistory", "暂无历史运行记录"
)
def toDict(self, serialize=True):
"""convert config items to `dict`"""
items = {}
@@ -399,27 +409,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`"""
@@ -511,17 +518,22 @@ 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 = ConfigItem("Info", "SeriesNumb", 1, RangeValidator(1, 6))
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(
@@ -531,6 +543,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`"""
@@ -599,7 +614,7 @@ class MaaUserConfig(QConfig):
class AppConfig(GlobalConfig):
VERSION = "4.3.3.0"
VERSION = "4.3.5.0"
gameid_refreshed = Signal()
PASSWORD_refreshed = Signal()
@@ -614,7 +629,6 @@ class AppConfig(GlobalConfig):
self.log_path = self.app_path / "debug/AUTO_MAA.log"
self.database_path = self.app_path / "data/data.db"
self.config_path = self.app_path / "config/config.json"
self.history_path = self.app_path / "history/main.json"
self.key_path = self.app_path / "data/key"
self.gameid_path = self.app_path / "data/gameid.txt"
self.version_path = self.app_path / "resources/version.json"
@@ -675,20 +689,18 @@ class AppConfig(GlobalConfig):
def get_gameid(self) -> None:
# 从MAA服务器获取活动关卡信息
for _ in range(3):
try:
response = requests.get(
"https://ota.maa.plus/MaaAssistantArknights/api/gui/StageActivity.json"
)
gameid_infos: List[
Dict[str, Union[str, Dict[str, Union[str, int]]]]
] = response.json()["Official"]["sideStoryStage"]
break
except Exception as e:
err = e
time.sleep(0.1)
Network.set_info(
mode="get",
url="https://ota.maa.plus/MaaAssistantArknights/api/gui/StageActivity.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
gameid_infos: List[Dict[str, Union[str, Dict[str, Union[str, int]]]]] = (
Network.response_json["Official"]["sideStoryStage"]
)
else:
logger.warning(f"无法从MAA服务器获取活动关卡信息:{err}")
logger.warning(f"无法从MAA服务器获取活动关卡信息:{Network.error_message}")
gameid_infos = []
gameid_dict = {"value": [], "text": []}
@@ -748,7 +760,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]},
@@ -783,13 +795,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:
"""检查用户数据文件并处理数据文件版本更新"""
@@ -1282,11 +1294,28 @@ 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 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),
@@ -1367,7 +1396,7 @@ class AppConfig(GlobalConfig):
# 如果已经找到了关卡,处理掉落物
if current_stage:
item_match: List[str] = re.findall(
r"^(?!\[)([\u4e00-\u9fa5A-Za-z0-9\-]+)\s*:\s*([\d,]+)(?:\s*\(\+[\d,]+\))?",
r"^(?!\[)(\S+?)\s*:\s*([\d,]+)(?:\s*\(\+[\d,]+\))?",
line,
re.M,
)
@@ -1449,9 +1478,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 == "所有项":
@@ -1474,9 +1503,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())}
@@ -1507,7 +1548,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 = {}
@@ -1520,43 +1563,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:
"""保存历史记录"""
history = {}
if self.history_path.exists():
with self.history_path.open(mode="r", encoding="utf-8") as f:
history = json.load(f)
history[key] = content
with self.history_path.open(mode="w", encoding="utf-8") as f:
json.dump(history, f, ensure_ascii=False, indent=4)
def get_history(self, key: str) -> dict:
"""获取历史记录"""
history = {}
if self.history_path.exists():
with self.history_path.open(mode="r", encoding="utf-8") as f:
history = json.load(f)
return history.get(
key, {"Time": "0000-00-00 00:00", "History": "暂无历史运行记录"}
)
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
@@ -27,10 +27,7 @@ v4.3
from loguru import logger
from PySide6.QtCore import Qt
from qfluentwidgets import (
InfoBar,
InfoBarPosition,
)
from qfluentwidgets import InfoBar, InfoBarPosition
class _MainInfoBar:

120
app/core/network.py Normal file
View File

@@ -0,0 +1,120 @@
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
# AUTO_MAA is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
# AUTO_MAA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
AUTO_MAA网络请求线程
v4.3
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import QThread, QEventLoop, QTimer
import time
import requests
from pathlib import Path
class _Network(QThread):
max_retries = 3
timeout = 10
backoff_factor = 0.1
def __init__(self) -> None:
super().__init__()
self.if_running = False
self.mode = None
self.url = None
self.loop = QEventLoop()
self.wait_loop = QEventLoop()
@logger.catch
def run(self) -> None:
"""运行网络请求线程"""
self.if_running = True
if self.mode == "get":
self.get_json(self.url)
elif self.mode == "get_file":
self.get_file(self.url, self.path)
self.if_running = False
def set_info(self, mode: str, url: str, path: Path = None) -> None:
"""设置网络请求信息"""
while self.if_running:
QTimer.singleShot(self.backoff_factor * 1000, self.wait_loop.quit)
self.wait_loop.exec()
self.mode = mode
self.url = url
self.path = path
self.stutus_code = None
self.response_json = None
self.error_message = None
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)
self.stutus_code = response.status_code
self.response_json = response.json()
self.error_message = None
break
except Exception as e:
self.stutus_code = response.status_code if response else None
self.response_json = None
self.error_message = str(e)
time.sleep(self.backoff_factor)
self.loop.quit()
def get_file(self, url: str, path: Path) -> None:
"""通过get方法下载文件"""
response = None
try:
response = requests.get(url, timeout=10)
if response.status_code == 200:
with open(path, "wb") as file:
file.write(response.content)
self.stutus_code = response.status_code
else:
self.stutus_code = response.status_code
self.error_message = "下载失败"
except Exception as e:
self.stutus_code = response.status_code if response else None
self.error_message = str(e)
self.loop.quit()
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,7 +172,6 @@ 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):
super(_TaskManager, self).__init__()
@@ -190,6 +195,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)
)
@@ -229,37 +235,33 @@ class _TaskManager(QObject):
self.task_dict[name].quit()
self.task_dict[name].wait()
def remove_task(self, mode: str, name: str, logs: str):
def remove_task(self, mode: str, name: str, logs: list):
"""任务结束后的处理"""
logger.info(f"任务结束:{name}")
MainInfoBar.push_info_bar("info", "任务结束", name, 3000)
self.task_dict[name].deleteLater()
if len(logs) > 0:
time = logs[0][1]["Time"]
history = ""
for log in logs:
Config.save_history(log[0], log[1])
history += (
f"任务名称:{log[0]}{log[1]["History"].replace("\n","\n ")}\n"
)
Config.save_history(name, {"Time": time, "History": history})
else:
Config.save_history(
name,
{
"Time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"History": "没有任务被执行",
},
)
self.task_dict.pop(name)
Config.running_list.remove(name)
if "调度队列" in name and "人工排查" not in mode:
if len(logs) > 0:
time = logs[0][1]["Time"]
history = ""
for log in logs:
history += f"任务名称:{log[0]}{log[1]["History"].replace("\n","\n ")}\n"
Config.save_history(name, {"Time": time, "History": history})
else:
Config.save_history(
name,
{
"Time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"History": "没有任务被执行",
},
)
if (
Config.queue_dict[name]["Config"].get(
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
@@ -287,6 +289,39 @@ class _TaskManager(QObject):
)
)
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):
"""推送对话框"""

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
@@ -64,8 +64,6 @@ class _MainTimer(QWidget):
if not info["Config"].get(info["Config"].queueSet_Enabled):
continue
history = Config.get_history(name)
data = info["Config"].toDict()
time_set = [
@@ -77,7 +75,8 @@ class _MainTimer(QWidget):
curtime = datetime.now().strftime("%Y-%m-%d %H:%M")
if (
curtime[11:16] in time_set
and curtime != history["Time"][:16]
and curtime
!= info["Config"].get(info["Config"].Data_LastProxyTime)[:16]
and name not in Config.running_list
):

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,8 @@ class MaaManager(QObject):
self.interrupt.connect(self.quit_monitor)
self.maa_version = None
self.maa_update_package = ""
self.set = config["Config"].toDict()
self.data = {}
@@ -107,12 +112,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()
@@ -186,7 +191,7 @@ class MaaManager(QObject):
logger.info(f"{self.name} | 开始代理用户: {user[0]}")
# 初始化代理情况记录和模式替换记录
# 初始化代理情况记录和模式替换
run_book = {"Annihilation": False, "Routine": False}
mode_book = {
"Annihilation": "自动代理_剿灭",
@@ -196,92 +201,80 @@ class MaaManager(QObject):
# 简洁模式用户默认开启日常选项
if user_data["Info"]["Mode"] == "简洁":
user_data["Info"]["Routine"] = True
# 详细模式用户首次代理需打开模拟器
elif user_data["Info"]["Mode"] == "详细":
check_book = {
"Annihilation": True,
"Routine": True,
}
self.if_open_emulator = True
user_logs_list = []
user_start_time = datetime.now()
# 尝试次数循环
for i in range(self.set["RunSet"]["RunTimesLimit"]):
# 剿灭-日常模式循环
for mode in ["Annihilation", "Routine"]:
if self.isInterruptionRequested:
break
logger.info(
f"{self.name} | 用户: {user[0]} - 尝试次数: {i + 1}/{self.set["RunSet"]["RunTimesLimit"]}"
# 剿灭模式;满足条件跳过剿灭
if (
mode == "Annihilation"
and self.set["RunSet"]["AnnihilationWeeklyLimit"]
and datetime.strptime(
user_data["Data"]["LastAnnihilationDate"], "%Y-%m-%d"
).isocalendar()[:2]
== datetime.strptime(curdate, "%Y-%m-%d").isocalendar()[:2]
):
logger.info(
f"{self.name} | 用户: {user_data["Info"]["Name"]} - 本周剿灭模式已达上限,跳过执行剿灭任务"
)
run_book[mode] = True
continue
else:
self.weekly_annihilation_limit_reached = False
if not user_data["Info"][mode]:
run_book[mode] = True
continue
if user_data["Info"]["Mode"] == "详细":
if not (
self.data[user[2]]["Path"] / f"{mode}/gui.json"
).exists():
logger.error(
f"{self.name} | 用户: {user[0]} - 未找到{mode_book[mode][5:7]}配置文件"
)
self.push_info_bar.emit(
"error",
"启动MAA代理进程失败",
f"未找到{user[0]}{mode_book[mode][5:7]}配置文件!",
-1,
)
run_book[mode] = False
continue
# 更新当前模式到界面
self.update_user_list.emit(
[
(
[f"{_[0]} - {mode_book[mode][5:7]}", _[1], _[2]]
if _[2] == user[2]
else _
)
for _ in self.user_list
]
)
# 剿灭-日常模式循环
for mode in ["Annihilation", "Routine"]:
# 尝试次数循环
for i in range(self.set["RunSet"]["RunTimesLimit"]):
if self.isInterruptionRequested:
break
# 剿灭模式;满足条件跳过剿灭
if (
mode == "Annihilation"
and self.set["RunSet"]["AnnihilationWeeklyLimit"]
and datetime.strptime(
user_data["Data"]["LastAnnihilationDate"], "%Y-%m-%d"
).isocalendar()[:2]
== datetime.strptime(curdate, "%Y-%m-%d").isocalendar()[:2]
):
logger.info(
f"{self.name} | 用户: {user_data["Info"]["Name"]} - 本周剿灭模式已达上限,跳过执行剿灭任务"
)
run_book[mode] = True
continue
else:
self.weekly_annihilation_limit_reached = False
if not user_data["Info"][mode]:
run_book[mode] = True
continue
if run_book[mode]:
continue
break
logger.info(
f"{self.name} | 用户: {user[0]} - 模式: {mode_book[mode]}"
)
if user_data["Info"]["Mode"] == "详细":
self.if_open_emulator = True
if (
check_book[mode]
and not (
self.data[user[2]]["Path"] / f"{mode}/gui.json"
).exists()
):
logger.error(
f"{self.name} | 用户: {user[0]} - 未找到{mode_book[mode][5:7]}配置文件"
)
self.push_info_bar.emit(
"error",
"启动MAA代理进程失败",
f"未找到{user[0]}{mode_book[mode][5:7]}配置文件!",
-1,
)
check_book[mode] = False
continue
elif not check_book[mode]:
continue
# 更新当前模式到界面
self.update_user_list.emit(
[
(
[f"{_[0]} - {mode_book[mode][5:7]}", _[1], _[2]]
if _[2] == user[2]
else _
)
for _ in self.user_list
]
f"{self.name} | 用户: {user[0]} - 模式: {mode_book[mode]} - 尝试次数: {i + 1}/{self.set["RunSet"]["RunTimesLimit"]}"
)
# 配置MAA
@@ -293,37 +286,144 @@ class MaaManager(QObject):
self.emulator_path = Path(
set["Configurations"]["Default"]["Start.EmulatorPath"]
)
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"
]
self.if_kill_emulator = bool(
set["Configurations"]["Default"]["MainFunction.PostActions"]
== "12"
)
self.if_open_emulator_process = bool(
set["Configurations"]["Default"][
"Start.OpenEmulatorAfterLaunch"
]
== "True"
)
# 任务开始前释放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)
# 增强任务任务开始前强杀ADB
if "ADB" in self.set["RunSet"]["EnhanceTask"]:
System.kill_process(self.ADB_path)
# 创建MAA任务
maa = subprocess.Popen(
[self.maa_exe_path],
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW,
)
# 监测MAA运行状态
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
@@ -339,9 +439,29 @@ class MaaManager(QObject):
)
# 无命令行中止MAA与其子程序
System.kill_process(self.maa_exe_path)
if "Emulator" in self.set["RunSet"]["EnhanceTask"]:
System.kill_process(self.emulator_path)
# 中止模拟器进程
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(
"用户自动代理出现异常!",
@@ -357,14 +477,51 @@ class MaaManager(QObject):
# 移除静默进程标记
Config.silence_list.remove(self.emulator_path)
# 增强任务:任务结束后强杀ADB和模拟器
if "ADB" in self.set["RunSet"]["EnhanceTask"]:
System.kill_process(self.ADB_path)
if (
self.if_kill_emulator
and "Emulator" in self.set["RunSet"]["EnhanceTask"]
):
System.kill_process(self.emulator_path)
# 任务结束后释放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:
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 (
@@ -392,23 +549,6 @@ class MaaManager(QObject):
{"user_name": user_data["Info"]["Name"]},
)
# 成功完成代理的用户修改相关参数
if run_book["Annihilation"] and run_book["Routine"]:
if (
user_data["Data"]["ProxyTimes"] == 0
and user_data["Info"]["RemainedDay"] != -1
):
user_data["Info"]["RemainedDay"] -= 1
user_data["Data"]["ProxyTimes"] += 1
user[1] = "完成"
Notify.push_plyer(
"成功完成一个自动代理任务!",
f"已完成用户 {user[0].replace("_", " 今天的")}任务",
f"已完成 {user[0].replace("_", "")}",
3,
)
break
if Config.get(Config.notify_IfSendStatistic):
statistics = Config.merge_maa_logs("指定项", user_logs_list)
@@ -428,8 +568,23 @@ class MaaManager(QObject):
"统计信息", f"用户 {user[0]} 的自动代理统计报告", statistics
)
# 录入代理失败的用户
if not (run_book["Annihilation"] and run_book["Routine"]):
if run_book["Annihilation"] and run_book["Routine"]:
# 成功完成代理的用户修改相关参数
if (
user_data["Data"]["ProxyTimes"] == 0
and user_data["Info"]["RemainedDay"] != -1
):
user_data["Info"]["RemainedDay"] -= 1
user_data["Data"]["ProxyTimes"] += 1
user[1] = "完成"
Notify.push_plyer(
"成功完成一个自动代理任务!",
f"已完成用户 {user[0].replace("_", " 今天的")}任务",
f"已完成 {user[0].replace("_", "")}",
3,
)
else:
# 录入代理失败的用户
user[1] = "异常"
self.update_user_list.emit(self.user_list)
@@ -475,7 +630,6 @@ class MaaManager(QObject):
# 创建MAA任务
maa = subprocess.Popen(
[self.maa_exe_path],
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW,
)
@@ -545,7 +699,6 @@ class MaaManager(QObject):
# 创建MAA任务
maa = subprocess.Popen(
[self.maa_exe_path],
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW,
)
# 记录当前时间
@@ -571,6 +724,9 @@ class MaaManager(QObject):
if self.isInterruptionRequested:
System.kill_process(self.maa_exe_path)
# 复原MAA配置文件
shutil.copy(self.config_path / "Default/gui.json", self.maa_set_path)
# 更新用户数据
updated_info = {_[2]: self.data[_[2]] for _ in self.user_list}
self.update_user_info.emit(self.config_path.name, updated_info)
@@ -681,6 +837,19 @@ 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:
# 获取最近一条日志的时间
@@ -703,22 +872,23 @@ class MaaManager(QObject):
self.weekly_annihilation_limit_reached = False
if mode == "自动代理_日常" and "任务出错: Fight" in log:
self.maa_result = "检测到MAA未能实际执行任务"
self.maa_result = "MAA未能实际执行任务"
elif "任务出错: StartUp" in log:
self.maa_result = "检测到MAA未能正确登录PRTS"
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进程异常"
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:
@@ -727,13 +897,14 @@ class MaaManager(QObject):
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:
@@ -779,7 +950,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进程
@@ -794,7 +965,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"] == "简洁"
):
@@ -821,7 +992,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"
@@ -869,10 +1040,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" # 显示托盘图标
@@ -892,21 +1073,22 @@ class MaaManager(QObject):
if user_data["Info"]["Mode"] == "简洁":
data["Global"][
"VersionUpdate.ScheduledUpdateCheck"
] = "False" # 定时检查更新
data["Global"][
"VersionUpdate.AutoDownloadUpdatePackage"
] = "False" # 自动下载更新包
data["Global"][
"VersionUpdate.AutoInstallUpdatePackage"
] = "False" # 自动安装更新包
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"][
@@ -1017,55 +1199,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"
] = str(
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" # 内置配置
@@ -1090,11 +1264,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"] == "详细":
@@ -1127,19 +1303,22 @@ class MaaManager(QObject):
if user_data["Info"]["GameId_2"] != "-"
else ""
) # 备选关卡2
# 备选关卡
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"]["Fight.RemainingSanityStage"] = (
user_data["Info"]["GameId_Remain"]
if user_data["Info"]["GameId_Remain"] != "-"
else ""
) # 剩余理智关卡
data["Configurations"]["Default"][
"MainFunction.Series.Quantity"
] = str(
user_data["Info"]["SeriesNumb"]
) # 连战次数
data["Configurations"]["Default"][
"GUI.UseAlternateStage"
] = "True" # 备选关卡
data["Configurations"]["Default"][
"Fight.UseRemainingSanityStage"
] = "True" # 使用剩余理智
# 人工排查配置
elif "人工排查" in mode:
@@ -1157,8 +1336,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后自动开启模拟器
# 账号切换
@@ -1272,12 +1451,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
and self.set["RunSet"]["TaskTransitionMethod"] != "ExitEmulator"
):
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
@@ -28,6 +28,7 @@ v4.3
from PySide6.QtWidgets import QWidget
from PySide6.QtCore import Signal
import requests
import time
from loguru import logger
from plyer import notification
import re
@@ -229,25 +230,41 @@ class Notification(QWidget):
content = f"{title}\n{content}"
data = {"msgtype": "text", "text": {"content": content}}
response = requests.post(
url=Config.get(Config.notify_CompanyWebHookBotUrl),
json=data,
)
if response.json()["errcode"] == 0:
logger.info("企业微信群机器人推送通知成功")
return True
# 从远程服务器获取最新主题图像
for _ in range(3):
try:
response = requests.post(
url=Config.get(Config.notify_CompanyWebHookBotUrl),
json=data,
timeout=10,
)
info = response.json()
break
except Exception as e:
err = e
time.sleep(0.1)
else:
logger.info("企业微信群机器人推送通知失败")
logger.error(response.json())
logger.error(f"推送企业微信群机器人时出错:{err}")
self.push_info_bar.emit(
"error",
"企业微信群机器人通知推送失败",
f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}',
f'使用企业微信群机器人推送通知时出错:{info["errmsg"]}',
-1,
)
return (
f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}'
return None
if info["errcode"] == 0:
logger.info("企业微信群机器人推送通知成功")
return True
else:
logger.error(f"企业微信群机器人推送通知失败:{info}")
self.push_info_bar.emit(
"error",
"企业微信群机器人通知推送失败",
f'使用企业微信群机器人推送通知时出错:{info["errmsg"]}',
-1,
)
return f'使用企业微信群机器人推送通知时出错:{info["errmsg"]}'
def send_test_notification(self):
"""发送测试通知到所有已启用的通知渠道"""

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, QWidget
import sys
import ctypes
import win32gui
@@ -113,6 +113,7 @@ class _SystemHandler:
elif mode == "KillSelf":
self.main_window.close()
QApplication.quit()
elif sys.platform.startswith("linux"):
@@ -138,6 +139,7 @@ class _SystemHandler:
elif mode == "KillSelf":
self.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,
@@ -107,7 +109,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 +128,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 +556,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 +566,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 +621,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"""
@@ -870,6 +930,31 @@ class TimeEditSettingCard(SettingCard):
self.TimeEdit.setTime(QTime.fromString(value, "HH:mm"))
class HistoryCard(HeaderCardWidget):
def __init__(self, qconfig: QConfig, configItem: ConfigItem, parent=None):
super().__init__(parent)
self.setTitle("历史运行记录")
self.qconfig = qconfig
self.configItem = configItem
self.text = TextBrowser()
self.text.setMinimumHeight(300)
if configItem:
self.setValue(self.qconfig.get(configItem))
configItem.valueChanged.connect(self.setValue)
self.viewLayout.addWidget(self.text)
def setValue(self, content: str):
if self.configItem:
self.qconfig.set(self.configItem, content)
self.text.setPlainText(content)
class UrlItem(QWidget):
"""Url item"""
@@ -1043,6 +1128,41 @@ class QuantifiedItemCard(CardWidget):
self.Layout.addWidget(self.Numb)
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
@@ -50,7 +50,7 @@ from typing import List, Dict
from app.core import Config, TaskManager, Task, MainInfoBar
from .Widget import StatefulItemCard
from .Widget import StatefulItemCard, ComboBoxMessageBox
class DispatchCenter(QWidget):
@@ -89,7 +89,7 @@ class DispatchCenter(QWidget):
dispatch_box = 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 +123,10 @@ 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.multi_button.show()
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(
@@ -143,22 +144,31 @@ class DispatchCenter(QWidget):
task.update_log_text.connect(
self.script_list["主调度台"].info.log_text.text.setText
)
task.accomplish.connect(lambda: self.disconnect_main_board(task.name))
task.accomplish.connect(
lambda logs: self.disconnect_main_board(task.name, logs)
)
def disconnect_main_board(self, name: str) -> None:
def disconnect_main_board(self, name: str, logs: list) -> None:
"""断开主调度台"""
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["主调度台"].info.log_text.text.setText(
Config.get_history(name)["History"]
self.script_list["主调度台"].top_bar.multi_button.hide()
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 = ""
for log in logs:
history += (
f"任务名称:{log[0]}{log[1]["History"].replace("\n","\n ")}\n"
)
self.script_list["主调度台"].info.log_text.text.setText(history)
else:
self.script_list["主调度台"].info.log_text.text.setText("没有任务被执行")
def update_top_bar(self):
"""更新顶栏"""
@@ -242,25 +252,29 @@ class DispatchBox(QWidget):
self.mode = ComboBox()
self.mode.setPlaceholderText("请选择调度模式")
self.button = PushButton("开始任务")
self.button.clicked.connect(self.start_task)
self.multi_button = PushButton("添加任务")
self.multi_button.clicked.connect(self.start_multi_task)
self.main_button = PushButton("开始任务")
self.main_button.clicked.connect(self.start_main_task)
self.multi_button.hide()
Layout.addWidget(self.Lable)
Layout.addWidget(self.object)
Layout.addWidget(self.mode)
Layout.addStretch(1)
Layout.addWidget(self.button)
Layout.addWidget(self.multi_button)
Layout.addWidget(self.main_button)
else:
self.Lable = SubtitleLabel(name, self)
self.button = PushButton("中止任务")
self.main_button = PushButton("中止任务")
Layout.addWidget(self.Lable)
Layout.addStretch(1)
Layout.addWidget(self.button)
Layout.addWidget(self.main_button)
def start_task(self):
def start_main_task(self):
"""开始任务"""
if self.object.currentIndex() == -1:
@@ -304,6 +318,74 @@ class DispatchBox(QWidget):
{"Queue": {"Member_1": self.object.currentData()}},
)
def start_multi_task(self):
"""开始任务"""
# 获取所有可用的队列和实例
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)
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)
choice = ComboBoxMessageBox(
self.window(),
"选择一个对象以添加相应多开任务",
["选择调度对象"],
[text_list],
[data_list],
)
if choice.exec() and choice.input[0].currentIndex() != -1:
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
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 DispatchInfoCard(HeaderCardWidget):
def __init__(self, parent=None):

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,22 @@ 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.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 +89,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
@@ -45,13 +45,11 @@ from qfluentwidgets import (
)
import re
import shutil
import requests
import json
import time
from datetime import datetime
from pathlib import Path
from app.core import Config, MainInfoBar
from app.core import Config, MainInfoBar, Network
from .Widget import Banner, IconButton
@@ -199,23 +197,21 @@ class Home(QWidget):
elif Config.get(Config.function_HomeImageMode) == "主题图像":
# 从远程服务器获取最新主题图像
for _ in range(3):
try:
response = requests.get(
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/theme_image.json"
)
theme_image = response.json()
break
except Exception as e:
err = e
time.sleep(0.1)
Network.set_info(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/theme_image.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
theme_image = Network.response_json
else:
logger.error(f"获取最新主题图像时出错:\n{err}")
logger.warning(f"获取最新主题图像时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"error",
"主题图像获取失败",
f"获取最新主题图像信息时出错!",
-1,
"warning",
"获取最新主题图像时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
@@ -238,15 +234,22 @@ class Home(QWidget):
> time_local
):
response = requests.get(theme_image["url"])
if response.status_code == 200:
Network.set_info(
mode="get_file",
url=theme_image["url"],
path=Config.app_path / "resources/images/Home/BannerTheme.jpg",
)
Network.start()
Network.loop.exec()
with open(
Config.app_path / "resources/images/Home/BannerTheme.jpg", "wb"
) as file:
file.write(response.content)
if Network.stutus_code == 200:
logger.info(f"主题图像「{theme_image["name"]}」下载成功")
with (Config.app_path / "resources/theme_image.json").open(
mode="w", encoding="utf-8"
) as f:
json.dump(theme_image, f, ensure_ascii=False, indent=4)
logger.success(f"主题图像「{theme_image["name"]}」下载成功")
MainInfoBar.push_info_bar(
"success",
"主题图像下载成功",
@@ -256,19 +259,14 @@ class Home(QWidget):
else:
logger.error("主题图像下载失败")
logger.warning(f"下载最新主题图像时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"error",
"主题图像下载失败",
f"主题图像下载失败:{response.status_code}",
-1,
"warning",
"下载最新主题图像时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
with (Config.app_path / "resources/theme_image.json").open(
mode="w", encoding="utf-8"
) as f:
json.dump(theme_image, f, ensure_ascii=False, indent=4)
else:
logger.info("主题图像已是最新")

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
@@ -140,9 +140,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(
@@ -324,7 +321,7 @@ class AUTO_MAA(MSFluentWindow):
logger.info("自动添加任务调度队列_1")
TaskManager.add_task(
"自动代理_主调度台",
"主任务队列",
"调度队列_1",
Config.queue_dict["调度队列_1"]["Config"].toDict(),
)
@@ -332,7 +329,7 @@ class AUTO_MAA(MSFluentWindow):
logger.info("自动添加任务脚本_1")
TaskManager.add_task(
"自动代理_主调度台", "主任务队列", {"Queue": {"Member_1": "脚本_1"}}
"自动代理_主调度台", "自定义队列", {"Queue": {"Member_1": "脚本_1"}}
)
else:

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
@@ -49,15 +49,13 @@ from qfluentwidgets import (
PrimaryToolButton,
)
from PySide6.QtCore import Qt, Signal
import requests
import time
from datetime import datetime
from functools import partial
from pathlib import Path
from typing import List
import shutil
from app.core import Config, MainInfoBar, TaskManager, MaaConfig, MaaUserConfig
from app.core import Config, MainInfoBar, TaskManager, MaaConfig, MaaUserConfig, Network
from app.services import Crypto
from app.utils import DownloadManager
from .Widget import (
@@ -71,6 +69,7 @@ from .Widget import (
ComboBoxSettingCard,
SwitchSettingCard,
PushAndSwitchButtonSettingCard,
PushAndComboBoxSettingCard,
)
@@ -337,20 +336,18 @@ class MemberManager(QWidget):
return None
# 从mirrorc服务器获取最新版本信息
for _ in range(3):
try:
response = requests.get(
"https://mirrorchyan.com/api/resources/MAA/latest?user_agent=AutoMaaGui&os=win&arch=x64&channel=stable"
)
maa_info = response.json()
break
except Exception as e:
err = e
time.sleep(0.1)
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{err}",
f"获取版本信息时出错:\n{Network.error_message}",
self.window(),
)
choice.cancelButton.hide()
@@ -611,54 +608,22 @@ class MemberManager(QWidget):
self.card_TaskTransitionMethod = ComboBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="任务切换方式",
content="简洁用户列表下相邻两个任务间的切换方式",
content="相邻两个任务间的切换方式,使用“详细”配置的用户固定为“重启模拟器”",
texts=["直接切换账号", "重启明日方舟", "重启模拟器"],
qconfig=self.config,
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”时视为无代理次数上限",
content="当用户本日代理成功次数达到该阈值时跳过代理阈值为“0”时视为无代理次数上限",
range=(0, 1024),
qconfig=self.config,
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="若超过该次数限制仍未完成代理,视为代理失败",
@@ -667,7 +632,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="每周剿灭模式执行到上限,本周剩下时间不再执行剿灭任务",
@@ -675,16 +658,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)
@@ -1053,7 +1044,7 @@ class MemberManager(QWidget):
self.name = name
self.dashboard = TableWidget(self)
self.dashboard.setColumnCount(10)
self.dashboard.setColumnCount(11)
self.dashboard.setHorizontalHeaderLabels(
[
"用户名",
@@ -1063,8 +1054,9 @@ class MemberManager(QWidget):
"代理情况",
"给药量",
"关卡选择",
"备选关卡-1",
"备选关卡-2",
"备选 - 1",
"备选 - 2",
"剩余理智",
"",
]
)
@@ -1074,14 +1066,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)
@@ -1103,7 +1095,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 "今日未进行代理"
)
@@ -1113,9 +1105,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 "本周剿灭未完成"
)
@@ -1213,8 +1203,22 @@ class MemberManager(QWidget):
else config.get(config.Info_GameId_2)
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
9,
QTableWidgetItem(
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):
@@ -1313,13 +1317,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(
@@ -1350,6 +1359,15 @@ class MemberManager(QWidget):
configItem=self.config.Info_MedicineNumb,
parent=self,
)
self.card_SeriesNumb = SpinBoxSettingCard(
icon=FluentIcon.GAME,
title="连战次数",
content="连战次数较大时建议搭配剩余理智关卡使用",
range=(1, 6),
qconfig=self.config,
configItem=self.config.Info_SeriesNumb,
parent=self,
)
self.card_GameId = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="关卡选择",
@@ -1362,7 +1380,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"],
@@ -1372,7 +1390,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"],
@@ -1380,6 +1398,16 @@ 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=Config.gameid_dict["ALL"]["text"],
qconfig=self.config,
configItem=self.config.Info_GameId_Remain,
parent=self,
)
self.card_UserLable = UserLableSettingCard(
icon=FluentIcon.INFO,
@@ -1408,16 +1436,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)
@@ -1428,6 +1459,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)
@@ -1441,7 +1473,7 @@ class MemberManager(QWidget):
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)
@@ -1456,12 +1488,12 @@ 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)
@@ -1479,6 +1511,10 @@ class MemberManager(QWidget):
Config.gameid_dict["ALL"]["value"],
Config.gameid_dict["ALL"]["text"],
)
self.card_GameId_Remain.reLoadOptions(
Config.gameid_dict["ALL"]["value"],
Config.gameid_dict["ALL"]["text"],
)
def refresh_password(self):

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
@@ -39,7 +39,6 @@ from qfluentwidgets import (
FluentIcon,
MessageBox,
HeaderCardWidget,
TextBrowser,
CommandBar,
)
from PySide6.QtCore import Qt
@@ -52,6 +51,7 @@ from .Widget import (
LineEditSettingCard,
TimeEditSettingCard,
NoOptionComboBoxSettingCard,
HistoryCard,
)
@@ -387,7 +387,11 @@ class QueueManager(QWidget):
self.queue_set = self.QueueSetSettingCard(self.config, self)
self.time = self.TimeSettingCard(self.config, self)
self.task = self.TaskSettingCard(self.config, self)
self.history = self.HistoryCard(f"调度队列_{uid}", self)
self.history = HistoryCard(
qconfig=self.config,
configItem=self.config.Data_LastProxyHistory,
parent=self,
)
content_layout.addWidget(self.queue_set)
content_layout.addWidget(self.time)
@@ -421,7 +425,7 @@ class QueueManager(QWidget):
self.card_Enable = SwitchSettingCard(
icon=FluentIcon.HOME,
title="状态",
content="调度队列状态",
content="调度队列状态,仅启用时会执行定时任务",
qconfig=self.config,
configItem=self.config.queueSet_Enabled,
parent=self,
@@ -704,16 +708,3 @@ class QueueManager(QWidget):
Layout.addWidget(self.card_Member_10)
self.viewLayout.addLayout(Layout)
class HistoryCard(HeaderCardWidget):
def __init__(self, name: str, parent=None):
super().__init__(parent)
self.setTitle("历史运行记录")
self.text = TextBrowser()
self.text.setMinimumHeight(300)
history = Config.get_history(name)
self.text.setPlainText(history["History"])
self.viewLayout.addWidget(self.text)

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,17 +26,12 @@ v4.3
"""
from loguru import logger
from PySide6.QtWidgets import (
QWidget,
QApplication,
QVBoxLayout,
)
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout
from PySide6.QtCore import Qt
from qfluentwidgets import (
ScrollArea,
FluentIcon,
MessageBox,
Dialog,
HyperlinkCard,
HeaderCardWidget,
ExpandGroupSettingCard,
@@ -46,15 +41,14 @@ from qfluentwidgets import (
import os
import re
import json
import time
import shutil
import requests
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
from app.core import Config, MainInfoBar, Network
from app.services import Crypto, System, Notify
from .Widget import (
SwitchSettingCard,
@@ -92,7 +86,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(self.check_update)
self.updater.card_CheckUpdate.clicked.connect(
lambda: self.check_update(if_click=True)
)
self.other.card_Notice.clicked.connect(self.show_notice)
content_layout.addWidget(self.function)
@@ -262,34 +258,36 @@ class Setting(QWidget):
if choice.exec():
break
def check_update(self) -> None:
def check_update(self, if_click: bool = False) -> None:
"""检查版本更新,调起文件下载进程"""
current_version = list(map(int, Config.VERSION.split(".")))
# 从远程服务器获取最新版本信息
for _ in range(3):
try:
response = requests.get(
f"https://mirrorchyan.com/api/resources/AUTO_MAA/latest?user_agent=AutoMaaGui&current_version={version_text(current_version)}&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&channel={Config.get(Config.update_UpdateType)}"
)
version_info: Dict[str, Union[int, str, Dict[str, str]]] = (
response.json()
)
break
except Exception as e:
err = e
time.sleep(0.1)
else:
choice = MessageBox(
"错误",
f"获取版本信息时出错:\n{err}",
self.window(),
if Network.if_running and if_click:
MainInfoBar.push_info_bar(
"warning", "请求速度过快", "上个网络请求还未结束,请稍等片刻", 5000
)
choice.cancelButton.hide()
choice.buttonLayout.insertStretch(1)
if choice.exec():
return None
return None
# 从远程服务器获取最新版本信息
Network.set_info(
mode="get",
url=f"https://mirrorchyan.com/api/resources/AUTO_MAA/latest?user_agent=AutoMaaGui&current_version={version_text(current_version)}&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&channel={Config.get(Config.update_UpdateType)}",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
version_info: Dict[str, Union[int, str, Dict[str, str]]] = (
Network.response_json
)
else:
logger.warning(f"获取版本信息时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取版本信息时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
if version_info["code"] != 0:
@@ -301,7 +299,7 @@ class Setting(QWidget):
7002: "填入的 CDK 错误",
7003: "填入的 CDK 今日下载次数已达上限",
7004: "填入的 CDK 类型和待下载的资源不匹配",
7005: "填入的 CDK 不合法",
7005: "填入的 CDK 已被封禁",
8001: "对应架构和系统下的资源不存在",
8002: "错误的系统参数",
8003: "错误的架构参数",
@@ -336,7 +334,9 @@ class Setting(QWidget):
)
# 有版本更新
if remote_version > current_version:
if version.parse(version_text(remote_version)) > version.parse(
version_text(current_version)
):
version_info_json: Dict[str, Dict[str, str]] = json.loads(
re.sub(
@@ -353,9 +353,11 @@ class Setting(QWidget):
all_version_info = {}
for v_i in [
info
for version, info in version_info_json.items()
if list(map(int, version.split("."))) > current_version
for ver, info in version_info_json.items()
if version.parse(version_text(list(map(int, ver.split(".")))))
> version.parse(version_text(current_version))
]:
for key, value in v_i.items():
if key in update_version_info:
update_version_info[key] += value.copy()
@@ -400,41 +402,38 @@ class Setting(QWidget):
return None
subprocess.Popen(
str(Config.app_path / "AUTO_Updater.active.exe"),
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW,
[Config.app_path / "AUTO_Updater.active.exe"],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
self.window().close()
QApplication.quit()
# 无版本更新
else:
MainInfoBar.push_info_bar("success", "更新检查", "已是最新版本~", 3000)
def show_notice(self, if_show: bool = True):
def show_notice(self, if_show: bool = True) -> None:
"""显示公告"""
# 从远程服务器获取最新公告
for _ in range(3):
try:
response = requests.get(
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/notice.json"
)
notice = response.json()
break
except Exception as e:
err = e
time.sleep(0.1)
Network.set_info(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/notice.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
notice = Network.response_json
else:
logger.warning(f"获取最新公告时出错:\n{err}")
if if_show:
choice = Dialog(
"网络错误",
f"获取最新公告时出错:\n{err}",
self,
)
choice.cancelButton.hide()
choice.buttonLayout.insertStretch(1)
choice.exec()
logger.warning(f"获取最新公告时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取最新公告时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
if (Config.app_path / "resources/notice.json").exists():
@@ -488,7 +487,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,
@@ -549,8 +548,8 @@ class FunctionSettingCard(HeaderCardWidget):
self.card_BossKey = LineEditSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="模拟器老板键",
content="输入模拟器老板快捷键,以“+”分隔",
text="请输入安卓模拟器老板键",
content="输入对应的模拟器老板键,请直接输入文字,多个键位之间请用“+”隔开。如“Alt+Q”",
text="以文字形式输入模拟器老板快捷",
qconfig=Config,
configItem=Config.function_BossKey,
parent=self,

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
@@ -31,8 +31,10 @@ import zipfile
import requests
import subprocess
import time
import psutil
import win32crypt
import base64
from packaging import version
from functools import partial
from pathlib import Path
@@ -179,7 +181,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:
@@ -189,6 +195,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):
"""下载管理器"""
@@ -199,18 +229,12 @@ 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"
@@ -259,25 +283,25 @@ 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"
return f"https://jp-download.fearr.xyz/MAA/MAA-{version_text(self.version)}-win-x64.zip"
if self.name == "AUTO_MAA":
@@ -292,17 +316,20 @@ 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, stream=True
self.config["url"],
allow_redirects=True,
timeout=10,
stream=True,
) as response:
if response.status_code == 200:
return response.url
@@ -399,7 +426,7 @@ class DownloadManager(QDialog):
url = self.get_download_url("下载")
self.downloaded_size_list: List[List[int, bool]] = []
response = requests.head(url)
response = requests.head(url, timeout=10)
self.file_size = int(response.headers.get("content-length", 0))
part_size = self.file_size // self.config["thread_numb"]
@@ -513,8 +540,9 @@ class DownloadManager(QDialog):
info: Dict[str, List[str]] = json.load(f)
if "deleted" in info:
for file_path in info:
(self.app_path / file_path).unlink()
for file_path in info["deleted"]:
if (self.app_path / file_path).exists():
(self.app_path / file_path).unlink()
(self.app_path / "changes.json").unlink()
@@ -526,15 +554,17 @@ class DownloadManager(QDialog):
# 主程序更新完成后打开对应程序
if not self.isInterruptionRequested and self.name == "AUTO_MAA":
subprocess.Popen(
str(self.app_path / "AUTO_MAA.exe"),
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW,
[self.app_path / "AUTO_MAA.exe"],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
elif not self.isInterruptionRequested and self.name == "MAA":
subprocess.Popen(
str(self.app_path / "MAA.exe"),
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW,
[self.app_path / "MAA.exe"],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
| subprocess.CREATE_NO_WINDOW,
)
self.update_info(f"{self.name}更新成功!")
@@ -661,7 +691,8 @@ if __name__ == "__main__":
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}"
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
@@ -696,7 +727,8 @@ if __name__ == "__main__":
for _ in range(3):
try:
response = requests.get(
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json"
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json",
timeout=10,
)
download_info = response.json()
@@ -715,7 +747,9 @@ if __name__ == "__main__":
(app_path / "changes.json").unlink()
# 启动更新线程
if remote_version > current_version:
if version.parse(version_text(remote_version)) > version.parse(
version_text(current_version)
):
app = AUTO_MAA_Downloader(
app_path,
"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
@@ -79,7 +79,7 @@ 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"
)
@@ -98,7 +98,7 @@ if __name__ == "__main__":
f" --file-version={version["updater_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_Updater"
" --remove-output downloader.py"
)

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)

View File

@@ -10,6 +10,14 @@
"TaskQueue.Mall.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,14 +38,16 @@
"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": "" #自定义基建配置文件地址
#设置
"Start.ClientType": "Bilibili"、 "Official" #服务器
G"Timer.Timer1": "False" #时间设置1
"Connect.AdbPath" #ADB路径
"Connect.Address": "127.0.0.1:16448" #连接地址
G"VersionUpdate.ScheduledUpdateCheck": "True" #定时检查更新
G"VersionUpdate.AutoDownloadUpdatePackage": "True" #自动下载更新包
G"VersionUpdate.AutoInstallUpdatePackage": "True" #自动安装更新包
@@ -47,4 +57,5 @@ G"Start.MinimizeDirectly": "True" #启动MAA后直接最小化
G"GUI.UseTray": "True" #显示托盘图标
G"GUI.MinimizeToTray": "False" #最小化时隐藏至托盘
"Start.EmulatorPath" #模拟器路径
"Connect.AdbPath" #ADB路径
"Start.EmulatorAddCommand": "-v 2" #附加命令
G"VersionUpdate.package": "MirrorChyanAppv5.15.6.zip" #更新包标识

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,24 +1,29 @@
{
"main_version": "4.3.3.0",
"main_version": "4.3.5.0",
"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- 公告样式优化",
"version_info": {
"4.3.3.0": {
"4.3.5.0": {
"新增功能": [
"用户设置中新增连战次数与剩余理智关卡两项配置项",
"支持自动代理时更新MAA"
],
"修复BUG": [
"修复更新器无法下载MAA的异常"
"适配MAAv5.16.0基建模式",
"适配自定义基建自动轮换功能"
],
"程序优化": [
"自动发版改为手动触发"
"移除增效任务"
]
},
"4.3.2.0": {
"4.3.5.2": {
"修复BUG": [
"修复更新器无法启动的异常"
"修复无法建立网络连接时软件卡死问题"
]
},
"4.3.1.0": {
"修复BUG": [
"覆盖规避v4.3.0错误包"
"4.3.5.1": {
"程序优化": [
"模拟器路径适配快捷方式"
]
}
},