Compare commits
77 Commits
v4.3.8-bet
...
v4.3.10-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85f3b4f607 | ||
|
|
916396f855 | ||
|
|
211c8d2b04 | ||
|
|
92e274d3fd | ||
|
|
d511ea48d5 | ||
|
|
1aa4da1adf | ||
|
|
0e8b6b0b6b | ||
|
|
1a2c1b976f | ||
|
|
1cc242fa51 | ||
|
|
18dfdba15d | ||
|
|
b04ac4eec6 | ||
|
|
c009f0c891 | ||
|
|
d2dc0bd295 | ||
|
|
ddbb5b7f19 | ||
|
|
954c25090b | ||
|
|
0b6cc59de1 | ||
|
|
2271b5741d | ||
|
|
8a438b041f | ||
|
|
dd92fcc4d8 | ||
|
|
8f66ca0e16 | ||
|
|
895ba1d24a | ||
| e49b807bef | |||
|
|
73c15b5e93 | ||
|
|
e505ea8c51 | ||
|
|
21e7df7c3e | ||
|
|
2d72ca66a4 | ||
|
|
4725a30165 | ||
|
|
f3c977f1b3 | ||
|
|
9a0e7265c6 | ||
|
|
3f8e2fbe6b | ||
|
|
590b13e916 | ||
|
|
0f6aee56e5 | ||
|
|
daf18e7295 | ||
|
|
9bcc87f663 | ||
|
|
e7205ce0aa | ||
|
|
e3c4b2edc8 | ||
|
|
222a3b35a2 | ||
|
|
cd5dfd56b2 | ||
|
|
7d5c6b8222 | ||
|
|
4dbf4736e4 | ||
|
|
d50504181e | ||
|
|
c7e94dfcd1 | ||
|
|
a752b67ca1 | ||
|
|
078736337d | ||
|
|
de1058a28c | ||
|
|
740797a689 | ||
|
|
26328920a2 | ||
|
|
9c447bbdf9 | ||
|
|
fac85a889f | ||
|
|
f5d898c89e | ||
| 974a4b634a | |||
| 3127c83603 | |||
|
|
8d69e43f72 | ||
|
|
86df9e7a50 | ||
| 59ff9bf818 | |||
|
|
1641e32e3d | ||
|
|
a482087abd | ||
| bc5b15cec2 | |||
| 3787c25a77 | |||
|
|
0b06b499e4 | ||
|
|
04079dd57b | ||
|
|
34ac0c5ab3 | ||
| 0d904b229e | |||
| c0f887ff9d | |||
|
|
cf95075d01 | ||
|
|
d78a764d87 | ||
|
|
a368f4b722 | ||
|
|
803fe4568f | ||
|
|
1162d5dcc1 | ||
|
|
aa83058e39 | ||
|
|
061f205224 | ||
|
|
5d966f98df | ||
|
|
0037914db8 | ||
|
|
13d0115475 | ||
|
|
5bdb5ad2bf | ||
| a5d733c8df | |||
|
|
0b038e2997 |
164
.github/workflows/build-app.yml
vendored
164
.github/workflows/build-app.yml
vendored
@@ -28,9 +28,11 @@ permissions:
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
|
||||
pre_check:
|
||||
name: Pre Checks
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Repo Check
|
||||
id: repo_check
|
||||
@@ -40,67 +42,198 @@ jobs:
|
||||
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
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install flake8 pytest
|
||||
pip install -r requirements.txt
|
||||
choco install innosetup
|
||||
echo "C:\Program Files (x86)\Inno Setup 6" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
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
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: |
|
||||
copy app\utils\package.py .\
|
||||
python package.py
|
||||
- name: Read version
|
||||
id: read_version
|
||||
$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: attach
|
||||
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: Upload unsigned main program
|
||||
id: upload-unsigned-main-program
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AUTO_MAA
|
||||
path: AUTO_MAA/AUTO_MAA.exe
|
||||
|
||||
- name: Sign main program
|
||||
id: sign_main_program
|
||||
uses: signpath/github-action-submit-signing-request@v1.2
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '787a1d5f-6177-4f30-9559-d2646473584a'
|
||||
project-slug: 'AUTO_MAA_'
|
||||
signing-policy-slug: 'test-signing'
|
||||
artifact-configuration-slug: "AUTO_MAA"
|
||||
github-artifact-id: '${{ steps.upload-unsigned-main-program.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: 'AUTO_MAA'
|
||||
|
||||
- name: Add other resources
|
||||
shell: pwsh
|
||||
run: |
|
||||
$MAIN_VERSION=(Get-Content -Path "version_info.txt" -TotalCount 1).Trim()
|
||||
"AUTO_MAA_version=$MAIN_VERSION" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
$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: Create 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.5
|
||||
with:
|
||||
path: AUTO_MAA.iss
|
||||
|
||||
- name: Upload unsigned setup program
|
||||
id: upload-unsigned-setup-program
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AUTO_MAA-Setup
|
||||
path: AUTO_MAA-Setup.exe
|
||||
|
||||
- name: Sign setup program
|
||||
id: sign_setup_program
|
||||
uses: signpath/github-action-submit-signing-request@v1.2
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '787a1d5f-6177-4f30-9559-d2646473584a'
|
||||
project-slug: 'AUTO_MAA_'
|
||||
signing-policy-slug: 'test-signing'
|
||||
artifact-configuration-slug: "AUTO_MAA-Setup"
|
||||
github-artifact-id: '${{ steps.upload-unsigned-setup-program.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: 'AUTO_MAA_Setup'
|
||||
|
||||
- name: Compress setup exe
|
||||
shell: pwsh
|
||||
run: 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_${{ env.AUTO_MAA_version }}
|
||||
path: AUTO_MAA_${{ env.AUTO_MAA_version }}.zip
|
||||
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
|
||||
|
||||
publish_release:
|
||||
name: Publish release
|
||||
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: Create release
|
||||
id: create_release
|
||||
run: |
|
||||
@@ -111,7 +244,7 @@ jobs:
|
||||
NOTES_MAIN="$(sed 's/\r$//g' <(tail -n +3 version_info.txt))"
|
||||
NOTES="$NOTES_MAIN
|
||||
|
||||
[已有 Mirror酱 CDK ?前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA)
|
||||
[已有 Mirror酱 CDK ?前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA&source=auto_maa-release)
|
||||
|
||||
\`\`\`本release通过GitHub Actions自动构建\`\`\`"
|
||||
if [ "${{ github.ref_name }}" == "main" ]; then
|
||||
@@ -122,6 +255,7 @@ jobs:
|
||||
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" $PRERELEASE_FLAG artifacts/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
||||
|
||||
- name: Trigger MirrorChyanUploading
|
||||
run: |
|
||||
gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan
|
||||
|
||||
24
README.md
24
README.md
@@ -13,7 +13,8 @@
|
||||
<a href="https://github.com/DLmaster361/AUTO_MAA/issues"><img alt="GitHub Issues" src="https://img.shields.io/github/issues/DLmaster361/AUTO_MAA?style=flat-square"></a>
|
||||
<a href="https://github.com/DLmaster361/AUTO_MAA/graphs/contributors"><img alt="GitHub Contributors" src="https://img.shields.io/github/contributors/DLmaster361/AUTO_MAA?style=flat-square"></a>
|
||||
<a href="https://github.com/DLmaster361/AUTO_MAA/blob/main/LICENSE"><img alt="GitHub License" src="https://img.shields.io/github/license/DLmaster361/AUTO_MAA?style=flat-square"></a>
|
||||
<a href="https://mirrorchyan.com/zh/projects?rid=AUTO_MAA"><img alt="mirrorc" src="https://img.shields.io/badge/Mirror%E9%85%B1-%239af3f6?logo=countingworkspro&logoColor=4f46e5"></a>
|
||||
<a href="https://deepwiki.com/DLmaster361/AUTO_MAA"><img alt="DeepWiki" src="https://deepwiki.com/badge.svg"></a>
|
||||
<a href="https://mirrorchyan.com/zh/projects?rid=AUTO_MAA&source=auto_maa-readme"><img alt="mirrorc" src="https://img.shields.io/badge/Mirror%E9%85%B1-%239af3f6?logo=countingworkspro&logoColor=4f46e5"></a>
|
||||
</p>
|
||||
|
||||
## 软件介绍
|
||||
@@ -22,6 +23,11 @@
|
||||
|
||||
本软件是明日方舟第三方软件`MAA`的第三方工具,即第3<sup>3</sup>方软件。旨在优化MAA多账号功能体验,并通过一些方法解决MAA项目未能解决的部分问题,提高代理的稳定性。
|
||||
|
||||
- **集中管理**:一站式管理多个MAA脚本与多个用户配置,和凌乱的散装脚本窗口说再见!
|
||||
- **无人值守**:自动处理MAA相关报错,再也不用为代理任务卡死时自己不在电脑旁烦恼啦!
|
||||
- **配置灵活**:通过调度队列与脚本的组合,自由实现您能想到的所有调度需求!
|
||||
- **信息统计**:自动统计用户的公招与关卡掉落物,看看这个月您的收益是多少!
|
||||
|
||||
### 原理
|
||||
|
||||
本软件可以存储多个明日方舟账号数据,并通过以下流程实现代理功能:
|
||||
@@ -32,11 +38,9 @@
|
||||
|
||||
### 优势
|
||||
|
||||
- **节省运行开销:** 只需要一份MAA软件与一个模拟器,无需多开就能完成多账号代理,羸弱的电脑也能代理日常。
|
||||
- **自定义空间大:** 依靠高级用户配置模式,支持MAA几乎所有设置选项自定义,支持模拟器多开。
|
||||
- **调度方法自由:** 通过调度队列功能,自由实现MAA多开等多种调度方式。
|
||||
- **一键代理无忧:** 无须中途手动修改MAA配置,将繁琐交给AUTO_MAA,把游戏留给自己。
|
||||
- **代理结果复核:** 通过人工排查功能核实各用户代理情况,堵住自动代理的最后一丝风险。
|
||||
- **高效稳定**:通过日志监测、异常处理等机制,保障代理任务顺利完成。
|
||||
- **简洁易用**:无需手动修改配置文件,实现自动化调度与多开管理。
|
||||
- **兼容扩展**:支持 MAA 几乎所有的配置选项,满足不同用户需求。
|
||||
|
||||
## 重要声明
|
||||
|
||||
@@ -71,6 +75,12 @@
|
||||
|
||||
可在[《AUTO_MAA开发者协作文档》](https://docs.qq.com/aio/DQ3Z5eHNxdmxFQmZX)的`开发任务`页面中查看开发进度。
|
||||
|
||||
## 特别鸣谢
|
||||
|
||||
- 下载服务器:由[AoXuan (@ClozyA)](https://github.com/ClozyA) 个人为项目赞助。
|
||||
|
||||
- EXE签名: Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
|
||||
|
||||
## 贡献者
|
||||
|
||||
感谢以下贡献者对本项目做出的贡献
|
||||
@@ -83,8 +93,6 @@
|
||||
|
||||

|
||||
|
||||
感谢 [AoXuan (@ClozyA)](https://github.com/ClozyA) 为本项目提供的下载服务器
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#DLmaster361/AUTO_MAA&Date)
|
||||
|
||||
@@ -29,9 +29,10 @@ __version__ = "4.2.0"
|
||||
__author__ = "DLmaster361 <DLmaster_361@163.com>"
|
||||
__license__ = "GPL-3.0 license"
|
||||
|
||||
from .config import QueueConfig, MaaConfig, MaaUserConfig, Config
|
||||
from .config import QueueConfig, MaaConfig, MaaUserConfig, MaaPlanConfig, Config
|
||||
from .main_info_bar import MainInfoBar
|
||||
from .network import Network
|
||||
from .sound_player import SoundPlayer
|
||||
from .task_manager import Task, TaskManager
|
||||
from .timer import MainTimer
|
||||
|
||||
@@ -40,8 +41,10 @@ __all__ = [
|
||||
"QueueConfig",
|
||||
"MaaConfig",
|
||||
"MaaUserConfig",
|
||||
"MaaPlanConfig",
|
||||
"MainInfoBar",
|
||||
"Network",
|
||||
"SoundPlayer",
|
||||
"Task",
|
||||
"TaskManager",
|
||||
"MainTimer",
|
||||
|
||||
@@ -27,12 +27,14 @@ v4.3
|
||||
|
||||
from loguru import logger
|
||||
from PySide6.QtCore import Signal
|
||||
import argparse
|
||||
import sqlite3
|
||||
import json
|
||||
import sys
|
||||
import shutil
|
||||
import re
|
||||
import base64
|
||||
import calendar
|
||||
from datetime import datetime, timedelta, date
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
@@ -78,110 +80,12 @@ class UrlListValidator(ConfigValidator):
|
||||
return list(set([_ for _ in urls if self.validate(_)]))
|
||||
|
||||
|
||||
class GlobalConfig(QConfig):
|
||||
"""全局配置"""
|
||||
class LQConfig(QConfig):
|
||||
"""局域配置类"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.function_HomeImageMode = OptionsConfigItem(
|
||||
"Function",
|
||||
"HomeImageMode",
|
||||
"默认",
|
||||
OptionsValidator(["默认", "自定义", "主题图像"]),
|
||||
)
|
||||
self.function_HistoryRetentionTime = OptionsConfigItem(
|
||||
"Function",
|
||||
"HistoryRetentionTime",
|
||||
0,
|
||||
OptionsValidator([7, 15, 30, 60, 90, 180, 365, 0]),
|
||||
)
|
||||
self.function_IfAllowSleep = ConfigItem(
|
||||
"Function", "IfAllowSleep", False, BoolValidator()
|
||||
)
|
||||
self.function_IfSilence = ConfigItem(
|
||||
"Function", "IfSilence", False, BoolValidator()
|
||||
)
|
||||
self.function_BossKey = ConfigItem("Function", "BossKey", "")
|
||||
self.function_UnattendedMode = ConfigItem(
|
||||
"Function", "UnattendedMode", False, BoolValidator()
|
||||
)
|
||||
self.function_IfAgreeBilibili = ConfigItem(
|
||||
"Function", "IfAgreeBilibili", False, BoolValidator()
|
||||
)
|
||||
self.function_IfSkipMumuSplashAds = ConfigItem(
|
||||
"Function", "IfSkipMumuSplashAds", False, BoolValidator()
|
||||
)
|
||||
|
||||
self.start_IfSelfStart = ConfigItem(
|
||||
"Start", "IfSelfStart", False, BoolValidator()
|
||||
)
|
||||
self.start_IfRunDirectly = ConfigItem(
|
||||
"Start", "IfRunDirectly", False, BoolValidator()
|
||||
)
|
||||
self.start_IfMinimizeDirectly = ConfigItem(
|
||||
"Start", "IfMinimizeDirectly", False, BoolValidator()
|
||||
)
|
||||
|
||||
self.ui_IfShowTray = ConfigItem("UI", "IfShowTray", False, BoolValidator())
|
||||
self.ui_IfToTray = ConfigItem("UI", "IfToTray", False, BoolValidator())
|
||||
self.ui_size = ConfigItem("UI", "size", "1200x700")
|
||||
self.ui_location = ConfigItem("UI", "location", "100x100")
|
||||
self.ui_maximized = ConfigItem("UI", "maximized", False, BoolValidator())
|
||||
|
||||
self.notify_SendTaskResultTime = OptionsConfigItem(
|
||||
"Notify",
|
||||
"SendTaskResultTime",
|
||||
"不推送",
|
||||
OptionsValidator(["不推送", "任何时刻", "仅失败时"]),
|
||||
)
|
||||
self.notify_IfSendStatistic = ConfigItem(
|
||||
"Notify", "IfSendStatistic", False, BoolValidator()
|
||||
)
|
||||
self.notify_IfSendSixStar = ConfigItem(
|
||||
"Notify", "IfSendSixStar", False, BoolValidator()
|
||||
)
|
||||
self.notify_IfPushPlyer = ConfigItem(
|
||||
"Notify", "IfPushPlyer", False, BoolValidator()
|
||||
)
|
||||
self.notify_IfSendMail = ConfigItem(
|
||||
"Notify", "IfSendMail", False, BoolValidator()
|
||||
)
|
||||
self.notify_SMTPServerAddress = ConfigItem("Notify", "SMTPServerAddress", "")
|
||||
self.notify_AuthorizationCode = ConfigItem("Notify", "AuthorizationCode", "")
|
||||
self.notify_FromAddress = ConfigItem("Notify", "FromAddress", "")
|
||||
self.notify_ToAddress = ConfigItem("Notify", "ToAddress", "")
|
||||
self.notify_IfServerChan = ConfigItem(
|
||||
"Notify", "IfServerChan", False, BoolValidator()
|
||||
)
|
||||
self.notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
||||
self.notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
||||
self.notify_ServerChanTag = ConfigItem("Notify", "ServerChanTag", "")
|
||||
self.notify_IfCompanyWebHookBot = ConfigItem(
|
||||
"Notify", "IfCompanyWebHookBot", False, BoolValidator()
|
||||
)
|
||||
self.notify_CompanyWebHookBotUrl = ConfigItem(
|
||||
"Notify", "CompanyWebHookBotUrl", ""
|
||||
)
|
||||
self.notify_IfPushDeer = ConfigItem(
|
||||
"Notify", "IfPushDeer", False, BoolValidator()
|
||||
)
|
||||
self.notify_IfPushDeerKey = ConfigItem("Notify", "PushDeerKey", "")
|
||||
|
||||
self.update_IfAutoUpdate = ConfigItem(
|
||||
"Update", "IfAutoUpdate", False, BoolValidator()
|
||||
)
|
||||
self.update_UpdateType = OptionsConfigItem(
|
||||
"Update", "UpdateType", "stable", OptionsValidator(["stable", "beta"])
|
||||
)
|
||||
self.update_ThreadNumb = RangeConfigItem(
|
||||
"Update", "ThreadNumb", 8, RangeValidator(1, 32)
|
||||
)
|
||||
self.update_ProxyUrlList = ConfigItem(
|
||||
"Update", "ProxyUrlList", [], UrlListValidator()
|
||||
)
|
||||
self.update_MirrorChyanCDK = ConfigItem("Update", "MirrorChyanCDK", "")
|
||||
|
||||
def toDict(self, serialize=True):
|
||||
"""convert config items to `dict`"""
|
||||
items = {}
|
||||
@@ -247,7 +151,113 @@ class GlobalConfig(QConfig):
|
||||
self.theme = self.get(self._cfg.themeMode)
|
||||
|
||||
|
||||
class QueueConfig(QConfig):
|
||||
class GlobalConfig(LQConfig):
|
||||
"""全局配置"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.function_HomeImageMode = OptionsConfigItem(
|
||||
"Function",
|
||||
"HomeImageMode",
|
||||
"默认",
|
||||
OptionsValidator(["默认", "自定义", "主题图像"]),
|
||||
)
|
||||
self.function_HistoryRetentionTime = OptionsConfigItem(
|
||||
"Function",
|
||||
"HistoryRetentionTime",
|
||||
0,
|
||||
OptionsValidator([7, 15, 30, 60, 90, 180, 365, 0]),
|
||||
)
|
||||
self.function_IfAllowSleep = ConfigItem(
|
||||
"Function", "IfAllowSleep", False, BoolValidator()
|
||||
)
|
||||
self.function_IfSilence = ConfigItem(
|
||||
"Function", "IfSilence", False, BoolValidator()
|
||||
)
|
||||
self.function_BossKey = ConfigItem("Function", "BossKey", "")
|
||||
self.function_UnattendedMode = ConfigItem(
|
||||
"Function", "UnattendedMode", False, BoolValidator()
|
||||
)
|
||||
self.function_IfAgreeBilibili = ConfigItem(
|
||||
"Function", "IfAgreeBilibili", False, BoolValidator()
|
||||
)
|
||||
self.function_IfSkipMumuSplashAds = ConfigItem(
|
||||
"Function", "IfSkipMumuSplashAds", False, BoolValidator()
|
||||
)
|
||||
|
||||
self.voice_Enabled = ConfigItem("Voice", "Enabled", False, BoolValidator())
|
||||
self.voice_Type = OptionsConfigItem(
|
||||
"Voice", "Type", "simple", OptionsValidator(["simple", "noisy"])
|
||||
)
|
||||
|
||||
self.start_IfSelfStart = ConfigItem(
|
||||
"Start", "IfSelfStart", False, BoolValidator()
|
||||
)
|
||||
self.start_IfRunDirectly = ConfigItem(
|
||||
"Start", "IfRunDirectly", False, BoolValidator()
|
||||
)
|
||||
self.start_IfMinimizeDirectly = ConfigItem(
|
||||
"Start", "IfMinimizeDirectly", False, BoolValidator()
|
||||
)
|
||||
|
||||
self.ui_IfShowTray = ConfigItem("UI", "IfShowTray", False, BoolValidator())
|
||||
self.ui_IfToTray = ConfigItem("UI", "IfToTray", False, BoolValidator())
|
||||
self.ui_size = ConfigItem("UI", "size", "1200x700")
|
||||
self.ui_location = ConfigItem("UI", "location", "100x100")
|
||||
self.ui_maximized = ConfigItem("UI", "maximized", False, BoolValidator())
|
||||
|
||||
self.notify_SendTaskResultTime = OptionsConfigItem(
|
||||
"Notify",
|
||||
"SendTaskResultTime",
|
||||
"不推送",
|
||||
OptionsValidator(["不推送", "任何时刻", "仅失败时"]),
|
||||
)
|
||||
self.notify_IfSendStatistic = ConfigItem(
|
||||
"Notify", "IfSendStatistic", False, BoolValidator()
|
||||
)
|
||||
self.notify_IfSendSixStar = ConfigItem(
|
||||
"Notify", "IfSendSixStar", False, BoolValidator()
|
||||
)
|
||||
self.notify_IfPushPlyer = ConfigItem(
|
||||
"Notify", "IfPushPlyer", False, BoolValidator()
|
||||
)
|
||||
self.notify_IfSendMail = ConfigItem(
|
||||
"Notify", "IfSendMail", False, BoolValidator()
|
||||
)
|
||||
self.notify_SMTPServerAddress = ConfigItem("Notify", "SMTPServerAddress", "")
|
||||
self.notify_AuthorizationCode = ConfigItem("Notify", "AuthorizationCode", "")
|
||||
self.notify_FromAddress = ConfigItem("Notify", "FromAddress", "")
|
||||
self.notify_ToAddress = ConfigItem("Notify", "ToAddress", "")
|
||||
self.notify_IfServerChan = ConfigItem(
|
||||
"Notify", "IfServerChan", False, BoolValidator()
|
||||
)
|
||||
self.notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
||||
self.notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
||||
self.notify_ServerChanTag = ConfigItem("Notify", "ServerChanTag", "")
|
||||
self.notify_IfCompanyWebHookBot = ConfigItem(
|
||||
"Notify", "IfCompanyWebHookBot", False, BoolValidator()
|
||||
)
|
||||
self.notify_CompanyWebHookBotUrl = ConfigItem(
|
||||
"Notify", "CompanyWebHookBotUrl", ""
|
||||
)
|
||||
|
||||
self.update_IfAutoUpdate = ConfigItem(
|
||||
"Update", "IfAutoUpdate", False, BoolValidator()
|
||||
)
|
||||
self.update_UpdateType = OptionsConfigItem(
|
||||
"Update", "UpdateType", "stable", OptionsValidator(["stable", "beta"])
|
||||
)
|
||||
self.update_ThreadNumb = RangeConfigItem(
|
||||
"Update", "ThreadNumb", 8, RangeValidator(1, 32)
|
||||
)
|
||||
self.update_ProxyUrlList = ConfigItem(
|
||||
"Update", "ProxyUrlList", [], UrlListValidator()
|
||||
)
|
||||
self.update_MirrorChyanCDK = ConfigItem("Update", "MirrorChyanCDK", "")
|
||||
|
||||
|
||||
class QueueConfig(LQConfig):
|
||||
"""队列配置"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -334,72 +344,8 @@ class QueueConfig(QConfig):
|
||||
"Data", "LastProxyHistory", "暂无历史运行记录"
|
||||
)
|
||||
|
||||
def toDict(self, serialize=True):
|
||||
"""convert config items to `dict`"""
|
||||
items = {}
|
||||
for name in dir(self._cfg):
|
||||
item = getattr(self._cfg, name)
|
||||
if not isinstance(item, ConfigItem):
|
||||
continue
|
||||
|
||||
value = item.serialize() if serialize else item.value
|
||||
if not items.get(item.group):
|
||||
if not item.name:
|
||||
items[item.group] = value
|
||||
else:
|
||||
items[item.group] = {}
|
||||
|
||||
if item.name:
|
||||
items[item.group][item.name] = value
|
||||
|
||||
return items
|
||||
|
||||
@exceptionHandler()
|
||||
def load(self, file=None, config=None):
|
||||
"""load config
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file: str or Path
|
||||
the path of json config file
|
||||
|
||||
config: Config
|
||||
config object to be initialized
|
||||
"""
|
||||
if isinstance(config, QConfig):
|
||||
self._cfg = config
|
||||
self._cfg.themeChanged.connect(self.themeChanged)
|
||||
|
||||
if isinstance(file, (str, Path)):
|
||||
self._cfg.file = Path(file)
|
||||
|
||||
try:
|
||||
with open(self._cfg.file, encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
except:
|
||||
cfg = {}
|
||||
|
||||
# map config items'key to item
|
||||
items = {}
|
||||
for name in dir(self._cfg):
|
||||
item = getattr(self._cfg, name)
|
||||
if isinstance(item, ConfigItem):
|
||||
items[item.key] = item
|
||||
|
||||
# update the value of config item
|
||||
for k, v in cfg.items():
|
||||
if not isinstance(v, dict) and items.get(k) is not None:
|
||||
items[k].deserializeFrom(v)
|
||||
elif isinstance(v, dict):
|
||||
for key, value in v.items():
|
||||
key = k + "." + key
|
||||
if items.get(key) is not None:
|
||||
items[key].deserializeFrom(value)
|
||||
|
||||
self.theme = self.get(self._cfg.themeMode)
|
||||
|
||||
|
||||
class MaaConfig(QConfig):
|
||||
class MaaConfig(LQConfig):
|
||||
"""MAA配置"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -417,6 +363,9 @@ class MaaConfig(QConfig):
|
||||
self.RunSet_ProxyTimesLimit = RangeConfigItem(
|
||||
"RunSet", "ProxyTimesLimit", 0, RangeValidator(0, 1024)
|
||||
)
|
||||
self.RunSet_ADBSearchRange = RangeConfigItem(
|
||||
"RunSet", "ADBSearchRange", 0, RangeValidator(0, 3)
|
||||
)
|
||||
self.RunSet_RunTimesLimit = RangeConfigItem(
|
||||
"RunSet", "RunTimesLimit", 3, RangeValidator(1, 1024)
|
||||
)
|
||||
@@ -433,72 +382,8 @@ class MaaConfig(QConfig):
|
||||
"RunSet", "AutoUpdateMaa", False, BoolValidator()
|
||||
)
|
||||
|
||||
def toDict(self, serialize=True):
|
||||
"""convert config items to `dict`"""
|
||||
items = {}
|
||||
for name in dir(self._cfg):
|
||||
item = getattr(self._cfg, name)
|
||||
if not isinstance(item, ConfigItem):
|
||||
continue
|
||||
|
||||
value = item.serialize() if serialize else item.value
|
||||
if not items.get(item.group):
|
||||
if not item.name:
|
||||
items[item.group] = value
|
||||
else:
|
||||
items[item.group] = {}
|
||||
|
||||
if item.name:
|
||||
items[item.group][item.name] = value
|
||||
|
||||
return items
|
||||
|
||||
@exceptionHandler()
|
||||
def load(self, file=None, config=None):
|
||||
"""load config
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file: str or Path
|
||||
the path of json config file
|
||||
|
||||
config: Config
|
||||
config object to be initialized
|
||||
"""
|
||||
if isinstance(config, QConfig):
|
||||
self._cfg = config
|
||||
self._cfg.themeChanged.connect(self.themeChanged)
|
||||
|
||||
if isinstance(file, (str, Path)):
|
||||
self._cfg.file = Path(file)
|
||||
|
||||
try:
|
||||
with open(self._cfg.file, encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
except:
|
||||
cfg = {}
|
||||
|
||||
# map config items'key to item
|
||||
items = {}
|
||||
for name in dir(self._cfg):
|
||||
item = getattr(self._cfg, name)
|
||||
if isinstance(item, ConfigItem):
|
||||
items[item.key] = item
|
||||
|
||||
# update the value of config item
|
||||
for k, v in cfg.items():
|
||||
if not isinstance(v, dict) and items.get(k) is not None:
|
||||
items[k].deserializeFrom(v)
|
||||
elif isinstance(v, dict):
|
||||
for key, value in v.items():
|
||||
key = k + "." + key
|
||||
if items.get(key) is not None:
|
||||
items[key].deserializeFrom(value)
|
||||
|
||||
self.theme = self.get(self._cfg.themeMode)
|
||||
|
||||
|
||||
class MaaUserConfig(QConfig):
|
||||
class MaaUserConfig(LQConfig):
|
||||
"""MAA用户配置"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -509,9 +394,7 @@ class MaaUserConfig(QConfig):
|
||||
self.Info_Mode = OptionsConfigItem(
|
||||
"Info", "Mode", "简洁", OptionsValidator(["简洁", "详细"])
|
||||
)
|
||||
self.Info_GameIdMode = OptionsConfigItem(
|
||||
"Info", "GameIdMode", "固定", OptionsValidator(["固定"])
|
||||
)
|
||||
self.Info_GameIdMode = ConfigItem("Info", "GameIdMode", "固定")
|
||||
self.Info_Server = OptionsConfigItem(
|
||||
"Info", "Server", "Official", OptionsValidator(["Official", "Bilibili"])
|
||||
)
|
||||
@@ -537,18 +420,21 @@ class MaaUserConfig(QConfig):
|
||||
self.Info_SeriesNumb = OptionsConfigItem(
|
||||
"Info",
|
||||
"SeriesNumb",
|
||||
"1",
|
||||
OptionsValidator(["1000", "6", "5", "4", "3", "2", "1", "-1"]),
|
||||
"0",
|
||||
OptionsValidator(["0", "6", "5", "4", "3", "2", "1", "-1"]),
|
||||
)
|
||||
self.Info_GameId = ConfigItem("Info", "GameId", "-")
|
||||
self.Info_GameId_1 = ConfigItem("Info", "GameId_1", "-")
|
||||
self.Info_GameId_2 = ConfigItem("Info", "GameId_2", "-")
|
||||
self.Info_GameId_Remain = ConfigItem("Info", "GameId_Remain", "-")
|
||||
self.Info_IfSkland = ConfigItem("Info", "IfSkland", False, BoolValidator())
|
||||
self.Info_SklandToken = ConfigItem("Info", "SklandToken", "")
|
||||
|
||||
self.Data_LastProxyDate = ConfigItem("Data", "LastProxyDate", "2000-01-01")
|
||||
self.Data_LastAnnihilationDate = ConfigItem(
|
||||
"Data", "LastAnnihilationDate", "2000-01-01"
|
||||
)
|
||||
self.Data_LastSklandDate = ConfigItem("Data", "LastSklandDate", "2000-01-01")
|
||||
self.Data_ProxyTimes = ConfigItem(
|
||||
"Data", "ProxyTimes", 0, RangeValidator(0, 1024)
|
||||
)
|
||||
@@ -557,74 +443,140 @@ class MaaUserConfig(QConfig):
|
||||
"Data", "CustomInfrastPlanIndex", "0"
|
||||
)
|
||||
|
||||
def toDict(self, serialize=True):
|
||||
"""convert config items to `dict`"""
|
||||
items = {}
|
||||
for name in dir(self._cfg):
|
||||
item = getattr(self._cfg, name)
|
||||
if not isinstance(item, ConfigItem):
|
||||
continue
|
||||
self.Task_IfWakeUp = ConfigItem("Task", "IfWakeUp", True, BoolValidator())
|
||||
self.Task_IfRecruiting = ConfigItem(
|
||||
"Task", "IfRecruiting", True, BoolValidator()
|
||||
)
|
||||
self.Task_IfBase = ConfigItem("Task", "IfBase", True, BoolValidator())
|
||||
self.Task_IfCombat = ConfigItem("Task", "IfCombat", True, BoolValidator())
|
||||
self.Task_IfMall = ConfigItem("Task", "IfMall", True, BoolValidator())
|
||||
self.Task_IfMission = ConfigItem("Task", "IfMission", True, BoolValidator())
|
||||
self.Task_IfAutoRoguelike = ConfigItem(
|
||||
"Task", "IfAutoRoguelike", False, BoolValidator()
|
||||
)
|
||||
self.Task_IfReclamation = ConfigItem(
|
||||
"Task", "IfReclamation", False, BoolValidator()
|
||||
)
|
||||
|
||||
value = item.serialize() if serialize else item.value
|
||||
if not items.get(item.group):
|
||||
if not item.name:
|
||||
items[item.group] = value
|
||||
else:
|
||||
items[item.group] = {}
|
||||
self.Notify_Enabled = ConfigItem("Notify", "Enabled", False, BoolValidator())
|
||||
self.Notify_IfSendStatistic = ConfigItem(
|
||||
"Notify", "IfSendStatistic", False, BoolValidator()
|
||||
)
|
||||
self.Notify_IfSendSixStar = ConfigItem(
|
||||
"Notify", "IfSendSixStar", False, BoolValidator()
|
||||
)
|
||||
self.Notify_IfSendMail = ConfigItem(
|
||||
"Notify", "IfSendMail", False, BoolValidator()
|
||||
)
|
||||
self.Notify_ToAddress = ConfigItem("Notify", "ToAddress", "")
|
||||
self.Notify_IfServerChan = ConfigItem(
|
||||
"Notify", "IfServerChan", False, BoolValidator()
|
||||
)
|
||||
self.Notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
||||
self.Notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
||||
self.Notify_ServerChanTag = ConfigItem("Notify", "ServerChanTag", "")
|
||||
self.Notify_IfCompanyWebHookBot = ConfigItem(
|
||||
"Notify", "IfCompanyWebHookBot", False, BoolValidator()
|
||||
)
|
||||
self.Notify_CompanyWebHookBotUrl = ConfigItem(
|
||||
"Notify", "CompanyWebHookBotUrl", ""
|
||||
)
|
||||
|
||||
if item.name:
|
||||
items[item.group][item.name] = value
|
||||
def get_plan_info(self) -> Dict[str, Union[str, int]]:
|
||||
"""获取当前的计划下信息"""
|
||||
|
||||
return items
|
||||
if self.get(self.Info_GameIdMode) == "固定":
|
||||
return {
|
||||
"MedicineNumb": self.get(self.Info_MedicineNumb),
|
||||
"SeriesNumb": self.get(self.Info_SeriesNumb),
|
||||
"GameId": self.get(self.Info_GameId),
|
||||
"GameId_1": self.get(self.Info_GameId_1),
|
||||
"GameId_2": self.get(self.Info_GameId_2),
|
||||
"GameId_Remain": self.get(self.Info_GameId_Remain),
|
||||
}
|
||||
elif "计划" in self.get(self.Info_GameIdMode):
|
||||
plan = Config.plan_dict[self.get(self.Info_GameIdMode)]["Config"]
|
||||
return {
|
||||
"MedicineNumb": plan.get(plan.get_current_info("MedicineNumb")),
|
||||
"SeriesNumb": plan.get(plan.get_current_info("SeriesNumb")),
|
||||
"GameId": plan.get(plan.get_current_info("GameId")),
|
||||
"GameId_1": plan.get(plan.get_current_info("GameId_1")),
|
||||
"GameId_2": plan.get(plan.get_current_info("GameId_2")),
|
||||
"GameId_Remain": plan.get(plan.get_current_info("GameId_Remain")),
|
||||
}
|
||||
|
||||
@exceptionHandler()
|
||||
def load(self, file=None, config=None):
|
||||
"""load config
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file: str or Path
|
||||
the path of json config file
|
||||
class MaaPlanConfig(LQConfig):
|
||||
"""MAA计划表配置"""
|
||||
|
||||
config: Config
|
||||
config object to be initialized
|
||||
"""
|
||||
if isinstance(config, QConfig):
|
||||
self._cfg = config
|
||||
self._cfg.themeChanged.connect(self.themeChanged)
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
if isinstance(file, (str, Path)):
|
||||
self._cfg.file = Path(file)
|
||||
self.Info_Name = ConfigItem("Info", "Name", "")
|
||||
self.Info_Mode = OptionsConfigItem(
|
||||
"Info", "Mode", "ALL", OptionsValidator(["ALL", "Weekly"])
|
||||
)
|
||||
|
||||
try:
|
||||
with open(self._cfg.file, encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
except:
|
||||
cfg = {}
|
||||
self.config_item_dict: dict[str, Dict[str, ConfigItem]] = {}
|
||||
|
||||
# map config items'key to item
|
||||
items = {}
|
||||
for name in dir(self._cfg):
|
||||
item = getattr(self._cfg, name)
|
||||
if isinstance(item, ConfigItem):
|
||||
items[item.key] = item
|
||||
for group in [
|
||||
"ALL",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
]:
|
||||
self.config_item_dict[group] = {}
|
||||
|
||||
# update the value of config item
|
||||
for k, v in cfg.items():
|
||||
if not isinstance(v, dict) and items.get(k) is not None:
|
||||
items[k].deserializeFrom(v)
|
||||
elif isinstance(v, dict):
|
||||
for key, value in v.items():
|
||||
key = k + "." + key
|
||||
if items.get(key) is not None:
|
||||
items[key].deserializeFrom(value)
|
||||
self.config_item_dict[group]["MedicineNumb"] = ConfigItem(
|
||||
group, "MedicineNumb", 0, RangeValidator(0, 1024)
|
||||
)
|
||||
self.config_item_dict[group]["SeriesNumb"] = OptionsConfigItem(
|
||||
group,
|
||||
"SeriesNumb",
|
||||
"0",
|
||||
OptionsValidator(["0", "6", "5", "4", "3", "2", "1", "-1"]),
|
||||
)
|
||||
self.config_item_dict[group]["GameId"] = ConfigItem(group, "GameId", "-")
|
||||
self.config_item_dict[group]["GameId_1"] = ConfigItem(
|
||||
group, "GameId_1", "-"
|
||||
)
|
||||
self.config_item_dict[group]["GameId_2"] = ConfigItem(
|
||||
group, "GameId_2", "-"
|
||||
)
|
||||
self.config_item_dict[group]["GameId_Remain"] = ConfigItem(
|
||||
group, "GameId_Remain", "-"
|
||||
)
|
||||
|
||||
self.theme = self.get(self._cfg.themeMode)
|
||||
for name in [
|
||||
"MedicineNumb",
|
||||
"SeriesNumb",
|
||||
"GameId",
|
||||
"GameId_1",
|
||||
"GameId_2",
|
||||
"GameId_Remain",
|
||||
]:
|
||||
setattr(self, f"{group}_{name}", self.config_item_dict[group][name])
|
||||
|
||||
def get_current_info(self, name: str) -> ConfigItem:
|
||||
"""获取当前的计划表配置项"""
|
||||
|
||||
if self.get(self.Info_Mode) == "ALL":
|
||||
return self.config_item_dict["ALL"][name]
|
||||
elif self.get(self.Info_Mode) == "Weekly":
|
||||
today = datetime.now().strftime("%A")
|
||||
if today in self.config_item_dict:
|
||||
return self.config_item_dict[today][name]
|
||||
else:
|
||||
return self.config_item_dict["ALL"][name]
|
||||
|
||||
|
||||
class AppConfig(GlobalConfig):
|
||||
|
||||
VERSION = "4.3.8.2"
|
||||
VERSION = "4.3.10.3"
|
||||
|
||||
gameid_refreshed = Signal()
|
||||
PASSWORD_refreshed = Signal()
|
||||
@@ -648,14 +600,42 @@ class AppConfig(GlobalConfig):
|
||||
self.PASSWORD = ""
|
||||
self.running_list = []
|
||||
self.silence_list = []
|
||||
self.info_bar_list = []
|
||||
self.gameid_dict = {
|
||||
"ALL": {"value": [], "text": []},
|
||||
"Today": {"value": [], "text": []},
|
||||
"Monday": {"value": [], "text": []},
|
||||
"Tuesday": {"value": [], "text": []},
|
||||
"Wednesday": {"value": [], "text": []},
|
||||
"Thursday": {"value": [], "text": []},
|
||||
"Friday": {"value": [], "text": []},
|
||||
"Saturday": {"value": [], "text": []},
|
||||
"Sunday": {"value": [], "text": []},
|
||||
}
|
||||
self.power_sign = "NoAction"
|
||||
self.if_ignore_silence = False
|
||||
self.if_database_opened = False
|
||||
|
||||
self.search_member()
|
||||
self.search_queue()
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="AUTO_MAA",
|
||||
description="A MAA Multi Account Management and Automation Tool",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--mode",
|
||||
choices=["gui", "cli"],
|
||||
default="gui",
|
||||
help="使用UI界面或命令行模式运行程序",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config",
|
||||
nargs="+",
|
||||
choices=list(self.member_dict.keys()) + list(self.queue_dict.keys()),
|
||||
help="指定需要运行哪些配置项",
|
||||
)
|
||||
self.args = parser.parse_args()
|
||||
|
||||
self.initialize()
|
||||
|
||||
def initialize(self) -> None:
|
||||
@@ -678,7 +658,8 @@ class AppConfig(GlobalConfig):
|
||||
def init_logger(self) -> None:
|
||||
"""初始化日志记录器"""
|
||||
|
||||
logger.remove(0)
|
||||
if self.args.mode != "cli":
|
||||
logger.remove(0)
|
||||
|
||||
logger.add(
|
||||
sink=self.log_path,
|
||||
@@ -691,10 +672,14 @@ class AppConfig(GlobalConfig):
|
||||
retention="1 month",
|
||||
compression="zip",
|
||||
)
|
||||
logger.info("")
|
||||
logger.info("===================================")
|
||||
logger.info("AUTO_MAA 主程序")
|
||||
logger.info(f"版本号: v{self.VERSION}")
|
||||
logger.info(f"根目录: {self.app_path}")
|
||||
logger.info(
|
||||
f"运行模式: {'图形化界面' if self.args.mode == 'gui' else '命令行界面'}"
|
||||
)
|
||||
logger.info("===================================")
|
||||
|
||||
logger.info("日志记录器初始化完成")
|
||||
@@ -702,21 +687,23 @@ class AppConfig(GlobalConfig):
|
||||
def get_gameid(self) -> None:
|
||||
|
||||
# 从MAA服务器获取活动关卡信息
|
||||
Network.set_info(
|
||||
network = Network.add_task(
|
||||
mode="get",
|
||||
url="https://ota.maa.plus/MaaAssistantArknights/api/gui/StageActivity.json",
|
||||
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 = []
|
||||
|
||||
gameid_dict = {"value": [], "text": []}
|
||||
ss_gameid_dict = {"value": [], "text": []}
|
||||
|
||||
for gameid_info in gameid_infos:
|
||||
|
||||
@@ -729,53 +716,11 @@ class AppConfig(GlobalConfig):
|
||||
gameid_info["Activity"]["UtcExpireTime"], "%Y/%m/%d %H:%M:%S"
|
||||
)
|
||||
):
|
||||
gameid_dict["value"].append(gameid_info["Value"])
|
||||
gameid_dict["text"].append(gameid_info["Value"])
|
||||
ss_gameid_dict["value"].append(gameid_info["Value"])
|
||||
ss_gameid_dict["text"].append(gameid_info["Value"])
|
||||
|
||||
# 生成全部关卡信息
|
||||
self.gameid_dict["ALL"]["value"] = gameid_dict["value"] + [
|
||||
"-",
|
||||
"1-7",
|
||||
"R8-11",
|
||||
"12-17-HARD",
|
||||
"CE-6",
|
||||
"AP-5",
|
||||
"CA-5",
|
||||
"LS-6",
|
||||
"SK-5",
|
||||
"PR-A-1",
|
||||
"PR-A-2",
|
||||
"PR-B-1",
|
||||
"PR-B-2",
|
||||
"PR-C-1",
|
||||
"PR-C-2",
|
||||
"PR-D-1",
|
||||
"PR-D-2",
|
||||
]
|
||||
self.gameid_dict["ALL"]["text"] = gameid_dict["text"] + [
|
||||
"当前/上次",
|
||||
"1-7",
|
||||
"R8-11",
|
||||
"12-17-HARD",
|
||||
"龙门币-6/5",
|
||||
"红票-5",
|
||||
"技能-5",
|
||||
"经验-6/5",
|
||||
"碳-5",
|
||||
"奶/盾芯片",
|
||||
"奶/盾芯片组",
|
||||
"术/狙芯片",
|
||||
"术/狙芯片组",
|
||||
"先/辅芯片",
|
||||
"先/辅芯片组",
|
||||
"近/特芯片",
|
||||
"近/特芯片组",
|
||||
]
|
||||
|
||||
# 生成本日关卡信息
|
||||
days = self.server_date().isoweekday()
|
||||
|
||||
gameid_list = [
|
||||
# 生成每日关卡信息
|
||||
gameid_daily_info = [
|
||||
{"value": "-", "text": "当前/上次", "days": [1, 2, 3, 4, 5, 6, 7]},
|
||||
{"value": "1-7", "text": "1-7", "days": [1, 2, 3, 4, 5, 6, 7]},
|
||||
{"value": "R8-11", "text": "R8-11", "days": [1, 2, 3, 4, 5, 6, 7]},
|
||||
@@ -799,12 +744,20 @@ class AppConfig(GlobalConfig):
|
||||
{"value": "PR-D-2", "text": "近/特芯片组", "days": [2, 3, 6, 7]},
|
||||
]
|
||||
|
||||
for gameid_info in gameid_list:
|
||||
if days in gameid_info["days"]:
|
||||
gameid_dict["value"].append(gameid_info["value"])
|
||||
gameid_dict["text"].append(gameid_info["text"])
|
||||
for day in range(0, 8):
|
||||
|
||||
self.gameid_dict["Today"] = gameid_dict
|
||||
today_gameid_dict = {"value": [], "text": []}
|
||||
|
||||
for gameid_info in gameid_daily_info:
|
||||
|
||||
if day in gameid_info["days"] or day == 0:
|
||||
today_gameid_dict["value"].append(gameid_info["value"])
|
||||
today_gameid_dict["text"].append(gameid_info["text"])
|
||||
|
||||
self.gameid_dict[calendar.day_name[day - 1] if day > 0 else "ALL"] = {
|
||||
"value": today_gameid_dict["value"] + ss_gameid_dict["value"],
|
||||
"text": today_gameid_dict["text"] + ss_gameid_dict["text"],
|
||||
}
|
||||
|
||||
self.gameid_refreshed.emit()
|
||||
|
||||
@@ -1235,6 +1188,28 @@ class AppConfig(GlobalConfig):
|
||||
sorted(user_dict.items(), key=lambda x: int(x[0][3:]))
|
||||
)
|
||||
|
||||
def search_plan(self) -> None:
|
||||
"""搜索所有计划表"""
|
||||
|
||||
self.plan_dict: Dict[str, Dict[str, Union[str, Path, MaaPlanConfig]]] = {}
|
||||
if (self.app_path / "config/MaaPlanConfig").exists():
|
||||
for maa_plan_dir in (self.app_path / "config/MaaPlanConfig").iterdir():
|
||||
if maa_plan_dir.is_dir():
|
||||
|
||||
maa_plan_config = MaaPlanConfig()
|
||||
maa_plan_config.load(maa_plan_dir / "config.json", maa_plan_config)
|
||||
maa_plan_config.save()
|
||||
|
||||
self.plan_dict[maa_plan_dir.name] = {
|
||||
"Type": "Maa",
|
||||
"Path": maa_plan_dir,
|
||||
"Config": maa_plan_config,
|
||||
}
|
||||
|
||||
self.plan_dict = dict(
|
||||
sorted(self.plan_dict.items(), key=lambda x: int(x[0][3:]))
|
||||
)
|
||||
|
||||
def search_queue(self):
|
||||
"""搜索所有调度队列实例"""
|
||||
|
||||
@@ -1282,6 +1257,16 @@ class AppConfig(GlobalConfig):
|
||||
if queue["Config"].get(queue["Config"].queue_Member_10) == old:
|
||||
queue["Config"].set(queue["Config"].queue_Member_10, new)
|
||||
|
||||
def change_plan(self, old: str, new: str) -> None:
|
||||
"""修改脚本管理所有下属用户的计划表配置参数"""
|
||||
|
||||
for member in self.member_dict.values():
|
||||
|
||||
for user in member["UserData"].values():
|
||||
|
||||
if user["Config"].get(user["Config"].Info_GameIdMode) == old:
|
||||
user["Config"].set(user["Config"].Info_GameIdMode, new)
|
||||
|
||||
def change_user_info(
|
||||
self, name: str, user_data: Dict[str, Dict[str, Union[str, Path, dict]]]
|
||||
) -> None:
|
||||
@@ -1301,6 +1286,10 @@ class AppConfig(GlobalConfig):
|
||||
user_config.Data_LastAnnihilationDate,
|
||||
info["Config"]["Data"]["LastAnnihilationDate"],
|
||||
)
|
||||
user_config.set(
|
||||
user_config.Data_LastSklandDate,
|
||||
info["Config"]["Data"]["LastSklandDate"],
|
||||
)
|
||||
user_config.set(
|
||||
user_config.Data_ProxyTimes, info["Config"]["Data"]["ProxyTimes"]
|
||||
)
|
||||
|
||||
@@ -30,28 +30,36 @@ from PySide6.QtCore import Qt
|
||||
from qfluentwidgets import InfoBar, InfoBarPosition
|
||||
|
||||
from .config import Config
|
||||
from .sound_player import SoundPlayer
|
||||
|
||||
|
||||
class _MainInfoBar:
|
||||
"""信息通知栏"""
|
||||
|
||||
def push_info_bar(self, mode: str, title: str, content: str, time: int):
|
||||
# 模式到 InfoBar 方法的映射
|
||||
mode_mapping = {
|
||||
"success": InfoBar.success,
|
||||
"warning": InfoBar.warning,
|
||||
"error": InfoBar.error,
|
||||
"info": InfoBar.info,
|
||||
}
|
||||
|
||||
def push_info_bar(
|
||||
self, mode: str, title: str, content: str, time: int, if_force: bool = False
|
||||
):
|
||||
"""推送到信息通知栏"""
|
||||
if Config.main_window is None:
|
||||
logger.error("信息通知栏未设置父窗口")
|
||||
return None
|
||||
|
||||
# 定义模式到 InfoBar 方法的映射
|
||||
mode_mapping = {
|
||||
"success": InfoBar.success,
|
||||
"warning": InfoBar.warning,
|
||||
"error": InfoBar.error,
|
||||
"info": InfoBar.info,
|
||||
}
|
||||
|
||||
# 根据 mode 获取对应的 InfoBar 方法
|
||||
info_bar_method = mode_mapping.get(mode)
|
||||
if info_bar_method:
|
||||
info_bar_method = self.mode_mapping.get(mode)
|
||||
|
||||
if not info_bar_method:
|
||||
logger.error(f"未知的通知栏模式: {mode}")
|
||||
return None
|
||||
|
||||
if Config.main_window.isVisible():
|
||||
info_bar_method(
|
||||
title=title,
|
||||
content=content,
|
||||
@@ -61,8 +69,21 @@ class _MainInfoBar:
|
||||
duration=time,
|
||||
parent=Config.main_window,
|
||||
)
|
||||
else:
|
||||
logger.error(f"未知的通知栏模式: {mode}")
|
||||
elif if_force:
|
||||
# 如果主窗口不可见且强制推送,则录入消息队列等待窗口显示后推送
|
||||
info_bar_item = {
|
||||
"mode": mode,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"time": time,
|
||||
}
|
||||
if info_bar_item not in Config.info_bar_list:
|
||||
Config.info_bar_list.append(info_bar_item)
|
||||
|
||||
if mode == "warning":
|
||||
SoundPlayer.play("发生异常")
|
||||
if mode == "error":
|
||||
SoundPlayer.play("发生错误")
|
||||
|
||||
|
||||
MainInfoBar = _MainInfoBar()
|
||||
|
||||
@@ -26,55 +26,46 @@ v4.3
|
||||
"""
|
||||
|
||||
from loguru import logger
|
||||
from PySide6.QtCore import QThread, QEventLoop, QTimer
|
||||
from PySide6.QtCore import QObject, QThread, QEventLoop
|
||||
import re
|
||||
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.setObjectName(
|
||||
f"NetworkThread-{mode}-{re.sub(r'(&cdk=)[^&]+(&)', r'\1******\2', url)}"
|
||||
)
|
||||
|
||||
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 +74,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 +96,56 @@ 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:
|
||||
"""获取网络请求结果"""
|
||||
|
||||
result = {
|
||||
"status_code": network_thread.status_code,
|
||||
"response_json": network_thread.response_json,
|
||||
"error_message": (
|
||||
re.sub(r"(&cdk=)[^&]+(&)", r"\1******\2", network_thread.error_message)
|
||||
if network_thread.error_message
|
||||
else None
|
||||
),
|
||||
}
|
||||
|
||||
network_thread.quit()
|
||||
network_thread.wait()
|
||||
self.task_queue.remove(network_thread)
|
||||
network_thread.deleteLater()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
Network = _Network()
|
||||
|
||||
69
app/core/sound_player.py
Normal file
69
app/core/sound_player.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# 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 QObject, QUrl
|
||||
from PySide6.QtMultimedia import QSoundEffect
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
from .config import Config
|
||||
|
||||
|
||||
class _SoundPlayer(QObject):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.sounds_path = Config.app_path / "resources/sounds"
|
||||
|
||||
def play(self, sound_name: str):
|
||||
|
||||
if not Config.get(Config.voice_Enabled):
|
||||
return
|
||||
|
||||
if (self.sounds_path / f"both/{sound_name}.wav").exists():
|
||||
|
||||
self.play_voice(self.sounds_path / f"both/{sound_name}.wav")
|
||||
|
||||
elif (
|
||||
self.sounds_path / Config.get(Config.voice_Type) / f"{sound_name}.wav"
|
||||
).exists():
|
||||
|
||||
self.play_voice(
|
||||
self.sounds_path / Config.get(Config.voice_Type) / f"{sound_name}.wav"
|
||||
)
|
||||
|
||||
def play_voice(self, sound_path: Path):
|
||||
|
||||
effect = QSoundEffect(self)
|
||||
effect.setVolume(1)
|
||||
effect.setSource(QUrl.fromLocalFile(sound_path))
|
||||
effect.play()
|
||||
|
||||
|
||||
SoundPlayer = _SoundPlayer()
|
||||
@@ -35,6 +35,7 @@ from typing import Dict, Union
|
||||
from .config import Config
|
||||
from .main_info_bar import MainInfoBar
|
||||
from .network import Network
|
||||
from .sound_player import SoundPlayer
|
||||
from app.models import MaaManager
|
||||
from app.services import System
|
||||
|
||||
@@ -44,6 +45,7 @@ class Task(QThread):
|
||||
|
||||
check_maa_version = Signal(str)
|
||||
push_info_bar = Signal(str, str, str, int)
|
||||
play_sound = Signal(str)
|
||||
question = Signal(str, str)
|
||||
question_response = Signal(bool)
|
||||
update_user_info = Signal(str, dict)
|
||||
@@ -59,6 +61,8 @@ class Task(QThread):
|
||||
):
|
||||
super(Task, self).__init__()
|
||||
|
||||
self.setObjectName(f"Task-{mode}-{name}")
|
||||
|
||||
self.mode = mode
|
||||
self.name = name
|
||||
self.info = info
|
||||
@@ -82,6 +86,7 @@ class Task(QThread):
|
||||
)
|
||||
self.task.check_maa_version.connect(self.check_maa_version.emit)
|
||||
self.task.push_info_bar.connect(self.push_info_bar.emit)
|
||||
self.task.play_sound.connect(self.play_sound.emit)
|
||||
self.task.accomplish.connect(lambda: self.accomplish.emit([]))
|
||||
|
||||
self.task.run()
|
||||
@@ -141,6 +146,7 @@ class Task(QThread):
|
||||
self.question_response.disconnect()
|
||||
self.question_response.connect(self.task.question_response.emit)
|
||||
self.task.push_info_bar.connect(self.push_info_bar.emit)
|
||||
self.task.play_sound.connect(self.play_sound.emit)
|
||||
self.task.create_user_list.connect(self.create_user_list.emit)
|
||||
self.task.update_user_list.connect(self.update_user_list.emit)
|
||||
self.task.update_log_text.connect(self.update_log_text.emit)
|
||||
@@ -191,6 +197,7 @@ class _TaskManager(QObject):
|
||||
|
||||
logger.info(f"任务开始:{name}")
|
||||
MainInfoBar.push_info_bar("info", "任务开始", name, 3000)
|
||||
SoundPlayer.play("任务开始")
|
||||
|
||||
Config.running_list.append(name)
|
||||
self.task_dict[name] = Task(mode, name, info)
|
||||
@@ -199,6 +206,7 @@ class _TaskManager(QObject):
|
||||
lambda title, content: self.push_dialog(name, title, content)
|
||||
)
|
||||
self.task_dict[name].push_info_bar.connect(MainInfoBar.push_info_bar)
|
||||
self.task_dict[name].play_sound.connect(SoundPlayer.play)
|
||||
self.task_dict[name].update_user_info.connect(Config.change_user_info)
|
||||
self.task_dict[name].accomplish.connect(
|
||||
lambda logs: self.remove_task(mode, name, logs)
|
||||
@@ -239,6 +247,7 @@ class _TaskManager(QObject):
|
||||
|
||||
logger.info(f"任务结束:{name}")
|
||||
MainInfoBar.push_info_bar("info", "任务结束", name, 3000)
|
||||
SoundPlayer.play("任务结束")
|
||||
|
||||
self.task_dict[name].deleteLater()
|
||||
self.task_dict.pop(name)
|
||||
@@ -274,23 +283,26 @@ class _TaskManager(QObject):
|
||||
)
|
||||
)
|
||||
|
||||
if Config.args.mode == "cli" and Config.power_sign == "NoAction":
|
||||
Config.set_power_sign("KillSelf")
|
||||
|
||||
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
|
||||
|
||||
@@ -26,23 +26,21 @@ 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
|
||||
import pyautogui
|
||||
from pathlib import Path
|
||||
import keyboard
|
||||
|
||||
from .config import Config
|
||||
from .task_manager import TaskManager
|
||||
from app.services import System
|
||||
|
||||
|
||||
class _MainTimer(QWidget):
|
||||
class _MainTimer(QObject):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.if_FailSafeException = False
|
||||
|
||||
self.Timer = QTimer()
|
||||
self.Timer.timeout.connect(self.timed_start)
|
||||
self.Timer.timeout.connect(self.set_silence)
|
||||
@@ -97,22 +95,30 @@ class _MainTimer(QWidget):
|
||||
):
|
||||
|
||||
windows = System.get_window_info()
|
||||
|
||||
# 排除雷电名为新通知的窗口
|
||||
windows = [
|
||||
window
|
||||
for window in windows
|
||||
if not (
|
||||
window[0] == "新通知" and Path(window[1]) in Config.silence_list
|
||||
)
|
||||
]
|
||||
|
||||
if any(
|
||||
str(emulator_path) in window
|
||||
for window in windows
|
||||
for emulator_path in Config.silence_list
|
||||
):
|
||||
try:
|
||||
pyautogui.hotkey(
|
||||
*[
|
||||
keyboard.press_and_release(
|
||||
"+".join(
|
||||
_.strip().lower()
|
||||
for _ in Config.get(Config.function_BossKey).split("+")
|
||||
]
|
||||
)
|
||||
)
|
||||
except pyautogui.FailSafeException as e:
|
||||
if not self.if_FailSafeException:
|
||||
logger.warning(f"FailSafeException: {e}")
|
||||
self.if_FailSafeException = True
|
||||
except Exception as e:
|
||||
logger.error(f"模拟按键时出错:{e}")
|
||||
|
||||
def check_power(self):
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ from PySide6.QtCore import QObject, Signal, QEventLoop, QFileSystemWatcher, QTim
|
||||
import json
|
||||
import subprocess
|
||||
import shutil
|
||||
import time
|
||||
import re
|
||||
import win32com.client
|
||||
from datetime import datetime, timedelta
|
||||
@@ -39,7 +38,7 @@ from jinja2 import Environment, FileSystemLoader
|
||||
from typing import Union, List, Dict
|
||||
|
||||
from app.core import Config, MaaConfig, MaaUserConfig
|
||||
from app.services import Notify, System
|
||||
from app.services import Notify, Crypto, System, skland_sign_in
|
||||
|
||||
|
||||
class MaaManager(QObject):
|
||||
@@ -50,6 +49,7 @@ class MaaManager(QObject):
|
||||
question_response = Signal(bool)
|
||||
update_user_info = Signal(str, dict)
|
||||
push_info_bar = Signal(str, str, str, int)
|
||||
play_sound = Signal(str)
|
||||
create_user_list = Signal(list)
|
||||
update_user_list = Signal(list)
|
||||
update_log_text = Signal(str)
|
||||
@@ -88,6 +88,8 @@ class MaaManager(QObject):
|
||||
self.question_response.connect(self.__capture_response)
|
||||
self.question_response.connect(self.question_loop.quit)
|
||||
|
||||
self.wait_loop = QEventLoop()
|
||||
|
||||
self.interrupt.connect(self.quit_monitor)
|
||||
|
||||
self.maa_version = None
|
||||
@@ -102,6 +104,9 @@ class MaaManager(QObject):
|
||||
"Path": info["Path"],
|
||||
"Config": info["Config"].toDict(),
|
||||
}
|
||||
planed_info = info["Config"].get_plan_info()
|
||||
for key, value in planed_info.items():
|
||||
self.data[name]["Config"]["Info"][key] = value
|
||||
|
||||
self.data = dict(sorted(self.data.items(), key=lambda x: int(x[0][3:])))
|
||||
|
||||
@@ -114,10 +119,15 @@ class MaaManager(QObject):
|
||||
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/tasks.json"
|
||||
self.port_range = [0] + [
|
||||
(i // 2 + 1) * (-1 if i % 2 else 1)
|
||||
for i in range(0, 2 * self.set["RunSet"]["ADBSearchRange"])
|
||||
]
|
||||
|
||||
def run(self):
|
||||
"""主进程,运行MAA代理进程"""
|
||||
|
||||
current_date = datetime.now().strftime("%m-%d")
|
||||
curdate = Config.server_date().strftime("%Y-%m-%d")
|
||||
begin_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
@@ -209,6 +219,51 @@ class MaaManager(QObject):
|
||||
user_logs_list = []
|
||||
user_start_time = datetime.now()
|
||||
|
||||
if user_data["Info"]["IfSkland"] and user_data["Info"]["SklandToken"]:
|
||||
|
||||
if user_data["Data"]["LastSklandDate"] != datetime.now().strftime(
|
||||
"%Y-%m-%d"
|
||||
):
|
||||
|
||||
self.update_log_text.emit("正在执行森空岛签到中\n请稍候~")
|
||||
|
||||
skland_result = skland_sign_in(
|
||||
Crypto.win_decryptor(user_data["Info"]["SklandToken"])
|
||||
)
|
||||
|
||||
for type, user_list in skland_result.items():
|
||||
|
||||
if type != "总计" and len(user_list) > 0:
|
||||
|
||||
logger.info(
|
||||
f"{self.name} | 用户: {user[0]} - 森空岛签到{type}: {'、'.join(user_list)}"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"info",
|
||||
f"森空岛签到{type}",
|
||||
"、".join(user_list),
|
||||
-1,
|
||||
)
|
||||
|
||||
if (
|
||||
skland_result["总计"] > 0
|
||||
and len(skland_result["失败"]) == 0
|
||||
):
|
||||
user_data["Data"][
|
||||
"LastSklandDate"
|
||||
] = datetime.now().strftime("%Y-%m-%d")
|
||||
self.play_sound.emit("森空岛签到成功")
|
||||
else:
|
||||
self.play_sound.emit("森空岛签到失败")
|
||||
|
||||
elif user_data["Info"]["IfSkland"]:
|
||||
logger.warning(
|
||||
f"{self.name} | 用户: {user[0]} - 未配置森空岛签到Token,跳过森空岛签到"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"warning", "森空岛签到失败", "未配置鹰角网络通行证登录凭证", -1
|
||||
)
|
||||
|
||||
# 剿灭-日常模式循环
|
||||
for mode in ["Annihilation", "Routine"]:
|
||||
|
||||
@@ -266,9 +321,23 @@ class MaaManager(QObject):
|
||||
)
|
||||
|
||||
# 解析任务构成
|
||||
if user_data["Info"]["Mode"] == "简洁":
|
||||
if mode == "Routine":
|
||||
|
||||
self.task_dict = {
|
||||
"WakeUp": str(user_data["Task"]["IfWakeUp"]),
|
||||
"Recruiting": str(user_data["Task"]["IfRecruiting"]),
|
||||
"Base": str(user_data["Task"]["IfBase"]),
|
||||
"Combat": str(user_data["Task"]["IfCombat"]),
|
||||
"Mission": str(user_data["Task"]["IfMission"]),
|
||||
"Mall": str(user_data["Task"]["IfMall"]),
|
||||
"AutoRoguelike": str(user_data["Task"]["IfAutoRoguelike"]),
|
||||
"Reclamation": str(user_data["Task"]["IfReclamation"]),
|
||||
}
|
||||
|
||||
elif mode == "Annihilation":
|
||||
|
||||
if user_data["Info"]["Mode"] == "简洁":
|
||||
|
||||
if mode == "Annihilation":
|
||||
self.task_dict = {
|
||||
"WakeUp": "True",
|
||||
"Recruiting": "False",
|
||||
@@ -280,52 +349,40 @@ class MaaManager(QObject):
|
||||
"Reclamation": "False",
|
||||
}
|
||||
|
||||
elif mode == "Routine":
|
||||
elif user_data["Info"]["Mode"] == "详细":
|
||||
|
||||
with (self.data[user[2]]["Path"] / f"{mode}/gui.json").open(
|
||||
mode="r", encoding="utf-8"
|
||||
) as f:
|
||||
data = json.load(f)
|
||||
|
||||
self.task_dict = {
|
||||
"WakeUp": "True",
|
||||
"Recruiting": "True",
|
||||
"Base": "True",
|
||||
"Combat": "True",
|
||||
"Mission": "True",
|
||||
"Mall": "True",
|
||||
"AutoRoguelike": "False",
|
||||
"Reclamation": "False",
|
||||
"WakeUp": data["Configurations"]["Default"][
|
||||
"TaskQueue.WakeUp.IsChecked"
|
||||
],
|
||||
"Recruiting": data["Configurations"]["Default"][
|
||||
"TaskQueue.Recruiting.IsChecked"
|
||||
],
|
||||
"Base": data["Configurations"]["Default"][
|
||||
"TaskQueue.Base.IsChecked"
|
||||
],
|
||||
"Combat": data["Configurations"]["Default"][
|
||||
"TaskQueue.Combat.IsChecked"
|
||||
],
|
||||
"Mission": data["Configurations"]["Default"][
|
||||
"TaskQueue.Mission.IsChecked"
|
||||
],
|
||||
"Mall": data["Configurations"]["Default"][
|
||||
"TaskQueue.Mall.IsChecked"
|
||||
],
|
||||
"AutoRoguelike": data["Configurations"]["Default"][
|
||||
"TaskQueue.AutoRoguelike.IsChecked"
|
||||
],
|
||||
"Reclamation": data["Configurations"]["Default"][
|
||||
"TaskQueue.Reclamation.IsChecked"
|
||||
],
|
||||
}
|
||||
|
||||
elif user_data["Info"]["Mode"] == "详细":
|
||||
|
||||
with (self.data[user[2]]["Path"] / f"{mode}/gui.json").open(
|
||||
mode="r", encoding="utf-8"
|
||||
) as f:
|
||||
data = json.load(f)
|
||||
|
||||
self.task_dict = {
|
||||
"WakeUp": data["Configurations"]["Default"][
|
||||
"TaskQueue.WakeUp.IsChecked"
|
||||
],
|
||||
"Recruiting": data["Configurations"]["Default"][
|
||||
"TaskQueue.Recruiting.IsChecked"
|
||||
],
|
||||
"Base": data["Configurations"]["Default"][
|
||||
"TaskQueue.Base.IsChecked"
|
||||
],
|
||||
"Combat": data["Configurations"]["Default"][
|
||||
"TaskQueue.Combat.IsChecked"
|
||||
],
|
||||
"Mission": data["Configurations"]["Default"][
|
||||
"TaskQueue.Mission.IsChecked"
|
||||
],
|
||||
"Mall": data["Configurations"]["Default"][
|
||||
"TaskQueue.Mall.IsChecked"
|
||||
],
|
||||
"AutoRoguelike": data["Configurations"]["Default"][
|
||||
"TaskQueue.AutoRoguelike.IsChecked"
|
||||
],
|
||||
"Reclamation": data["Configurations"]["Default"][
|
||||
"TaskQueue.Reclamation.IsChecked"
|
||||
],
|
||||
}
|
||||
|
||||
# 尝试次数循环
|
||||
for i in range(self.set["RunSet"]["RunTimesLimit"]):
|
||||
|
||||
@@ -386,6 +443,12 @@ class MaaManager(QObject):
|
||||
self.if_open_emulator = True
|
||||
break
|
||||
|
||||
self.wait_time = int(
|
||||
set["Configurations"]["Default"][
|
||||
"Start.EmulatorWaitSeconds"
|
||||
]
|
||||
)
|
||||
|
||||
self.ADB_path = Path(
|
||||
set["Configurations"]["Default"]["Connect.AdbPath"]
|
||||
)
|
||||
@@ -410,6 +473,7 @@ 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,
|
||||
@@ -428,6 +492,9 @@ 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,
|
||||
@@ -446,6 +513,8 @@ class MaaManager(QObject):
|
||||
# 添加静默进程标记
|
||||
Config.silence_list.append(self.emulator_path)
|
||||
|
||||
self.search_ADB_address()
|
||||
|
||||
# 创建MAA任务
|
||||
maa = subprocess.Popen(
|
||||
[self.maa_exe_path],
|
||||
@@ -489,10 +558,7 @@ class MaaManager(QObject):
|
||||
"检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s"
|
||||
)
|
||||
|
||||
for _ in range(10):
|
||||
if self.isInterruptionRequested:
|
||||
break
|
||||
time.sleep(1)
|
||||
self.sleep(10)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.name} | 用户: {user[0]} - 代理任务异常: {self.maa_result}"
|
||||
@@ -517,6 +583,12 @@ class MaaManager(QObject):
|
||||
) as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 记录自定义基建索引
|
||||
if self.task_dict["Base"] == "False":
|
||||
user_data["Data"]["CustomInfrastPlanIndex"] = data[
|
||||
"Configurations"
|
||||
]["Default"]["Infrast.CustomInfrastPlanIndex"]
|
||||
|
||||
# 记录更新包路径
|
||||
if (
|
||||
data["Global"]["VersionUpdate.package"]
|
||||
@@ -536,16 +608,15 @@ class MaaManager(QObject):
|
||||
f"{user[0].replace("_", " ")}的{mode_book[mode][5:7]}出现异常",
|
||||
1,
|
||||
)
|
||||
for _ in range(10):
|
||||
if self.isInterruptionRequested:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
# 移除静默进程标记
|
||||
Config.silence_list.remove(self.emulator_path)
|
||||
if i == self.set["RunSet"]["RunTimesLimit"] - 1:
|
||||
self.play_sound.emit("子任务失败")
|
||||
else:
|
||||
self.play_sound.emit(self.maa_result)
|
||||
self.sleep(10)
|
||||
|
||||
# 任务结束后释放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,
|
||||
@@ -567,29 +638,6 @@ class MaaManager(QObject):
|
||||
self.emulator_process.wait()
|
||||
self.if_open_emulator = True
|
||||
|
||||
# 执行MAA解压更新动作
|
||||
if self.maa_update_package:
|
||||
|
||||
logger.info(
|
||||
f"{self.name} | 检测到MAA更新,正在执行更新动作"
|
||||
)
|
||||
|
||||
self.update_log_text.emit(
|
||||
f"检测到MAA存在更新\nMAA正在执行更新动作\n请等待10s"
|
||||
)
|
||||
self.set_maa("更新MAA", None)
|
||||
subprocess.Popen(
|
||||
[self.maa_exe_path],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
for _ in range(10):
|
||||
if self.isInterruptionRequested:
|
||||
break
|
||||
time.sleep(1)
|
||||
System.kill_process(self.maa_exe_path)
|
||||
|
||||
logger.info(f"{self.name} | 更新动作结束")
|
||||
|
||||
# 记录剿灭情况
|
||||
if (
|
||||
mode == "Annihilation"
|
||||
@@ -607,33 +655,57 @@ class MaaManager(QObject):
|
||||
Config.app_path
|
||||
/ f"history/{curdate}/{user_data["Info"]["Name"]}/{start_time.strftime("%H-%M-%S")}.json",
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfSendSixStar) and if_six_star:
|
||||
|
||||
if if_six_star:
|
||||
self.push_notification(
|
||||
"公招六星",
|
||||
f"喜报:用户 {user[0]} 公招出六星啦!",
|
||||
{"user_name": user_data["Info"]["Name"]},
|
||||
{
|
||||
"user_name": user_data["Info"]["Name"],
|
||||
},
|
||||
user_data,
|
||||
)
|
||||
self.play_sound.emit("六星喜报")
|
||||
|
||||
# 执行MAA解压更新动作
|
||||
if self.maa_update_package:
|
||||
|
||||
logger.info(
|
||||
f"{self.name} | 检测到MAA更新,正在执行更新动作"
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfSendStatistic):
|
||||
self.update_log_text.emit(
|
||||
f"检测到MAA存在更新\nMAA正在执行更新动作\n请等待10s"
|
||||
)
|
||||
self.play_sound.emit("MAA更新")
|
||||
self.set_maa("更新MAA", None)
|
||||
subprocess.Popen(
|
||||
[self.maa_exe_path],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
self.sleep(10)
|
||||
System.kill_process(self.maa_exe_path)
|
||||
|
||||
statistics = Config.merge_maa_logs("指定项", user_logs_list)
|
||||
statistics["user_info"] = user[0]
|
||||
statistics["start_time"] = user_start_time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
statistics["end_time"] = datetime.now().strftime(
|
||||
"%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
statistics["maa_result"] = (
|
||||
"代理任务全部完成"
|
||||
if (run_book["Annihilation"] and run_book["Routine"])
|
||||
else "代理任务未全部完成"
|
||||
)
|
||||
self.push_notification(
|
||||
"统计信息", f"用户 {user[0]} 的自动代理统计报告", statistics
|
||||
)
|
||||
self.maa_update_package = ""
|
||||
|
||||
logger.info(f"{self.name} | 更新动作结束")
|
||||
|
||||
# 发送统计信息
|
||||
statistics = Config.merge_maa_logs("指定项", user_logs_list)
|
||||
statistics["user_index"] = user[2]
|
||||
statistics["user_info"] = user[0]
|
||||
statistics["start_time"] = user_start_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
statistics["end_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
statistics["maa_result"] = (
|
||||
"代理任务全部完成"
|
||||
if (run_book["Annihilation"] and run_book["Routine"])
|
||||
else "代理任务未全部完成"
|
||||
)
|
||||
self.push_notification(
|
||||
"统计信息",
|
||||
f"{current_date} | 用户 {user[0]} 的自动代理统计报告",
|
||||
statistics,
|
||||
user_data,
|
||||
)
|
||||
|
||||
if run_book["Annihilation"] and run_book["Routine"]:
|
||||
# 成功完成代理的用户修改相关参数
|
||||
@@ -719,10 +791,7 @@ class MaaManager(QObject):
|
||||
# 无命令行中止MAA与其子程序
|
||||
System.kill_process(self.maa_exe_path)
|
||||
self.if_open_emulator = True
|
||||
for _ in range(10):
|
||||
if self.isInterruptionRequested:
|
||||
break
|
||||
time.sleep(1)
|
||||
self.sleep(10)
|
||||
|
||||
# 登录成功,结束循环
|
||||
if run_book[0]:
|
||||
@@ -730,6 +799,7 @@ class MaaManager(QObject):
|
||||
# 登录失败,询问是否结束循环
|
||||
elif not self.isInterruptionRequested:
|
||||
|
||||
self.play_sound.emit("排查重试")
|
||||
if not self.push_question(
|
||||
"操作提示", "MAA未能正确登录到PRTS,是否重试?"
|
||||
):
|
||||
@@ -738,6 +808,7 @@ class MaaManager(QObject):
|
||||
# 登录成功,录入人工排查情况
|
||||
if run_book[0] and not self.isInterruptionRequested:
|
||||
|
||||
self.play_sound.emit("排查录入")
|
||||
if self.push_question(
|
||||
"操作提示", "请检查用户代理情况,该用户是否正确完成代理任务?"
|
||||
):
|
||||
@@ -804,9 +875,9 @@ class MaaManager(QObject):
|
||||
|
||||
# 保存运行日志
|
||||
title = (
|
||||
f"{self.set["MaaSet"]["Name"]}的{self.mode[:4]}任务报告"
|
||||
f"{current_date} | {self.set["MaaSet"]["Name"]}的{self.mode[:4]}任务报告"
|
||||
if self.set["MaaSet"]["Name"] != ""
|
||||
else f"{self.mode[:4]}任务报告"
|
||||
else f"{current_date} | {self.mode[:4]}任务报告"
|
||||
)
|
||||
result = {
|
||||
"title": f"{self.mode[:4]}任务报告",
|
||||
@@ -826,6 +897,17 @@ class MaaManager(QObject):
|
||||
self.data[_]["Config"]["Info"]["Name"] for _ in wait_index
|
||||
],
|
||||
}
|
||||
|
||||
# 生成结果文本
|
||||
result_text = (
|
||||
f"任务开始时间:{result["start_time"]},结束时间:{result["end_time"]}\n"
|
||||
f"已完成数:{result["completed_count"]},未完成数:{result["uncompleted_count"]}\n\n"
|
||||
)
|
||||
if len(result["failed_user"]) > 0:
|
||||
result_text += f"{self.mode[2:4]}未成功的用户:\n{"\n".join(result["failed_user"])}\n"
|
||||
if len(result["waiting_user"]) > 0:
|
||||
result_text += f"\n未开始{self.mode[2:4]}的用户:\n{"\n".join(result["waiting_user"])}\n"
|
||||
|
||||
# 推送代理结果通知
|
||||
Notify.push_plyer(
|
||||
title.replace("报告", "已完成!"),
|
||||
@@ -833,15 +915,7 @@ class MaaManager(QObject):
|
||||
f"已完成用户数:{len(over_index)},未完成用户数:{len(error_index) + len(wait_index)}",
|
||||
10,
|
||||
)
|
||||
if Config.get(Config.notify_SendTaskResultTime) == "任何时刻" or (
|
||||
Config.get(Config.notify_SendTaskResultTime) == "仅失败时"
|
||||
and len(error_index) + len(wait_index) != 0
|
||||
):
|
||||
result_text = self.push_notification("代理结果", title, result)
|
||||
else:
|
||||
result_text = self.push_notification(
|
||||
"代理结果", title, result, if_get_text_only=True
|
||||
)
|
||||
self.push_notification("代理结果", title, result)
|
||||
|
||||
self.agree_bilibili(False)
|
||||
self.log_monitor.deleteLater()
|
||||
@@ -856,6 +930,7 @@ class MaaManager(QObject):
|
||||
|
||||
self.maa_result = "任务被手动中止"
|
||||
self.isInterruptionRequested = True
|
||||
self.wait_loop.quit()
|
||||
|
||||
def push_question(self, title: str, message: str) -> bool:
|
||||
|
||||
@@ -866,6 +941,98 @@ class MaaManager(QObject):
|
||||
def __capture_response(self, response: bool) -> None:
|
||||
self.response = response
|
||||
|
||||
def sleep(self, time: int) -> None:
|
||||
"""非阻塞型等待"""
|
||||
|
||||
QTimer.singleShot(time * 1000, self.wait_loop.quit)
|
||||
self.wait_loop.exec()
|
||||
|
||||
def search_ADB_address(self) -> None:
|
||||
"""搜索ADB实际地址"""
|
||||
|
||||
self.update_log_text.emit(
|
||||
f"即将搜索ADB实际地址\n正在等待模拟器完成启动\n请等待{self.wait_time}s"
|
||||
)
|
||||
|
||||
self.sleep(self.wait_time)
|
||||
|
||||
if self.isInterruptionRequested:
|
||||
return None
|
||||
|
||||
# 移除静默进程标记
|
||||
Config.silence_list.remove(self.emulator_path)
|
||||
|
||||
if "-" in self.ADB_address:
|
||||
ADB_ip = f"{self.ADB_address.split("-")[0]}-"
|
||||
ADB_port = int(self.ADB_address.split("-")[1])
|
||||
|
||||
elif ":" in self.ADB_address:
|
||||
ADB_ip = f"{self.ADB_address.split(':')[0]}:"
|
||||
ADB_port = int(self.ADB_address.split(":")[1])
|
||||
|
||||
logger.info(
|
||||
f"{self.name} | 正在搜索ADB实际地址,ADB前缀:{ADB_ip},初始端口:{ADB_port},搜索范围:{self.port_range}"
|
||||
)
|
||||
|
||||
for port in self.port_range:
|
||||
|
||||
ADB_address = f"{ADB_ip}{ADB_port + port}"
|
||||
|
||||
# 尝试通过ADB连接到指定地址
|
||||
connect_result = subprocess.run(
|
||||
[self.ADB_path, "connect", ADB_address],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
stdin=subprocess.DEVNULL,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
if "connected" in connect_result.stdout:
|
||||
|
||||
# 检查连接状态
|
||||
devices_result = subprocess.run(
|
||||
[self.ADB_path, "devices"],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
stdin=subprocess.DEVNULL,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
)
|
||||
if ADB_address in devices_result.stdout:
|
||||
|
||||
logger.info(f"{self.name} | ADB实际地址:{ADB_address}")
|
||||
|
||||
# 断开连接
|
||||
subprocess.run(
|
||||
[self.ADB_path, "disconnect", ADB_address],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
|
||||
self.ADB_address = ADB_address
|
||||
|
||||
# 覆写当前ADB地址
|
||||
System.kill_process(self.maa_exe_path)
|
||||
with self.maa_set_path.open(mode="r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
data["Configurations"]["Default"][
|
||||
"Connect.Address"
|
||||
] = self.ADB_address
|
||||
data["Configurations"]["Default"]["Start.EmulatorWaitSeconds"] = "0"
|
||||
with self.maa_set_path.open(mode="w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||
|
||||
self.play_sound.emit("ADB成功")
|
||||
return None
|
||||
|
||||
else:
|
||||
logger.info(f"{self.name} | 无法连接到ADB地址:{ADB_address}")
|
||||
else:
|
||||
logger.info(f"{self.name} | 无法连接到ADB地址:{ADB_address}")
|
||||
|
||||
if not self.isInterruptionRequested:
|
||||
self.play_sound.emit("ADB失败")
|
||||
|
||||
def refresh_maa_log(self) -> None:
|
||||
"""刷新MAA日志"""
|
||||
|
||||
@@ -1168,6 +1335,9 @@ class MaaManager(QObject):
|
||||
]["Id"]
|
||||
|
||||
# 按预设设定任务
|
||||
data["Configurations"]["Default"][
|
||||
"TaskQueue.WakeUp.IsChecked"
|
||||
] = "True" # 开始唤醒
|
||||
data["Configurations"]["Default"]["TaskQueue.Recruiting.IsChecked"] = (
|
||||
self.task_dict["Recruiting"]
|
||||
) # 自动公招
|
||||
@@ -1192,10 +1362,6 @@ class MaaManager(QObject):
|
||||
|
||||
if user_data["Info"]["Mode"] == "简洁":
|
||||
|
||||
data["Configurations"]["Default"][
|
||||
"TaskQueue.WakeUp.IsChecked"
|
||||
] = "True" # 开始唤醒
|
||||
|
||||
data["Configurations"]["Default"]["Start.ClientType"] = user_data[
|
||||
"Info"
|
||||
][
|
||||
@@ -1294,7 +1460,9 @@ class MaaManager(QObject):
|
||||
] = "True" # 备选关卡
|
||||
data["Configurations"]["Default"][
|
||||
"Fight.UseRemainingSanityStage"
|
||||
] = "True" # 使用剩余理智
|
||||
] = (
|
||||
"True" if user_data["Info"]["GameId_Remain"] != "-" else "False"
|
||||
) # 使用剩余理智
|
||||
data["Configurations"]["Default"][
|
||||
"Fight.UseExpiringMedicine"
|
||||
] = "True" # 无限吃48小时内过期的理智药
|
||||
@@ -1392,7 +1560,9 @@ class MaaManager(QObject):
|
||||
] = "True" # 备选关卡
|
||||
data["Configurations"]["Default"][
|
||||
"Fight.UseRemainingSanityStage"
|
||||
] = "True" # 使用剩余理智
|
||||
] = (
|
||||
"True" if user_data["Info"]["GameId_Remain"] != "-" else "False"
|
||||
) # 使用剩余理智
|
||||
|
||||
# 基建模式
|
||||
if (
|
||||
@@ -1418,12 +1588,20 @@ class MaaManager(QObject):
|
||||
"Start.RunDirectly"
|
||||
] = "True" # 启动MAA后直接运行
|
||||
data["Global"]["Start.MinimizeDirectly"] = "True" # 启动MAA后直接最小化
|
||||
|
||||
data["Global"]["GUI.UseTray"] = "True" # 显示托盘图标
|
||||
data["Global"]["GUI.MinimizeToTray"] = "True" # 最小化时隐藏至托盘
|
||||
data["Configurations"]["Default"]["Start.OpenEmulatorAfterLaunch"] = str(
|
||||
self.if_open_emulator
|
||||
) # 启动MAA后自动开启模拟器
|
||||
data["Global"][
|
||||
"VersionUpdate.ScheduledUpdateCheck"
|
||||
] = "False" # 定时检查更新
|
||||
data["Global"][
|
||||
"VersionUpdate.AutoDownloadUpdatePackage"
|
||||
] = "False" # 自动下载更新包
|
||||
data["Global"][
|
||||
"VersionUpdate.AutoInstallUpdatePackage"
|
||||
] = "False" # 自动安装更新包
|
||||
|
||||
# 账号切换
|
||||
if user_data["Info"]["Server"] == "Official":
|
||||
@@ -1439,15 +1617,6 @@ 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"
|
||||
][
|
||||
@@ -1494,6 +1663,15 @@ class MaaManager(QObject):
|
||||
data["Configurations"]["Default"][
|
||||
"Start.OpenEmulatorAfterLaunch"
|
||||
] = "False" # 启动MAA后自动开启模拟器
|
||||
data["Global"][
|
||||
"VersionUpdate.ScheduledUpdateCheck"
|
||||
] = "False" # 定时检查更新
|
||||
data["Global"][
|
||||
"VersionUpdate.AutoDownloadUpdatePackage"
|
||||
] = "False" # 自动下载更新包
|
||||
data["Global"][
|
||||
"VersionUpdate.AutoInstallUpdatePackage"
|
||||
] = "False" # 自动安装更新包
|
||||
|
||||
if Config.get(Config.function_IfSilence):
|
||||
data["Global"][
|
||||
@@ -1502,15 +1680,6 @@ class MaaManager(QObject):
|
||||
|
||||
if "全局" in mode:
|
||||
|
||||
data["Global"][
|
||||
"VersionUpdate.ScheduledUpdateCheck"
|
||||
] = "False" # 定时检查更新
|
||||
data["Global"][
|
||||
"VersionUpdate.AutoDownloadUpdatePackage"
|
||||
] = "False" # 自动下载更新包
|
||||
data["Global"][
|
||||
"VersionUpdate.AutoInstallUpdatePackage"
|
||||
] = "False" # 自动安装更新包
|
||||
data["Configurations"]["Default"][
|
||||
"TaskQueue.WakeUp.IsChecked"
|
||||
] = "False" # 开始唤醒
|
||||
@@ -1635,16 +1804,21 @@ class MaaManager(QObject):
|
||||
mode: str,
|
||||
title: str,
|
||||
message: Union[str, dict],
|
||||
if_get_text_only: bool = False,
|
||||
) -> str:
|
||||
user_data: Dict[str, Dict[str, Union[str, int, bool]]] = None,
|
||||
) -> None:
|
||||
"""通过所有渠道推送通知"""
|
||||
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(str(Config.app_path / "resources/html"))
|
||||
)
|
||||
|
||||
if mode == "代理结果":
|
||||
|
||||
if mode == "代理结果" and (
|
||||
Config.get(Config.notify_SendTaskResultTime) == "任何时刻"
|
||||
or (
|
||||
Config.get(Config.notify_SendTaskResultTime) == "仅失败时"
|
||||
and message["uncompleted_count"] != 0
|
||||
)
|
||||
):
|
||||
# 生成文本通知内容
|
||||
message_text = (
|
||||
f"任务开始时间:{message["start_time"]},结束时间:{message["end_time"]}\n"
|
||||
@@ -1656,9 +1830,6 @@ class MaaManager(QObject):
|
||||
if len(message["waiting_user"]) > 0:
|
||||
message_text += f"\n未开始{self.mode[2:4]}的用户:\n{"\n".join(message["waiting_user"])}\n"
|
||||
|
||||
if if_get_text_only:
|
||||
return message_text
|
||||
|
||||
# 生成HTML通知内容
|
||||
message["failed_user"] = "、".join(message["failed_user"])
|
||||
message["waiting_user"] = "、".join(message["waiting_user"])
|
||||
@@ -1666,11 +1837,31 @@ class MaaManager(QObject):
|
||||
template = env.get_template("MAA_result.html")
|
||||
message_html = template.render(message)
|
||||
|
||||
Notify.send_mail("网页", title, message_html)
|
||||
Notify.ServerChanPush(title, f"{message_text}\n\nAUTO_MAA 敬上")
|
||||
Notify.CompanyWebHookBotPush(title, f"{message_text}\n\nAUTO_MAA 敬上")
|
||||
# ServerChan的换行是两个换行符。故而将\n替换为\n\n
|
||||
serverchan_message = message_text.replace("\n", "\n\n")
|
||||
|
||||
return message_text
|
||||
# 发送全局通知
|
||||
|
||||
if Config.get(Config.notify_IfSendMail):
|
||||
Notify.send_mail(
|
||||
"网页", title, message_html, Config.get(Config.notify_ToAddress)
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfServerChan):
|
||||
Notify.ServerChanPush(
|
||||
title,
|
||||
f"{serverchan_message}\n\nAUTO_MAA 敬上",
|
||||
Config.get(Config.notify_ServerChanKey),
|
||||
Config.get(Config.notify_ServerChanTag),
|
||||
Config.get(Config.notify_ServerChanChannel),
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfCompanyWebHookBot):
|
||||
Notify.CompanyWebHookBotPush(
|
||||
title,
|
||||
f"{message_text}\n\nAUTO_MAA 敬上",
|
||||
Config.get(Config.notify_CompanyWebHookBotUrl),
|
||||
)
|
||||
|
||||
elif mode == "统计信息":
|
||||
|
||||
@@ -1699,18 +1890,155 @@ class MaaManager(QObject):
|
||||
template = env.get_template("MAA_statistics.html")
|
||||
message_html = template.render(message)
|
||||
|
||||
Notify.send_mail("网页", title, message_html)
|
||||
# ServerChan的换行是两个换行符。故而将\n替换为\n\n
|
||||
serverchan_message = message_text.replace("\n", "\n\n")
|
||||
Notify.ServerChanPush(title, f"{serverchan_message}\n\nAUTO_MAA 敬上")
|
||||
Notify.CompanyWebHookBotPush(title, f"{message_text}\n\nAUTO_MAA 敬上")
|
||||
|
||||
# 发送全局通知
|
||||
if Config.get(Config.notify_IfSendStatistic):
|
||||
|
||||
if Config.get(Config.notify_IfSendMail):
|
||||
Notify.send_mail(
|
||||
"网页", title, message_html, Config.get(Config.notify_ToAddress)
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfServerChan):
|
||||
Notify.ServerChanPush(
|
||||
title,
|
||||
f"{serverchan_message}\n\nAUTO_MAA 敬上",
|
||||
Config.get(Config.notify_ServerChanKey),
|
||||
Config.get(Config.notify_ServerChanTag),
|
||||
Config.get(Config.notify_ServerChanChannel),
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfCompanyWebHookBot):
|
||||
Notify.CompanyWebHookBotPush(
|
||||
title,
|
||||
f"{message_text}\n\nAUTO_MAA 敬上",
|
||||
Config.get(Config.notify_CompanyWebHookBotUrl),
|
||||
)
|
||||
|
||||
# 发送用户单独通知
|
||||
if (
|
||||
user_data["Notify"]["Enabled"]
|
||||
and user_data["Notify"]["IfSendStatistic"]
|
||||
):
|
||||
|
||||
# 发送邮件通知
|
||||
if user_data["Notify"]["IfSendMail"]:
|
||||
if user_data["Notify"]["ToAddress"]:
|
||||
Notify.send_mail(
|
||||
"网页",
|
||||
title,
|
||||
message_html,
|
||||
user_data["Notify"]["ToAddress"],
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.name} | 用户邮箱地址为空,无法发送用户单独的邮件通知"
|
||||
)
|
||||
|
||||
# 发送ServerChan通知
|
||||
if user_data["Notify"]["IfServerChan"]:
|
||||
if user_data["Notify"]["ServerChanKey"]:
|
||||
Notify.ServerChanPush(
|
||||
title,
|
||||
f"{serverchan_message}\n\nAUTO_MAA 敬上",
|
||||
user_data["Notify"]["ServerChanKey"],
|
||||
user_data["Notify"]["ServerChanTag"],
|
||||
user_data["Notify"]["ServerChanChannel"],
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.name} |用户ServerChan密钥为空,无法发送用户单独的ServerChan通知"
|
||||
)
|
||||
|
||||
# 推送CompanyWebHookBot通知
|
||||
if user_data["Notify"]["IfCompanyWebHookBot"]:
|
||||
if user_data["Notify"]["CompanyWebHookBotUrl"]:
|
||||
Notify.CompanyWebHookBotPush(
|
||||
title,
|
||||
f"{message_text}\n\nAUTO_MAA 敬上",
|
||||
user_data["Notify"]["CompanyWebHookBotUrl"],
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.name} |用户CompanyWebHookBot密钥为空,无法发送用户单独的CompanyWebHookBot通知"
|
||||
)
|
||||
|
||||
elif mode == "公招六星":
|
||||
|
||||
# 生成HTML通知内容
|
||||
template = env.get_template("MAA_six_star.html")
|
||||
|
||||
message_html = template.render(message)
|
||||
|
||||
Notify.send_mail("网页", title, message_html)
|
||||
Notify.ServerChanPush(title, "好羡慕~\n\nAUTO_MAA 敬上")
|
||||
Notify.CompanyWebHookBotPush(title, "好羡慕~\n\nAUTO_MAA 敬上")
|
||||
# 发送全局通知
|
||||
if Config.get(Config.notify_IfSendSixStar):
|
||||
|
||||
if Config.get(Config.notify_IfSendMail):
|
||||
Notify.send_mail(
|
||||
"网页", title, message_html, Config.get(Config.notify_ToAddress)
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfServerChan):
|
||||
Notify.ServerChanPush(
|
||||
title,
|
||||
"好羡慕~\n\nAUTO_MAA 敬上",
|
||||
Config.get(Config.notify_ServerChanKey),
|
||||
Config.get(Config.notify_ServerChanTag),
|
||||
Config.get(Config.notify_ServerChanChannel),
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfCompanyWebHookBot):
|
||||
Notify.CompanyWebHookBotPush(
|
||||
title,
|
||||
"好羡慕~\n\nAUTO_MAA 敬上",
|
||||
Config.get(Config.notify_CompanyWebHookBotUrl),
|
||||
)
|
||||
|
||||
# 发送用户单独通知
|
||||
if user_data["Notify"]["Enabled"] and user_data["Notify"]["IfSendSixStar"]:
|
||||
|
||||
# 发送邮件通知
|
||||
if user_data["Notify"]["IfSendMail"]:
|
||||
if user_data["Notify"]["ToAddress"]:
|
||||
Notify.send_mail(
|
||||
"网页",
|
||||
title,
|
||||
message_html,
|
||||
user_data["Notify"]["ToAddress"],
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.name} | 用户邮箱地址为空,无法发送用户单独的邮件通知"
|
||||
)
|
||||
|
||||
# 发送ServerChan通知
|
||||
if user_data["Notify"]["IfServerChan"]:
|
||||
|
||||
if user_data["Notify"]["ServerChanKey"]:
|
||||
Notify.ServerChanPush(
|
||||
title,
|
||||
"好羡慕~\n\nAUTO_MAA 敬上",
|
||||
user_data["Notify"]["ServerChanKey"],
|
||||
user_data["Notify"]["ServerChanTag"],
|
||||
user_data["Notify"]["ServerChanChannel"],
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.name} |用户ServerChan密钥为空,无法发送用户单独的ServerChan通知"
|
||||
)
|
||||
|
||||
# 推送CompanyWebHookBot通知
|
||||
if user_data["Notify"]["IfCompanyWebHookBot"]:
|
||||
if user_data["Notify"]["CompanyWebHookBotUrl"]:
|
||||
Notify.CompanyWebHookBotPush(
|
||||
title,
|
||||
"好羡慕~\n\nAUTO_MAA 敬上",
|
||||
user_data["Notify"]["CompanyWebHookBotUrl"],
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.name} |用户CompanyWebHookBot密钥为空,无法发送用户单独的CompanyWebHookBot通知"
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -32,5 +32,6 @@ __license__ = "GPL-3.0 license"
|
||||
from .notification import Notify
|
||||
from .security import Crypto
|
||||
from .system import System
|
||||
from .skland import skland_sign_in
|
||||
|
||||
__all__ = ["Notify", "Crypto", "System"]
|
||||
__all__ = ["Notify", "Crypto", "System", "skland_sign_in"]
|
||||
|
||||
@@ -25,23 +25,24 @@ v4.3
|
||||
作者:DLmaster_361
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QWidget
|
||||
from PySide6.QtCore import Signal
|
||||
import requests
|
||||
import time
|
||||
from loguru import logger
|
||||
from plyer import notification
|
||||
import re
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
import time
|
||||
from email.header import Header
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import formataddr
|
||||
|
||||
import requests
|
||||
from PySide6.QtCore import QObject, Signal
|
||||
from loguru import logger
|
||||
from plyer import notification
|
||||
|
||||
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)
|
||||
|
||||
@@ -65,218 +66,210 @@ class Notification(QWidget):
|
||||
|
||||
return True
|
||||
|
||||
def send_mail(self, mode, title, content) -> None:
|
||||
def send_mail(self, mode, title, content, to_address) -> None:
|
||||
"""推送邮件通知"""
|
||||
|
||||
if Config.get(Config.notify_IfSendMail):
|
||||
|
||||
if (
|
||||
Config.get(Config.notify_SMTPServerAddress) == ""
|
||||
or Config.get(Config.notify_AuthorizationCode) == ""
|
||||
or not bool(
|
||||
re.match(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||
Config.get(Config.notify_FromAddress),
|
||||
)
|
||||
)
|
||||
or not bool(
|
||||
re.match(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||
Config.get(Config.notify_ToAddress),
|
||||
)
|
||||
)
|
||||
):
|
||||
logger.error(
|
||||
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"邮件通知推送异常",
|
||||
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
# 定义邮件正文
|
||||
if mode == "文本":
|
||||
message = MIMEText(content, "plain", "utf-8")
|
||||
elif mode == "网页":
|
||||
message = MIMEMultipart("alternative")
|
||||
message["From"] = formataddr(
|
||||
(
|
||||
Header("AUTO_MAA通知服务", "utf-8").encode(),
|
||||
Config.get(Config.notify_FromAddress),
|
||||
)
|
||||
) # 发件人显示的名字
|
||||
message["To"] = formataddr(
|
||||
(
|
||||
Header("AUTO_MAA用户", "utf-8").encode(),
|
||||
Config.get(Config.notify_ToAddress),
|
||||
)
|
||||
) # 收件人显示的名字
|
||||
message["Subject"] = Header(title, "utf-8")
|
||||
|
||||
if mode == "网页":
|
||||
message.attach(MIMEText(content, "html", "utf-8"))
|
||||
|
||||
smtpObj = smtplib.SMTP_SSL(
|
||||
Config.get(Config.notify_SMTPServerAddress),
|
||||
465,
|
||||
)
|
||||
smtpObj.login(
|
||||
if (
|
||||
Config.get(Config.notify_SMTPServerAddress) == ""
|
||||
or Config.get(Config.notify_AuthorizationCode) == ""
|
||||
or not bool(
|
||||
re.match(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||
Config.get(Config.notify_FromAddress),
|
||||
Crypto.win_decryptor(Config.get(Config.notify_AuthorizationCode)),
|
||||
)
|
||||
smtpObj.sendmail(
|
||||
)
|
||||
or not bool(
|
||||
re.match(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||
to_address,
|
||||
)
|
||||
)
|
||||
):
|
||||
logger.error(
|
||||
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"邮件通知推送异常",
|
||||
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
# 定义邮件正文
|
||||
if mode == "文本":
|
||||
message = MIMEText(content, "plain", "utf-8")
|
||||
elif mode == "网页":
|
||||
message = MIMEMultipart("alternative")
|
||||
message["From"] = formataddr(
|
||||
(
|
||||
Header("AUTO_MAA通知服务", "utf-8").encode(),
|
||||
Config.get(Config.notify_FromAddress),
|
||||
Config.get(Config.notify_ToAddress),
|
||||
message.as_string(),
|
||||
)
|
||||
smtpObj.quit()
|
||||
logger.success("邮件发送成功")
|
||||
except Exception as e:
|
||||
logger.error(f"发送邮件时出错:\n{e}")
|
||||
self.push_info_bar.emit("error", "发送邮件时出错", f"{e}", -1)
|
||||
|
||||
def ServerChanPush(self, title, content):
|
||||
"""使用Server酱推送通知(支持 tag 和 channel,避免使用SDK)"""
|
||||
if Config.get(Config.notify_IfServerChan):
|
||||
send_key = Config.get(Config.notify_ServerChanKey)
|
||||
|
||||
if not send_key:
|
||||
logger.error("请正确设置Server酱的SendKey")
|
||||
self.push_info_bar.emit(
|
||||
"error", "Server酱通知推送异常", "请正确设置Server酱的SendKey", -1
|
||||
) # 发件人显示的名字
|
||||
message["To"] = formataddr(
|
||||
(
|
||||
Header("AUTO_MAA用户", "utf-8").encode(),
|
||||
to_address,
|
||||
)
|
||||
return None
|
||||
) # 收件人显示的名字
|
||||
message["Subject"] = Header(title, "utf-8")
|
||||
|
||||
try:
|
||||
# 构造 URL
|
||||
if send_key.startswith("sctp"):
|
||||
match = re.match(r"^sctp(\d+)t", send_key)
|
||||
if match:
|
||||
url = f"https://{match.group(1)}.push.ft07.com/send/{send_key}.send"
|
||||
else:
|
||||
raise ValueError("SendKey 格式错误(sctp)")
|
||||
if mode == "网页":
|
||||
message.attach(MIMEText(content, "html", "utf-8"))
|
||||
|
||||
smtpObj = smtplib.SMTP_SSL(
|
||||
Config.get(Config.notify_SMTPServerAddress),
|
||||
465,
|
||||
)
|
||||
smtpObj.login(
|
||||
Config.get(Config.notify_FromAddress),
|
||||
Crypto.win_decryptor(Config.get(Config.notify_AuthorizationCode)),
|
||||
)
|
||||
smtpObj.sendmail(
|
||||
Config.get(Config.notify_FromAddress),
|
||||
to_address,
|
||||
message.as_string(),
|
||||
)
|
||||
smtpObj.quit()
|
||||
logger.success("邮件发送成功")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"发送邮件时出错:\n{e}")
|
||||
self.push_info_bar.emit("error", "发送邮件时出错", f"{e}", -1)
|
||||
return None
|
||||
return None
|
||||
|
||||
def ServerChanPush(self, title, content, send_key, tag, channel):
|
||||
"""使用Server酱推送通知"""
|
||||
if not send_key:
|
||||
logger.error("请正确设置Server酱的SendKey")
|
||||
self.push_info_bar.emit(
|
||||
"error", "Server酱通知推送异常", "请正确设置Server酱的SendKey", -1
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
# 构造 URL
|
||||
if send_key.startswith("sctp"):
|
||||
match = re.match(r"^sctp(\d+)t", send_key)
|
||||
if match:
|
||||
url = f"https://{match.group(1)}.push.ft07.com/send/{send_key}.send"
|
||||
else:
|
||||
url = f"https://sctapi.ftqq.com/{send_key}.send"
|
||||
|
||||
# 构建 tags 和 channel
|
||||
def is_valid(s):
|
||||
return s == "" or (
|
||||
s == "|".join(s.split("|"))
|
||||
and (s.count("|") == 0 or all(s.split("|")))
|
||||
)
|
||||
|
||||
tags = "|".join(
|
||||
_.strip()
|
||||
for _ in Config.get(Config.notify_ServerChanTag).split("|")
|
||||
)
|
||||
channels = "|".join(
|
||||
_.strip()
|
||||
for _ in Config.get(Config.notify_ServerChanChannel).split("|")
|
||||
)
|
||||
|
||||
options = {}
|
||||
if is_valid(tags):
|
||||
options["tags"] = tags
|
||||
else:
|
||||
logger.warning("Server酱 Tag 配置不正确,将被忽略")
|
||||
self.push_info_bar.emit(
|
||||
"warning",
|
||||
"Server酱通知推送异常",
|
||||
"请正确设置 ServerChan 的 Tag",
|
||||
-1,
|
||||
)
|
||||
|
||||
if is_valid(channels):
|
||||
options["channel"] = channels
|
||||
else:
|
||||
logger.warning("Server酱 Channel 配置不正确,将被忽略")
|
||||
self.push_info_bar.emit(
|
||||
"warning",
|
||||
"Server酱通知推送异常",
|
||||
"请正确设置 ServerChan 的 Channel",
|
||||
-1,
|
||||
)
|
||||
|
||||
# 请求发送
|
||||
params = {"title": title, "desp": content, **options}
|
||||
headers = {"Content-Type": "application/json;charset=utf-8"}
|
||||
|
||||
response = requests.post(url, json=params, headers=headers, timeout=10)
|
||||
result = response.json()
|
||||
|
||||
if result.get("code") == 0:
|
||||
logger.info("Server酱推送通知成功")
|
||||
return True
|
||||
else:
|
||||
error_code = result.get("code", "-1")
|
||||
logger.error(f"Server酱通知推送失败:响应码:{error_code}")
|
||||
self.push_info_bar.emit(
|
||||
"error", "Server酱通知推送失败", f"响应码:{error_code}", -1
|
||||
)
|
||||
return f"Server酱通知推送失败:{error_code}"
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Server酱通知推送异常")
|
||||
self.push_info_bar.emit(
|
||||
"error", "Server酱通知推送异常", f"请检查相关设置,如还有问题可联系开发者", -1
|
||||
)
|
||||
return f"Server酱通知推送异常:{str(e)}"
|
||||
|
||||
def CompanyWebHookBotPush(self, title, content):
|
||||
"""使用企业微信群机器人推送通知"""
|
||||
if Config.get(Config.notify_IfCompanyWebHookBot):
|
||||
|
||||
if Config.get(Config.notify_CompanyWebHookBotUrl) == "":
|
||||
logger.error("请正确设置企业微信群机器人的WebHook地址")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送异常",
|
||||
"请正确设置企业微信群机器人的WebHook地址",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
|
||||
content = f"{title}\n{content}"
|
||||
data = {"msgtype": "text", "text": {"content": content}}
|
||||
# 从远程服务器获取最新主题图像
|
||||
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)
|
||||
raise ValueError("SendKey 格式错误(sctp)")
|
||||
else:
|
||||
logger.error(f"推送企业微信群机器人时出错:{err}")
|
||||
url = f"https://sctapi.ftqq.com/{send_key}.send"
|
||||
|
||||
# 构建 tags 和 channel
|
||||
def is_valid(s):
|
||||
return s == "" or (
|
||||
s == "|".join(s.split("|"))
|
||||
and (s.count("|") == 0 or all(s.split("|")))
|
||||
)
|
||||
|
||||
tags = "|".join(_.strip() for _ in tag.split("|"))
|
||||
channels = "|".join(_.strip() for _ in channel.split("|"))
|
||||
|
||||
options = {}
|
||||
if is_valid(tags):
|
||||
options["tags"] = tags
|
||||
else:
|
||||
logger.warning("Server酱 Tag 配置不正确,将被忽略")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送失败",
|
||||
f'使用企业微信群机器人推送通知时出错:{info["errmsg"]}',
|
||||
"warning",
|
||||
"Server酱通知推送异常",
|
||||
"请正确设置 ServerChan 的 Tag",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
|
||||
if info["errcode"] == 0:
|
||||
logger.info("企业微信群机器人推送通知成功")
|
||||
if is_valid(channels):
|
||||
options["channel"] = channels
|
||||
else:
|
||||
logger.warning("Server酱 Channel 配置不正确,将被忽略")
|
||||
self.push_info_bar.emit(
|
||||
"warning",
|
||||
"Server酱通知推送异常",
|
||||
"请正确设置 ServerChan 的 Channel",
|
||||
-1,
|
||||
)
|
||||
|
||||
# 请求发送
|
||||
params = {"title": title, "desp": content, **options}
|
||||
headers = {"Content-Type": "application/json;charset=utf-8"}
|
||||
|
||||
response = requests.post(url, json=params, headers=headers, timeout=10)
|
||||
result = response.json()
|
||||
|
||||
if result.get("code") == 0:
|
||||
logger.info("Server酱推送通知成功")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"企业微信群机器人推送通知失败:{info}")
|
||||
error_code = result.get("code", "-1")
|
||||
logger.error(f"Server酱通知推送失败:响应码:{error_code}")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送失败",
|
||||
f'使用企业微信群机器人推送通知时出错:{info["errmsg"]}',
|
||||
-1,
|
||||
"error", "Server酱通知推送失败", f"响应码:{error_code}", -1
|
||||
)
|
||||
return f'使用企业微信群机器人推送通知时出错:{info["errmsg"]}'
|
||||
return f"Server酱通知推送失败:{error_code}"
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Server酱通知推送异常")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"Server酱通知推送异常",
|
||||
"请检查相关设置和网络连接。如全部配置正确,请稍后再试。",
|
||||
-1,
|
||||
)
|
||||
return f"Server酱通知推送异常:{str(e)}"
|
||||
|
||||
def CompanyWebHookBotPush(self, title, content, webhook_url):
|
||||
"""使用企业微信群机器人推送通知"""
|
||||
if webhook_url == "":
|
||||
logger.error("请正确设置企业微信群机器人的WebHook地址")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送异常",
|
||||
"请正确设置企业微信群机器人的WebHook地址",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
|
||||
content = f"{title}\n{content}"
|
||||
data = {"msgtype": "text", "text": {"content": content}}
|
||||
|
||||
for _ in range(3):
|
||||
try:
|
||||
response = requests.post(
|
||||
url=webhook_url,
|
||||
json=data,
|
||||
timeout=10,
|
||||
)
|
||||
info = response.json()
|
||||
break
|
||||
except Exception as e:
|
||||
err = e
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
logger.error(f"推送企业微信群机器人时出错:{err}")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送失败",
|
||||
f"使用企业微信群机器人推送通知时出错:{err}",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
|
||||
if info["errcode"] == 0:
|
||||
logger.info("企业微信群机器人推送通知成功")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"企业微信群机器人推送通知失败:{info}")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送失败",
|
||||
f"使用企业微信群机器人推送通知时出错:{err}",
|
||||
-1,
|
||||
)
|
||||
return f"使用企业微信群机器人推送通知时出错:{err}"
|
||||
|
||||
def send_test_notification(self):
|
||||
"""发送测试通知到所有已启用的通知渠道"""
|
||||
@@ -294,6 +287,7 @@ class Notification(QWidget):
|
||||
"文本",
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_ToAddress),
|
||||
)
|
||||
|
||||
# 发送Server酱通知
|
||||
@@ -301,6 +295,9 @@ class Notification(QWidget):
|
||||
self.ServerChanPush(
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_ServerChanKey),
|
||||
Config.get(Config.notify_ServerChanTag),
|
||||
Config.get(Config.notify_ServerChanChannel),
|
||||
)
|
||||
|
||||
# 发送企业微信机器人通知
|
||||
@@ -308,6 +305,7 @@ class Notification(QWidget):
|
||||
self.CompanyWebHookBotPush(
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_CompanyWebHookBotUrl),
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
239
app/services/skland.py
Normal file
239
app/services/skland.py
Normal file
@@ -0,0 +1,239 @@
|
||||
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
|
||||
# Copyright © 2024-2025 DLmaster361
|
||||
|
||||
# This file incorporates work covered by the following copyright and
|
||||
# permission notice:
|
||||
#
|
||||
# skland-checkin-ghaction Copyright © 2023 Yanstory
|
||||
# https://github.com/Yanstory/skland-checkin-ghaction
|
||||
|
||||
# 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、ClozyA
|
||||
"""
|
||||
|
||||
from loguru import logger
|
||||
import time
|
||||
import json
|
||||
import hmac
|
||||
import hashlib
|
||||
import requests
|
||||
from urllib import parse
|
||||
|
||||
|
||||
def skland_sign_in(token) -> dict:
|
||||
"""森空岛签到"""
|
||||
|
||||
app_code = "4ca99fa6b56cc2ba"
|
||||
# 用于获取grant code
|
||||
grant_code_url = "https://as.hypergryph.com/user/oauth2/v2/grant"
|
||||
# 用于获取cred
|
||||
cred_code_url = "https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code"
|
||||
# 查询角色绑定
|
||||
binding_url = "https://zonai.skland.com/api/v1/game/player/binding"
|
||||
# 签到接口
|
||||
sign_url = "https://zonai.skland.com/api/v1/game/attendance"
|
||||
|
||||
# 基础请求头
|
||||
header = {
|
||||
"cred": "",
|
||||
"User-Agent": "Skland/1.5.1 (com.hypergryph.skland; build:100501001; Android 34;) Okhttp/4.11.0",
|
||||
"Accept-Encoding": "gzip",
|
||||
"Connection": "close",
|
||||
}
|
||||
header_login = header.copy()
|
||||
header_for_sign = {
|
||||
"platform": "1",
|
||||
"timestamp": "",
|
||||
"dId": "",
|
||||
"vName": "1.5.1",
|
||||
}
|
||||
|
||||
# 生成签名
|
||||
def generate_signature(token_for_sign: str, path, body_or_query):
|
||||
"""
|
||||
生成请求签名
|
||||
:param token_for_sign: 用于加密的token
|
||||
:param path: 请求路径(如 /api/v1/game/player/binding)
|
||||
:param body_or_query: GET用query字符串,POST用body字符串
|
||||
:return: (sign, 新的header_for_sign字典)
|
||||
"""
|
||||
t = str(int(time.time()) - 2) # 时间戳,-2秒以防服务器时间不一致
|
||||
token_bytes = token_for_sign.encode("utf-8")
|
||||
header_ca = dict(header_for_sign)
|
||||
header_ca["timestamp"] = t
|
||||
header_ca_str = json.dumps(header_ca, separators=(",", ":"))
|
||||
s = path + body_or_query + t + header_ca_str # 拼接原始字符串
|
||||
# HMAC-SHA256 + MD5得到最终sign
|
||||
hex_s = hmac.new(token_bytes, s.encode("utf-8"), hashlib.sha256).hexdigest()
|
||||
md5 = hashlib.md5(hex_s.encode("utf-8")).hexdigest()
|
||||
return md5, header_ca
|
||||
|
||||
# 获取带签名的header
|
||||
def get_sign_header(url: str, method, body, old_header, sign_token):
|
||||
"""
|
||||
获取带签名的请求头
|
||||
:param url: 请求完整url
|
||||
:param method: 请求方式 GET/POST
|
||||
:param body: POST请求体或GET时为None
|
||||
:param old_header: 原始请求头
|
||||
:param sign_token: 当前会话的签名token
|
||||
:return: 新请求头
|
||||
"""
|
||||
h = json.loads(json.dumps(old_header))
|
||||
p = parse.urlparse(url)
|
||||
if method.lower() == "get":
|
||||
sign, header_ca = generate_signature(sign_token, p.path, p.query)
|
||||
else:
|
||||
sign, header_ca = generate_signature(
|
||||
sign_token, p.path, json.dumps(body) if body else ""
|
||||
)
|
||||
h["sign"] = sign
|
||||
for i in header_ca:
|
||||
h[i] = header_ca[i]
|
||||
return h
|
||||
|
||||
# 复制请求头并添加cred
|
||||
def copy_header(cred):
|
||||
v = json.loads(json.dumps(header))
|
||||
v["cred"] = cred
|
||||
return v
|
||||
|
||||
# 使用token一步步拿到cred和sign_token
|
||||
def login_by_token(token_code):
|
||||
"""
|
||||
:param token_code: 你的skyland token
|
||||
:return: (cred, sign_token)
|
||||
"""
|
||||
try:
|
||||
# token为json对象时提取data.content
|
||||
t = json.loads(token_code)
|
||||
token_code = t["data"]["content"]
|
||||
except:
|
||||
pass
|
||||
grant_code = get_grant_code(token_code)
|
||||
return get_cred(grant_code)
|
||||
|
||||
# 通过grant code换cred和sign_token
|
||||
def get_cred(grant):
|
||||
rsp = requests.post(
|
||||
cred_code_url, json={"code": grant, "kind": 1}, headers=header_login
|
||||
).json()
|
||||
if rsp["code"] != 0:
|
||||
raise Exception(f'获得cred失败:{rsp.get("messgae")}')
|
||||
sign_token = rsp["data"]["token"]
|
||||
cred = rsp["data"]["cred"]
|
||||
return cred, sign_token
|
||||
|
||||
# 通过token换grant code
|
||||
def get_grant_code(token):
|
||||
rsp = requests.post(
|
||||
grant_code_url,
|
||||
json={"appCode": app_code, "token": token, "type": 0},
|
||||
headers=header_login,
|
||||
).json()
|
||||
if rsp["status"] != 0:
|
||||
raise Exception(f'使用token: {token} 获得认证代码失败:{rsp.get("msg")}')
|
||||
return rsp["data"]["code"]
|
||||
|
||||
# 获取已绑定的角色列表
|
||||
def get_binding_list(cred, sign_token):
|
||||
"""
|
||||
查询绑定的角色
|
||||
:param cred: 当前cred
|
||||
:param sign_token: 当前sign_token
|
||||
:return: 角色列表
|
||||
"""
|
||||
v = []
|
||||
rsp = requests.get(
|
||||
binding_url,
|
||||
headers=get_sign_header(
|
||||
binding_url, "get", None, copy_header(cred), sign_token
|
||||
),
|
||||
).json()
|
||||
if rsp["code"] != 0:
|
||||
logger.error(f"森空岛服务 | 请求角色列表出现问题:{rsp['message']}")
|
||||
if rsp.get("message") == "用户未登录":
|
||||
logger.error(f"森空岛服务 | 用户登录可能失效了,请重新登录!")
|
||||
return v
|
||||
# 只取明日方舟(arknights)的绑定账号
|
||||
for i in rsp["data"]["list"]:
|
||||
if i.get("appCode") != "arknights":
|
||||
continue
|
||||
v.extend(i.get("bindingList"))
|
||||
return v
|
||||
|
||||
# 执行签到
|
||||
def do_sign(cred, sign_token) -> dict:
|
||||
"""
|
||||
对所有绑定的角色进行签到
|
||||
:param cred: 当前cred
|
||||
:param sign_token: 当前sign_token
|
||||
:return: 签到结果字典
|
||||
"""
|
||||
|
||||
characters = get_binding_list(cred, sign_token)
|
||||
result = {"成功": [], "重复": [], "失败": [], "总计": len(characters)}
|
||||
|
||||
for character in characters:
|
||||
|
||||
body = {
|
||||
"uid": character.get("uid"),
|
||||
"gameId": character.get("channelMasterId"),
|
||||
}
|
||||
rsp = requests.post(
|
||||
sign_url,
|
||||
headers=get_sign_header(
|
||||
sign_url, "post", body, copy_header(cred), sign_token
|
||||
),
|
||||
json=body,
|
||||
).json()
|
||||
|
||||
if rsp["code"] != 0:
|
||||
|
||||
result[
|
||||
"重复" if rsp.get("message") == "请勿重复签到!" else "失败"
|
||||
].append(
|
||||
f"{character.get("nickName")}({character.get("channelName")})"
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
result["成功"].append(
|
||||
f"{character.get("nickName")}({character.get("channelName")})"
|
||||
)
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
return result
|
||||
|
||||
# 主流程
|
||||
try:
|
||||
# 拿到cred和sign_token
|
||||
cred, sign_token = login_by_token(token)
|
||||
time.sleep(1)
|
||||
# 依次签到
|
||||
return do_sign(cred, sign_token)
|
||||
except Exception as e:
|
||||
logger.error(f"森空岛服务 | 森空岛签到失败: {e}")
|
||||
return {"成功": [], "重复": [], "失败": [], "总计": 0}
|
||||
@@ -112,6 +112,7 @@ class _SystemHandler:
|
||||
|
||||
Config.main_window.close()
|
||||
QApplication.quit()
|
||||
sys.exit(0)
|
||||
|
||||
elif sys.platform.startswith("linux"):
|
||||
|
||||
@@ -138,6 +139,7 @@ class _SystemHandler:
|
||||
|
||||
Config.main_window.close()
|
||||
QApplication.quit()
|
||||
sys.exit(0)
|
||||
|
||||
def is_startup(self) -> bool:
|
||||
"""判断程序是否已经开机自启"""
|
||||
|
||||
833
app/ui/Widget.py
833
app/ui/Widget.py
@@ -1,6 +1,12 @@
|
||||
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
|
||||
# Copyright © 2024-2025 DLmaster361
|
||||
|
||||
# This file incorporates work covered by the following copyright and
|
||||
# permission notice:
|
||||
#
|
||||
# ZenlessZoneZero-OneDragon Copyright © 2024-2025 DoctorReid
|
||||
# https://github.com/DoctorReid/ZenlessZoneZero-OneDragon
|
||||
|
||||
# This file is part of AUTO_MAA.
|
||||
|
||||
# AUTO_MAA is free software: you can redistribute it and/or modify
|
||||
@@ -25,17 +31,24 @@ v4.3
|
||||
作者:DLmaster_361
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from typing import Optional, Union, List, Dict
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import markdown
|
||||
from PySide6.QtCore import Qt, QTime, QTimer, QEvent, QSize
|
||||
from PySide6.QtGui import QIcon, QPixmap, QPainter, QPainterPath
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QWidget,
|
||||
QWidget,
|
||||
QLabel,
|
||||
QHBoxLayout,
|
||||
QVBoxLayout,
|
||||
QSizePolicy,
|
||||
)
|
||||
from PySide6.QtCore import Qt, QTime, QTimer, QEvent, QSize
|
||||
from PySide6.QtGui import QIcon, QPixmap, QPainter, QPainterPath
|
||||
from qfluentwidgets import (
|
||||
LineEdit,
|
||||
PasswordLineEdit,
|
||||
@@ -74,15 +87,10 @@ from qfluentwidgets import (
|
||||
ScrollArea,
|
||||
Pivot,
|
||||
PivotItem,
|
||||
FlyoutViewBase,
|
||||
PushSettingCard,
|
||||
)
|
||||
from qfluentwidgets.common.overload import singledispatchmethod
|
||||
import os
|
||||
import re
|
||||
import markdown
|
||||
from datetime import datetime
|
||||
from urllib.parse import urlparse
|
||||
from functools import partial
|
||||
from typing import Optional, Union, List, Dict
|
||||
|
||||
from app.core import Config
|
||||
from app.services import Crypto
|
||||
@@ -108,6 +116,8 @@ class LineEditMessageBox(MessageBoxBase):
|
||||
self.viewLayout.addWidget(self.title)
|
||||
self.viewLayout.addWidget(self.input)
|
||||
|
||||
self.input.setFocus()
|
||||
|
||||
|
||||
class ComboBoxMessageBox(MessageBoxBase):
|
||||
"""选择对话框"""
|
||||
@@ -152,7 +162,7 @@ class ProgressRingMessageBox(MessageBoxBase):
|
||||
super().__init__(parent)
|
||||
self.title = SubtitleLabel(title)
|
||||
|
||||
self.time = 100
|
||||
self.time = 100 if Config.args.mode == "gui" else 1
|
||||
Widget = QWidget()
|
||||
Layout = QHBoxLayout(Widget)
|
||||
self.ring = ProgressRing()
|
||||
@@ -271,6 +281,41 @@ class NoticeMessageBox(MessageBoxBase):
|
||||
self.Layout.addStretch(1)
|
||||
|
||||
|
||||
class SettingFlyoutView(FlyoutViewBase):
|
||||
"""设置卡二级菜单弹出组件"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
title: str,
|
||||
setting_cards: List[Union[SettingCard, HeaderCardWidget]],
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
self.title = SubtitleLabel(title)
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setSpacing(0)
|
||||
content_layout.setContentsMargins(0, 0, 11, 0)
|
||||
for setting_card in setting_cards:
|
||||
content_layout.addWidget(setting_card)
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
scrollArea.setWidget(content_widget)
|
||||
|
||||
self.viewLayout = QVBoxLayout(self)
|
||||
self.viewLayout.setSpacing(12)
|
||||
self.viewLayout.setContentsMargins(20, 16, 9, 16)
|
||||
self.viewLayout.addWidget(self.title)
|
||||
self.viewLayout.addWidget(scrollArea)
|
||||
|
||||
self.setVisible(False)
|
||||
|
||||
|
||||
class SwitchSettingCard(SettingCard):
|
||||
"""Setting card with switch button"""
|
||||
|
||||
@@ -431,24 +476,27 @@ class LineEditSettingCard(SettingCard):
|
||||
self.LineEdit.setMinimumWidth(250)
|
||||
self.LineEdit.setPlaceholderText(text)
|
||||
|
||||
if configItem:
|
||||
self.setValue(self.qconfig.get(configItem))
|
||||
configItem.valueChanged.connect(self.setValue)
|
||||
|
||||
self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
|
||||
self.configItem.valueChanged.connect(self.setValue)
|
||||
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||
|
||||
self.setValue(self.qconfig.get(configItem))
|
||||
|
||||
def __textChanged(self, content: str):
|
||||
self.setValue(content.strip())
|
||||
|
||||
self.configItem.valueChanged.disconnect(self.setValue)
|
||||
self.qconfig.set(self.configItem, content.strip())
|
||||
self.configItem.valueChanged.connect(self.setValue)
|
||||
|
||||
self.textChanged.emit(content.strip())
|
||||
|
||||
def setValue(self, content: str):
|
||||
if self.configItem:
|
||||
self.qconfig.set(self.configItem, content.strip())
|
||||
|
||||
self.LineEdit.textChanged.disconnect(self.__textChanged)
|
||||
self.LineEdit.setText(content.strip())
|
||||
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||
|
||||
|
||||
class PasswordLineEditSettingCard(SettingCard):
|
||||
@@ -477,35 +525,29 @@ class PasswordLineEditSettingCard(SettingCard):
|
||||
self.LineEdit.setPlaceholderText(text)
|
||||
if algorithm == "AUTO":
|
||||
self.LineEdit.setViewPasswordButtonVisible(False)
|
||||
self.if_setValue = False
|
||||
|
||||
if configItem:
|
||||
self.setValue(self.qconfig.get(configItem))
|
||||
configItem.valueChanged.connect(self.setValue)
|
||||
|
||||
self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
|
||||
self.configItem.valueChanged.connect(self.setValue)
|
||||
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||
|
||||
self.setValue(self.qconfig.get(configItem))
|
||||
|
||||
def __textChanged(self, content: str):
|
||||
|
||||
if self.if_setValue:
|
||||
return None
|
||||
|
||||
self.configItem.valueChanged.disconnect(self.setValue)
|
||||
if self.algorithm == "DPAPI":
|
||||
self.setValue(Crypto.win_encryptor(content))
|
||||
self.qconfig.set(self.configItem, Crypto.win_encryptor(content))
|
||||
elif self.algorithm == "AUTO":
|
||||
self.setValue(Crypto.AUTO_encryptor(content))
|
||||
self.qconfig.set(self.configItem, Crypto.AUTO_encryptor(content))
|
||||
self.configItem.valueChanged.connect(self.setValue)
|
||||
|
||||
self.textChanged.emit()
|
||||
|
||||
def setValue(self, content: str):
|
||||
|
||||
self.if_setValue = True
|
||||
|
||||
if self.configItem:
|
||||
self.qconfig.set(self.configItem, content)
|
||||
|
||||
self.LineEdit.textChanged.disconnect(self.__textChanged)
|
||||
if self.algorithm == "DPAPI":
|
||||
self.LineEdit.setText(Crypto.win_decryptor(content))
|
||||
elif self.algorithm == "AUTO":
|
||||
@@ -521,59 +563,7 @@ class PasswordLineEditSettingCard(SettingCard):
|
||||
self.LineEdit.setText("************")
|
||||
self.LineEdit.setPasswordVisible(False)
|
||||
self.LineEdit.setReadOnly(True)
|
||||
|
||||
self.if_setValue = False
|
||||
|
||||
|
||||
class UserLableSettingCard(SettingCard):
|
||||
"""Setting card with User's Lable"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
qconfig: QConfig,
|
||||
configItems: Dict[str, ConfigItem],
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(icon, title, content, parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItems = configItems
|
||||
self.Lable = SubtitleLabel(self)
|
||||
|
||||
if configItems:
|
||||
for configItem in configItems.values():
|
||||
configItem.valueChanged.connect(self.setValue)
|
||||
self.setValue()
|
||||
|
||||
self.hBoxLayout.addWidget(self.Lable, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
|
||||
def setValue(self):
|
||||
if self.configItems:
|
||||
|
||||
text_list = []
|
||||
if not self.qconfig.get(self.configItems["IfPassCheck"]):
|
||||
text_list.append("未通过人工排查")
|
||||
text_list.append(
|
||||
f"今日已代理{self.qconfig.get(self.configItems["ProxyTimes"])}次"
|
||||
if Config.server_date().strftime("%Y-%m-%d")
|
||||
== self.qconfig.get(self.configItems["LastProxyDate"])
|
||||
else "今日未进行代理"
|
||||
)
|
||||
text_list.append(
|
||||
"本周剿灭已完成"
|
||||
if datetime.strptime(
|
||||
self.qconfig.get(self.configItems["LastAnnihilationDate"]),
|
||||
"%Y-%m-%d",
|
||||
).isocalendar()[:2]
|
||||
== Config.server_date().isocalendar()[:2]
|
||||
else "本周剿灭未完成"
|
||||
)
|
||||
|
||||
self.Lable.setText(" | ".join(text_list))
|
||||
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||
|
||||
|
||||
class PushAndSwitchButtonSettingCard(SettingCard):
|
||||
@@ -624,6 +614,83 @@ class PushAndSwitchButtonSettingCard(SettingCard):
|
||||
self.switchButton.setText("开" if isChecked else "关")
|
||||
|
||||
|
||||
class PasswordLineAndSwitchButtonSettingCard(SettingCard):
|
||||
"""Setting card with PasswordLineEdit and SwitchButton"""
|
||||
|
||||
textChanged = Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
text: str,
|
||||
algorithm: str,
|
||||
qconfig: QConfig,
|
||||
configItem_bool: ConfigItem,
|
||||
configItem_info: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(icon, title, content, parent)
|
||||
self.algorithm = algorithm
|
||||
self.qconfig = qconfig
|
||||
self.configItem_bool = configItem_bool
|
||||
self.configItem_info = configItem_info
|
||||
self.LineEdit = PasswordLineEdit(self)
|
||||
self.LineEdit.setMinimumWidth(200)
|
||||
self.LineEdit.setPlaceholderText(text)
|
||||
if algorithm == "AUTO":
|
||||
self.LineEdit.setViewPasswordButtonVisible(False)
|
||||
self.SwitchButton = SwitchButton(self)
|
||||
|
||||
self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
self.hBoxLayout.addWidget(self.SwitchButton, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
|
||||
self.configItem_info.valueChanged.connect(self.setInfo)
|
||||
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||
self.configItem_bool.valueChanged.connect(self.SwitchButton.setChecked)
|
||||
self.SwitchButton.checkedChanged.connect(
|
||||
lambda isChecked: self.qconfig.set(self.configItem_bool, isChecked)
|
||||
)
|
||||
|
||||
self.setInfo(self.qconfig.get(configItem_info))
|
||||
self.SwitchButton.setChecked(self.qconfig.get(configItem_bool))
|
||||
|
||||
def __textChanged(self, content: str):
|
||||
|
||||
self.configItem_info.valueChanged.disconnect(self.setInfo)
|
||||
if self.algorithm == "DPAPI":
|
||||
self.qconfig.set(self.configItem_info, Crypto.win_encryptor(content))
|
||||
elif self.algorithm == "AUTO":
|
||||
self.qconfig.set(self.configItem_info, Crypto.AUTO_encryptor(content))
|
||||
self.configItem_info.valueChanged.connect(self.setInfo)
|
||||
|
||||
self.textChanged.emit()
|
||||
|
||||
def setInfo(self, content: str):
|
||||
|
||||
self.LineEdit.textChanged.disconnect(self.__textChanged)
|
||||
if self.algorithm == "DPAPI":
|
||||
self.LineEdit.setText(Crypto.win_decryptor(content))
|
||||
elif self.algorithm == "AUTO":
|
||||
if Crypto.check_PASSWORD(Config.PASSWORD):
|
||||
self.LineEdit.setText(Crypto.AUTO_decryptor(content, Config.PASSWORD))
|
||||
self.LineEdit.setPasswordVisible(True)
|
||||
self.LineEdit.setReadOnly(False)
|
||||
elif Config.PASSWORD:
|
||||
self.LineEdit.setText("管理密钥错误")
|
||||
self.LineEdit.setPasswordVisible(True)
|
||||
self.LineEdit.setReadOnly(True)
|
||||
else:
|
||||
self.LineEdit.setText("************")
|
||||
self.LineEdit.setPasswordVisible(False)
|
||||
self.LineEdit.setReadOnly(True)
|
||||
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||
|
||||
|
||||
class PushAndComboBoxSettingCard(SettingCard):
|
||||
"""Setting card with push & combo box"""
|
||||
|
||||
@@ -724,7 +791,7 @@ class NoOptionComboBoxSettingCard(SettingCard):
|
||||
value: List[str],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
configItem: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
@@ -757,7 +824,7 @@ class NoOptionComboBoxSettingCard(SettingCard):
|
||||
|
||||
def reLoadOptions(self, value: List[str], texts: List[str]):
|
||||
|
||||
self.comboBox.currentIndexChanged.disconnect()
|
||||
self.comboBox.currentIndexChanged.disconnect(self._onCurrentIndexChanged)
|
||||
self.comboBox.clear()
|
||||
self.optionToText = {o: t for o, t in zip(value, texts)}
|
||||
for text, option in zip(texts, value):
|
||||
@@ -779,7 +846,7 @@ class EditableComboBoxSettingCard(SettingCard):
|
||||
value: List[str],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
configItem: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
@@ -829,7 +896,7 @@ class EditableComboBoxSettingCard(SettingCard):
|
||||
|
||||
def reLoadOptions(self, value: List[str], texts: List[str]):
|
||||
|
||||
self.comboBox.currentIndexChanged.disconnect()
|
||||
self.comboBox.currentIndexChanged.disconnect(self._onCurrentIndexChanged)
|
||||
self.comboBox.clear()
|
||||
self.optionToText = {o: t for o, t in zip(value, texts)}
|
||||
for text, option in zip(texts, value):
|
||||
@@ -867,6 +934,169 @@ class EditableComboBoxSettingCard(SettingCard):
|
||||
self.currentIndexChanged.emit(self.count() - 1)
|
||||
|
||||
|
||||
class SpinBoxWithPlanSettingCard(SpinBoxSettingCard):
|
||||
|
||||
textChanged = Signal(int)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
range: tuple[int, int],
|
||||
qconfig: QConfig,
|
||||
configItem: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(icon, title, content, range, qconfig, configItem, parent)
|
||||
|
||||
self.configItem_plan = None
|
||||
|
||||
self.LineEdit = LineEdit(self)
|
||||
self.LineEdit.setMinimumWidth(150)
|
||||
self.LineEdit.setReadOnly(True)
|
||||
self.LineEdit.setVisible(False)
|
||||
|
||||
self.hBoxLayout.insertWidget(5, self.LineEdit, 0, Qt.AlignRight)
|
||||
|
||||
def setText(self, value: int) -> None:
|
||||
self.LineEdit.setText(str(value))
|
||||
|
||||
def switch_mode(self, mode: str) -> None:
|
||||
"""切换模式"""
|
||||
|
||||
if mode == "固定":
|
||||
|
||||
self.LineEdit.setVisible(False)
|
||||
self.SpinBox.setVisible(True)
|
||||
|
||||
elif mode == "计划":
|
||||
|
||||
self.SpinBox.setVisible(False)
|
||||
self.LineEdit.setVisible(True)
|
||||
|
||||
def change_plan(self, configItem_plan: ConfigItem) -> None:
|
||||
"""切换计划"""
|
||||
|
||||
if self.configItem_plan is not None:
|
||||
self.configItem_plan.valueChanged.disconnect(self.setText)
|
||||
self.configItem_plan = configItem_plan
|
||||
self.configItem_plan.valueChanged.connect(self.setText)
|
||||
self.setText(self.qconfig.get(self.configItem_plan))
|
||||
|
||||
|
||||
class ComboBoxWithPlanSettingCard(ComboBoxSettingCard):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(icon, title, content, texts, qconfig, configItem, parent)
|
||||
|
||||
self.configItem_plan = None
|
||||
|
||||
self.LineEdit = LineEdit(self)
|
||||
self.LineEdit.setMinimumWidth(150)
|
||||
self.LineEdit.setReadOnly(True)
|
||||
self.LineEdit.setVisible(False)
|
||||
|
||||
self.hBoxLayout.insertWidget(5, self.LineEdit, 0, Qt.AlignRight)
|
||||
|
||||
def setText(self, value: str) -> None:
|
||||
|
||||
if value not in self.optionToText:
|
||||
self.optionToText[value] = value
|
||||
|
||||
self.LineEdit.setText(self.optionToText[value])
|
||||
|
||||
def switch_mode(self, mode: str) -> None:
|
||||
"""切换模式"""
|
||||
|
||||
if mode == "固定":
|
||||
|
||||
self.LineEdit.setVisible(False)
|
||||
self.comboBox.setVisible(True)
|
||||
|
||||
elif mode == "计划":
|
||||
|
||||
self.comboBox.setVisible(False)
|
||||
self.LineEdit.setVisible(True)
|
||||
|
||||
def change_plan(self, configItem_plan: ConfigItem) -> None:
|
||||
"""切换计划"""
|
||||
|
||||
if self.configItem_plan is not None:
|
||||
self.configItem_plan.valueChanged.disconnect(self.setText)
|
||||
self.configItem_plan = configItem_plan
|
||||
self.configItem_plan.valueChanged.connect(self.setText)
|
||||
self.setText(self.qconfig.get(self.configItem_plan))
|
||||
|
||||
|
||||
class EditableComboBoxWithPlanSettingCard(EditableComboBoxSettingCard):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
value: List[str],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(
|
||||
icon, title, content, value, texts, qconfig, configItem, parent
|
||||
)
|
||||
|
||||
self.configItem_plan = None
|
||||
|
||||
self.LineEdit = LineEdit(self)
|
||||
self.LineEdit.setMinimumWidth(150)
|
||||
self.LineEdit.setReadOnly(True)
|
||||
self.LineEdit.setVisible(False)
|
||||
|
||||
self.hBoxLayout.insertWidget(5, self.LineEdit, 0, Qt.AlignRight)
|
||||
|
||||
def setText(self, value: str) -> None:
|
||||
|
||||
if value not in self.optionToText:
|
||||
self.optionToText[value] = value
|
||||
|
||||
self.LineEdit.setText(self.optionToText[value])
|
||||
|
||||
def switch_mode(self, mode: str) -> None:
|
||||
"""切换模式"""
|
||||
|
||||
if mode == "固定":
|
||||
|
||||
self.LineEdit.setVisible(False)
|
||||
self.comboBox.setVisible(True)
|
||||
|
||||
elif mode == "计划":
|
||||
|
||||
self.comboBox.setVisible(False)
|
||||
self.LineEdit.setVisible(True)
|
||||
|
||||
def change_plan(self, configItem_plan: ConfigItem) -> None:
|
||||
"""切换计划"""
|
||||
|
||||
if self.configItem_plan is not None:
|
||||
self.configItem_plan.valueChanged.disconnect(self.setText)
|
||||
self.configItem_plan = configItem_plan
|
||||
self.configItem_plan.valueChanged.connect(self.setText)
|
||||
self.setText(self.qconfig.get(self.configItem_plan))
|
||||
|
||||
|
||||
class TimeEditSettingCard(SettingCard):
|
||||
|
||||
enabledChanged = Signal(bool)
|
||||
@@ -933,6 +1163,425 @@ class TimeEditSettingCard(SettingCard):
|
||||
self.TimeEdit.setTime(QTime.fromString(value, "HH:mm"))
|
||||
|
||||
|
||||
class UserLableSettingCard(SettingCard):
|
||||
"""Setting card with User's Lable"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
qconfig: QConfig,
|
||||
configItems: Dict[str, ConfigItem],
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(icon, title, content, parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItems = configItems
|
||||
self.Lable = SubtitleLabel(self)
|
||||
|
||||
if configItems:
|
||||
for configItem in configItems.values():
|
||||
configItem.valueChanged.connect(self.setValue)
|
||||
self.setValue()
|
||||
|
||||
self.hBoxLayout.addWidget(self.Lable, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
|
||||
def setValue(self):
|
||||
|
||||
text_list = []
|
||||
|
||||
if self.configItems:
|
||||
|
||||
if not self.qconfig.get(self.configItems["IfPassCheck"]):
|
||||
text_list.append("未通过人工排查")
|
||||
text_list.append(
|
||||
f"今日已代理{self.qconfig.get(self.configItems["ProxyTimes"])}次"
|
||||
if Config.server_date().strftime("%Y-%m-%d")
|
||||
== self.qconfig.get(self.configItems["LastProxyDate"])
|
||||
else "今日未进行代理"
|
||||
)
|
||||
text_list.append(
|
||||
"本周剿灭已完成"
|
||||
if datetime.strptime(
|
||||
self.qconfig.get(self.configItems["LastAnnihilationDate"]),
|
||||
"%Y-%m-%d",
|
||||
).isocalendar()[:2]
|
||||
== Config.server_date().isocalendar()[:2]
|
||||
else "本周剿灭未完成"
|
||||
)
|
||||
if self.qconfig.get(self.configItems["IfSkland"]):
|
||||
text_list.append(
|
||||
"森空岛已签到"
|
||||
if datetime.now().strftime("%Y-%m-%d")
|
||||
== self.qconfig.get(self.configItems["LastSklandDate"])
|
||||
else "森空岛未签到"
|
||||
)
|
||||
|
||||
self.Lable.setText(" | ".join(text_list))
|
||||
|
||||
|
||||
class UserTaskSettingCard(PushSettingCard):
|
||||
"""Setting card with User's Task"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
text: str,
|
||||
qconfig: QConfig,
|
||||
configItems: Dict[str, ConfigItem],
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(text, icon, title, content, parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItems = configItems
|
||||
self.Lable = SubtitleLabel(self)
|
||||
|
||||
if configItems:
|
||||
for config_item in configItems.values():
|
||||
config_item.valueChanged.connect(self.setValues)
|
||||
self.setValues()
|
||||
|
||||
self.hBoxLayout.addWidget(self.Lable, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
|
||||
def setValues(self):
|
||||
|
||||
text_list = []
|
||||
|
||||
if self.configItems:
|
||||
|
||||
if self.qconfig.get(self.configItems["IfWakeUp"]):
|
||||
text_list.append("开始唤醒")
|
||||
if self.qconfig.get(self.configItems["IfRecruiting"]):
|
||||
text_list.append("自动公招")
|
||||
if self.qconfig.get(self.configItems["IfBase"]):
|
||||
text_list.append("基建换班")
|
||||
if self.qconfig.get(self.configItems["IfCombat"]):
|
||||
text_list.append("刷理智")
|
||||
if self.qconfig.get(self.configItems["IfMall"]):
|
||||
text_list.append("获取信用及购物")
|
||||
if self.qconfig.get(self.configItems["IfMission"]):
|
||||
text_list.append("领取奖励")
|
||||
if self.qconfig.get(self.configItems["IfAutoRoguelike"]):
|
||||
text_list.append("自动肉鸽")
|
||||
if self.qconfig.get(self.configItems["IfReclamation"]):
|
||||
text_list.append("生息演算")
|
||||
|
||||
if text_list:
|
||||
self.setContent(f"任务序列:{" - ".join(text_list)}")
|
||||
else:
|
||||
self.setContent("未启用任何任务项")
|
||||
|
||||
|
||||
class UserNoticeSettingCard(PushAndSwitchButtonSettingCard):
|
||||
"""Setting card with User's Notice"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
icon: Union[str, QIcon, FluentIconBase],
|
||||
title: str,
|
||||
content: Union[str, None],
|
||||
text: str,
|
||||
qconfig: QConfig,
|
||||
configItem: ConfigItem,
|
||||
configItems: Dict[str, ConfigItem],
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(icon, title, content, text, qconfig, configItem, parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItems = configItems
|
||||
self.Lable = SubtitleLabel(self)
|
||||
|
||||
if configItems:
|
||||
for config_item in configItems.values():
|
||||
config_item.valueChanged.connect(self.setValues)
|
||||
self.setValues()
|
||||
|
||||
self.hBoxLayout.addWidget(self.Lable, 0, Qt.AlignRight)
|
||||
self.hBoxLayout.addSpacing(16)
|
||||
|
||||
def setValues(self):
|
||||
|
||||
def short_str(s: str) -> str:
|
||||
if s.startswith(("SC", "sc")):
|
||||
# SendKey:首4 + 末4
|
||||
return f"{s[:4]}***{s[-4:]}" if len(s) > 8 else s
|
||||
|
||||
elif s.startswith(("http://", "https://")):
|
||||
# Webhook URL:域名 + 路径尾3
|
||||
parsed_url = urlparse(s)
|
||||
domain = parsed_url.netloc
|
||||
path_tail = (
|
||||
parsed_url.path[-3:]
|
||||
if len(parsed_url.path) > 3
|
||||
else parsed_url.path
|
||||
)
|
||||
return f"{domain}***{path_tail}"
|
||||
|
||||
elif "@" in s:
|
||||
# 邮箱:@前3/6 + 域名
|
||||
username, domain = s.split("@", 1)
|
||||
displayed_name = f"{username[:3]}***" if len(username) > 6 else username
|
||||
return f"{displayed_name}@{domain}"
|
||||
|
||||
else:
|
||||
# 普通字符串:末尾3字符
|
||||
return f"***{s[-3:]}" if len(s) > 3 else s
|
||||
|
||||
text_list = []
|
||||
|
||||
if self.configItems:
|
||||
|
||||
if not (
|
||||
self.qconfig.get(self.configItems["IfSendStatistic"])
|
||||
or self.qconfig.get(self.configItems["IfSendSixStar"])
|
||||
):
|
||||
text_list.append("未启用任何通知项")
|
||||
|
||||
if self.qconfig.get(self.configItems["IfSendStatistic"]):
|
||||
text_list.append("统计信息已启用")
|
||||
if self.qconfig.get(self.configItems["IfSendSixStar"]):
|
||||
text_list.append("六星喜报已启用")
|
||||
|
||||
if self.qconfig.get(self.configItems["IfSendMail"]):
|
||||
text_list.append(
|
||||
f"邮箱通知:{short_str(self.qconfig.get(self.configItems["ToAddress"]))}"
|
||||
)
|
||||
if self.qconfig.get(self.configItems["IfServerChan"]):
|
||||
text_list.append(
|
||||
f"Server酱通知:{short_str(self.qconfig.get(self.configItems["ServerChanKey"]))}"
|
||||
)
|
||||
if self.qconfig.get(self.configItems["IfCompanyWebHookBot"]):
|
||||
text_list.append(
|
||||
f"企业微信通知:{short_str(self.qconfig.get(self.configItems["CompanyWebHookBotUrl"]))}"
|
||||
)
|
||||
|
||||
self.setContent(" | ".join(text_list))
|
||||
|
||||
|
||||
class StatusSwitchSetting(SwitchButton):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
qconfig: QConfig,
|
||||
configItem_check: ConfigItem,
|
||||
configItem_enable: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItem_check = configItem_check
|
||||
self.configItem_enable = configItem_enable
|
||||
self.setOffText("")
|
||||
self.setOnText("")
|
||||
|
||||
if configItem_check:
|
||||
self.setValue(self.qconfig.get(configItem_check))
|
||||
configItem_check.valueChanged.connect(self.setValue)
|
||||
if configItem_enable:
|
||||
self.setEnabled(self.qconfig.get(configItem_enable))
|
||||
configItem_enable.valueChanged.connect(self.setEnabled)
|
||||
|
||||
self.checkedChanged.connect(self.setValue)
|
||||
|
||||
def setValue(self, isChecked: bool):
|
||||
if self.configItem_check:
|
||||
self.qconfig.set(self.configItem_check, isChecked)
|
||||
|
||||
self.setChecked(isChecked)
|
||||
|
||||
|
||||
class ComboBoxSetting(ComboBox):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItem = configItem
|
||||
|
||||
self.optionToText = {o: t for o, t in zip(configItem.options, texts)}
|
||||
for text, option in zip(texts, configItem.options):
|
||||
self.addItem(text, userData=option)
|
||||
|
||||
self.setCurrentText(self.optionToText[self.qconfig.get(configItem)])
|
||||
self.currentIndexChanged.connect(self._onCurrentIndexChanged)
|
||||
configItem.valueChanged.connect(self.setValue)
|
||||
|
||||
def _onCurrentIndexChanged(self, index: int):
|
||||
|
||||
self.qconfig.set(self.configItem, self.itemData(index))
|
||||
|
||||
def setValue(self, value):
|
||||
if value not in self.optionToText:
|
||||
return
|
||||
|
||||
self.setCurrentText(self.optionToText[value])
|
||||
self.qconfig.set(self.configItem, value)
|
||||
|
||||
|
||||
class NoOptionComboBoxSetting(ComboBox):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: List[str],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItem = configItem
|
||||
|
||||
self.optionToText = {o: t for o, t in zip(value, texts)}
|
||||
for text, option in zip(texts, value):
|
||||
self.addItem(text, userData=option)
|
||||
|
||||
self.setCurrentText(self.optionToText[self.qconfig.get(configItem)])
|
||||
self.currentIndexChanged.connect(self._onCurrentIndexChanged)
|
||||
configItem.valueChanged.connect(self.setValue)
|
||||
|
||||
def _onCurrentIndexChanged(self, index: int):
|
||||
|
||||
self.qconfig.set(self.configItem, self.itemData(index))
|
||||
|
||||
def setValue(self, value):
|
||||
if value not in self.optionToText:
|
||||
return
|
||||
|
||||
self.setCurrentText(self.optionToText[value])
|
||||
self.qconfig.set(self.configItem, value)
|
||||
|
||||
def reLoadOptions(self, value: List[str], texts: List[str]):
|
||||
|
||||
self.currentIndexChanged.disconnect(self._onCurrentIndexChanged)
|
||||
self.clear()
|
||||
self.optionToText = {o: t for o, t in zip(value, texts)}
|
||||
for text, option in zip(texts, value):
|
||||
self.addItem(text, userData=option)
|
||||
self.setCurrentText(self.optionToText[self.qconfig.get(self.configItem)])
|
||||
self.currentIndexChanged.connect(self._onCurrentIndexChanged)
|
||||
|
||||
|
||||
class EditableComboBoxSetting(EditableComboBox):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: List[str],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItem = configItem
|
||||
|
||||
self.optionToText = {o: t for o, t in zip(value, texts)}
|
||||
for text, option in zip(texts, value):
|
||||
self.addItem(text, userData=option)
|
||||
|
||||
if qconfig.get(configItem) not in self.optionToText:
|
||||
self.optionToText[qconfig.get(configItem)] = qconfig.get(configItem)
|
||||
self.addItem(qconfig.get(configItem), userData=qconfig.get(configItem))
|
||||
|
||||
self.setCurrentText(self.optionToText[qconfig.get(configItem)])
|
||||
self.currentIndexChanged.connect(self._onCurrentIndexChanged)
|
||||
configItem.valueChanged.connect(self.setValue)
|
||||
|
||||
def _onCurrentIndexChanged(self, index: int):
|
||||
|
||||
self.qconfig.set(
|
||||
self.configItem,
|
||||
(self.itemData(index) if self.itemData(index) else self.itemText(index)),
|
||||
)
|
||||
|
||||
def setValue(self, value):
|
||||
if value not in self.optionToText:
|
||||
self.optionToText[value] = value
|
||||
if self.findText(value) == -1:
|
||||
self.addItem(value, userData=value)
|
||||
else:
|
||||
self.setItemData(self.findText(value), value)
|
||||
|
||||
self.setCurrentText(self.optionToText[value])
|
||||
self.qconfig.set(self.configItem, value)
|
||||
|
||||
def reLoadOptions(self, value: List[str], texts: List[str]):
|
||||
|
||||
self.currentIndexChanged.disconnect(self._onCurrentIndexChanged)
|
||||
self.clear()
|
||||
self.optionToText = {o: t for o, t in zip(value, texts)}
|
||||
for text, option in zip(texts, value):
|
||||
self.addItem(text, userData=option)
|
||||
if self.qconfig.get(self.configItem) not in self.optionToText:
|
||||
self.optionToText[self.qconfig.get(self.configItem)] = self.qconfig.get(
|
||||
self.configItem
|
||||
)
|
||||
self.addItem(
|
||||
self.qconfig.get(self.configItem),
|
||||
userData=self.qconfig.get(self.configItem),
|
||||
)
|
||||
self.setCurrentText(self.optionToText[self.qconfig.get(self.configItem)])
|
||||
self.currentIndexChanged.connect(self._onCurrentIndexChanged)
|
||||
|
||||
def _onReturnPressed(self):
|
||||
if not self.text():
|
||||
return
|
||||
|
||||
index = self.findText(self.text())
|
||||
if index >= 0 and index != self.currentIndex():
|
||||
self._currentIndex = index
|
||||
self.currentIndexChanged.emit(index)
|
||||
elif index == -1:
|
||||
self.addItem(self.text())
|
||||
self.setCurrentIndex(self.count() - 1)
|
||||
self.currentIndexChanged.emit(self.count() - 1)
|
||||
|
||||
|
||||
class SpinBoxSetting(SpinBox):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
range: tuple[int, int],
|
||||
qconfig: QConfig,
|
||||
configItem: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
super().__init__(parent)
|
||||
self.qconfig = qconfig
|
||||
self.configItem = configItem
|
||||
self.setRange(range[0], range[1])
|
||||
|
||||
if configItem:
|
||||
self.set_value(qconfig.get(configItem))
|
||||
configItem.valueChanged.connect(self.set_value)
|
||||
|
||||
self.valueChanged.connect(self.set_value)
|
||||
|
||||
def set_value(self, value: int):
|
||||
if self.configItem:
|
||||
self.qconfig.set(self.configItem, value)
|
||||
|
||||
self.setValue(value)
|
||||
|
||||
|
||||
class HistoryCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, qconfig: QConfig, configItem: ConfigItem, parent=None):
|
||||
@@ -1052,9 +1701,7 @@ class UrlListSettingCard(ExpandSettingCard):
|
||||
"""show confirm dialog"""
|
||||
|
||||
choice = MessageBox(
|
||||
"确认",
|
||||
f"确定要删除 {item.url} 代理网址吗?",
|
||||
self.window(),
|
||||
"确认", f"确定要删除 {item.url} 代理网址吗?", self.window()
|
||||
)
|
||||
if choice.exec():
|
||||
self.__removeUrl(item)
|
||||
|
||||
@@ -48,7 +48,7 @@ from PySide6.QtGui import QTextCursor
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
from app.core import Config, TaskManager, Task, MainInfoBar
|
||||
from app.core import Config, TaskManager, Task, MainInfoBar, SoundPlayer
|
||||
from .Widget import StatefulItemCard, ComboBoxMessageBox, PivotArea
|
||||
|
||||
|
||||
@@ -333,28 +333,24 @@ class DispatchCenter(QWidget):
|
||||
|
||||
self.setObjectName(name)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
self.top_bar = self.DispatchTopBar(self, name)
|
||||
self.info = self.DispatchInfoCard(self)
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(0, 0, 0, 0)
|
||||
content_layout.addWidget(self.top_bar)
|
||||
content_layout.addWidget(self.info)
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
|
||||
self.top_bar = self.DispatchTopBar(self, name)
|
||||
self.info = self.DispatchInfoCard(self)
|
||||
|
||||
content_layout.addWidget(self.top_bar)
|
||||
content_layout.addWidget(self.info)
|
||||
|
||||
scrollArea.setWidget(content_widget)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(scrollArea)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
class DispatchTopBar(CardWidget):
|
||||
|
||||
def __init__(self, parent=None, name: str = None):
|
||||
|
||||
@@ -25,6 +25,7 @@ v4.3
|
||||
作者:DLmaster_361
|
||||
"""
|
||||
|
||||
from loguru import logger
|
||||
import zipfile
|
||||
import requests
|
||||
import subprocess
|
||||
@@ -78,12 +79,15 @@ class DownloadProcess(QThread):
|
||||
) -> None:
|
||||
super(DownloadProcess, self).__init__()
|
||||
|
||||
self.setObjectName(f"DownloadProcess-{url}-{start_byte}-{end_byte}")
|
||||
|
||||
self.url = url
|
||||
self.start_byte = start_byte
|
||||
self.end_byte = end_byte
|
||||
self.download_path = download_path
|
||||
self.check_times = check_times
|
||||
|
||||
@logger.catch
|
||||
def run(self) -> None:
|
||||
|
||||
# 清理可能存在的临时文件
|
||||
@@ -157,10 +161,13 @@ class ZipExtractProcess(QThread):
|
||||
def __init__(self, name: str, app_path: Path, download_path: Path) -> None:
|
||||
super(ZipExtractProcess, self).__init__()
|
||||
|
||||
self.setObjectName(f"ZipExtractProcess-{name}")
|
||||
|
||||
self.name = name
|
||||
self.app_path = app_path
|
||||
self.download_path = download_path
|
||||
|
||||
@logger.catch
|
||||
def run(self) -> None:
|
||||
|
||||
try:
|
||||
@@ -234,6 +241,7 @@ class DownloadManager(QDialog):
|
||||
self.download_path = app_path / "DOWNLOAD_TEMP.zip" # 临时下载文件的路径
|
||||
self.download_process_dict: Dict[str, DownloadProcess] = {}
|
||||
self.timer_dict: Dict[str, QTimer] = {}
|
||||
self.if_speed_test_accomplish = False
|
||||
|
||||
self.resize(700, 70)
|
||||
|
||||
@@ -405,6 +413,15 @@ class DownloadManager(QDialog):
|
||||
if not self.download_process_dict:
|
||||
self.download_process_clear.emit()
|
||||
|
||||
# 当有速度大于1 MB/s的链接或存在3个即以上链接测速完成时,停止其他测速
|
||||
if not self.if_speed_test_accomplish and (
|
||||
sum(1 for speed in self.test_speed_result.values() if speed > 0) >= 3
|
||||
or any(speed > 1 for speed in self.test_speed_result.values())
|
||||
):
|
||||
self.if_speed_test_accomplish = True
|
||||
for timer in self.timer_dict.values():
|
||||
timer.timeout.emit()
|
||||
|
||||
if any(speed == -1 for _, speed in self.test_speed_result.items()):
|
||||
return None
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ from pathlib import Path
|
||||
from typing import Union, List, Dict
|
||||
|
||||
|
||||
from app.core import Config
|
||||
from app.core import Config, SoundPlayer
|
||||
from .Widget import StatefulItemCard, QuantifiedItemCard, QuickExpandGroupCard
|
||||
|
||||
|
||||
@@ -61,27 +61,30 @@ class History(QWidget):
|
||||
super().__init__(parent)
|
||||
self.setObjectName("历史记录")
|
||||
|
||||
self.history_top_bar = self.HistoryTopBar(self)
|
||||
self.history_top_bar.search_history.connect(self.reload_history)
|
||||
|
||||
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)
|
||||
self.content_layout.setContentsMargins(0, 0, 11, 0)
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
scrollArea.setWidget(content_widget)
|
||||
layout = QVBoxLayout()
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(self.history_top_bar)
|
||||
layout.addWidget(scrollArea)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.history_card_list = []
|
||||
|
||||
def reload_history(self, mode: str, start_date: QDate, end_date: QDate) -> None:
|
||||
"""加载历史记录界面"""
|
||||
|
||||
SoundPlayer.play("历史记录查询")
|
||||
|
||||
while self.content_layout.count() > 0:
|
||||
item = self.content_layout.takeAt(0)
|
||||
if item.spacerItem():
|
||||
|
||||
@@ -62,14 +62,6 @@ class Home(QWidget):
|
||||
self.banner = Banner()
|
||||
self.banner_text = TextBrowser()
|
||||
|
||||
widget = QWidget()
|
||||
Layout = QVBoxLayout(widget)
|
||||
|
||||
Layout.addWidget(self.banner)
|
||||
Layout.addWidget(self.banner_text)
|
||||
Layout.setStretch(0, 2)
|
||||
Layout.setStretch(1, 3)
|
||||
|
||||
v_layout = QVBoxLayout(self.banner)
|
||||
v_layout.setContentsMargins(0, 0, 0, 15)
|
||||
v_layout.setSpacing(5)
|
||||
@@ -146,14 +138,22 @@ class Home(QWidget):
|
||||
# 将底部水平布局添加到垂直布局
|
||||
v_layout.addLayout(h2_layout)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(0, 0, 0, 0)
|
||||
content_layout.addWidget(self.banner)
|
||||
content_layout.addWidget(self.banner_text)
|
||||
content_layout.setStretch(0, 2)
|
||||
content_layout.setStretch(1, 3)
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
scrollArea.setWidget(widget)
|
||||
scrollArea.setWidget(content_widget)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(scrollArea)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.set_banner()
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -401,4 +405,6 @@ class ButtonGroup(SimpleCardWidget):
|
||||
|
||||
def open_sales(self):
|
||||
"""打开 MirrorChyan 链接"""
|
||||
QDesktopServices.openUrl(QUrl("https://mirrorchyan.com/"))
|
||||
QDesktopServices.openUrl(
|
||||
QUrl("https://mirrorchyan.com/zh/get-start?source=auto_maa-home")
|
||||
)
|
||||
|
||||
@@ -45,10 +45,11 @@ from datetime import datetime, timedelta
|
||||
import shutil
|
||||
import darkdetect
|
||||
|
||||
from app.core import Config, TaskManager, MainTimer, MainInfoBar
|
||||
from app.core import Config, TaskManager, MainTimer, MainInfoBar, SoundPlayer
|
||||
from app.services import Notify, Crypto, System
|
||||
from .home import Home
|
||||
from .member_manager import MemberManager
|
||||
from .plan_manager import PlanManager
|
||||
from .queue_manager import QueueManager
|
||||
from .dispatch_center import DispatchCenter
|
||||
from .history import History
|
||||
@@ -80,6 +81,7 @@ class AUTO_MAA(MSFluentWindow):
|
||||
|
||||
# 创建主窗口
|
||||
self.home = Home(self)
|
||||
self.plan_manager = PlanManager(self)
|
||||
self.member_manager = MemberManager(self)
|
||||
self.queue_manager = QueueManager(self)
|
||||
self.dispatch_center = DispatchCenter(self)
|
||||
@@ -100,6 +102,13 @@ class AUTO_MAA(MSFluentWindow):
|
||||
FluentIcon.ROBOT,
|
||||
NavigationItemPosition.TOP,
|
||||
)
|
||||
self.addSubInterface(
|
||||
self.plan_manager,
|
||||
FluentIcon.CALENDAR,
|
||||
"计划管理",
|
||||
FluentIcon.CALENDAR,
|
||||
NavigationItemPosition.TOP,
|
||||
)
|
||||
self.addSubInterface(
|
||||
self.queue_manager,
|
||||
FluentIcon.BOOK_SHELF,
|
||||
@@ -128,23 +137,7 @@ class AUTO_MAA(MSFluentWindow):
|
||||
FluentIcon.SETTING,
|
||||
NavigationItemPosition.BOTTOM,
|
||||
)
|
||||
self.stackedWidget.currentChanged.connect(
|
||||
lambda index: (
|
||||
self.queue_manager.reload_member_name() if index == 2 else None
|
||||
)
|
||||
)
|
||||
self.stackedWidget.currentChanged.connect(
|
||||
lambda index: (
|
||||
self.dispatch_center.pivot.setCurrentItem("主调度台")
|
||||
if index == 3
|
||||
else None
|
||||
)
|
||||
)
|
||||
self.stackedWidget.currentChanged.connect(
|
||||
lambda index: (
|
||||
self.dispatch_center.update_top_bar() if index == 3 else None
|
||||
)
|
||||
)
|
||||
self.stackedWidget.currentChanged.connect(self.__currentChanged)
|
||||
|
||||
# 创建系统托盘及其菜单
|
||||
self.tray = QSystemTrayIcon(
|
||||
@@ -241,6 +234,117 @@ class AUTO_MAA(MSFluentWindow):
|
||||
else:
|
||||
self.setStyleSheet("background-color: #ffffff;")
|
||||
|
||||
def set_min_method(self) -> None:
|
||||
"""设置最小化方法"""
|
||||
|
||||
if Config.get(Config.ui_IfToTray):
|
||||
|
||||
self.titleBar.minBtn.clicked.disconnect()
|
||||
self.titleBar.minBtn.clicked.connect(lambda: self.show_ui("隐藏到托盘"))
|
||||
|
||||
else:
|
||||
|
||||
self.titleBar.minBtn.clicked.disconnect()
|
||||
self.titleBar.minBtn.clicked.connect(self.window().showMinimized)
|
||||
|
||||
def on_tray_activated(self, reason):
|
||||
"""双击返回主界面"""
|
||||
if reason == QSystemTrayIcon.DoubleClick:
|
||||
self.show_ui("显示主窗口")
|
||||
|
||||
def show_ui(
|
||||
self, mode: str, if_quick: bool = False, if_start: bool = False
|
||||
) -> None:
|
||||
"""配置窗口状态"""
|
||||
|
||||
if Config.args.mode != "gui":
|
||||
return None
|
||||
|
||||
self.switch_theme()
|
||||
|
||||
if mode == "显示主窗口":
|
||||
|
||||
# 配置主窗口
|
||||
if not self.window().isVisible():
|
||||
size = list(
|
||||
map(
|
||||
int,
|
||||
Config.get(Config.ui_size).split("x"),
|
||||
)
|
||||
)
|
||||
location = list(
|
||||
map(
|
||||
int,
|
||||
Config.get(Config.ui_location).split("x"),
|
||||
)
|
||||
)
|
||||
if self.window().isMaximized():
|
||||
self.window().showNormal()
|
||||
self.window().setGeometry(location[0], location[1], size[0], size[1])
|
||||
self.window().show()
|
||||
if not if_quick:
|
||||
if (
|
||||
Config.get(Config.ui_maximized)
|
||||
and not self.window().isMaximized()
|
||||
):
|
||||
self.titleBar.maxBtn.click()
|
||||
SoundPlayer.play("欢迎回来")
|
||||
self.show_ui("配置托盘")
|
||||
elif if_start:
|
||||
if Config.get(Config.ui_maximized) and not self.window().isMaximized():
|
||||
self.titleBar.maxBtn.click()
|
||||
self.show_ui("配置托盘")
|
||||
|
||||
# 如果窗口不在屏幕内,则重置窗口位置
|
||||
if not any(
|
||||
self.window().geometry().intersects(screen.availableGeometry())
|
||||
for screen in QApplication.screens()
|
||||
):
|
||||
self.window().showNormal()
|
||||
self.window().setGeometry(100, 100, 1200, 700)
|
||||
|
||||
self.window().raise_()
|
||||
self.window().activateWindow()
|
||||
|
||||
while Config.info_bar_list:
|
||||
info_bar_item = Config.info_bar_list.pop(0)
|
||||
MainInfoBar.push_info_bar(
|
||||
info_bar_item["mode"],
|
||||
info_bar_item["title"],
|
||||
info_bar_item["content"],
|
||||
info_bar_item["time"],
|
||||
)
|
||||
|
||||
elif mode == "配置托盘":
|
||||
|
||||
if Config.get(Config.ui_IfShowTray):
|
||||
self.tray.show()
|
||||
else:
|
||||
self.tray.hide()
|
||||
|
||||
elif mode == "隐藏到托盘":
|
||||
|
||||
# 保存窗口相关属性
|
||||
if not self.window().isMaximized():
|
||||
|
||||
Config.set(
|
||||
Config.ui_size,
|
||||
f"{self.geometry().width()}x{self.geometry().height()}",
|
||||
)
|
||||
Config.set(
|
||||
Config.ui_location,
|
||||
f"{self.geometry().x()}x{self.geometry().y()}",
|
||||
)
|
||||
|
||||
Config.set(Config.ui_maximized, self.window().isMaximized())
|
||||
Config.save()
|
||||
|
||||
# 隐藏主窗口
|
||||
if not if_quick:
|
||||
|
||||
self.window().hide()
|
||||
self.tray.show()
|
||||
|
||||
def start_up_task(self) -> None:
|
||||
"""启动时任务"""
|
||||
|
||||
@@ -278,23 +382,40 @@ class AUTO_MAA(MSFluentWindow):
|
||||
|
||||
self.titleBar.minBtn.click()
|
||||
|
||||
def set_min_method(self) -> None:
|
||||
"""设置最小化方法"""
|
||||
if Config.args.config:
|
||||
|
||||
if Config.get(Config.ui_IfToTray):
|
||||
for config in [_ for _ in Config.args.config if _ in Config.queue_dict]:
|
||||
|
||||
self.titleBar.minBtn.clicked.disconnect()
|
||||
self.titleBar.minBtn.clicked.connect(lambda: self.show_ui("隐藏到托盘"))
|
||||
TaskManager.add_task(
|
||||
"自动代理_新调度台",
|
||||
config,
|
||||
Config.queue_dict["调度队列_1"]["Config"].toDict(),
|
||||
)
|
||||
|
||||
else:
|
||||
for config in [_ for _ in Config.args.config if _ in Config.member_dict]:
|
||||
|
||||
self.titleBar.minBtn.clicked.disconnect()
|
||||
self.titleBar.minBtn.clicked.connect(self.window().showMinimized)
|
||||
TaskManager.add_task(
|
||||
"自动代理_新调度台",
|
||||
"自定义队列",
|
||||
{"Queue": {"Member_1": config}},
|
||||
)
|
||||
|
||||
def on_tray_activated(self, reason):
|
||||
"""双击返回主界面"""
|
||||
if reason == QSystemTrayIcon.DoubleClick:
|
||||
self.show_ui("显示主窗口")
|
||||
if not any(
|
||||
_ in (list(Config.member_dict.keys()) + list(Config.queue_dict.keys()))
|
||||
for _ in Config.args.config
|
||||
):
|
||||
|
||||
logger.warning(
|
||||
"当前运行模式为命令行模式,由于您使用了错误的 --config 参数进行配置,程序自动退出"
|
||||
)
|
||||
System.set_power("KillSelf")
|
||||
|
||||
elif Config.args.mode == "cli":
|
||||
|
||||
logger.warning(
|
||||
"当前运行模式为命令行模式,由于您未使用 --config 参数进行配置,程序自动退出"
|
||||
)
|
||||
System.set_power("KillSelf")
|
||||
|
||||
def clean_old_logs(self):
|
||||
"""
|
||||
@@ -351,75 +472,16 @@ class AUTO_MAA(MSFluentWindow):
|
||||
"warning", "启动主任务失败", "“调度队列_1”与“脚本_1”均不存在", -1
|
||||
)
|
||||
|
||||
def show_ui(self, mode: str, if_quick: bool = False) -> None:
|
||||
"""配置窗口状态"""
|
||||
def __currentChanged(self, index: int) -> None:
|
||||
"""切换界面时任务"""
|
||||
|
||||
self.switch_theme()
|
||||
|
||||
if mode == "显示主窗口":
|
||||
|
||||
# 配置主窗口
|
||||
if not self.window().isVisible():
|
||||
size = list(
|
||||
map(
|
||||
int,
|
||||
Config.get(Config.ui_size).split("x"),
|
||||
)
|
||||
)
|
||||
location = list(
|
||||
map(
|
||||
int,
|
||||
Config.get(Config.ui_location).split("x"),
|
||||
)
|
||||
)
|
||||
if self.window().isMaximized():
|
||||
self.window().showNormal()
|
||||
self.window().setGeometry(location[0], location[1], size[0], size[1])
|
||||
self.window().show()
|
||||
if not if_quick:
|
||||
if Config.get(Config.ui_maximized):
|
||||
self.titleBar.maxBtn.click()
|
||||
self.show_ui("配置托盘")
|
||||
|
||||
if not any(
|
||||
self.window().geometry().intersects(screen.availableGeometry())
|
||||
for screen in QApplication.screens()
|
||||
):
|
||||
self.window().showNormal()
|
||||
self.window().setGeometry(100, 100, 1200, 700)
|
||||
|
||||
self.window().raise_()
|
||||
self.window().activateWindow()
|
||||
|
||||
elif mode == "配置托盘":
|
||||
|
||||
if Config.get(Config.ui_IfShowTray):
|
||||
self.tray.show()
|
||||
else:
|
||||
self.tray.hide()
|
||||
|
||||
elif mode == "隐藏到托盘":
|
||||
|
||||
# 保存窗口相关属性
|
||||
if not self.window().isMaximized():
|
||||
|
||||
Config.set(
|
||||
Config.ui_size,
|
||||
f"{self.geometry().width()}x{self.geometry().height()}",
|
||||
)
|
||||
Config.set(
|
||||
Config.ui_location,
|
||||
f"{self.geometry().x()}x{self.geometry().y()}",
|
||||
)
|
||||
|
||||
Config.set(Config.ui_maximized, self.window().isMaximized())
|
||||
Config.save()
|
||||
|
||||
# 隐藏主窗口
|
||||
if not if_quick:
|
||||
|
||||
self.window().hide()
|
||||
self.tray.show()
|
||||
if index == 1:
|
||||
self.member_manager.reload_plan_name()
|
||||
elif index == 3:
|
||||
self.queue_manager.reload_member_name()
|
||||
elif index == 4:
|
||||
self.dispatch_center.pivot.setCurrentItem("主调度台")
|
||||
self.dispatch_center.update_top_bar()
|
||||
|
||||
def closeEvent(self, event: QCloseEvent):
|
||||
"""清理残余进程"""
|
||||
|
||||
@@ -47,6 +47,8 @@ from qfluentwidgets import (
|
||||
PushSettingCard,
|
||||
TableWidget,
|
||||
PrimaryToolButton,
|
||||
Flyout,
|
||||
FlyoutAnimationType,
|
||||
)
|
||||
from PySide6.QtCore import Signal
|
||||
from datetime import datetime
|
||||
@@ -56,7 +58,15 @@ from typing import List
|
||||
import shutil
|
||||
import json
|
||||
|
||||
from app.core import Config, MainInfoBar, TaskManager, MaaConfig, MaaUserConfig, Network
|
||||
from app.core import (
|
||||
Config,
|
||||
MainInfoBar,
|
||||
TaskManager,
|
||||
MaaConfig,
|
||||
MaaUserConfig,
|
||||
Network,
|
||||
SoundPlayer,
|
||||
)
|
||||
from app.services import Crypto
|
||||
from .downloader import DownloadManager
|
||||
from .Widget import (
|
||||
@@ -64,13 +74,21 @@ from .Widget import (
|
||||
LineEditSettingCard,
|
||||
SpinBoxSettingCard,
|
||||
ComboBoxMessageBox,
|
||||
EditableComboBoxSettingCard,
|
||||
SettingFlyoutView,
|
||||
NoOptionComboBoxSettingCard,
|
||||
ComboBoxWithPlanSettingCard,
|
||||
EditableComboBoxWithPlanSettingCard,
|
||||
SpinBoxWithPlanSettingCard,
|
||||
PasswordLineEditSettingCard,
|
||||
PasswordLineAndSwitchButtonSettingCard,
|
||||
UserLableSettingCard,
|
||||
UserTaskSettingCard,
|
||||
ComboBoxSettingCard,
|
||||
SwitchSettingCard,
|
||||
PushAndSwitchButtonSettingCard,
|
||||
PushAndComboBoxSettingCard,
|
||||
StatusSwitchSetting,
|
||||
UserNoticeSettingCard,
|
||||
PivotArea,
|
||||
)
|
||||
|
||||
@@ -172,13 +190,14 @@ class MemberManager(QWidget):
|
||||
MainInfoBar.push_info_bar(
|
||||
"success", "操作成功", f"添加脚本实例 脚本_{index}", 3000
|
||||
)
|
||||
SoundPlayer.play("添加脚本实例")
|
||||
|
||||
def del_setting_box(self):
|
||||
"""删除一个脚本实例"""
|
||||
|
||||
name = self.member_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("删除脚本实例时未选择脚本实例")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择脚本实例", "请选择一个脚本实例", 5000
|
||||
@@ -192,11 +211,7 @@ class MemberManager(QWidget):
|
||||
)
|
||||
return None
|
||||
|
||||
choice = MessageBox(
|
||||
"确认",
|
||||
f"确定要删除 {name} 实例吗?",
|
||||
self.window(),
|
||||
)
|
||||
choice = MessageBox("确认", f"确定要删除 {name} 实例吗?", self.window())
|
||||
if choice.exec():
|
||||
|
||||
self.member_manager.clear_SettingBox()
|
||||
@@ -216,13 +231,14 @@ class MemberManager(QWidget):
|
||||
MainInfoBar.push_info_bar(
|
||||
"success", "操作成功", f"删除脚本实例 {name}", 3000
|
||||
)
|
||||
SoundPlayer.play("删除脚本实例")
|
||||
|
||||
def left_setting_box(self):
|
||||
"""向左移动脚本实例"""
|
||||
|
||||
name = self.member_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("向左移动脚本实例时未选择脚本实例")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择脚本实例", "请选择一个脚本实例", 5000
|
||||
@@ -270,7 +286,7 @@ class MemberManager(QWidget):
|
||||
|
||||
name = self.member_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("向右移动脚本实例时未选择脚本实例")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择脚本实例", "请选择一个脚本实例", 5000
|
||||
@@ -328,20 +344,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
|
||||
@@ -371,19 +387,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:
|
||||
|
||||
@@ -420,11 +436,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
|
||||
@@ -471,11 +487,57 @@ class MemberManager(QWidget):
|
||||
self.key.setIcon(FluentIcon.HIDE)
|
||||
self.key.setChecked(False)
|
||||
|
||||
def reload_plan_name(self):
|
||||
"""刷新计划表名称"""
|
||||
|
||||
plan_list = [
|
||||
["固定"] + [_ for _ in Config.plan_dict.keys()],
|
||||
["固定"]
|
||||
+ [
|
||||
(
|
||||
k
|
||||
if v["Config"].get(v["Config"].Info_Name) == ""
|
||||
else f"{k} - {v["Config"].get(v["Config"].Info_Name)}"
|
||||
)
|
||||
for k, v in Config.plan_dict.items()
|
||||
],
|
||||
]
|
||||
for member in self.member_manager.script_list:
|
||||
|
||||
if isinstance(member, MemberManager.MemberSettingBox.MaaSettingBox):
|
||||
|
||||
for user_setting in member.user_setting.user_manager.script_list:
|
||||
|
||||
user_setting.card_GameIdMode.comboBox.currentIndexChanged.disconnect(
|
||||
user_setting.switch_gameid_mode
|
||||
)
|
||||
user_setting.card_GameIdMode.reLoadOptions(
|
||||
plan_list[0], plan_list[1]
|
||||
)
|
||||
user_setting.card_GameIdMode.comboBox.currentIndexChanged.connect(
|
||||
user_setting.switch_gameid_mode
|
||||
)
|
||||
|
||||
self.refresh_plan_info()
|
||||
|
||||
def refresh_dashboard(self):
|
||||
"""刷新所有脚本实例的用户仪表盘"""
|
||||
|
||||
for script in self.member_manager.script_list:
|
||||
script.user_setting.user_manager.user_dashboard.load_info()
|
||||
for member in self.member_manager.script_list:
|
||||
|
||||
if isinstance(member, MemberManager.MemberSettingBox.MaaSettingBox):
|
||||
member.user_setting.user_manager.user_dashboard.load_info()
|
||||
|
||||
def refresh_plan_info(self):
|
||||
"""刷新所有计划信息"""
|
||||
|
||||
for member in self.member_manager.script_list:
|
||||
|
||||
if isinstance(member, MemberManager.MemberSettingBox.MaaSettingBox):
|
||||
|
||||
member.user_setting.user_manager.user_dashboard.load_info()
|
||||
for user_setting in member.user_setting.user_manager.script_list:
|
||||
user_setting.switch_gameid_mode()
|
||||
|
||||
class MemberSettingBox(QWidget):
|
||||
"""脚本管理子页面组"""
|
||||
@@ -563,29 +625,25 @@ class MemberManager(QWidget):
|
||||
self.setObjectName(f"脚本_{uid}")
|
||||
self.config = Config.member_dict[f"脚本_{uid}"]["Config"]
|
||||
|
||||
layout = QVBoxLayout()
|
||||
self.app_setting = self.AppSettingCard(f"脚本_{uid}", self.config, self)
|
||||
self.user_setting = self.UserManager(f"脚本_{uid}", self)
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(0, 0, 11, 0)
|
||||
content_layout.addWidget(self.app_setting)
|
||||
content_layout.addWidget(self.user_setting)
|
||||
content_layout.addStretch(1)
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
|
||||
self.app_setting = self.AppSettingCard(f"脚本_{uid}", self.config, self)
|
||||
self.user_setting = self.UserManager(f"脚本_{uid}", self)
|
||||
|
||||
content_layout.addWidget(self.app_setting)
|
||||
content_layout.addWidget(self.user_setting)
|
||||
content_layout.addStretch(1)
|
||||
|
||||
scrollArea.setWidget(content_widget)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(scrollArea)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
class AppSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, name: str, config: MaaConfig, parent=None):
|
||||
@@ -698,6 +756,15 @@ class MemberManager(QWidget):
|
||||
configItem=self.config.RunSet_ProxyTimesLimit,
|
||||
parent=self,
|
||||
)
|
||||
self.card_ADBSearchRange = SpinBoxSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="ADB端口号搜索范围",
|
||||
content="在【±端口号范围】内搜索实际ADB端口号",
|
||||
range=(0, 3),
|
||||
qconfig=self.config,
|
||||
configItem=self.config.RunSet_ADBSearchRange,
|
||||
parent=self,
|
||||
)
|
||||
self.card_RunTimesLimit = SpinBoxSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="代理重试次数限制",
|
||||
@@ -746,6 +813,7 @@ class MemberManager(QWidget):
|
||||
Layout = QVBoxLayout(widget)
|
||||
Layout.addWidget(self.card_TaskTransitionMethod)
|
||||
Layout.addWidget(self.card_ProxyTimesLimit)
|
||||
Layout.addWidget(self.card_ADBSearchRange)
|
||||
Layout.addWidget(self.card_RunTimesLimit)
|
||||
Layout.addWidget(self.card_AnnihilationTimeLimit)
|
||||
Layout.addWidget(self.card_RoutineTimeLimit)
|
||||
@@ -828,13 +896,14 @@ class MemberManager(QWidget):
|
||||
MainInfoBar.push_info_bar(
|
||||
"success", "操作成功", f"{self.name} 添加 用户_{index}", 3000
|
||||
)
|
||||
SoundPlayer.play("添加用户")
|
||||
|
||||
def del_user(self):
|
||||
"""删除一个用户"""
|
||||
|
||||
name = self.user_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("未选择用户")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择用户", "请先选择一个用户", 5000
|
||||
@@ -855,9 +924,7 @@ class MemberManager(QWidget):
|
||||
return None
|
||||
|
||||
choice = MessageBox(
|
||||
"确认",
|
||||
f"确定要删除 {name} 吗?",
|
||||
self.window(),
|
||||
"确认", f"确定要删除 {name} 吗?", self.window()
|
||||
)
|
||||
if choice.exec():
|
||||
|
||||
@@ -889,13 +956,14 @@ class MemberManager(QWidget):
|
||||
MainInfoBar.push_info_bar(
|
||||
"success", "操作成功", f"{self.name} 删除 {name}", 3000
|
||||
)
|
||||
SoundPlayer.play("删除用户")
|
||||
|
||||
def left_user(self):
|
||||
"""向前移动用户"""
|
||||
|
||||
name = self.user_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("未选择用户")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择用户", "请先选择一个用户", 5000
|
||||
@@ -954,7 +1022,7 @@ class MemberManager(QWidget):
|
||||
|
||||
name = self.user_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("未选择用户")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择用户", "请先选择一个用户", 5000
|
||||
@@ -1190,6 +1258,8 @@ class MemberManager(QWidget):
|
||||
else "本周剿灭未完成"
|
||||
)
|
||||
|
||||
gameid_info = config.get_plan_info()
|
||||
|
||||
button = PrimaryToolButton(
|
||||
FluentIcon.CHEVRON_RIGHT, self
|
||||
)
|
||||
@@ -1220,14 +1290,14 @@ class MemberManager(QWidget):
|
||||
else "******"
|
||||
),
|
||||
)
|
||||
self.dashboard.setItem(
|
||||
self.dashboard.setCellWidget(
|
||||
int(name[3:]) - 1,
|
||||
3,
|
||||
QTableWidgetItem(
|
||||
"启用"
|
||||
if config.get(config.Info_Status)
|
||||
and config.get(config.Info_RemainedDay) != 0
|
||||
else "禁用"
|
||||
StatusSwitchSetting(
|
||||
qconfig=config,
|
||||
configItem_check=config.Info_Status,
|
||||
configItem_enable=config.Info_RemainedDay,
|
||||
parent=self,
|
||||
),
|
||||
)
|
||||
self.dashboard.setItem(
|
||||
@@ -1238,9 +1308,7 @@ class MemberManager(QWidget):
|
||||
self.dashboard.setItem(
|
||||
int(name[3:]) - 1,
|
||||
5,
|
||||
QTableWidgetItem(
|
||||
str(config.get(config.Info_MedicineNumb))
|
||||
),
|
||||
QTableWidgetItem(str(gameid_info["MedicineNumb"])),
|
||||
)
|
||||
self.dashboard.setItem(
|
||||
int(name[3:]) - 1,
|
||||
@@ -1248,12 +1316,12 @@ class MemberManager(QWidget):
|
||||
QTableWidgetItem(
|
||||
Config.gameid_dict["ALL"]["text"][
|
||||
Config.gameid_dict["ALL"]["value"].index(
|
||||
config.get(config.Info_GameId)
|
||||
gameid_info["GameId"]
|
||||
)
|
||||
]
|
||||
if config.get(config.Info_GameId)
|
||||
if gameid_info["GameId"]
|
||||
in Config.gameid_dict["ALL"]["value"]
|
||||
else config.get(config.Info_GameId)
|
||||
else gameid_info["GameId"]
|
||||
),
|
||||
)
|
||||
self.dashboard.setItem(
|
||||
@@ -1262,12 +1330,12 @@ class MemberManager(QWidget):
|
||||
QTableWidgetItem(
|
||||
Config.gameid_dict["ALL"]["text"][
|
||||
Config.gameid_dict["ALL"]["value"].index(
|
||||
config.get(config.Info_GameId_1)
|
||||
gameid_info["GameId_1"]
|
||||
)
|
||||
]
|
||||
if config.get(config.Info_GameId_1)
|
||||
if gameid_info["GameId_1"]
|
||||
in Config.gameid_dict["ALL"]["value"]
|
||||
else config.get(config.Info_GameId_1)
|
||||
else gameid_info["GameId_1"]
|
||||
),
|
||||
)
|
||||
self.dashboard.setItem(
|
||||
@@ -1276,12 +1344,12 @@ class MemberManager(QWidget):
|
||||
QTableWidgetItem(
|
||||
Config.gameid_dict["ALL"]["text"][
|
||||
Config.gameid_dict["ALL"]["value"].index(
|
||||
config.get(config.Info_GameId_2)
|
||||
gameid_info["GameId_2"]
|
||||
)
|
||||
]
|
||||
if config.get(config.Info_GameId_2)
|
||||
if gameid_info["GameId_2"]
|
||||
in Config.gameid_dict["ALL"]["value"]
|
||||
else config.get(config.Info_GameId_2)
|
||||
else gameid_info["GameId_2"]
|
||||
),
|
||||
)
|
||||
self.dashboard.setItem(
|
||||
@@ -1289,22 +1357,20 @@ class MemberManager(QWidget):
|
||||
9,
|
||||
QTableWidgetItem(
|
||||
"不使用"
|
||||
if config.get(config.Info_GameId_Remain) == "-"
|
||||
if gameid_info["GameId_Remain"] == "-"
|
||||
else (
|
||||
(
|
||||
Config.gameid_dict["ALL"]["text"][
|
||||
Config.gameid_dict["ALL"][
|
||||
"value"
|
||||
].index(
|
||||
config.get(
|
||||
config.Info_GameId_Remain
|
||||
)
|
||||
gameid_info["GameId_Remain"]
|
||||
)
|
||||
]
|
||||
)
|
||||
if config.get(config.Info_GameId_Remain)
|
||||
if gameid_info["GameId_Remain"]
|
||||
in Config.gameid_dict["ALL"]["value"]
|
||||
else config.get(config.Info_GameId_Remain)
|
||||
else gameid_info["GameId_Remain"]
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -1328,6 +1394,19 @@ class MemberManager(QWidget):
|
||||
f"用户_{uid}"
|
||||
]["Path"]
|
||||
|
||||
plan_list = [
|
||||
["固定"] + [_ for _ in Config.plan_dict.keys()],
|
||||
["固定"]
|
||||
+ [
|
||||
(
|
||||
k
|
||||
if v["Config"].get(v["Config"].Info_Name) == ""
|
||||
else f"{k} - {v["Config"].get(v["Config"].Info_Name)}"
|
||||
)
|
||||
for k, v in Config.plan_dict.items()
|
||||
],
|
||||
]
|
||||
|
||||
self.card_Name = LineEditSettingCard(
|
||||
icon=FluentIcon.PEOPLE,
|
||||
title="用户名",
|
||||
@@ -1355,15 +1434,17 @@ class MemberManager(QWidget):
|
||||
configItem=self.config.Info_Mode,
|
||||
parent=self,
|
||||
)
|
||||
self.card_GameIdMode = ComboBoxSettingCard(
|
||||
self.card_GameIdMode = NoOptionComboBoxSettingCard(
|
||||
icon=FluentIcon.DICTIONARY,
|
||||
title="关卡配置模式",
|
||||
content="刷理智关卡号的配置模式",
|
||||
texts=["固定"],
|
||||
value=plan_list[0],
|
||||
texts=plan_list[1],
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Info_GameIdMode,
|
||||
parent=self,
|
||||
)
|
||||
self.card_GameIdMode.comboBox.setMinimumWidth(0)
|
||||
self.card_Server = ComboBoxSettingCard(
|
||||
icon=FluentIcon.PROJECTOR,
|
||||
title="服务器",
|
||||
@@ -1441,7 +1522,7 @@ class MemberManager(QWidget):
|
||||
configItem=self.config.Info_Notes,
|
||||
parent=self,
|
||||
)
|
||||
self.card_MedicineNumb = SpinBoxSettingCard(
|
||||
self.card_MedicineNumb = SpinBoxWithPlanSettingCard(
|
||||
icon=FluentIcon.GAME,
|
||||
title="吃理智药",
|
||||
content="吃理智药次数,输入0以关闭",
|
||||
@@ -1450,7 +1531,7 @@ class MemberManager(QWidget):
|
||||
configItem=self.config.Info_MedicineNumb,
|
||||
parent=self,
|
||||
)
|
||||
self.card_SeriesNumb = ComboBoxSettingCard(
|
||||
self.card_SeriesNumb = ComboBoxWithPlanSettingCard(
|
||||
icon=FluentIcon.GAME,
|
||||
title="连战次数",
|
||||
content="连战次数较大时建议搭配剩余理智关卡使用",
|
||||
@@ -1460,7 +1541,7 @@ class MemberManager(QWidget):
|
||||
parent=self,
|
||||
)
|
||||
self.card_SeriesNumb.comboBox.setMinimumWidth(150)
|
||||
self.card_GameId = EditableComboBoxSettingCard(
|
||||
self.card_GameId = EditableComboBoxWithPlanSettingCard(
|
||||
icon=FluentIcon.GAME,
|
||||
title="关卡选择",
|
||||
content="按下回车以添加自定义关卡号",
|
||||
@@ -1470,7 +1551,7 @@ class MemberManager(QWidget):
|
||||
configItem=self.config.Info_GameId,
|
||||
parent=self,
|
||||
)
|
||||
self.card_GameId_1 = EditableComboBoxSettingCard(
|
||||
self.card_GameId_1 = EditableComboBoxWithPlanSettingCard(
|
||||
icon=FluentIcon.GAME,
|
||||
title="备选关卡 - 1",
|
||||
content="按下回车以添加自定义关卡号",
|
||||
@@ -1480,7 +1561,7 @@ class MemberManager(QWidget):
|
||||
configItem=self.config.Info_GameId_1,
|
||||
parent=self,
|
||||
)
|
||||
self.card_GameId_2 = EditableComboBoxSettingCard(
|
||||
self.card_GameId_2 = EditableComboBoxWithPlanSettingCard(
|
||||
icon=FluentIcon.GAME,
|
||||
title="备选关卡 - 2",
|
||||
content="按下回车以添加自定义关卡号",
|
||||
@@ -1490,19 +1571,33 @@ class MemberManager(QWidget):
|
||||
configItem=self.config.Info_GameId_2,
|
||||
parent=self,
|
||||
)
|
||||
self.card_GameId_Remain = EditableComboBoxSettingCard(
|
||||
icon=FluentIcon.GAME,
|
||||
title="剩余理智关卡",
|
||||
content="按下回车以添加自定义关卡号",
|
||||
value=Config.gameid_dict["ALL"]["value"],
|
||||
texts=[
|
||||
"不使用" if _ == "当前/上次" else _
|
||||
for _ in Config.gameid_dict["ALL"]["text"]
|
||||
],
|
||||
self.card_GameId_Remain = (
|
||||
EditableComboBoxWithPlanSettingCard(
|
||||
icon=FluentIcon.GAME,
|
||||
title="剩余理智关卡",
|
||||
content="按下回车以添加自定义关卡号",
|
||||
value=Config.gameid_dict["ALL"]["value"],
|
||||
texts=[
|
||||
"不使用" if _ == "当前/上次" else _
|
||||
for _ in Config.gameid_dict["ALL"]["text"]
|
||||
],
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Info_GameId_Remain,
|
||||
parent=self,
|
||||
)
|
||||
)
|
||||
self.card_Skland = PasswordLineAndSwitchButtonSettingCard(
|
||||
icon=FluentIcon.CERTIFICATE,
|
||||
title="森空岛签到",
|
||||
content="此功能具有一定风险,请谨慎使用!获取登录凭证请查阅「文档-进阶功能」。",
|
||||
text="鹰角网络通行证登录凭证",
|
||||
algorithm="DPAPI",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Info_GameId_Remain,
|
||||
configItem_bool=self.config.Info_IfSkland,
|
||||
configItem_info=self.config.Info_SklandToken,
|
||||
parent=self,
|
||||
)
|
||||
self.card_Skland.LineEdit.setMinimumWidth(250)
|
||||
|
||||
self.card_UserLable = UserLableSettingCard(
|
||||
icon=FluentIcon.INFO,
|
||||
@@ -1514,10 +1609,153 @@ class MemberManager(QWidget):
|
||||
"LastAnnihilationDate": self.config.Data_LastAnnihilationDate,
|
||||
"ProxyTimes": self.config.Data_ProxyTimes,
|
||||
"IfPassCheck": self.config.Data_IfPassCheck,
|
||||
"IfSkland": self.config.Info_IfSkland,
|
||||
"LastSklandDate": self.config.Data_LastSklandDate,
|
||||
},
|
||||
parent=self,
|
||||
)
|
||||
|
||||
# 单独任务卡片
|
||||
self.card_TaskSet = UserTaskSettingCard(
|
||||
icon=FluentIcon.LIBRARY,
|
||||
title="自动日常代理任务序列",
|
||||
content="未启用任何任务项",
|
||||
text="设置",
|
||||
qconfig=self.config,
|
||||
configItems={
|
||||
"IfWakeUp": self.config.Task_IfWakeUp,
|
||||
"IfRecruiting": self.config.Task_IfRecruiting,
|
||||
"IfBase": self.config.Task_IfBase,
|
||||
"IfCombat": self.config.Task_IfCombat,
|
||||
"IfMall": self.config.Task_IfMall,
|
||||
"IfMission": self.config.Task_IfMission,
|
||||
"IfAutoRoguelike": self.config.Task_IfAutoRoguelike,
|
||||
"IfReclamation": self.config.Task_IfReclamation,
|
||||
},
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfWakeUp = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="开始唤醒",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfWakeUp,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfRecruiting = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="自动公招",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfRecruiting,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfBase = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="基建换班",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfBase,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfCombat = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="刷理智",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfCombat,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfMall = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="获取信用及购物",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfMall,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfMission = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="领取奖励",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfMission,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfAutoRoguelike = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="自动肉鸽",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfAutoRoguelike,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfReclamation = SwitchSettingCard(
|
||||
icon=FluentIcon.TILES,
|
||||
title="生息演算",
|
||||
content="",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Task_IfReclamation,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
self.TaskSetCard = SettingFlyoutView(
|
||||
self,
|
||||
"自动日常代理任务序列设置",
|
||||
[
|
||||
self.card_IfWakeUp,
|
||||
self.card_IfRecruiting,
|
||||
self.card_IfBase,
|
||||
self.card_IfCombat,
|
||||
self.card_IfMall,
|
||||
self.card_IfMission,
|
||||
self.card_IfAutoRoguelike,
|
||||
self.card_IfReclamation,
|
||||
],
|
||||
)
|
||||
|
||||
# 单独通知卡片
|
||||
self.card_NotifySet = UserNoticeSettingCard(
|
||||
icon=FluentIcon.MAIL,
|
||||
title="用户单独通知设置",
|
||||
content="未启用任何通知项",
|
||||
text="设置",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_Enabled,
|
||||
configItems={
|
||||
"IfSendStatistic": self.config.Notify_IfSendStatistic,
|
||||
"IfSendSixStar": self.config.Notify_IfSendSixStar,
|
||||
"IfSendMail": self.config.Notify_IfSendMail,
|
||||
"ToAddress": self.config.Notify_ToAddress,
|
||||
"IfServerChan": self.config.Notify_IfServerChan,
|
||||
"ServerChanKey": self.config.Notify_ServerChanKey,
|
||||
"IfCompanyWebHookBot": self.config.Notify_IfCompanyWebHookBot,
|
||||
"CompanyWebHookBotUrl": self.config.Notify_CompanyWebHookBotUrl,
|
||||
},
|
||||
parent=self,
|
||||
)
|
||||
self.card_NotifyContent = self.NotifyContentSettingCard(
|
||||
self.config, self
|
||||
)
|
||||
self.card_EMail = self.EMailSettingCard(self.config, self)
|
||||
self.card_ServerChan = self.ServerChanSettingCard(
|
||||
self.config, self
|
||||
)
|
||||
self.card_CompanyWebhookBot = (
|
||||
self.CompanyWechatPushSettingCard(self.config, self)
|
||||
)
|
||||
|
||||
self.NotifySetCard = SettingFlyoutView(
|
||||
self,
|
||||
"用户通知设置",
|
||||
[
|
||||
self.card_NotifyContent,
|
||||
self.card_EMail,
|
||||
self.card_ServerChan,
|
||||
self.card_CompanyWebhookBot,
|
||||
],
|
||||
)
|
||||
|
||||
h1_layout = QHBoxLayout()
|
||||
h1_layout.addWidget(self.card_Name)
|
||||
h1_layout.addWidget(self.card_Id)
|
||||
@@ -1555,6 +1793,9 @@ class MemberManager(QWidget):
|
||||
Layout.addLayout(h6_layout)
|
||||
Layout.addLayout(h7_layout)
|
||||
Layout.addLayout(h8_layout)
|
||||
Layout.addWidget(self.card_Skland)
|
||||
Layout.addWidget(self.card_TaskSet)
|
||||
Layout.addWidget(self.card_NotifySet)
|
||||
|
||||
self.viewLayout.addLayout(Layout)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
@@ -1574,10 +1815,16 @@ class MemberManager(QWidget):
|
||||
self.card_InfrastMode.clicked.connect(
|
||||
self.set_infrastructure
|
||||
)
|
||||
self.card_TaskSet.clicked.connect(self.set_task)
|
||||
self.card_NotifySet.clicked.connect(self.set_notify)
|
||||
self.card_GameIdMode.comboBox.currentIndexChanged.connect(
|
||||
self.switch_gameid_mode
|
||||
)
|
||||
Config.gameid_refreshed.connect(self.refresh_gameid)
|
||||
Config.PASSWORD_refreshed.connect(self.refresh_password)
|
||||
|
||||
self.switch_mode()
|
||||
self.switch_gameid_mode()
|
||||
self.switch_infrastructure()
|
||||
|
||||
def switch_mode(self) -> None:
|
||||
@@ -1596,6 +1843,40 @@ class MemberManager(QWidget):
|
||||
self.card_Annihilation.button.setVisible(True)
|
||||
self.card_Routine.setVisible(True)
|
||||
|
||||
def switch_gameid_mode(self) -> None:
|
||||
|
||||
for card, name in zip(
|
||||
[
|
||||
self.card_MedicineNumb,
|
||||
self.card_SeriesNumb,
|
||||
self.card_GameId,
|
||||
self.card_GameId_1,
|
||||
self.card_GameId_2,
|
||||
self.card_GameId_Remain,
|
||||
],
|
||||
[
|
||||
"MedicineNumb",
|
||||
"SeriesNumb",
|
||||
"GameId",
|
||||
"GameId_1",
|
||||
"GameId_2",
|
||||
"GameId_Remain",
|
||||
],
|
||||
):
|
||||
|
||||
card.switch_mode(
|
||||
self.config.get(self.config.Info_GameIdMode)[:2]
|
||||
)
|
||||
if (
|
||||
self.config.get(self.config.Info_GameIdMode)
|
||||
!= "固定"
|
||||
):
|
||||
card.change_plan(
|
||||
Config.plan_dict[
|
||||
self.config.get(self.config.Info_GameIdMode)
|
||||
]["Config"].get_current_info(name)
|
||||
)
|
||||
|
||||
def switch_infrastructure(self) -> None:
|
||||
|
||||
if (
|
||||
@@ -1698,3 +1979,178 @@ class MemberManager(QWidget):
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
def set_task(self) -> None:
|
||||
"""设置用户任务序列相关配置"""
|
||||
|
||||
self.TaskSetCard.setVisible(True)
|
||||
Flyout.make(
|
||||
self.TaskSetCard,
|
||||
self.card_TaskSet,
|
||||
self,
|
||||
aniType=FlyoutAnimationType.PULL_UP,
|
||||
isDeleteOnClose=False,
|
||||
)
|
||||
|
||||
def set_notify(self) -> None:
|
||||
"""设置用户通知相关配置"""
|
||||
|
||||
self.NotifySetCard.setVisible(True)
|
||||
Flyout.make(
|
||||
self.NotifySetCard,
|
||||
self.card_NotifySet,
|
||||
self,
|
||||
aniType=FlyoutAnimationType.PULL_UP,
|
||||
isDeleteOnClose=False,
|
||||
)
|
||||
|
||||
class NotifyContentSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, config: MaaUserConfig, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("用户通知内容选项")
|
||||
|
||||
self.config = config
|
||||
|
||||
self.card_IfSendStatistic = SwitchSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="推送统计信息",
|
||||
content="推送自动代理统计信息的通知",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_IfSendStatistic,
|
||||
parent=self,
|
||||
)
|
||||
self.card_IfSendSixStar = SwitchSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="推送公招高资喜报",
|
||||
content="公招出现六星词条时推送喜报",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_IfSendSixStar,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
Layout = QVBoxLayout()
|
||||
Layout.addWidget(self.card_IfSendStatistic)
|
||||
Layout.addWidget(self.card_IfSendSixStar)
|
||||
self.viewLayout.addLayout(Layout)
|
||||
self.viewLayout.setSpacing(3)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
|
||||
class EMailSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, config: MaaUserConfig, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("用户邮箱通知")
|
||||
|
||||
self.config = config
|
||||
|
||||
self.card_IfSendMail = SwitchSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="推送用户邮件通知",
|
||||
content="是否启用用户邮件通知功能",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_IfSendMail,
|
||||
parent=self,
|
||||
)
|
||||
self.card_ToAddress = LineEditSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="用户收信邮箱地址",
|
||||
content="接收用户通知的邮箱地址",
|
||||
text="请输入用户收信邮箱地址",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_ToAddress,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
Layout = QVBoxLayout()
|
||||
Layout.addWidget(self.card_IfSendMail)
|
||||
Layout.addWidget(self.card_ToAddress)
|
||||
self.viewLayout.addLayout(Layout)
|
||||
self.viewLayout.setSpacing(3)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
|
||||
class ServerChanSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, config: MaaUserConfig, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("用户ServerChan通知")
|
||||
|
||||
self.config = config
|
||||
|
||||
self.card_IfServerChan = SwitchSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="推送用户Server酱通知",
|
||||
content="是否启用用户Server酱通知功能",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_IfServerChan,
|
||||
parent=self,
|
||||
)
|
||||
self.card_ServerChanKey = LineEditSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="用户SendKey",
|
||||
content="SC3与SCT均须填写",
|
||||
text="请输入用户SendKey",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_ServerChanKey,
|
||||
parent=self,
|
||||
)
|
||||
self.card_ServerChanChannel = LineEditSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="用户ServerChanChannel代码",
|
||||
content="留空则默认,多个请使用“|”隔开",
|
||||
text="请输入Channel代码,仅SCT生效",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_ServerChanChannel,
|
||||
parent=self,
|
||||
)
|
||||
self.card_ServerChanTag = LineEditSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="用户Tag内容",
|
||||
content="留空则默认,多个请使用“|”隔开",
|
||||
text="请输入加入推送的Tag,仅SC3生效",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_ServerChanTag,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
Layout = QVBoxLayout()
|
||||
Layout.addWidget(self.card_IfServerChan)
|
||||
Layout.addWidget(self.card_ServerChanKey)
|
||||
Layout.addWidget(self.card_ServerChanChannel)
|
||||
Layout.addWidget(self.card_ServerChanTag)
|
||||
self.viewLayout.addLayout(Layout)
|
||||
self.viewLayout.setSpacing(3)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
|
||||
class CompanyWechatPushSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, config: MaaUserConfig, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("用户企业微信推送")
|
||||
|
||||
self.config = config
|
||||
|
||||
self.card_IfCompanyWebHookBot = SwitchSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="推送用户企业微信机器人通知",
|
||||
content="是否启用用户企微机器人通知功能",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_IfCompanyWebHookBot,
|
||||
parent=self,
|
||||
)
|
||||
self.card_CompanyWebHookBotUrl = LineEditSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="WebhookUrl",
|
||||
content="用户企微群机器人Webhook地址",
|
||||
text="请输入用户Webhook的Url",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Notify_CompanyWebHookBotUrl,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
Layout = QVBoxLayout()
|
||||
Layout.addWidget(self.card_IfCompanyWebHookBot)
|
||||
Layout.addWidget(self.card_CompanyWebHookBotUrl)
|
||||
self.viewLayout.addLayout(Layout)
|
||||
self.viewLayout.setSpacing(3)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
|
||||
497
app/ui/plan_manager.py
Normal file
497
app/ui/plan_manager.py
Normal file
@@ -0,0 +1,497 @@
|
||||
# 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.QtWidgets import (
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QStackedWidget,
|
||||
QHeaderView,
|
||||
)
|
||||
from qfluentwidgets import (
|
||||
Action,
|
||||
FluentIcon,
|
||||
MessageBox,
|
||||
HeaderCardWidget,
|
||||
CommandBar,
|
||||
TableWidget,
|
||||
)
|
||||
from typing import List, Dict, Union
|
||||
import shutil
|
||||
|
||||
from app.core import Config, MainInfoBar, MaaPlanConfig, SoundPlayer
|
||||
from .Widget import (
|
||||
ComboBoxMessageBox,
|
||||
LineEditSettingCard,
|
||||
ComboBoxSettingCard,
|
||||
SpinBoxSetting,
|
||||
EditableComboBoxSetting,
|
||||
ComboBoxSetting,
|
||||
PivotArea,
|
||||
)
|
||||
|
||||
|
||||
class PlanManager(QWidget):
|
||||
"""计划管理父界面"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setObjectName("计划管理")
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
self.tools = CommandBar()
|
||||
|
||||
self.plan_manager = self.PlanSettingBox(self)
|
||||
|
||||
# 逐个添加动作
|
||||
self.tools.addActions(
|
||||
[
|
||||
Action(FluentIcon.ADD_TO, "新建计划表", triggered=self.add_setting_box),
|
||||
Action(
|
||||
FluentIcon.REMOVE_FROM, "删除计划表", triggered=self.del_setting_box
|
||||
),
|
||||
]
|
||||
)
|
||||
self.tools.addSeparator()
|
||||
self.tools.addActions(
|
||||
[
|
||||
Action(
|
||||
FluentIcon.LEFT_ARROW, "向左移动", triggered=self.left_setting_box
|
||||
),
|
||||
Action(
|
||||
FluentIcon.RIGHT_ARROW, "向右移动", triggered=self.right_setting_box
|
||||
),
|
||||
]
|
||||
)
|
||||
self.tools.addSeparator()
|
||||
|
||||
layout.addWidget(self.tools)
|
||||
layout.addWidget(self.plan_manager)
|
||||
|
||||
def add_setting_box(self):
|
||||
"""添加一个计划表"""
|
||||
|
||||
choice = ComboBoxMessageBox(
|
||||
self.window(),
|
||||
"选择一个计划类型以添加相应计划表",
|
||||
["选择计划类型"],
|
||||
[["MAA"]],
|
||||
)
|
||||
if choice.exec() and choice.input[0].currentIndex() != -1:
|
||||
|
||||
if choice.input[0].currentText() == "MAA":
|
||||
|
||||
index = len(Config.plan_dict) + 1
|
||||
|
||||
maa_plan_config = MaaPlanConfig()
|
||||
maa_plan_config.load(
|
||||
Config.app_path / f"config/MaaPlanConfig/计划_{index}/config.json",
|
||||
maa_plan_config,
|
||||
)
|
||||
maa_plan_config.save()
|
||||
|
||||
Config.plan_dict[f"计划_{index}"] = {
|
||||
"Type": "Maa",
|
||||
"Path": Config.app_path / f"config/MaaPlanConfig/计划_{index}",
|
||||
"Config": maa_plan_config,
|
||||
}
|
||||
|
||||
self.plan_manager.add_MaaPlanSettingBox(index)
|
||||
self.plan_manager.switch_SettingBox(index)
|
||||
|
||||
logger.success(f"计划管理 计划_{index} 添加成功")
|
||||
MainInfoBar.push_info_bar(
|
||||
"success", "操作成功", f"添加计划表 计划_{index}", 3000
|
||||
)
|
||||
SoundPlayer.play("添加计划表")
|
||||
|
||||
def del_setting_box(self):
|
||||
"""删除一个计划表"""
|
||||
|
||||
name = self.plan_manager.pivot.currentRouteKey()
|
||||
|
||||
if name is None:
|
||||
logger.warning("删除计划表时未选择计划表")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择计划表", "请选择一个计划表", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if len(Config.running_list) > 0:
|
||||
logger.warning("删除计划表时调度队列未停止运行")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "调度中心正在执行任务", "请等待或手动中止任务", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
choice = MessageBox("确认", f"确定要删除 {name} 吗?", self.window())
|
||||
if choice.exec():
|
||||
|
||||
self.plan_manager.clear_SettingBox()
|
||||
|
||||
shutil.rmtree(Config.plan_dict[name]["Path"])
|
||||
Config.change_plan(name, "禁用")
|
||||
for i in range(int(name[3:]) + 1, len(Config.plan_dict) + 1):
|
||||
if Config.plan_dict[f"计划_{i}"]["Path"].exists():
|
||||
Config.plan_dict[f"计划_{i}"]["Path"].rename(
|
||||
Config.plan_dict[f"计划_{i}"]["Path"].with_name(f"计划_{i-1}")
|
||||
)
|
||||
Config.change_plan(f"计划_{i}", f"计划_{i-1}")
|
||||
|
||||
self.plan_manager.show_SettingBox(max(int(name[3:]) - 1, 1))
|
||||
|
||||
logger.success(f"计划表 {name} 删除成功")
|
||||
MainInfoBar.push_info_bar("success", "操作成功", f"删除计划表 {name}", 3000)
|
||||
SoundPlayer.play("删除计划表")
|
||||
|
||||
def left_setting_box(self):
|
||||
"""向左移动计划表"""
|
||||
|
||||
name = self.plan_manager.pivot.currentRouteKey()
|
||||
|
||||
if name is None:
|
||||
logger.warning("向左移动计划表时未选择计划表")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择计划表", "请选择一个计划表", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
index = int(name[3:])
|
||||
|
||||
if index == 1:
|
||||
logger.warning("向左移动计划表时已到达最左端")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "已经是第一个计划表", "无法向左移动", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if len(Config.running_list) > 0:
|
||||
logger.warning("向左移动计划表时调度队列未停止运行")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "调度中心正在执行任务", "请等待或手动中止任务", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
self.plan_manager.clear_SettingBox()
|
||||
|
||||
Config.plan_dict[name]["Path"].rename(
|
||||
Config.plan_dict[name]["Path"].with_name("计划_0")
|
||||
)
|
||||
Config.change_plan(name, "计划_0")
|
||||
Config.plan_dict[f"计划_{index-1}"]["Path"].rename(
|
||||
Config.plan_dict[name]["Path"]
|
||||
)
|
||||
Config.change_plan(f"计划_{index-1}", name)
|
||||
Config.plan_dict[name]["Path"].with_name("计划_0").rename(
|
||||
Config.plan_dict[f"计划_{index-1}"]["Path"]
|
||||
)
|
||||
Config.change_plan("计划_0", f"计划_{index-1}")
|
||||
|
||||
self.plan_manager.show_SettingBox(index - 1)
|
||||
|
||||
logger.success(f"计划表 {name} 左移成功")
|
||||
MainInfoBar.push_info_bar("success", "操作成功", f"左移计划表 {name}", 3000)
|
||||
|
||||
def right_setting_box(self):
|
||||
"""向右移动计划表"""
|
||||
|
||||
name = self.plan_manager.pivot.currentRouteKey()
|
||||
|
||||
if name is None:
|
||||
logger.warning("向右移动计划表时未选择计划表")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择计划表", "请选择一个计划表", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
index = int(name[3:])
|
||||
|
||||
if index == len(Config.plan_dict):
|
||||
logger.warning("向右移动计划表时已到达最右端")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "已经是最后一个计划表", "无法向右移动", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if len(Config.running_list) > 0:
|
||||
logger.warning("向右移动计划表时调度队列未停止运行")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "调度中心正在执行任务", "请等待或手动中止任务", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
self.plan_manager.clear_SettingBox()
|
||||
|
||||
Config.plan_dict[name]["Path"].rename(
|
||||
Config.plan_dict[name]["Path"].with_name("计划_0")
|
||||
)
|
||||
Config.change_plan(name, "计划_0")
|
||||
Config.plan_dict[f"计划_{index+1}"]["Path"].rename(
|
||||
Config.plan_dict[name]["Path"]
|
||||
)
|
||||
Config.change_plan(f"计划_{index+1}", name)
|
||||
Config.plan_dict[name]["Path"].with_name("计划_0").rename(
|
||||
Config.plan_dict[f"计划_{index+1}"]["Path"]
|
||||
)
|
||||
Config.change_plan("计划_0", f"计划_{index+1}")
|
||||
|
||||
self.plan_manager.show_SettingBox(index + 1)
|
||||
|
||||
logger.success(f"计划表 {name} 右移成功")
|
||||
MainInfoBar.push_info_bar("success", "操作成功", f"右移计划表 {name}", 3000)
|
||||
|
||||
class PlanSettingBox(QWidget):
|
||||
"""计划管理子页面组"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setObjectName("计划管理页面组")
|
||||
|
||||
self.pivotArea = PivotArea(self)
|
||||
self.pivot = self.pivotArea.pivot
|
||||
|
||||
self.stackedWidget = QStackedWidget(self)
|
||||
self.stackedWidget.setContentsMargins(0, 0, 0, 0)
|
||||
self.stackedWidget.setStyleSheet("background: transparent; border: none;")
|
||||
|
||||
self.script_list: List[PlanManager.PlanSettingBox.MaaPlanSettingBox] = []
|
||||
|
||||
self.Layout = QVBoxLayout(self)
|
||||
self.Layout.addWidget(self.pivotArea)
|
||||
self.Layout.addWidget(self.stackedWidget)
|
||||
self.Layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.pivot.currentItemChanged.connect(
|
||||
lambda index: self.switch_SettingBox(
|
||||
int(index[3:]), if_chang_pivot=False
|
||||
)
|
||||
)
|
||||
|
||||
self.show_SettingBox(1)
|
||||
|
||||
def show_SettingBox(self, index) -> None:
|
||||
"""加载所有子界面"""
|
||||
|
||||
Config.search_plan()
|
||||
|
||||
for name, info in Config.plan_dict.items():
|
||||
if info["Type"] == "Maa":
|
||||
self.add_MaaPlanSettingBox(int(name[3:]))
|
||||
|
||||
self.switch_SettingBox(index)
|
||||
|
||||
def switch_SettingBox(self, index: int, if_chang_pivot: bool = True) -> None:
|
||||
"""切换到指定的子界面"""
|
||||
|
||||
if len(Config.plan_dict) == 0:
|
||||
return None
|
||||
|
||||
if index > len(Config.plan_dict):
|
||||
return None
|
||||
|
||||
if if_chang_pivot:
|
||||
self.pivot.setCurrentItem(self.script_list[index - 1].objectName())
|
||||
self.stackedWidget.setCurrentWidget(self.script_list[index - 1])
|
||||
|
||||
def clear_SettingBox(self) -> None:
|
||||
"""清空所有子界面"""
|
||||
|
||||
for sub_interface in self.script_list:
|
||||
Config.gameid_refreshed.disconnect(sub_interface.refresh_gameid)
|
||||
self.stackedWidget.removeWidget(sub_interface)
|
||||
sub_interface.deleteLater()
|
||||
self.script_list.clear()
|
||||
self.pivot.clear()
|
||||
|
||||
def add_MaaPlanSettingBox(self, uid: int) -> None:
|
||||
"""添加一个MAA设置界面"""
|
||||
|
||||
maa_plan_setting_box = self.MaaPlanSettingBox(uid, self)
|
||||
|
||||
self.script_list.append(maa_plan_setting_box)
|
||||
|
||||
self.stackedWidget.addWidget(self.script_list[-1])
|
||||
|
||||
self.pivot.addItem(routeKey=f"计划_{uid}", text=f"计划 {uid}")
|
||||
|
||||
class MaaPlanSettingBox(HeaderCardWidget):
|
||||
"""MAA类计划设置界面"""
|
||||
|
||||
def __init__(self, uid: int, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setObjectName(f"计划_{uid}")
|
||||
self.setTitle("MAA计划表")
|
||||
self.config = Config.plan_dict[f"计划_{uid}"]["Config"]
|
||||
|
||||
self.card_Name = LineEditSettingCard(
|
||||
icon=FluentIcon.EDIT,
|
||||
title="计划表名称",
|
||||
content="用于标识计划表的名称",
|
||||
text="请输入计划表名称",
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Info_Name,
|
||||
parent=self,
|
||||
)
|
||||
self.card_Mode = ComboBoxSettingCard(
|
||||
icon=FluentIcon.DICTIONARY,
|
||||
title="计划模式",
|
||||
content="全局模式下计划内容固定,周计划模式下计划按周一到周日切换",
|
||||
texts=["全局", "周计划"],
|
||||
qconfig=self.config,
|
||||
configItem=self.config.Info_Mode,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
self.table = TableWidget(self)
|
||||
self.table.setColumnCount(8)
|
||||
self.table.setRowCount(6)
|
||||
self.table.setHorizontalHeaderLabels(
|
||||
["全局", "周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
)
|
||||
self.table.setVerticalHeaderLabels(
|
||||
[
|
||||
"吃理智药",
|
||||
"连战次数",
|
||||
"关卡选择",
|
||||
"备选 - 1",
|
||||
"备选 - 2",
|
||||
"剩余理智",
|
||||
]
|
||||
)
|
||||
self.table.setAlternatingRowColors(False)
|
||||
self.table.setEditTriggers(TableWidget.NoEditTriggers)
|
||||
for col in range(8):
|
||||
self.table.horizontalHeader().setSectionResizeMode(
|
||||
col, QHeaderView.ResizeMode.Stretch
|
||||
)
|
||||
for row in range(6):
|
||||
self.table.verticalHeader().setSectionResizeMode(
|
||||
row, QHeaderView.ResizeMode.ResizeToContents
|
||||
)
|
||||
|
||||
self.item_dict: Dict[
|
||||
str,
|
||||
Dict[
|
||||
str,
|
||||
Union[SpinBoxSetting, ComboBoxSetting, EditableComboBoxSetting],
|
||||
],
|
||||
] = {}
|
||||
|
||||
for col, (group, name_dict) in enumerate(
|
||||
self.config.config_item_dict.items()
|
||||
):
|
||||
|
||||
self.item_dict[group] = {}
|
||||
|
||||
for row, (name, configItem) in enumerate(name_dict.items()):
|
||||
|
||||
if name == "MedicineNumb":
|
||||
self.item_dict[group][name] = SpinBoxSetting(
|
||||
range=(0, 1024),
|
||||
qconfig=self.config,
|
||||
configItem=configItem,
|
||||
parent=self,
|
||||
)
|
||||
elif name == "SeriesNumb":
|
||||
self.item_dict[group][name] = ComboBoxSetting(
|
||||
texts=["AUTO", "6", "5", "4", "3", "2", "1", "不选择"],
|
||||
qconfig=self.config,
|
||||
configItem=configItem,
|
||||
parent=self,
|
||||
)
|
||||
elif name == "GameId_Remain":
|
||||
self.item_dict[group][name] = EditableComboBoxSetting(
|
||||
value=Config.gameid_dict[group]["value"],
|
||||
texts=[
|
||||
"不使用" if _ == "当前/上次" else _
|
||||
for _ in Config.gameid_dict[group]["text"]
|
||||
],
|
||||
qconfig=self.config,
|
||||
configItem=configItem,
|
||||
parent=self,
|
||||
)
|
||||
elif "GameId" in name:
|
||||
self.item_dict[group][name] = EditableComboBoxSetting(
|
||||
value=Config.gameid_dict[group]["value"],
|
||||
texts=Config.gameid_dict[group]["text"],
|
||||
qconfig=self.config,
|
||||
configItem=configItem,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
self.table.setCellWidget(row, col, self.item_dict[group][name])
|
||||
|
||||
Layout = QVBoxLayout()
|
||||
Layout.addWidget(self.card_Name)
|
||||
Layout.addWidget(self.card_Mode)
|
||||
Layout.addWidget(self.table)
|
||||
|
||||
self.viewLayout.addLayout(Layout)
|
||||
self.viewLayout.setSpacing(3)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
|
||||
self.card_Mode.comboBox.currentIndexChanged.connect(self.switch_mode)
|
||||
Config.gameid_refreshed.connect(self.refresh_gameid)
|
||||
|
||||
self.switch_mode()
|
||||
|
||||
def switch_mode(self) -> None:
|
||||
"""切换计划模式"""
|
||||
|
||||
for group, name_dict in self.item_dict.items():
|
||||
for name, setting_item in name_dict.items():
|
||||
setting_item.setEnabled(
|
||||
(group == "ALL")
|
||||
== (self.config.get(self.config.Info_Mode) == "ALL")
|
||||
)
|
||||
|
||||
def refresh_gameid(self):
|
||||
|
||||
for group, name_dict in self.item_dict.items():
|
||||
|
||||
for name, setting_item in name_dict.items():
|
||||
|
||||
if name == "GameId_Remain":
|
||||
|
||||
setting_item.reLoadOptions(
|
||||
Config.gameid_dict[group]["value"],
|
||||
[
|
||||
"不使用" if _ == "当前/上次" else _
|
||||
for _ in Config.gameid_dict[group]["text"]
|
||||
],
|
||||
)
|
||||
|
||||
elif "GameId" in name:
|
||||
|
||||
setting_item.reLoadOptions(
|
||||
Config.gameid_dict[group]["value"],
|
||||
Config.gameid_dict[group]["text"],
|
||||
)
|
||||
@@ -42,7 +42,7 @@ from qfluentwidgets import (
|
||||
)
|
||||
from typing import List
|
||||
|
||||
from app.core import QueueConfig, Config, MainInfoBar
|
||||
from app.core import QueueConfig, Config, MainInfoBar, SoundPlayer
|
||||
from .Widget import (
|
||||
SwitchSettingCard,
|
||||
ComboBoxSettingCard,
|
||||
@@ -116,13 +116,14 @@ class QueueManager(QWidget):
|
||||
|
||||
logger.success(f"调度队列_{index} 添加成功")
|
||||
MainInfoBar.push_info_bar("success", "操作成功", f"添加 调度队列_{index}", 3000)
|
||||
SoundPlayer.play("添加调度队列")
|
||||
|
||||
def del_setting_box(self):
|
||||
"""删除一个调度队列实例"""
|
||||
|
||||
name = self.queue_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("未选择调度队列")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择调度队列", "请先选择一个调度队列", 5000
|
||||
@@ -136,11 +137,7 @@ class QueueManager(QWidget):
|
||||
)
|
||||
return None
|
||||
|
||||
choice = MessageBox(
|
||||
"确认",
|
||||
f"确定要删除 {name} 吗?",
|
||||
self.window(),
|
||||
)
|
||||
choice = MessageBox("确认", f"确定要删除 {name} 吗?", self.window())
|
||||
if choice.exec():
|
||||
|
||||
self.queue_manager.clear_SettingBox()
|
||||
@@ -158,13 +155,14 @@ class QueueManager(QWidget):
|
||||
|
||||
logger.success(f"{name} 删除成功")
|
||||
MainInfoBar.push_info_bar("success", "操作成功", f"删除 {name}", 3000)
|
||||
SoundPlayer.play("删除调度队列")
|
||||
|
||||
def left_setting_box(self):
|
||||
"""向左移动调度队列实例"""
|
||||
|
||||
name = self.queue_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("未选择调度队列")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择调度队列", "请先选择一个调度队列", 5000
|
||||
@@ -209,7 +207,7 @@ class QueueManager(QWidget):
|
||||
|
||||
name = self.queue_manager.pivot.currentRouteKey()
|
||||
|
||||
if name == None:
|
||||
if name is None:
|
||||
logger.warning("未选择调度队列")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择调度队列", "请先选择一个调度队列", 5000
|
||||
@@ -379,16 +377,6 @@ class QueueManager(QWidget):
|
||||
self.setObjectName(f"调度队列_{uid}")
|
||||
self.config = Config.queue_dict[f"调度队列_{uid}"]["Config"]
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
|
||||
self.queue_set = self.QueueSetSettingCard(self.config, self)
|
||||
self.time = self.TimeSettingCard(self.config, self)
|
||||
self.task = self.TaskSettingCard(self.config, self)
|
||||
@@ -398,18 +386,24 @@ class QueueManager(QWidget):
|
||||
parent=self,
|
||||
)
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(0, 0, 11, 0)
|
||||
content_layout.addWidget(self.queue_set)
|
||||
content_layout.addWidget(self.time)
|
||||
content_layout.addWidget(self.task)
|
||||
content_layout.addWidget(self.history)
|
||||
content_layout.addStretch(1)
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
scrollArea.setWidget(content_widget)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(scrollArea)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
class QueueSetSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, config: QueueConfig, parent=None):
|
||||
|
||||
@@ -49,7 +49,7 @@ from packaging import version
|
||||
from pathlib import Path
|
||||
from typing import Dict, Union
|
||||
|
||||
from app.core import Config, MainInfoBar, Network
|
||||
from app.core import Config, MainInfoBar, Network, SoundPlayer
|
||||
from app.services import Crypto, System, Notify
|
||||
from .downloader import DownloadManager
|
||||
from .Widget import (
|
||||
@@ -70,10 +70,8 @@ class Setting(QWidget):
|
||||
super().__init__(parent)
|
||||
self.setObjectName("设置")
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
|
||||
self.function = FunctionSettingCard(self)
|
||||
self.voice = VoiceSettingCard(self)
|
||||
self.start = StartSettingCard(self)
|
||||
self.ui = UiSettingCard(self)
|
||||
self.notification = NotifySettingCard(self)
|
||||
@@ -93,7 +91,11 @@ class Setting(QWidget):
|
||||
)
|
||||
self.other.card_Notice.clicked.connect(lambda: self.show_notice(if_show=True))
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(0, 0, 11, 0)
|
||||
content_layout.addWidget(self.function)
|
||||
content_layout.addWidget(self.voice)
|
||||
content_layout.addWidget(self.start)
|
||||
content_layout.addWidget(self.ui)
|
||||
content_layout.addWidget(self.notification)
|
||||
@@ -106,9 +108,9 @@ class Setting(QWidget):
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
scrollArea.setWidget(content_widget)
|
||||
layout = QVBoxLayout()
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(scrollArea)
|
||||
self.setLayout(layout)
|
||||
|
||||
def agree_bilibili(self) -> None:
|
||||
"""授权bilibili游戏隐私政策"""
|
||||
@@ -252,9 +254,7 @@ class Setting(QWidget):
|
||||
choice.exec()
|
||||
else:
|
||||
choice = MessageBox(
|
||||
"确认",
|
||||
"您没有输入管理密钥,是否取消修改管理密钥?",
|
||||
self.window(),
|
||||
"确认", "您没有输入管理密钥,是否取消修改管理密钥?", self.window()
|
||||
)
|
||||
if choice.exec():
|
||||
break
|
||||
@@ -264,31 +264,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¤t_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参数不正确",
|
||||
@@ -321,11 +316,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
|
||||
@@ -383,6 +378,7 @@ class Setting(QWidget):
|
||||
all_version_info[key] = value.copy()
|
||||
|
||||
# 询问是否开始版本更新
|
||||
SoundPlayer.play("有新版本")
|
||||
choice = NoticeMessageBox(
|
||||
self.window(),
|
||||
"版本更新",
|
||||
@@ -408,20 +404,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
|
||||
@@ -464,9 +462,12 @@ class Setting(QWidget):
|
||||
"发现新版本",
|
||||
f"{version_text(current_version)} --> {version_text(remote_version)}",
|
||||
3600000,
|
||||
if_force=True,
|
||||
)
|
||||
SoundPlayer.play("有新版本")
|
||||
else:
|
||||
MainInfoBar.push_info_bar("success", "更新检查", "已是最新版本~", 3000)
|
||||
SoundPlayer.play("无新版本")
|
||||
|
||||
def start_setup(self) -> None:
|
||||
subprocess.Popen(
|
||||
@@ -489,20 +490,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
|
||||
@@ -534,11 +535,38 @@ class Setting(QWidget):
|
||||
choice = NoticeMessageBox(self.window(), "公告", notice["notice_dict"])
|
||||
choice.button_cancel.hide()
|
||||
choice.button_layout.insertStretch(0, 1)
|
||||
SoundPlayer.play("公告展示")
|
||||
if choice.exec():
|
||||
with (Config.app_path / "resources/notice.json").open(
|
||||
mode="w", encoding="utf-8"
|
||||
) as f:
|
||||
json.dump(notice, f, ensure_ascii=False, indent=4)
|
||||
else:
|
||||
import random
|
||||
|
||||
if random.random() < 0.1:
|
||||
cc = NoticeMessageBox(
|
||||
self.window(),
|
||||
"用户守则",
|
||||
{
|
||||
"用户守则 - 第一版": """
|
||||
0. 用户守则的每一条都应该清晰可读、不含任何语法错误。如果发现任何一条不符合以上描述,请忽视它。
|
||||
1. AUTO_MAA 的所有版本均包含完整源代码与 LICENSE 文件,若发现此内容缺失,请立即关闭软件,并联系最近的 AUTO_MAA 开发者。
|
||||
2. AUTO_MAA 不会对您许下任何承诺,请自行保护好自己的数据,若软件运行过程中发生了数据损坏,项目组不负任何责任。
|
||||
3. AUTO_MAA 只会注册一个启动项,若发现两个 AUTO_MAA 同时自启动,请立即使用系统或杀软的 **启动项管理** 功能删除所有名为 AUTO_MAA 的启动项后重启软件。
|
||||
4. AUTO_MAA 正式版不应该包含命令行窗口,如果您看到了它,请立即关闭软件,通过 AUTO_MAA.exe 文件重新打开软件。
|
||||
5. 深色模式是危险的,但并非无法使用。
|
||||
6. 第 0 条规则不存在。如果你看到了,请忘记它,并正常使用软件
|
||||
7. **Mirror 酱** 是善良的,你只要付出小小的代价,就能得到祂的庇护。
|
||||
8. AUTO_MAA 没有实时合成语音的能力,软件所有语音都存储在本地。如果听到本地不存在的语音,立即关闭扬声器,并检查是否有未知脚本在运行。
|
||||
9. AUTO_MAA 不会在周六凌晨更新。如果收到更新提示,请忽略,不要查看更新内容,直到第二天天亮。
|
||||
10. 用户守则仅有一页""",
|
||||
"--- 标记文档中止 ---": "xdfv-serfcx-jiol,m: !1 $bad food of do $5b 9630-300 $daad 100-1\n\n// 0 == o //\n\n∠( °ω°)/",
|
||||
},
|
||||
)
|
||||
cc.button_cancel.hide()
|
||||
cc.button_layout.insertStretch(0, 1)
|
||||
cc.exec()
|
||||
|
||||
elif (
|
||||
datetime.now()
|
||||
@@ -547,8 +575,9 @@ class Setting(QWidget):
|
||||
):
|
||||
|
||||
MainInfoBar.push_info_bar(
|
||||
"info", "有新公告", "请前往设置界面查看公告", 3600000
|
||||
"info", "有新公告", "请前往设置界面查看公告", 3600000, if_force=True
|
||||
)
|
||||
SoundPlayer.play("公告通知")
|
||||
return None
|
||||
|
||||
|
||||
@@ -657,6 +686,36 @@ class FunctionSettingCard(HeaderCardWidget):
|
||||
self.addGroupWidget(widget)
|
||||
|
||||
|
||||
class VoiceSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("音效")
|
||||
|
||||
self.card_Enabled = SwitchSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="音效开关",
|
||||
content="是否启用音效",
|
||||
qconfig=Config,
|
||||
configItem=Config.voice_Enabled,
|
||||
parent=self,
|
||||
)
|
||||
self.card_Type = ComboBoxSettingCard(
|
||||
icon=FluentIcon.PAGE_RIGHT,
|
||||
title="音效模式",
|
||||
content="选择音效的播放模式",
|
||||
texts=["简洁", "聒噪"],
|
||||
qconfig=Config,
|
||||
configItem=Config.voice_Type,
|
||||
parent=self,
|
||||
)
|
||||
|
||||
Layout = QVBoxLayout()
|
||||
Layout.addWidget(self.card_Enabled)
|
||||
Layout.addWidget(self.card_Type)
|
||||
self.viewLayout.addLayout(Layout)
|
||||
|
||||
|
||||
class StartSettingCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@@ -1060,7 +1119,9 @@ class UpdaterSettingCard(HeaderCardWidget):
|
||||
parent=self,
|
||||
)
|
||||
mirrorchyan_url = HyperlinkButton(
|
||||
"https://mirrorchyan.com/", "获取Mirror酱CDK", self
|
||||
"https://mirrorchyan.com/zh/get-start?source=auto_maa-setting_card",
|
||||
"获取Mirror酱CDK",
|
||||
self,
|
||||
)
|
||||
self.card_MirrorChyanCDK.hBoxLayout.insertWidget(
|
||||
5, mirrorchyan_url, 0, Qt.AlignRight
|
||||
|
||||
@@ -71,12 +71,12 @@ if __name__ == "__main__":
|
||||
|
||||
os.system(
|
||||
"powershell -Command python -m nuitka --standalone --onefile --mingw64"
|
||||
" --enable-plugins=pyside6 --windows-console-mode=disable"
|
||||
" --enable-plugins=pyside6 --windows-console-mode=attach"
|
||||
" --onefile-tempdir-spec='{TEMP}\\AUTO_MAA'"
|
||||
" --windows-icon-from-ico=resources\\icons\\AUTO_MAA.ico"
|
||||
" --company-name='AUTO_MAA Team' --product-name=AUTO_MAA"
|
||||
f" --file-version={version["main_version"]}"
|
||||
f" --product-version={version["main_version"]}"
|
||||
f" --file-version={version['main_version']}"
|
||||
f" --product-version={version['main_version']}"
|
||||
" --file-description='AUTO_MAA Component'"
|
||||
" --copyright='Copyright © 2024-2025 DLmaster361'"
|
||||
" --assume-yes-for-downloads --output-filename=AUTO_MAA"
|
||||
|
||||
22
main.py
22
main.py
@@ -25,6 +25,25 @@ v4.3
|
||||
作者:DLmaster_361
|
||||
"""
|
||||
|
||||
# 屏蔽广告
|
||||
import builtins
|
||||
|
||||
original_print = builtins.print
|
||||
|
||||
|
||||
def no_print(*args, **kwargs):
|
||||
if (
|
||||
args
|
||||
and isinstance(args[0], str)
|
||||
and "QFluentWidgets Pro is now released." in args[0]
|
||||
):
|
||||
return
|
||||
return original_print(*args, **kwargs)
|
||||
|
||||
|
||||
builtins.print = no_print
|
||||
|
||||
|
||||
from loguru import logger
|
||||
from PySide6.QtWidgets import QApplication
|
||||
from qfluentwidgets import FluentTranslator
|
||||
@@ -42,8 +61,7 @@ def main():
|
||||
from app.ui.main_window import AUTO_MAA
|
||||
|
||||
window = AUTO_MAA()
|
||||
window.show_ui("显示主窗口")
|
||||
window.show_ui("配置托盘")
|
||||
window.show_ui("显示主窗口", if_start=True)
|
||||
window.start_up_task()
|
||||
sys.exit(application.exec())
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@ plyer
|
||||
PySide6
|
||||
PySide6-Fluent-Widgets[full]
|
||||
psutil
|
||||
opencv-python
|
||||
pywin32
|
||||
pyautogui
|
||||
keyboard
|
||||
pycryptodome
|
||||
requests
|
||||
markdown
|
||||
|
||||
@@ -58,4 +58,5 @@ G"GUI.UseTray": "True" #显示托盘图标
|
||||
G"GUI.MinimizeToTray": "False" #最小化时隐藏至托盘
|
||||
"Start.EmulatorPath" #模拟器路径
|
||||
"Start.EmulatorAddCommand": "-v 2" #附加命令
|
||||
"Start.EmulatorWaitSeconds": "10" #等待模拟器启动时间
|
||||
G"VersionUpdate.package": "MirrorChyanAppv5.15.6.zip" #更新包标识
|
||||
File diff suppressed because one or more lines are too long
BIN
resources/sounds/both/删除用户.wav
Normal file
BIN
resources/sounds/both/删除用户.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/删除脚本实例.wav
Normal file
BIN
resources/sounds/both/删除脚本实例.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/删除计划表.wav
Normal file
BIN
resources/sounds/both/删除计划表.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/删除调度队列.wav
Normal file
BIN
resources/sounds/both/删除调度队列.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/欢迎回来.wav
Normal file
BIN
resources/sounds/both/欢迎回来.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/添加用户.wav
Normal file
BIN
resources/sounds/both/添加用户.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/添加脚本实例.wav
Normal file
BIN
resources/sounds/both/添加脚本实例.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/添加计划表.wav
Normal file
BIN
resources/sounds/both/添加计划表.wav
Normal file
Binary file not shown.
BIN
resources/sounds/both/添加调度队列.wav
Normal file
BIN
resources/sounds/both/添加调度队列.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/ADB失败.wav
Normal file
BIN
resources/sounds/noisy/ADB失败.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/ADB成功.wav
Normal file
BIN
resources/sounds/noisy/ADB成功.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA在完成任务前中止.wav
Normal file
BIN
resources/sounds/noisy/MAA在完成任务前中止.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA在完成任务前退出.wav
Normal file
BIN
resources/sounds/noisy/MAA在完成任务前退出.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA更新.wav
Normal file
BIN
resources/sounds/noisy/MAA更新.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA未检测到任何模拟器.wav
Normal file
BIN
resources/sounds/noisy/MAA未检测到任何模拟器.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA未能正确登录PRTS.wav
Normal file
BIN
resources/sounds/noisy/MAA未能正确登录PRTS.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA的ADB连接异常.wav
Normal file
BIN
resources/sounds/noisy/MAA的ADB连接异常.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA进程超时.wav
Normal file
BIN
resources/sounds/noisy/MAA进程超时.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/MAA部分任务执行失败.wav
Normal file
BIN
resources/sounds/noisy/MAA部分任务执行失败.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/任务开始.wav
Normal file
BIN
resources/sounds/noisy/任务开始.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/任务结束.wav
Normal file
BIN
resources/sounds/noisy/任务结束.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/公告展示.wav
Normal file
BIN
resources/sounds/noisy/公告展示.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/公告通知.wav
Normal file
BIN
resources/sounds/noisy/公告通知.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/六星喜报.wav
Normal file
BIN
resources/sounds/noisy/六星喜报.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/历史记录查询.wav
Normal file
BIN
resources/sounds/noisy/历史记录查询.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/发生异常.wav
Normal file
BIN
resources/sounds/noisy/发生异常.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/发生错误.wav
Normal file
BIN
resources/sounds/noisy/发生错误.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/子任务失败.wav
Normal file
BIN
resources/sounds/noisy/子任务失败.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/排查录入.wav
Normal file
BIN
resources/sounds/noisy/排查录入.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/排查重试.wav
Normal file
BIN
resources/sounds/noisy/排查重试.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/无新版本.wav
Normal file
BIN
resources/sounds/noisy/无新版本.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/有新版本.wav
Normal file
BIN
resources/sounds/noisy/有新版本.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/森空岛签到失败.wav
Normal file
BIN
resources/sounds/noisy/森空岛签到失败.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/森空岛签到成功.wav
Normal file
BIN
resources/sounds/noisy/森空岛签到成功.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/任务开始.wav
Normal file
BIN
resources/sounds/simple/任务开始.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/任务结束.wav
Normal file
BIN
resources/sounds/simple/任务结束.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/公告展示.wav
Normal file
BIN
resources/sounds/simple/公告展示.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/公告通知.wav
Normal file
BIN
resources/sounds/simple/公告通知.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/历史记录查询.wav
Normal file
BIN
resources/sounds/simple/历史记录查询.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/发生异常.wav
Normal file
BIN
resources/sounds/simple/发生异常.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/发生错误.wav
Normal file
BIN
resources/sounds/simple/发生错误.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/无新版本.wav
Normal file
BIN
resources/sounds/simple/无新版本.wav
Normal file
Binary file not shown.
BIN
resources/sounds/simple/有新版本.wav
Normal file
BIN
resources/sounds/simple/有新版本.wav
Normal file
Binary file not shown.
@@ -1,30 +1,27 @@
|
||||
{
|
||||
"main_version": "4.3.8.2",
|
||||
"main_version": "4.3.10.3",
|
||||
"version_info": {
|
||||
"4.3.8.2": {
|
||||
"修复bug": [
|
||||
"日志分析忽略MAA超时提示"
|
||||
"4.3.10.3": {
|
||||
"程序优化": [
|
||||
"使用 keyboard 模块替代 pyautogui 模块"
|
||||
]
|
||||
},
|
||||
"4.3.8.1": {
|
||||
"4.3.10.2": {
|
||||
"新增功能": [
|
||||
"自定义基建显示配置名称 #46",
|
||||
"主调度台添加仅一次电源任务"
|
||||
"公招喜报模板优化",
|
||||
"支持使用命令行调用"
|
||||
],
|
||||
"修复bug": [
|
||||
"电源相关选项改为所有任务完成后生效",
|
||||
"适配MAAv5.16.3的ADB报错信息更改"
|
||||
"修复BUG": [
|
||||
"修复更新动作重复执行问题"
|
||||
],
|
||||
"程序优化": [
|
||||
"UI样式优化,进一步适配win10主题"
|
||||
"Mirror 酱链接添加`source`字段,用于标识来源",
|
||||
"优化下载器测速中止条件"
|
||||
]
|
||||
},
|
||||
"4.3.7.0": {
|
||||
"4.3.10.1": {
|
||||
"新增功能": [
|
||||
"下载器支持完整mirrorc列表"
|
||||
],
|
||||
"程序优化": [
|
||||
"重构更新逻辑,去除独立更新器"
|
||||
"森空岛签到功能上线"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user