Merge branch 'dev'

This commit is contained in:
DLmaster361
2025-05-31 11:20:04 +08:00
11 changed files with 315 additions and 165 deletions

173
.github/workflows/build-app-test.yml vendored Normal file
View File

@@ -0,0 +1,173 @@
# 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
name: Build AUTO_MAA
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
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: Read version
id: get_version
run: |
$version = (Get-Content resources/version.json | ConvertFrom-Json).main_version
echo "main_version=$version" >> $env:GITHUB_OUTPUT
- name: Nuitka build main program
uses: Nuitka/Nuitka-Action@main
with:
script-name: main.py
mode: app
enable-plugins: pyside6
onefile-tempdir-spec: "{TEMP}/AUTO_MAA"
windows-console-mode: disable
windows-icon-from-ico: resources/icons/AUTO_MAA.ico
company-name: AUTO_MAA Team
product-name: AUTO_MAA
file-version: ${{ steps.get_version.outputs.main_version }}
product-version: ${{ steps.get_version.outputs.main_version }}
file-description: AUTO_MAA Component
copyright: Copyright © 2024-2025 DLmaster361
assume-yes-for-downloads: true
output-file: AUTO_MAA
output-dir: AUTO_MAA
- name: Prepare setup files
shell: pwsh
run: |
$root = "${{ github.workspace }}"
$ver = "${{ steps.get_version.outputs.main_version }}"
Copy-Item "$root/app" "$root/AUTO_MAA/app" -Recurse
Copy-Item "$root/resources" "$root/AUTO_MAA/resources" -Recurse
Copy-Item "$root/main.py" "$root/AUTO_MAA/"
Copy-Item "$root/requirements.txt" "$root/AUTO_MAA/"
Copy-Item "$root/README.md" "$root/AUTO_MAA/"
Copy-Item "$root/LICENSE" "$root/AUTO_MAA/"
- name: Prepare Inno Setup script
shell: pwsh
run: |
$root = "${{ github.workspace }}"
$ver = "${{ steps.get_version.outputs.main_version }}"
$iss = Get-Content "$root/app/utils/AUTO_MAA.iss" -Raw
$iss = $iss -replace '#define MyAppVersion ""', "#define MyAppVersion `"$ver`""
$iss = $iss -replace '#define MyAppPath ""', "#define MyAppPath `"$root/AUTO_MAA`""
$iss = $iss -replace '#define OutputDir ""', "#define OutputDir `"$root`""
Set-Content -Path "$root/AUTO_MAA.iss" -Value $iss
- name: Build setup program
uses: Minionguyjpro/Inno-Setup-Action@v1.2.2
with:
path: ${{ github.workspace }}/AUTO_MAA.iss
- name: Zip setup exe
shell: pwsh
run: |
$root = "${{ github.workspace }}"
New-Item -ItemType Directory -Force -Path "$root/AUTO_MAA_Setup"
Move-Item "$root/AUTO_MAA-Setup.exe" "$root/AUTO_MAA_Setup/"
Compress-Archive -Path AUTO_MAA_Setup/* -DestinationPath AUTO_MAA_${{ steps.get_version.outputs.main_version }}.zip
- name: Generate version info
shell: python
run: |
import json
from pathlib import Path
def version_text(version_numb):
while len(version_numb) < 4:
version_numb.append(0)
if version_numb[3] == 0:
return f"v{'.'.join(str(_) for _ in version_numb[0:3])}"
else:
return f"v{'.'.join(str(_) for _ in version_numb[0:3])}-beta.{version_numb[3]}"
def version_info_markdown(info):
version_info = ""
for key, value in info.items():
version_info += f"## {key}\n"
for v in value:
version_info += f"- {v}\n"
return version_info
root_path = Path(".")
version = json.loads((root_path / "resources/version.json").read_text(encoding="utf-8"))
main_version_numb = list(map(int, version["main_version"].split(".")))
all_version_info = {}
for v_i in version["version_info"].values():
for key, value in v_i.items():
if key in all_version_info:
all_version_info[key] += value.copy()
else:
all_version_info[key] = value.copy()
(root_path / "version_info.txt").write_text(
f"{version_text(main_version_numb)}\n\n<!--{json.dumps(version['version_info'], ensure_ascii=False)}-->\n{version_info_markdown(all_version_info)}",
encoding="utf-8",
)
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: AUTO_MAA_${{ steps.get_version.outputs.main_version }}
path: AUTO_MAA_${{ steps.get_version.outputs.main_version }}.zip
- name: Upload Version_Info Artifact
uses: actions/upload-artifact@v4
with:
name: version_info
path: version_info.txt

View File

@@ -567,7 +567,7 @@ class MaaPlanConfig(LQConfig):
class AppConfig(GlobalConfig):
VERSION = "4.3.8.0"
VERSION = "4.3.9.1"
gameid_refreshed = Signal()
PASSWORD_refreshed = Signal()
@@ -652,18 +652,20 @@ class AppConfig(GlobalConfig):
def get_gameid(self) -> None:
# 从MAA服务器获取活动关卡信息
Network.set_info(
network = Network.add_task(
mode="get",
url="https://api.maa.plus/MaaAssistantArknights/api/gui/StageActivity.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
gameid_infos: List[Dict[str, Union[str, Dict[str, Union[str, int]]]]] = (
Network.response_json["Official"]["sideStoryStage"]
network_result["response_json"]["Official"]["sideStoryStage"]
)
else:
logger.warning(f"无法从MAA服务器获取活动关卡信息:{Network.error_message}")
logger.warning(
f"无法从MAA服务器获取活动关卡信息:{network_result['error_message']}"
)
gameid_infos = []
ss_gameid_dict = {"value": [], "text": []}

View File

@@ -26,55 +26,41 @@ v4.3
"""
from loguru import logger
from PySide6.QtCore import QThread, QEventLoop, QTimer
from PySide6.QtCore import QObject, QThread, QEventLoop
import time
import requests
from pathlib import Path
class _Network(QThread):
class NetworkThread(QThread):
"""网络请求线程类"""
max_retries = 3
timeout = 10
backoff_factor = 0.1
def __init__(self) -> None:
def __init__(self, mode: str, url: str, path: Path = None) -> 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.status_code = None
self.response_json = None
self.error_message = None
self.loop = QEventLoop()
@logger.catch
def run(self) -> None:
"""运行网络请求线程"""
if self.mode == "get":
self.get_json(self.url)
elif self.mode == "get_file":
self.get_file(self.url, self.path)
def get_json(self, url: str) -> None:
"""通过get方法获取json数据"""
@@ -83,12 +69,12 @@ class _Network(QThread):
for _ in range(self.max_retries):
try:
response = requests.get(url, timeout=self.timeout)
self.stutus_code = response.status_code
self.status_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.status_code = response.status_code if response else None
self.response_json = None
self.error_message = str(e)
time.sleep(self.backoff_factor)
@@ -105,16 +91,47 @@ class _Network(QThread):
if response.status_code == 200:
with open(path, "wb") as file:
file.write(response.content)
self.stutus_code = response.status_code
self.status_code = response.status_code
else:
self.stutus_code = response.status_code
self.status_code = response.status_code
self.error_message = "下载失败"
except Exception as e:
self.stutus_code = response.status_code if response else None
self.status_code = response.status_code if response else None
self.error_message = str(e)
self.loop.quit()
class _Network(QObject):
"""网络请求线程类"""
def __init__(self) -> None:
super().__init__()
self.task_queue = []
def add_task(self, mode: str, url: str, path: Path = None) -> NetworkThread:
"""添加网络请求任务"""
network_thread = NetworkThread(mode, url, path)
self.task_queue.append(network_thread)
network_thread.start()
return network_thread
def get_result(self, network_thread: NetworkThread) -> dict:
"""获取网络请求结果"""
self.task_queue.remove(network_thread)
return {
"status_code": network_thread.status_code,
"response_json": network_thread.response_json,
"error_message": network_thread.error_message,
}
Network = _Network()

View File

@@ -277,20 +277,20 @@ class _TaskManager(QObject):
def check_maa_version(self, v: str):
"""检查MAA版本"""
Network.set_info(
network = Network.add_task(
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
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
maa_info = network_result["response_json"]
else:
logger.warning(f"获取MAA版本信息时出错{Network.error_message}")
logger.warning(f"获取MAA版本信息时出错{network_result['error_message']}")
MainInfoBar.push_info_bar(
"warning",
"获取MAA版本信息时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)
return None

View File

@@ -26,8 +26,7 @@ v4.3
"""
from loguru import logger
from PySide6.QtWidgets import QWidget
from PySide6.QtCore import QTimer
from PySide6.QtCore import QObject, QTimer
from datetime import datetime
from pathlib import Path
import pyautogui
@@ -37,7 +36,7 @@ from .task_manager import TaskManager
from app.services import System
class _MainTimer(QWidget):
class _MainTimer(QObject):
def __init__(self, parent=None):
super().__init__(parent)

View File

@@ -426,11 +426,11 @@ class MaaManager(QObject):
# 任务开始前释放ADB
try:
logger.info(f"{self.name} | 释放ADB{self.ADB_address}")
subprocess.run(
[self.ADB_path, "disconnect", self.ADB_address],
creationflags=subprocess.CREATE_NO_WINDOW,
)
logger.info(f"{self.name} | 释放ADB{self.ADB_address}")
except subprocess.CalledProcessError as e:
# 忽略错误,因为可能本来就没有连接
logger.warning(f"{self.name} | 释放ADB时出现异常{e}")
@@ -445,13 +445,13 @@ class MaaManager(QObject):
if self.if_open_emulator_process:
try:
logger.info(
f"{self.name} | 启动模拟器:{self.emulator_path},参数:{self.emulator_arguments}"
)
self.emulator_process = subprocess.Popen(
[self.emulator_path, *self.emulator_arguments],
creationflags=subprocess.CREATE_NO_WINDOW,
)
logger.info(
f"{self.name} | 启动模拟器:{self.emulator_path},参数:{self.emulator_arguments}"
)
except Exception as e:
logger.error(f"{self.name} | 启动模拟器时出现异常:{e}")
self.push_info_bar.emit(
@@ -571,11 +571,11 @@ class MaaManager(QObject):
# 任务结束后释放ADB
try:
logger.info(f"{self.name} | 释放ADB{self.ADB_address}")
subprocess.run(
[self.ADB_path, "disconnect", self.ADB_address],
creationflags=subprocess.CREATE_NO_WINDOW,
)
logger.info(f"{self.name} | 释放ADB{self.ADB_address}")
except subprocess.CalledProcessError as e:
# 忽略错误,因为可能本来就没有连接
logger.warning(f"{self.name} | 释放ADB时出现异常{e}")

View File

@@ -34,8 +34,7 @@ from email.mime.text import MIMEText
from email.utils import formataddr
import requests
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QWidget
from PySide6.QtCore import QObject, Signal
from loguru import logger
from plyer import notification
@@ -43,7 +42,7 @@ from app.core import Config
from app.services.security import Crypto
class Notification(QWidget):
class Notification(QObject):
push_info_bar = Signal(str, str, str, int)

View File

@@ -199,20 +199,22 @@ class Home(QWidget):
elif Config.get(Config.function_HomeImageMode) == "主题图像":
# 从远程服务器获取最新主题图像
Network.set_info(
network = Network.add_task(
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
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
theme_image = network_result["response_json"]
else:
logger.warning(f"获取最新主题图像时出错:{Network.error_message}")
logger.warning(
f"获取最新主题图像时出错:{network_result['error_message']}"
)
MainInfoBar.push_info_bar(
"warning",
"获取最新主题图像时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)
return None
@@ -236,15 +238,15 @@ class Home(QWidget):
> time_local
):
Network.set_info(
network = Network.add_task(
mode="get_file",
url=theme_image["url"],
path=Config.app_path / "resources/images/Home/BannerTheme.jpg",
)
Network.start()
Network.loop.exec()
network.loop.exec()
network_result = Network.get_result(network)
if Network.stutus_code == 200:
if network_result["status_code"] == 200:
with (Config.app_path / "resources/theme_image.json").open(
mode="w", encoding="utf-8"
@@ -261,11 +263,13 @@ class Home(QWidget):
else:
logger.warning(f"下载最新主题图像时出错:{Network.error_message}")
logger.warning(
f"下载最新主题图像时出错:{network_result['error_message']}"
)
MainInfoBar.push_info_bar(
"warning",
"下载最新主题图像时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)

View File

@@ -333,20 +333,20 @@ class MemberManager(QWidget):
return None
# 从远程服务器获取应用列表
Network.set_info(
network = Network.add_task(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/apps_info.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
apps_info = Network.response_json
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
apps_info = network_result["response_json"]
else:
logger.warning(f"获取应用列表时出错:{Network.error_message}")
logger.warning(f"获取应用列表时出错:{network_result['error_message']}")
MainInfoBar.push_info_bar(
"warning",
"获取应用列表时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)
return None
@@ -376,19 +376,19 @@ class MemberManager(QWidget):
return None
# 从mirrorc服务器获取最新版本信息
Network.set_info(
network = Network.add_task(
mode="get",
url=f"https://mirrorchyan.com/api/resources/{app_rid}/latest?user_agent=AutoMaaGui&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&os={apps_info[app_name]["os"]}&arch={apps_info[app_name]["arch"]}&channel=stable",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
app_info = Network.response_json
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
app_info = network_result["response_json"]
else:
if Network.response_json:
if network_result["response_json"]:
app_info = Network.response_json
app_info = network_result["response_json"]
if app_info["code"] != 0:
@@ -425,11 +425,11 @@ class MemberManager(QWidget):
return None
logger.warning(f"获取版本信息时出错:{Network.error_message}")
logger.warning(f"获取版本信息时出错:{network_result['error_message']}")
MainInfoBar.push_info_bar(
"warning",
"获取版本信息时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)
return None

View File

@@ -262,31 +262,26 @@ class Setting(QWidget):
current_version = list(map(int, Config.VERSION.split(".")))
if Network.if_running and if_show:
MainInfoBar.push_info_bar(
"warning", "请求速度过快", "上个网络请求还未结束,请稍等片刻", 5000
)
return None
# 从远程服务器获取最新版本信息
Network.set_info(
network = Network.add_task(
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
)
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
version_info: Dict[str, Union[int, str, Dict[str, str]]] = network_result[
"response_json"
]
else:
if Network.response_json:
if network_result["response_json"]:
version_info = Network.response_json
version_info = network_result["response_json"]
if version_info["code"] != 0:
logger.error(f"获取版本信息时出错:{version_info["msg"]}")
logger.error(f"获取版本信息时出错:{version_info['msg']}")
error_remark_dict = {
1001: "获取版本信息的URL参数不正确",
@@ -319,11 +314,11 @@ class Setting(QWidget):
return None
logger.warning(f"获取版本信息时出错:{Network.error_message}")
logger.warning(f"获取版本信息时出错:{network_result['error_message']}")
MainInfoBar.push_info_bar(
"warning",
"获取版本信息时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)
return None
@@ -406,20 +401,22 @@ class Setting(QWidget):
else:
# 从远程服务器获取代理信息
Network.set_info(
network = Network.add_task(
mode="get",
url="https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json",
)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
download_info = Network.response_json
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
download_info = network_result["response_json"]
else:
logger.warning(f"获取应用列表时出错:{Network.error_message}")
logger.warning(
f"获取应用列表时出错:{network_result['error_message']}"
)
MainInfoBar.push_info_bar(
"warning",
"获取应用列表时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)
return None
@@ -488,20 +485,20 @@ class Setting(QWidget):
"""显示公告"""
# 从远程服务器获取最新公告
Network.set_info(
network = Network.add_task(
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
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
notice = network_result["response_json"]
else:
logger.warning(f"获取最新公告时出错:{Network.error_message}")
logger.warning(f"获取最新公告时出错:{network_result['error_message']}")
MainInfoBar.push_info_bar(
"warning",
"获取最新公告时出错",
f"网络错误:{Network.stutus_code}",
f"网络错误:{network_result['status_code']}",
5000,
)
return None

View File

@@ -1,53 +1,12 @@
{
"main_version": "4.3.8.0",
"main_version": "4.3.9.1",
"version_info": {
"4.3.8.0": {
"新增功能": [
"吐司通知在主窗口隐藏时不再弹出"
]
},
"4.3.8.4": {
"新增功能": [
"支持为每一个用户执行独立通知",
"输入文本框适配文本插入操作",
"计划表功能上线",
"静默控制时长从全任务内缩短至搜索ADB时段内",
"UI界面添加自动日常代理任务序列设置项"
],
"4.3.9.1": {
"修复bug": [
"修复雷电模拟器静默模式无法正常识别模拟器是否隐藏相关问题"
]
},
"4.3.8.3": {
"新增功能": [
"用户仪表盘支持直接控制用户状态"
],
"修复bug": [
"修复雷电ADB端口号相关问题"
]
},
"4.3.8.2": {
"新增功能": [
"添加ADB端口号宽幅适配能力"
],
"修复bug": [
"日志分析忽略MAA超时提示"
"网络模块支持并发请求"
],
"程序优化": [
"配置类定义方法优化"
]
},
"4.3.8.1": {
"新增功能": [
"自定义基建显示配置名称 #46",
"主调度台添加仅一次电源任务"
],
"修复bug": [
"电源相关选项改为所有任务完成后生效",
"适配MAAv5.16.3的ADB报错信息更改"
],
"程序优化": [
"UI样式优化进一步适配win10主题"
"非UI组件转为QObject类"
]
}
}