Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
5e46040db6 | ||
|
|
f2b04dd0f6 | ||
|
|
2177c1b40e | ||
|
|
d1f4cffe8f | ||
|
|
74ce441b90 | ||
|
|
5893aa2426 | ||
|
|
cb7e7bf9d4 | ||
|
|
fbfdc6aa12 |
22
.github/workflows/build-app.yml
vendored
22
.github/workflows/build-app.yml
vendored
@@ -55,6 +55,8 @@ jobs:
|
||||
python -m pip install --upgrade pip
|
||||
pip install flake8 pytest
|
||||
pip install -r requirements.txt
|
||||
choco install innosetup
|
||||
echo "C:\Program Files (x86)\Inno Setup 6" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
@@ -71,8 +73,6 @@ jobs:
|
||||
run: |
|
||||
$MAIN_VERSION=(Get-Content -Path "version_info.txt" -TotalCount 1).Trim()
|
||||
"AUTO_MAA_version=$MAIN_VERSION" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
$UPDATER_VERSION=(Get-Content -Path "version_info.txt" -TotalCount 2 | Select-Object -Index 1).Trim()
|
||||
"updater_version=$UPDATER_VERSION" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -83,17 +83,6 @@ jobs:
|
||||
with:
|
||||
name: version_info
|
||||
path: version_info.txt
|
||||
- name: Compile Inno Setup Script
|
||||
run: |
|
||||
choco install innosetup
|
||||
echo "C:\Program Files (x86)\Inno Setup 6" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
Expand-Archive -Path AUTO_MAA_${{ env.AUTO_MAA_version }}.zip -DestinationPath AUTO_MAA -Force
|
||||
ISCC.exe "AUTO_MAA.iss"
|
||||
- name: Upload Setup Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AUTO_MAA_Setup
|
||||
path: AUTO_MAA-Setup.exe
|
||||
publish_release:
|
||||
name: Publish release
|
||||
needs: build_AUTO_MAA
|
||||
@@ -120,8 +109,11 @@ jobs:
|
||||
NAME="$(sed 's/\r$//g' <(head -n 1 version_info.txt))"
|
||||
TAGNAME="$(sed 's/\r$//g' <(head -n 1 version_info.txt))"
|
||||
NOTES_MAIN="$(sed 's/\r$//g' <(tail -n +3 version_info.txt))"
|
||||
NOTES_TAIL="\`\`\`本release通过GitHub Actions自动构建\`\`\`"
|
||||
NOTES="$NOTES_MAIN<br><br>$NOTES_TAIL"
|
||||
NOTES="$NOTES_MAIN
|
||||
|
||||
[已有 Mirror酱 CDK ?前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA)
|
||||
|
||||
\`\`\`本release通过GitHub Actions自动构建\`\`\`"
|
||||
if [ "${{ github.ref_name }}" == "main" ]; then
|
||||
PRERELEASE_FLAG=""
|
||||
else
|
||||
|
||||
13
README.md
13
README.md
@@ -22,6 +22,11 @@
|
||||
|
||||
本软件是明日方舟第三方软件`MAA`的第三方工具,即第3<sup>3</sup>方软件。旨在优化MAA多账号功能体验,并通过一些方法解决MAA项目未能解决的部分问题,提高代理的稳定性。
|
||||
|
||||
- **集中管理**:一站式管理多个MAA脚本与多个用户配置,和凌乱的散装脚本窗口说再见!
|
||||
- **无人值守**:自动处理MAA相关报错,再也不用为代理任务卡死时自己不在电脑旁烦恼啦!
|
||||
- **配置灵活**:通过调度队列与脚本的组合,自由实现您能想到的所有调度需求!
|
||||
- **信息统计**:自动统计用户的公招与关卡掉落物,看看这个月您的收益是多少!
|
||||
|
||||
### 原理
|
||||
|
||||
本软件可以存储多个明日方舟账号数据,并通过以下流程实现代理功能:
|
||||
@@ -32,11 +37,9 @@
|
||||
|
||||
### 优势
|
||||
|
||||
- **节省运行开销:** 只需要一份MAA软件与一个模拟器,无需多开就能完成多账号代理,羸弱的电脑也能代理日常。
|
||||
- **自定义空间大:** 依靠高级用户配置模式,支持MAA几乎所有设置选项自定义,支持模拟器多开。
|
||||
- **调度方法自由:** 通过调度队列功能,自由实现MAA多开等多种调度方式。
|
||||
- **一键代理无忧:** 无须中途手动修改MAA配置,将繁琐交给AUTO_MAA,把游戏留给自己。
|
||||
- **代理结果复核:** 通过人工排查功能核实各用户代理情况,堵住自动代理的最后一丝风险。
|
||||
- **高效稳定**:通过日志监测、异常处理等机制,保障代理任务顺利完成。
|
||||
- **简洁易用**:无需手动修改配置文件,实现自动化调度与多开管理。
|
||||
- **兼容扩展**:支持 MAA 几乎所有的配置选项,满足不同用户需求。
|
||||
|
||||
## 重要声明
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ from .core import QueueConfig, MaaConfig, MaaUserConfig, Task, TaskManager, Main
|
||||
from .models import MaaManager
|
||||
from .services import Notify, Crypto, System
|
||||
from .ui import AUTO_MAA
|
||||
from .utils import DownloadManager
|
||||
|
||||
__all__ = [
|
||||
"QueueConfig",
|
||||
@@ -47,5 +46,4 @@ __all__ = [
|
||||
"Crypto",
|
||||
"System",
|
||||
"AUTO_MAA",
|
||||
"DownloadManager",
|
||||
]
|
||||
|
||||
@@ -29,7 +29,7 @@ __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 .task_manager import Task, TaskManager
|
||||
@@ -40,6 +40,7 @@ __all__ = [
|
||||
"QueueConfig",
|
||||
"MaaConfig",
|
||||
"MaaUserConfig",
|
||||
"MaaPlanConfig",
|
||||
"MainInfoBar",
|
||||
"Network",
|
||||
"Task",
|
||||
|
||||
@@ -33,6 +33,7 @@ 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,7 +79,78 @@ class UrlListValidator(ConfigValidator):
|
||||
return list(set([_ for _ in urls if self.validate(_)]))
|
||||
|
||||
|
||||
class GlobalConfig(QConfig):
|
||||
class LQConfig(QConfig):
|
||||
"""局域配置类"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
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 GlobalConfig(LQConfig):
|
||||
"""全局配置"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -163,10 +235,6 @@ class GlobalConfig(QConfig):
|
||||
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()
|
||||
@@ -182,72 +250,8 @@ class GlobalConfig(QConfig):
|
||||
)
|
||||
self.update_MirrorChyanCDK = ConfigItem("Update", "MirrorChyanCDK", "")
|
||||
|
||||
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 QueueConfig(QConfig):
|
||||
class QueueConfig(LQConfig):
|
||||
"""队列配置"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -260,8 +264,10 @@ class QueueConfig(QConfig):
|
||||
self.queueSet_AfterAccomplish = OptionsConfigItem(
|
||||
"QueueSet",
|
||||
"AfterAccomplish",
|
||||
"None",
|
||||
OptionsValidator(["None", "KillSelf", "Sleep", "Hibernate", "Shutdown"]),
|
||||
"NoAction",
|
||||
OptionsValidator(
|
||||
["NoAction", "KillSelf", "Sleep", "Hibernate", "Shutdown"]
|
||||
),
|
||||
)
|
||||
|
||||
self.time_TimeEnabled_0 = ConfigItem(
|
||||
@@ -332,72 +338,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:
|
||||
@@ -415,6 +357,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)
|
||||
)
|
||||
@@ -431,72 +376,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:
|
||||
@@ -507,9 +388,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"])
|
||||
)
|
||||
@@ -535,8 +414,8 @@ 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", "-")
|
||||
@@ -555,78 +434,145 @@ 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.6.0"
|
||||
VERSION = "4.3.8.0"
|
||||
|
||||
gameid_refreshed = Signal()
|
||||
PASSWORD_refreshed = Signal()
|
||||
user_info_changed = Signal()
|
||||
power_sign_changed = Signal()
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
@@ -645,10 +591,18 @@ 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
|
||||
|
||||
@@ -700,7 +654,7 @@ class AppConfig(GlobalConfig):
|
||||
# 从MAA服务器获取活动关卡信息
|
||||
Network.set_info(
|
||||
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()
|
||||
@@ -712,7 +666,7 @@ class AppConfig(GlobalConfig):
|
||||
logger.warning(f"无法从MAA服务器获取活动关卡信息:{Network.error_message}")
|
||||
gameid_infos = []
|
||||
|
||||
gameid_dict = {"value": [], "text": []}
|
||||
ss_gameid_dict = {"value": [], "text": []}
|
||||
|
||||
for gameid_info in gameid_infos:
|
||||
|
||||
@@ -725,53 +679,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]},
|
||||
@@ -795,12 +707,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()
|
||||
|
||||
@@ -1231,6 +1151,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):
|
||||
"""搜索所有调度队列实例"""
|
||||
|
||||
@@ -1278,6 +1220,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:
|
||||
@@ -1310,6 +1262,12 @@ class AppConfig(GlobalConfig):
|
||||
|
||||
self.user_info_changed.emit()
|
||||
|
||||
def set_power_sign(self, sign: str) -> None:
|
||||
"""设置当前电源状态"""
|
||||
|
||||
self.power_sign = sign
|
||||
self.power_sign_changed.emit()
|
||||
|
||||
def save_history(self, key: str, content: dict) -> None:
|
||||
"""保存历史记录"""
|
||||
|
||||
|
||||
@@ -35,23 +35,30 @@ from .config import Config
|
||||
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 +68,16 @@ 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)
|
||||
|
||||
|
||||
MainInfoBar = _MainInfoBar()
|
||||
|
||||
@@ -265,28 +265,14 @@ class _TaskManager(QObject):
|
||||
Config.queue_dict[name]["Config"].get(
|
||||
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
|
||||
)
|
||||
!= "None"
|
||||
!= "NoAction"
|
||||
and Config.power_sign == "NoAction"
|
||||
):
|
||||
|
||||
from app.ui import ProgressRingMessageBox
|
||||
|
||||
mode_book = {
|
||||
"Shutdown": "关机",
|
||||
"Hibernate": "休眠",
|
||||
"Sleep": "睡眠",
|
||||
"KillSelf": "关闭AUTO_MAA",
|
||||
}
|
||||
|
||||
choice = ProgressRingMessageBox(
|
||||
Config.main_window,
|
||||
f"{mode_book[Config.queue_dict[name]["Config"].get(Config.queue_dict[name]["Config"].queueSet_AfterAccomplish)]}倒计时",
|
||||
)
|
||||
if choice.exec():
|
||||
System.set_power(
|
||||
Config.queue_dict[name]["Config"].get(
|
||||
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
|
||||
)
|
||||
Config.set_power_sign(
|
||||
Config.queue_dict[name]["Config"].get(
|
||||
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
|
||||
)
|
||||
)
|
||||
|
||||
def check_maa_version(self, v: str):
|
||||
"""检查MAA版本"""
|
||||
|
||||
@@ -29,6 +29,7 @@ from loguru import logger
|
||||
from PySide6.QtWidgets import QWidget
|
||||
from PySide6.QtCore import QTimer
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import pyautogui
|
||||
|
||||
from .config import Config
|
||||
@@ -46,6 +47,7 @@ class _MainTimer(QWidget):
|
||||
self.Timer = QTimer()
|
||||
self.Timer.timeout.connect(self.timed_start)
|
||||
self.Timer.timeout.connect(self.set_silence)
|
||||
self.Timer.timeout.connect(self.check_power)
|
||||
self.Timer.start(1000)
|
||||
self.LongTimer = QTimer()
|
||||
self.LongTimer.timeout.connect(self.long_timed_task)
|
||||
@@ -96,6 +98,16 @@ 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
|
||||
@@ -113,5 +125,27 @@ class _MainTimer(QWidget):
|
||||
logger.warning(f"FailSafeException: {e}")
|
||||
self.if_FailSafeException = True
|
||||
|
||||
def check_power(self):
|
||||
|
||||
if Config.power_sign != "NoAction" and not Config.running_list:
|
||||
|
||||
from app.ui import ProgressRingMessageBox
|
||||
|
||||
mode_book = {
|
||||
"KillSelf": "退出软件",
|
||||
"Sleep": "睡眠",
|
||||
"Hibernate": "休眠",
|
||||
"Shutdown": "关机",
|
||||
}
|
||||
|
||||
choice = ProgressRingMessageBox(
|
||||
Config.main_window, f"{mode_book[Config.power_sign]}倒计时"
|
||||
)
|
||||
if choice.exec():
|
||||
System.set_power(Config.power_sign)
|
||||
Config.set_power_sign("NoAction")
|
||||
else:
|
||||
Config.set_power_sign("NoAction")
|
||||
|
||||
|
||||
MainTimer = _MainTimer()
|
||||
|
||||
@@ -102,6 +102,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 +117,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")
|
||||
|
||||
@@ -266,9 +274,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 +302,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 +396,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"]
|
||||
)
|
||||
@@ -414,6 +430,7 @@ class MaaManager(QObject):
|
||||
[self.ADB_path, "disconnect", self.ADB_address],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
logger.info(f"{self.name} | 释放ADB:{self.ADB_address}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
# 忽略错误,因为可能本来就没有连接
|
||||
logger.warning(f"{self.name} | 释放ADB时出现异常:{e}")
|
||||
@@ -432,6 +449,9 @@ class MaaManager(QObject):
|
||||
[self.emulator_path, *self.emulator_arguments],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
logger.info(
|
||||
f"{self.name} | 启动模拟器:{self.emulator_path},参数:{self.emulator_arguments}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.name} | 启动模拟器时出现异常:{e}")
|
||||
self.push_info_bar.emit(
|
||||
@@ -446,6 +466,8 @@ class MaaManager(QObject):
|
||||
# 添加静默进程标记
|
||||
Config.silence_list.append(self.emulator_path)
|
||||
|
||||
self.search_ADB_address()
|
||||
|
||||
# 创建MAA任务
|
||||
maa = subprocess.Popen(
|
||||
[self.maa_exe_path],
|
||||
@@ -517,6 +539,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"]
|
||||
@@ -541,15 +569,13 @@ class MaaManager(QObject):
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
# 移除静默进程标记
|
||||
Config.silence_list.remove(self.emulator_path)
|
||||
|
||||
# 任务结束后释放ADB
|
||||
try:
|
||||
subprocess.run(
|
||||
[self.ADB_path, "disconnect", self.ADB_address],
|
||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
logger.info(f"{self.name} | 释放ADB:{self.ADB_address}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
# 忽略错误,因为可能本来就没有连接
|
||||
logger.warning(f"{self.name} | 释放ADB时出现异常:{e}")
|
||||
@@ -567,6 +593,33 @@ class MaaManager(QObject):
|
||||
self.emulator_process.wait()
|
||||
self.if_open_emulator = True
|
||||
|
||||
# 记录剿灭情况
|
||||
if (
|
||||
mode == "Annihilation"
|
||||
and self.weekly_annihilation_limit_reached
|
||||
):
|
||||
user_data["Data"]["LastAnnihilationDate"] = curdate
|
||||
# 保存运行日志以及统计信息
|
||||
if_six_star = Config.save_maa_log(
|
||||
Config.app_path
|
||||
/ f"history/{curdate}/{user_data["Info"]["Name"]}/{start_time.strftime("%H-%M-%S")}.log",
|
||||
self.check_maa_log(start_time, mode_book[mode]),
|
||||
self.maa_result,
|
||||
)
|
||||
user_logs_list.append(
|
||||
Config.app_path
|
||||
/ f"history/{curdate}/{user_data["Info"]["Name"]}/{start_time.strftime("%H-%M-%S")}.json",
|
||||
)
|
||||
if if_six_star:
|
||||
self.push_notification(
|
||||
"公招六星",
|
||||
f"喜报:用户 {user[0]} 公招出六星啦!",
|
||||
{
|
||||
"user_name": user_data["Info"]["Name"],
|
||||
},
|
||||
user_data,
|
||||
)
|
||||
|
||||
# 执行MAA解压更新动作
|
||||
if self.maa_update_package:
|
||||
|
||||
@@ -590,50 +643,23 @@ class MaaManager(QObject):
|
||||
|
||||
logger.info(f"{self.name} | 更新动作结束")
|
||||
|
||||
# 记录剿灭情况
|
||||
if (
|
||||
mode == "Annihilation"
|
||||
and self.weekly_annihilation_limit_reached
|
||||
):
|
||||
user_data["Data"]["LastAnnihilationDate"] = curdate
|
||||
# 保存运行日志以及统计信息
|
||||
if_six_star = Config.save_maa_log(
|
||||
Config.app_path
|
||||
/ f"history/{curdate}/{user_data["Info"]["Name"]}/{start_time.strftime("%H-%M-%S")}.log",
|
||||
self.check_maa_log(start_time, mode_book[mode]),
|
||||
self.maa_result,
|
||||
)
|
||||
user_logs_list.append(
|
||||
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:
|
||||
|
||||
self.push_notification(
|
||||
"公招六星",
|
||||
f"喜报:用户 {user[0]} 公招出六星啦!",
|
||||
{"user_name": user_data["Info"]["Name"]},
|
||||
)
|
||||
|
||||
if Config.get(Config.notify_IfSendStatistic):
|
||||
|
||||
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
|
||||
)
|
||||
# 发送统计信息
|
||||
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"]:
|
||||
# 成功完成代理的用户修改相关参数
|
||||
@@ -804,9 +830,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 +852,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 +870,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()
|
||||
@@ -866,6 +895,86 @@ class MaaManager(QObject):
|
||||
def __capture_response(self, response: bool) -> None:
|
||||
self.response = response
|
||||
|
||||
def search_ADB_address(self) -> None:
|
||||
"""搜索ADB实际地址"""
|
||||
|
||||
self.update_log_text.emit(
|
||||
f"即将搜索ADB实际地址\n正在等待模拟器完成启动\n请等待{self.wait_time}s"
|
||||
)
|
||||
|
||||
for _ in range(self.wait_time):
|
||||
if self.isInterruptionRequested:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
# 移除静默进程标记
|
||||
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,
|
||||
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,
|
||||
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)
|
||||
|
||||
return None
|
||||
|
||||
else:
|
||||
logger.info(f"{self.name} | 无法连接到ADB地址:{ADB_address}")
|
||||
else:
|
||||
logger.info(f"{self.name} | 无法连接到ADB地址:{ADB_address}")
|
||||
|
||||
def refresh_maa_log(self) -> None:
|
||||
"""刷新MAA日志"""
|
||||
|
||||
@@ -923,6 +1032,8 @@ class MaaManager(QObject):
|
||||
latest_time = start_time
|
||||
for _ in logs[::-1]:
|
||||
try:
|
||||
if "如果长时间无进一步日志更新,可能需要手动干预。" in _:
|
||||
continue
|
||||
latest_time = datetime.strptime(_[1:20], "%Y-%m-%d %H:%M:%S")
|
||||
break
|
||||
except ValueError:
|
||||
@@ -965,7 +1076,7 @@ class MaaManager(QObject):
|
||||
else:
|
||||
self.maa_result = "MAA部分任务执行失败"
|
||||
|
||||
elif "请「检查连接设置」或「尝试重启模拟器与 ADB」或「重启电脑」" in log:
|
||||
elif "请 「检查连接设置」 → 「尝试重启模拟器与 ADB」 → 「重启电脑」" in log:
|
||||
self.maa_result = "MAA的ADB连接异常"
|
||||
|
||||
elif "未检测到任何模拟器" in log:
|
||||
@@ -991,7 +1102,7 @@ class MaaManager(QObject):
|
||||
elif mode == "人工排查":
|
||||
if "完成任务: StartUp" in log:
|
||||
self.maa_result = "Success!"
|
||||
elif "请「检查连接设置」或「尝试重启模拟器与 ADB」或「重启电脑」" in log:
|
||||
elif "请 「检查连接设置」 → 「尝试重启模拟器与 ADB」 → 「重启电脑」" in log:
|
||||
self.maa_result = "MAA的ADB连接异常"
|
||||
elif "未检测到任何模拟器" in log:
|
||||
self.maa_result = "MAA未检测到任何模拟器"
|
||||
@@ -1166,6 +1277,9 @@ class MaaManager(QObject):
|
||||
]["Id"]
|
||||
|
||||
# 按预设设定任务
|
||||
data["Configurations"]["Default"][
|
||||
"TaskQueue.WakeUp.IsChecked"
|
||||
] = "True" # 开始唤醒
|
||||
data["Configurations"]["Default"]["TaskQueue.Recruiting.IsChecked"] = (
|
||||
self.task_dict["Recruiting"]
|
||||
) # 自动公招
|
||||
@@ -1190,10 +1304,6 @@ class MaaManager(QObject):
|
||||
|
||||
if user_data["Info"]["Mode"] == "简洁":
|
||||
|
||||
data["Configurations"]["Default"][
|
||||
"TaskQueue.WakeUp.IsChecked"
|
||||
] = "True" # 开始唤醒
|
||||
|
||||
data["Configurations"]["Default"]["Start.ClientType"] = user_data[
|
||||
"Info"
|
||||
][
|
||||
@@ -1292,7 +1402,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小时内过期的理智药
|
||||
@@ -1390,7 +1502,9 @@ class MaaManager(QObject):
|
||||
] = "True" # 备选关卡
|
||||
data["Configurations"]["Default"][
|
||||
"Fight.UseRemainingSanityStage"
|
||||
] = "True" # 使用剩余理智
|
||||
] = (
|
||||
"True" if user_data["Info"]["GameId_Remain"] != "-" else "False"
|
||||
) # 使用剩余理智
|
||||
|
||||
# 基建模式
|
||||
if (
|
||||
@@ -1416,12 +1530,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":
|
||||
@@ -1437,15 +1559,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"
|
||||
][
|
||||
@@ -1492,6 +1605,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"][
|
||||
@@ -1500,15 +1622,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" # 开始唤醒
|
||||
@@ -1633,16 +1746,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"
|
||||
@@ -1654,9 +1772,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"])
|
||||
@@ -1664,11 +1779,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 == "统计信息":
|
||||
|
||||
@@ -1697,18 +1832,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
|
||||
|
||||
@@ -25,18 +25,20 @@ 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 Signal
|
||||
from PySide6.QtWidgets import QWidget
|
||||
from loguru import logger
|
||||
from plyer import notification
|
||||
|
||||
from app.core import Config
|
||||
from app.services.security import Crypto
|
||||
|
||||
@@ -65,218 +67,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 +288,7 @@ class Notification(QWidget):
|
||||
"文本",
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_ToAddress),
|
||||
)
|
||||
|
||||
# 发送Server酱通知
|
||||
@@ -301,6 +296,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 +306,7 @@ class Notification(QWidget):
|
||||
self.CompanyWebHookBotPush(
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_CompanyWebHookBotUrl),
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@@ -26,7 +26,7 @@ v4.3
|
||||
"""
|
||||
|
||||
from loguru import logger
|
||||
from PySide6.QtWidgets import QApplication, QWidget
|
||||
from PySide6.QtWidgets import QApplication
|
||||
import sys
|
||||
import ctypes
|
||||
import win32gui
|
||||
@@ -87,7 +87,7 @@ class _SystemHandler:
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
|
||||
if mode == "None":
|
||||
if mode == "NoAction":
|
||||
|
||||
logger.info("不执行系统电源操作")
|
||||
|
||||
@@ -115,7 +115,7 @@ class _SystemHandler:
|
||||
|
||||
elif sys.platform.startswith("linux"):
|
||||
|
||||
if mode == "None":
|
||||
if mode == "NoAction":
|
||||
|
||||
logger.info("不执行系统电源操作")
|
||||
|
||||
|
||||
822
app/ui/Widget.py
822
app/ui/Widget.py
@@ -25,17 +25,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,
|
||||
@@ -71,15 +78,13 @@ from qfluentwidgets import (
|
||||
SwitchButton,
|
||||
IndicatorPosition,
|
||||
Slider,
|
||||
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
|
||||
@@ -105,6 +110,8 @@ class LineEditMessageBox(MessageBoxBase):
|
||||
self.viewLayout.addWidget(self.title)
|
||||
self.viewLayout.addWidget(self.input)
|
||||
|
||||
self.input.setFocus()
|
||||
|
||||
|
||||
class ComboBoxMessageBox(MessageBoxBase):
|
||||
"""选择对话框"""
|
||||
@@ -268,6 +275,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"""
|
||||
|
||||
@@ -428,24 +470,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):
|
||||
@@ -474,35 +519,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":
|
||||
@@ -518,59 +557,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):
|
||||
@@ -721,7 +708,7 @@ class NoOptionComboBoxSettingCard(SettingCard):
|
||||
value: List[str],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
configItem: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
@@ -754,7 +741,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):
|
||||
@@ -776,7 +763,7 @@ class EditableComboBoxSettingCard(SettingCard):
|
||||
value: List[str],
|
||||
texts: List[str],
|
||||
qconfig: QConfig,
|
||||
configItem: OptionsConfigItem,
|
||||
configItem: ConfigItem,
|
||||
parent=None,
|
||||
):
|
||||
|
||||
@@ -826,7 +813,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):
|
||||
@@ -864,6 +851,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)
|
||||
@@ -930,6 +1080,418 @@ 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 "本周剿灭未完成"
|
||||
)
|
||||
|
||||
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):
|
||||
@@ -1049,9 +1611,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)
|
||||
@@ -1128,6 +1688,84 @@ class QuantifiedItemCard(CardWidget):
|
||||
self.Layout.addWidget(self.Numb)
|
||||
|
||||
|
||||
class PivotArea(ScrollArea):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
# 创建中间容器并设置布局
|
||||
self.center_container = QWidget()
|
||||
self.center_layout = QHBoxLayout(self.center_container)
|
||||
self.center_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.center_layout.setSpacing(0)
|
||||
self.center_container.setStyleSheet("background: transparent; border: none;")
|
||||
self.center_container.setFixedHeight(45)
|
||||
|
||||
self.pivot = self._Pivot(self)
|
||||
self.pivot.ItemNumbChanged.connect(
|
||||
lambda: QTimer.singleShot(
|
||||
100,
|
||||
lambda: (
|
||||
self.center_container.setFixedWidth(
|
||||
max(self.width() - 2, self.pivot.width())
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
self.center_layout.addStretch(1)
|
||||
self.center_layout.addWidget(self.pivot)
|
||||
self.center_layout.addStretch(1)
|
||||
|
||||
self.setWidgetResizable(False)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.viewport().setCursor(Qt.ArrowCursor)
|
||||
self.setStyleSheet("background: transparent; border: none;")
|
||||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.setWidget(self.center_container)
|
||||
|
||||
def wheelEvent(self, event):
|
||||
scroll_bar = self.horizontalScrollBar()
|
||||
if scroll_bar.maximum() > 0:
|
||||
delta = event.angleDelta().y()
|
||||
scroll_bar.setValue(scroll_bar.value() - delta // 15)
|
||||
event.ignore()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
|
||||
self.center_container.setFixedWidth(max(self.width() - 2, self.pivot.width()))
|
||||
QTimer.singleShot(
|
||||
100,
|
||||
lambda: (
|
||||
self.center_container.setFixedWidth(
|
||||
max(self.width() - 2, self.pivot.width())
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
class _Pivot(Pivot):
|
||||
|
||||
ItemNumbChanged = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
def insertWidget(
|
||||
self, index: int, routeKey: str, widget: PivotItem, onClick=None
|
||||
):
|
||||
super().insertWidget(index, routeKey, widget, onClick)
|
||||
self.ItemNumbChanged.emit()
|
||||
|
||||
def removeWidget(self, routeKey: str):
|
||||
super().removeWidget(routeKey)
|
||||
self.ItemNumbChanged.emit()
|
||||
|
||||
def clear(self):
|
||||
super().clear()
|
||||
self.ItemNumbChanged.emit()
|
||||
|
||||
|
||||
class QuickExpandGroupCard(ExpandGroupSettingCard):
|
||||
"""全局配置"""
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ from PySide6.QtWidgets import (
|
||||
QHBoxLayout,
|
||||
)
|
||||
from qfluentwidgets import (
|
||||
BodyLabel,
|
||||
CardWidget,
|
||||
Pivot,
|
||||
ScrollArea,
|
||||
FluentIcon,
|
||||
HeaderCardWidget,
|
||||
@@ -44,13 +44,12 @@ from qfluentwidgets import (
|
||||
SubtitleLabel,
|
||||
PushButton,
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QTextCursor
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
from app.core import Config, TaskManager, Task, MainInfoBar
|
||||
from .Widget import StatefulItemCard, ComboBoxMessageBox
|
||||
from .Widget import StatefulItemCard, ComboBoxMessageBox, PivotArea
|
||||
|
||||
|
||||
class DispatchCenter(QWidget):
|
||||
@@ -60,13 +59,29 @@ class DispatchCenter(QWidget):
|
||||
|
||||
self.setObjectName("调度中枢")
|
||||
|
||||
self.pivot = Pivot(self)
|
||||
self.multi_button = PushButton(FluentIcon.ADD, "添加任务", self)
|
||||
self.multi_button.setToolTip("添加任务")
|
||||
self.multi_button.clicked.connect(self.start_multi_task)
|
||||
|
||||
self.power_combox = ComboBox()
|
||||
self.power_combox.addItem("无动作", userData="NoAction")
|
||||
self.power_combox.addItem("退出软件", userData="KillSelf")
|
||||
self.power_combox.addItem("睡眠", userData="Sleep")
|
||||
self.power_combox.addItem("休眠", userData="Hibernate")
|
||||
self.power_combox.addItem("关机", userData="Shutdown")
|
||||
self.power_combox.setCurrentText("无动作")
|
||||
self.power_combox.currentIndexChanged.connect(self.set_power_sign)
|
||||
|
||||
self.pivotArea = PivotArea(self)
|
||||
self.pivot = self.pivotArea.pivot
|
||||
|
||||
self.stackedWidget = QStackedWidget(self)
|
||||
self.Layout = QVBoxLayout(self)
|
||||
self.stackedWidget.setContentsMargins(0, 0, 0, 0)
|
||||
self.stackedWidget.setStyleSheet("background: transparent; border: none;")
|
||||
|
||||
self.script_list: Dict[str, DispatchBox] = {}
|
||||
self.script_list: Dict[str, DispatchCenter.DispatchBox] = {}
|
||||
|
||||
dispatch_box = DispatchBox("主调度台", self)
|
||||
dispatch_box = self.DispatchBox("主调度台", self)
|
||||
self.script_list["主调度台"] = dispatch_box
|
||||
self.stackedWidget.addWidget(self.script_list["主调度台"])
|
||||
self.pivot.addItem(
|
||||
@@ -76,7 +91,15 @@ class DispatchCenter(QWidget):
|
||||
icon=FluentIcon.CAFE,
|
||||
)
|
||||
|
||||
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
|
||||
h_layout = QHBoxLayout()
|
||||
h_layout.addWidget(self.multi_button)
|
||||
h_layout.addWidget(self.pivotArea)
|
||||
h_layout.addWidget(BodyLabel("全部完成后", self))
|
||||
h_layout.addWidget(self.power_combox)
|
||||
h_layout.setContentsMargins(11, 5, 11, 0)
|
||||
|
||||
self.Layout = QVBoxLayout(self)
|
||||
self.Layout.addLayout(h_layout)
|
||||
self.Layout.addWidget(self.stackedWidget)
|
||||
self.Layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@@ -87,7 +110,7 @@ class DispatchCenter(QWidget):
|
||||
def add_board(self, task: Task) -> None:
|
||||
"""添加一个调度台界面"""
|
||||
|
||||
dispatch_box = DispatchBox(task.name, self)
|
||||
dispatch_box = self.DispatchBox(task.name, self)
|
||||
|
||||
dispatch_box.top_bar.main_button.clicked.connect(
|
||||
lambda: TaskManager.stop_task(task.name)
|
||||
@@ -123,7 +146,6 @@ class DispatchCenter(QWidget):
|
||||
self.script_list["主调度台"].top_bar.Lable.show()
|
||||
self.script_list["主调度台"].top_bar.object.hide()
|
||||
self.script_list["主调度台"].top_bar.mode.hide()
|
||||
self.script_list["主调度台"].top_bar.multi_button.show()
|
||||
self.script_list["主调度台"].top_bar.main_button.clicked.disconnect()
|
||||
self.script_list["主调度台"].top_bar.main_button.setText("中止任务")
|
||||
self.script_list["主调度台"].top_bar.main_button.clicked.connect(
|
||||
@@ -154,7 +176,6 @@ class DispatchCenter(QWidget):
|
||||
self.script_list["主调度台"].top_bar.Lable.hide()
|
||||
self.script_list["主调度台"].top_bar.object.show()
|
||||
self.script_list["主调度台"].top_bar.mode.show()
|
||||
self.script_list["主调度台"].top_bar.multi_button.hide()
|
||||
self.script_list["主调度台"].top_bar.main_button.clicked.disconnect()
|
||||
self.script_list["主调度台"].top_bar.main_button.setText("开始任务")
|
||||
self.script_list["主调度台"].top_bar.main_button.clicked.connect(
|
||||
@@ -208,301 +229,323 @@ class DispatchCenter(QWidget):
|
||||
self.script_list["主调度台"].top_bar.mode.addItems(["自动代理", "人工排查"])
|
||||
self.script_list["主调度台"].top_bar.mode.setCurrentIndex(0)
|
||||
|
||||
def update_power_sign(self) -> None:
|
||||
"""更新电源设置"""
|
||||
|
||||
class DispatchBox(QWidget):
|
||||
mode_book = {
|
||||
"NoAction": "无动作",
|
||||
"KillSelf": "退出软件",
|
||||
"Sleep": "睡眠",
|
||||
"Hibernate": "休眠",
|
||||
"Shutdown": "关机",
|
||||
}
|
||||
self.power_combox.currentIndexChanged.disconnect()
|
||||
self.power_combox.setCurrentText(mode_book[Config.power_sign])
|
||||
self.power_combox.currentIndexChanged.connect(self.set_power_sign)
|
||||
|
||||
def __init__(self, name: str, parent=None):
|
||||
super().__init__(parent)
|
||||
def set_power_sign(self) -> None:
|
||||
"""设置所有任务完成后动作"""
|
||||
|
||||
self.setObjectName(name)
|
||||
if not Config.running_list:
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
|
||||
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.addWidget(scrollArea)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
class DispatchTopBar(CardWidget):
|
||||
|
||||
def __init__(self, parent=None, name: str = None):
|
||||
super().__init__(parent)
|
||||
|
||||
Layout = QHBoxLayout(self)
|
||||
|
||||
if name == "主调度台":
|
||||
|
||||
self.Lable = SubtitleLabel("", self)
|
||||
self.Lable.hide()
|
||||
self.object = ComboBox()
|
||||
self.object.setPlaceholderText("请选择调度对象")
|
||||
self.mode = ComboBox()
|
||||
self.mode.setPlaceholderText("请选择调度模式")
|
||||
|
||||
self.multi_button = PushButton("添加任务")
|
||||
self.multi_button.clicked.connect(self.start_multi_task)
|
||||
self.main_button = PushButton("开始任务")
|
||||
self.main_button.clicked.connect(self.start_main_task)
|
||||
self.multi_button.hide()
|
||||
|
||||
Layout.addWidget(self.Lable)
|
||||
Layout.addWidget(self.object)
|
||||
Layout.addWidget(self.mode)
|
||||
Layout.addStretch(1)
|
||||
Layout.addWidget(self.multi_button)
|
||||
Layout.addWidget(self.main_button)
|
||||
|
||||
else:
|
||||
|
||||
self.Lable = SubtitleLabel(name, self)
|
||||
self.main_button = PushButton("中止任务")
|
||||
|
||||
Layout.addWidget(self.Lable)
|
||||
Layout.addStretch(1)
|
||||
Layout.addWidget(self.main_button)
|
||||
|
||||
def start_main_task(self):
|
||||
"""开始任务"""
|
||||
|
||||
if self.object.currentIndex() == -1:
|
||||
logger.warning("未选择调度对象")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择调度对象", "请选择后再开始任务", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if self.mode.currentIndex() == -1:
|
||||
logger.warning("未选择调度模式")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择调度模式", "请选择后再开始任务", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if self.object.currentData() in Config.running_list:
|
||||
logger.warning(f"任务已存在:{self.object.currentData()}")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "任务已存在", self.object.currentData(), 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if "调度队列" in self.object.currentData():
|
||||
|
||||
logger.info(f"用户添加任务:{self.object.currentData()}")
|
||||
TaskManager.add_task(
|
||||
f"{self.mode.currentText()}_主调度台",
|
||||
self.object.currentData(),
|
||||
Config.queue_dict[self.object.currentData()]["Config"].toDict(),
|
||||
)
|
||||
|
||||
elif "脚本" in self.object.currentData():
|
||||
|
||||
if Config.member_dict[self.object.currentData()]["Type"] == "Maa":
|
||||
|
||||
logger.info(f"用户添加任务:{self.object.currentData()}")
|
||||
TaskManager.add_task(
|
||||
f"{self.mode.currentText()}_主调度台",
|
||||
"自定义队列",
|
||||
{"Queue": {"Member_1": self.object.currentData()}},
|
||||
)
|
||||
|
||||
def start_multi_task(self):
|
||||
"""开始任务"""
|
||||
|
||||
# 获取所有可用的队列和实例
|
||||
text_list = []
|
||||
data_list = []
|
||||
for name, info in Config.queue_dict.items():
|
||||
if name in Config.running_list:
|
||||
continue
|
||||
text_list.append(
|
||||
"队列"
|
||||
if info["Config"].get(info["Config"].queueSet_Name) == ""
|
||||
else f"队列 - {info["Config"].get(info["Config"].queueSet_Name)}"
|
||||
)
|
||||
data_list.append(name)
|
||||
|
||||
for name, info in Config.member_dict.items():
|
||||
if name in Config.running_list:
|
||||
continue
|
||||
text_list.append(
|
||||
f"实例 - {info['Type']}"
|
||||
if info["Config"].get(info["Config"].MaaSet_Name) == ""
|
||||
else f"实例 - {info['Type']} - {info["Config"].get(info["Config"].MaaSet_Name)}"
|
||||
)
|
||||
data_list.append(name)
|
||||
|
||||
choice = ComboBoxMessageBox(
|
||||
self.window(),
|
||||
"选择一个对象以添加相应多开任务",
|
||||
["选择调度对象"],
|
||||
[text_list],
|
||||
[data_list],
|
||||
self.power_combox.currentIndexChanged.disconnect()
|
||||
self.power_combox.setCurrentText("无动作")
|
||||
self.power_combox.currentIndexChanged.connect(self.set_power_sign)
|
||||
logger.warning("没有正在运行的任务,无法设置任务完成后动作")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning",
|
||||
"没有正在运行的任务",
|
||||
"无法设置任务完成后动作",
|
||||
5000,
|
||||
)
|
||||
|
||||
if choice.exec() and choice.input[0].currentIndex() != -1:
|
||||
else:
|
||||
|
||||
if choice.input[0].currentData() in Config.running_list:
|
||||
logger.warning(f"任务已存在:{choice.input[0].currentData()}")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "任务已存在", choice.input[0].currentData(), 5000
|
||||
)
|
||||
return None
|
||||
Config.set_power_sign(self.power_combox.currentData())
|
||||
|
||||
if "调度队列" in choice.input[0].currentData():
|
||||
def start_multi_task(self) -> None:
|
||||
"""开始任务"""
|
||||
|
||||
# 获取所有可用的队列和实例
|
||||
text_list = []
|
||||
data_list = []
|
||||
for name, info in Config.queue_dict.items():
|
||||
if name in Config.running_list:
|
||||
continue
|
||||
text_list.append(
|
||||
"队列"
|
||||
if info["Config"].get(info["Config"].queueSet_Name) == ""
|
||||
else f"队列 - {info["Config"].get(info["Config"].queueSet_Name)}"
|
||||
)
|
||||
data_list.append(name)
|
||||
|
||||
for name, info in Config.member_dict.items():
|
||||
if name in Config.running_list:
|
||||
continue
|
||||
text_list.append(
|
||||
f"实例 - {info['Type']}"
|
||||
if info["Config"].get(info["Config"].MaaSet_Name) == ""
|
||||
else f"实例 - {info['Type']} - {info["Config"].get(info["Config"].MaaSet_Name)}"
|
||||
)
|
||||
data_list.append(name)
|
||||
|
||||
choice = ComboBoxMessageBox(
|
||||
self.window(),
|
||||
"选择一个对象以添加相应多开任务",
|
||||
["选择调度对象"],
|
||||
[text_list],
|
||||
[data_list],
|
||||
)
|
||||
|
||||
if choice.exec() and choice.input[0].currentIndex() != -1:
|
||||
|
||||
if choice.input[0].currentData() in Config.running_list:
|
||||
logger.warning(f"任务已存在:{choice.input[0].currentData()}")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "任务已存在", choice.input[0].currentData(), 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if "调度队列" in choice.input[0].currentData():
|
||||
|
||||
logger.info(f"用户添加任务:{choice.input[0].currentData()}")
|
||||
TaskManager.add_task(
|
||||
"自动代理_新调度台",
|
||||
choice.input[0].currentData(),
|
||||
Config.queue_dict[choice.input[0].currentData()]["Config"].toDict(),
|
||||
)
|
||||
|
||||
elif "脚本" in choice.input[0].currentData():
|
||||
|
||||
if Config.member_dict[choice.input[0].currentData()]["Type"] == "Maa":
|
||||
|
||||
logger.info(f"用户添加任务:{choice.input[0].currentData()}")
|
||||
TaskManager.add_task(
|
||||
"自动代理_新调度台",
|
||||
choice.input[0].currentData(),
|
||||
Config.queue_dict[choice.input[0].currentData()][
|
||||
"Config"
|
||||
].toDict(),
|
||||
f"自定义队列 - {choice.input[0].currentData()}",
|
||||
{"Queue": {"Member_1": choice.input[0].currentData()}},
|
||||
)
|
||||
|
||||
elif "脚本" in choice.input[0].currentData():
|
||||
class DispatchBox(QWidget):
|
||||
|
||||
if (
|
||||
Config.member_dict[choice.input[0].currentData()]["Type"]
|
||||
== "Maa"
|
||||
):
|
||||
|
||||
logger.info(f"用户添加任务:{choice.input[0].currentData()}")
|
||||
TaskManager.add_task(
|
||||
"自动代理_新调度台",
|
||||
f"自定义队列 - {choice.input[0].currentData()}",
|
||||
{"Queue": {"Member_1": choice.input[0].currentData()}},
|
||||
)
|
||||
|
||||
class DispatchInfoCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
def __init__(self, name: str, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setTitle("调度信息")
|
||||
self.setObjectName(name)
|
||||
|
||||
self.task = self.TaskInfoCard(self)
|
||||
self.user = self.UserInfoCard(self)
|
||||
self.log_text = self.LogCard(self)
|
||||
self.top_bar = self.DispatchTopBar(self, name)
|
||||
self.info = self.DispatchInfoCard(self)
|
||||
|
||||
self.viewLayout.addWidget(self.task)
|
||||
self.viewLayout.addWidget(self.user)
|
||||
self.viewLayout.addWidget(self.log_text)
|
||||
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)
|
||||
|
||||
self.viewLayout.setStretch(0, 1)
|
||||
self.viewLayout.setStretch(1, 1)
|
||||
self.viewLayout.setStretch(2, 5)
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
scrollArea.setWidget(content_widget)
|
||||
|
||||
def update_board(self, task_list: list, user_list: list, log: str):
|
||||
"""更新调度信息"""
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(scrollArea)
|
||||
|
||||
self.task.update_task(task_list)
|
||||
self.user.update_user(user_list)
|
||||
self.log_text.text.setText(log)
|
||||
class DispatchTopBar(CardWidget):
|
||||
|
||||
class TaskInfoCard(HeaderCardWidget):
|
||||
def __init__(self, parent=None, name: str = None):
|
||||
super().__init__(parent)
|
||||
|
||||
Layout = QHBoxLayout(self)
|
||||
|
||||
if name == "主调度台":
|
||||
|
||||
self.Lable = SubtitleLabel("", self)
|
||||
self.Lable.hide()
|
||||
self.object = ComboBox()
|
||||
self.object.setPlaceholderText("请选择调度对象")
|
||||
self.mode = ComboBox()
|
||||
self.mode.setPlaceholderText("请选择调度模式")
|
||||
|
||||
self.main_button = PushButton("开始任务")
|
||||
self.main_button.clicked.connect(self.start_main_task)
|
||||
|
||||
Layout.addWidget(self.Lable)
|
||||
Layout.addWidget(self.object)
|
||||
Layout.addWidget(self.mode)
|
||||
Layout.addStretch(1)
|
||||
Layout.addWidget(self.main_button)
|
||||
|
||||
else:
|
||||
|
||||
self.Lable = SubtitleLabel(name, self)
|
||||
self.main_button = PushButton("中止任务")
|
||||
|
||||
Layout.addWidget(self.Lable)
|
||||
Layout.addStretch(1)
|
||||
Layout.addWidget(self.main_button)
|
||||
|
||||
def start_main_task(self):
|
||||
"""开始任务"""
|
||||
|
||||
if self.object.currentIndex() == -1:
|
||||
logger.warning("未选择调度对象")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择调度对象", "请选择后再开始任务", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if self.mode.currentIndex() == -1:
|
||||
logger.warning("未选择调度模式")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "未选择调度模式", "请选择后再开始任务", 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if self.object.currentData() in Config.running_list:
|
||||
logger.warning(f"任务已存在:{self.object.currentData()}")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning", "任务已存在", self.object.currentData(), 5000
|
||||
)
|
||||
return None
|
||||
|
||||
if "调度队列" in self.object.currentData():
|
||||
|
||||
logger.info(f"用户添加任务:{self.object.currentData()}")
|
||||
TaskManager.add_task(
|
||||
f"{self.mode.currentText()}_主调度台",
|
||||
self.object.currentData(),
|
||||
Config.queue_dict[self.object.currentData()]["Config"].toDict(),
|
||||
)
|
||||
|
||||
elif "脚本" in self.object.currentData():
|
||||
|
||||
if Config.member_dict[self.object.currentData()]["Type"] == "Maa":
|
||||
|
||||
logger.info(f"用户添加任务:{self.object.currentData()}")
|
||||
TaskManager.add_task(
|
||||
f"{self.mode.currentText()}_主调度台",
|
||||
"自定义队列",
|
||||
{"Queue": {"Member_1": self.object.currentData()}},
|
||||
)
|
||||
|
||||
class DispatchInfoCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("任务队列")
|
||||
|
||||
self.Layout = QVBoxLayout()
|
||||
self.viewLayout.addLayout(self.Layout)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
self.setTitle("调度信息")
|
||||
|
||||
self.task_cards: List[StatefulItemCard] = []
|
||||
self.task = self.TaskInfoCard(self)
|
||||
self.user = self.UserInfoCard(self)
|
||||
self.log_text = self.LogCard(self)
|
||||
|
||||
def create_task(self, task_list: list):
|
||||
"""创建任务队列"""
|
||||
self.viewLayout.addWidget(self.task)
|
||||
self.viewLayout.addWidget(self.user)
|
||||
self.viewLayout.addWidget(self.log_text)
|
||||
|
||||
while self.Layout.count() > 0:
|
||||
item = self.Layout.takeAt(0)
|
||||
if item.spacerItem():
|
||||
self.Layout.removeItem(item.spacerItem())
|
||||
elif item.widget():
|
||||
item.widget().deleteLater()
|
||||
self.viewLayout.setStretch(0, 1)
|
||||
self.viewLayout.setStretch(1, 1)
|
||||
self.viewLayout.setStretch(2, 5)
|
||||
|
||||
self.task_cards = []
|
||||
def update_board(self, task_list: list, user_list: list, log: str):
|
||||
"""更新调度信息"""
|
||||
|
||||
for task in task_list:
|
||||
self.task.update_task(task_list)
|
||||
self.user.update_user(user_list)
|
||||
self.log_text.text.setText(log)
|
||||
|
||||
self.task_cards.append(StatefulItemCard(task))
|
||||
self.Layout.addWidget(self.task_cards[-1])
|
||||
class TaskInfoCard(HeaderCardWidget):
|
||||
|
||||
self.Layout.addStretch(1)
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("任务队列")
|
||||
|
||||
def update_task(self, task_list: list):
|
||||
"""更新任务队列"""
|
||||
self.Layout = QVBoxLayout()
|
||||
self.viewLayout.addLayout(self.Layout)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
|
||||
for i in range(len(task_list)):
|
||||
self.task_cards: List[StatefulItemCard] = []
|
||||
|
||||
self.task_cards[i].update_status(task_list[i][1])
|
||||
def create_task(self, task_list: list):
|
||||
"""创建任务队列"""
|
||||
|
||||
class UserInfoCard(HeaderCardWidget):
|
||||
while self.Layout.count() > 0:
|
||||
item = self.Layout.takeAt(0)
|
||||
if item.spacerItem():
|
||||
self.Layout.removeItem(item.spacerItem())
|
||||
elif item.widget():
|
||||
item.widget().deleteLater()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("用户队列")
|
||||
self.task_cards = []
|
||||
|
||||
self.Layout = QVBoxLayout()
|
||||
self.viewLayout.addLayout(self.Layout)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
for task in task_list:
|
||||
|
||||
self.user_cards: List[StatefulItemCard] = []
|
||||
self.task_cards.append(StatefulItemCard(task))
|
||||
self.Layout.addWidget(self.task_cards[-1])
|
||||
|
||||
def create_user(self, user_list: list):
|
||||
"""创建用户队列"""
|
||||
self.Layout.addStretch(1)
|
||||
|
||||
while self.Layout.count() > 0:
|
||||
item = self.Layout.takeAt(0)
|
||||
if item.spacerItem():
|
||||
self.Layout.removeItem(item.spacerItem())
|
||||
elif item.widget():
|
||||
item.widget().deleteLater()
|
||||
def update_task(self, task_list: list):
|
||||
"""更新任务队列"""
|
||||
|
||||
self.user_cards = []
|
||||
for i in range(len(task_list)):
|
||||
|
||||
for user in user_list:
|
||||
self.task_cards[i].update_status(task_list[i][1])
|
||||
|
||||
self.user_cards.append(StatefulItemCard(user))
|
||||
self.Layout.addWidget(self.user_cards[-1])
|
||||
class UserInfoCard(HeaderCardWidget):
|
||||
|
||||
self.Layout.addStretch(1)
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("用户队列")
|
||||
|
||||
def update_user(self, user_list: list):
|
||||
"""更新用户队列"""
|
||||
self.Layout = QVBoxLayout()
|
||||
self.viewLayout.addLayout(self.Layout)
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
|
||||
for i in range(len(user_list)):
|
||||
self.user_cards: List[StatefulItemCard] = []
|
||||
|
||||
self.user_cards[i].Label.setText(user_list[i][0])
|
||||
self.user_cards[i].update_status(user_list[i][1])
|
||||
def create_user(self, user_list: list):
|
||||
"""创建用户队列"""
|
||||
|
||||
class LogCard(HeaderCardWidget):
|
||||
while self.Layout.count() > 0:
|
||||
item = self.Layout.takeAt(0)
|
||||
if item.spacerItem():
|
||||
self.Layout.removeItem(item.spacerItem())
|
||||
elif item.widget():
|
||||
item.widget().deleteLater()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("日志")
|
||||
self.user_cards = []
|
||||
|
||||
self.text = TextBrowser()
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
self.viewLayout.addWidget(self.text)
|
||||
for user in user_list:
|
||||
|
||||
self.text.textChanged.connect(self.to_end)
|
||||
self.user_cards.append(StatefulItemCard(user))
|
||||
self.Layout.addWidget(self.user_cards[-1])
|
||||
|
||||
def to_end(self):
|
||||
"""滚动到底部"""
|
||||
self.Layout.addStretch(1)
|
||||
|
||||
self.text.moveCursor(QTextCursor.End)
|
||||
self.text.ensureCursorVisible()
|
||||
def update_user(self, user_list: list):
|
||||
"""更新用户队列"""
|
||||
|
||||
for i in range(len(user_list)):
|
||||
|
||||
self.user_cards[i].Label.setText(user_list[i][0])
|
||||
self.user_cards[i].update_status(user_list[i][1])
|
||||
|
||||
class LogCard(HeaderCardWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setTitle("日志")
|
||||
|
||||
self.text = TextBrowser()
|
||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||
self.viewLayout.addWidget(self.text)
|
||||
|
||||
self.text.textChanged.connect(self.to_end)
|
||||
|
||||
def to_end(self):
|
||||
"""滚动到底部"""
|
||||
|
||||
self.text.moveCursor(QTextCursor.End)
|
||||
self.text.ensureCursorVisible()
|
||||
|
||||
@@ -21,24 +21,19 @@
|
||||
"""
|
||||
AUTO_MAA
|
||||
AUTO_MAA更新器
|
||||
v1.2
|
||||
v4.3
|
||||
作者:DLmaster_361
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import zipfile
|
||||
import requests
|
||||
import subprocess
|
||||
import time
|
||||
import psutil
|
||||
import win32crypt
|
||||
import base64
|
||||
from packaging import version
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout
|
||||
from PySide6.QtWidgets import QDialog, QVBoxLayout
|
||||
from qfluentwidgets import (
|
||||
ProgressBar,
|
||||
IndeterminateProgressBar,
|
||||
@@ -46,7 +41,7 @@ from qfluentwidgets import (
|
||||
setTheme,
|
||||
Theme,
|
||||
)
|
||||
from PySide6.QtGui import QIcon, QCloseEvent
|
||||
from PySide6.QtGui import QCloseEvent
|
||||
from PySide6.QtCore import QThread, Signal, QTimer, QEventLoop
|
||||
|
||||
from typing import List, Dict, Union
|
||||
@@ -237,14 +232,9 @@ class DownloadManager(QDialog):
|
||||
self.version = version
|
||||
self.config = config
|
||||
self.download_path = app_path / "DOWNLOAD_TEMP.zip" # 临时下载文件的路径
|
||||
self.version_path = app_path / "resources/version.json"
|
||||
self.download_process_dict: Dict[str, DownloadProcess] = {}
|
||||
self.timer_dict: Dict[str, QTimer] = {}
|
||||
|
||||
self.setWindowTitle("AUTO_MAA更新器")
|
||||
self.setWindowIcon(
|
||||
QIcon(str(app_path / "resources/icons/AUTO_MAA_Updater.ico"))
|
||||
)
|
||||
self.resize(700, 70)
|
||||
|
||||
setTheme(Theme.AUTO, lazy=True)
|
||||
@@ -539,41 +529,25 @@ class DownloadManager(QDialog):
|
||||
self.zip_extract.start()
|
||||
self.zip_loop.exec()
|
||||
|
||||
self.update_info("正在删除已弃用的文件")
|
||||
if (self.app_path / "changes.json").exists():
|
||||
|
||||
with (self.app_path / "changes.json").open(mode="r", encoding="utf-8") as f:
|
||||
info: Dict[str, List[str]] = json.load(f)
|
||||
|
||||
if "deleted" in info:
|
||||
for file_path in info["deleted"]:
|
||||
if (self.app_path / file_path).exists():
|
||||
(self.app_path / file_path).unlink()
|
||||
|
||||
(self.app_path / "changes.json").unlink()
|
||||
|
||||
self.update_info("正在删除临时文件")
|
||||
self.update_progress(0, 0, 0)
|
||||
if (self.app_path / "changes.json").exists():
|
||||
(self.app_path / "changes.json").unlink()
|
||||
if self.download_path.exists():
|
||||
self.download_path.unlink()
|
||||
|
||||
# 主程序更新完成后打开对应程序
|
||||
if not self.isInterruptionRequested and self.name == "AUTO_MAA":
|
||||
subprocess.Popen(
|
||||
[self.app_path / "AUTO_MAA.exe"],
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
|
||||
| subprocess.DETACHED_PROCESS
|
||||
| subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
elif not self.isInterruptionRequested and self.name == "MAA":
|
||||
# 下载完成后打开对应程序
|
||||
if not self.isInterruptionRequested and self.name == "MAA":
|
||||
subprocess.Popen(
|
||||
[self.app_path / "MAA.exe"],
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
|
||||
| subprocess.DETACHED_PROCESS
|
||||
| subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
|
||||
self.update_info(f"{self.name}更新成功!")
|
||||
if self.name == "AUTO_MAA":
|
||||
self.update_info(f"即将安装{self.name}")
|
||||
else:
|
||||
self.update_info(f"{self.name}下载成功!")
|
||||
self.update_progress(0, 100, 100)
|
||||
self.download_accomplish.emit()
|
||||
|
||||
@@ -615,142 +589,3 @@ class DownloadManager(QDialog):
|
||||
self.requestInterruption()
|
||||
|
||||
event.accept()
|
||||
|
||||
|
||||
class AUTO_MAA_Downloader(QApplication):
|
||||
def __init__(
|
||||
self, app_path: Path, name: str, main_version: list, config: dict
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.main = DownloadManager(app_path, name, main_version, config)
|
||||
self.main.show()
|
||||
self.main.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# 获取软件自身的路径
|
||||
app_path = Path(sys.argv[0]).resolve().parent
|
||||
|
||||
# 从本地版本信息文件获取当前版本信息
|
||||
if (app_path / "resources/version.json").exists():
|
||||
with (app_path / "resources/version.json").open(
|
||||
mode="r", encoding="utf-8"
|
||||
) as f:
|
||||
current_version_info = json.load(f)
|
||||
current_version = list(
|
||||
map(int, current_version_info["main_version"].split("."))
|
||||
)
|
||||
else:
|
||||
current_version = [0, 0, 0, 0]
|
||||
|
||||
# 从本地配置文件获取更新信息
|
||||
if (app_path / "config/config.json").exists():
|
||||
with (app_path / "config/config.json").open(mode="r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
if "Update" in config:
|
||||
|
||||
if "UpdateType" in config["Update"]:
|
||||
update_type = config["Update"]["UpdateType"]
|
||||
else:
|
||||
update_type = "stable"
|
||||
if "ProxyUrlList" in config["Update"]:
|
||||
proxy_list = config["Update"]["ProxyUrlList"]
|
||||
else:
|
||||
proxy_list = []
|
||||
if "ThreadNumb" in config["Update"]:
|
||||
thread_numb = config["Update"]["ThreadNumb"]
|
||||
else:
|
||||
thread_numb = 8
|
||||
if "MirrorChyanCDK" in config["Update"]:
|
||||
mirrorchyan_CDK = (
|
||||
win32crypt.CryptUnprotectData(
|
||||
base64.b64decode(config["Update"]["MirrorChyanCDK"]),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
)[1].decode("utf-8")
|
||||
if config["Update"]["MirrorChyanCDK"]
|
||||
else ""
|
||||
)
|
||||
else:
|
||||
mirrorchyan_CDK = ""
|
||||
|
||||
else:
|
||||
update_type = "stable"
|
||||
proxy_list = []
|
||||
thread_numb = 8
|
||||
mirrorchyan_CDK = ""
|
||||
else:
|
||||
update_type = "stable"
|
||||
proxy_list = []
|
||||
thread_numb = 8
|
||||
mirrorchyan_CDK = ""
|
||||
|
||||
# 从远程服务器获取最新版本信息
|
||||
for _ in range(3):
|
||||
try:
|
||||
response = requests.get(
|
||||
f"https://mirrorchyan.com/api/resources/AUTO_MAA/latest?user_agent=AutoMaaDownloader¤t_version={version_text(current_version)}&cdk={mirrorchyan_CDK}&channel={update_type}",
|
||||
timeout=10,
|
||||
)
|
||||
version_info: Dict[str, Union[int, str, Dict[str, str]]] = response.json()
|
||||
break
|
||||
except Exception as e:
|
||||
err = e
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
sys.exit(f"获取版本信息时出错:\n{err}")
|
||||
|
||||
if version_info["code"] == 0:
|
||||
|
||||
if "url" in version_info["data"]:
|
||||
download_config = {
|
||||
"mode": "MirrorChyan",
|
||||
"thread_numb": 1,
|
||||
"url": version_info["data"]["url"],
|
||||
}
|
||||
else:
|
||||
|
||||
download_config = {"mode": "Proxy", "thread_numb": thread_numb}
|
||||
else:
|
||||
sys.exit(f"获取版本信息时出错:{version_info["msg"]}")
|
||||
|
||||
remote_version = list(
|
||||
map(
|
||||
int,
|
||||
version_info["data"]["version_name"][1:].replace("-beta", "").split("."),
|
||||
)
|
||||
)
|
||||
|
||||
if download_config["mode"] == "Proxy":
|
||||
for _ in range(3):
|
||||
try:
|
||||
response = requests.get(
|
||||
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/download_info.json",
|
||||
timeout=10,
|
||||
)
|
||||
download_info = response.json()
|
||||
|
||||
download_config["proxy_list"] = list(
|
||||
set(proxy_list + download_info["proxy_list"])
|
||||
)
|
||||
download_config["download_dict"] = download_info["download_dict"]
|
||||
break
|
||||
except Exception as e:
|
||||
err = e
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
sys.exit(f"获取代理信息时出错:{err}")
|
||||
|
||||
if (app_path / "changes.json").exists():
|
||||
(app_path / "changes.json").unlink()
|
||||
|
||||
# 启动更新线程
|
||||
if version.parse(version_text(remote_version)) > version.parse(
|
||||
version_text(current_version)
|
||||
):
|
||||
app = AUTO_MAA_Downloader(app_path, "AUTO_MAA", remote_version, download_config)
|
||||
sys.exit(app.exec())
|
||||
@@ -61,19 +61,22 @@ 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 = []
|
||||
|
||||
|
||||
@@ -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,12 +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.setWidget(widget)
|
||||
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)
|
||||
|
||||
self.set_banner()
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ v4.3
|
||||
from loguru import logger
|
||||
from PySide6.QtWidgets import QApplication, QSystemTrayIcon
|
||||
from qfluentwidgets import (
|
||||
qconfig,
|
||||
Action,
|
||||
SystemTrayMenu,
|
||||
SplashScreen,
|
||||
@@ -50,6 +49,7 @@ from app.core import Config, TaskManager, MainTimer, MainInfoBar
|
||||
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
|
||||
@@ -81,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)
|
||||
@@ -101,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,
|
||||
@@ -129,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(
|
||||
@@ -182,7 +174,7 @@ class AUTO_MAA(MSFluentWindow):
|
||||
Action(
|
||||
FluentIcon.POWER_BUTTON,
|
||||
"退出主程序",
|
||||
triggered=lambda: (self.window().close(), QApplication.quit()),
|
||||
triggered=lambda: System.set_power("KillSelf"),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -193,6 +185,7 @@ class AUTO_MAA(MSFluentWindow):
|
||||
self.set_min_method()
|
||||
|
||||
Config.user_info_changed.connect(self.member_manager.refresh_dashboard)
|
||||
Config.power_sign_changed.connect(self.dispatch_center.update_power_sign)
|
||||
TaskManager.create_gui.connect(self.dispatch_center.add_board)
|
||||
TaskManager.connect_gui.connect(self.dispatch_center.connect_main_board)
|
||||
Notify.push_info_bar.connect(MainInfoBar.push_info_bar)
|
||||
@@ -241,16 +234,123 @@ 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:
|
||||
"""配置窗口状态"""
|
||||
|
||||
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()
|
||||
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:
|
||||
"""启动时任务"""
|
||||
|
||||
# 清理旧日志
|
||||
self.clean_old_logs()
|
||||
|
||||
# 清理临时更新器
|
||||
if (Config.app_path / "AUTO_Updater.active.exe").exists():
|
||||
# 清理安装包
|
||||
if (Config.app_path / "AUTO_MAA-Setup.exe").exists():
|
||||
try:
|
||||
(Config.app_path / "AUTO_Updater.active.exe").unlink()
|
||||
(Config.app_path / "AUTO_MAA-Setup.exe").unlink()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -278,24 +378,6 @@ class AUTO_MAA(MSFluentWindow):
|
||||
|
||||
self.titleBar.minBtn.click()
|
||||
|
||||
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 clean_old_logs(self):
|
||||
"""
|
||||
删除超过用户设定天数的日志文件(基于目录日期)
|
||||
@@ -351,75 +433,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):
|
||||
"""清理残余进程"""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
495
app/ui/plan_manager.py
Normal file
495
app/ui/plan_manager.py
Normal file
@@ -0,0 +1,495 @@
|
||||
# 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
|
||||
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
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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"],
|
||||
)
|
||||
@@ -34,14 +34,12 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
from qfluentwidgets import (
|
||||
Action,
|
||||
Pivot,
|
||||
ScrollArea,
|
||||
FluentIcon,
|
||||
MessageBox,
|
||||
HeaderCardWidget,
|
||||
CommandBar,
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
from typing import List
|
||||
|
||||
from app.core import QueueConfig, Config, MainInfoBar
|
||||
@@ -52,6 +50,7 @@ from .Widget import (
|
||||
TimeEditSettingCard,
|
||||
NoOptionComboBoxSettingCard,
|
||||
HistoryCard,
|
||||
PivotArea,
|
||||
)
|
||||
|
||||
|
||||
@@ -123,7 +122,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
|
||||
@@ -137,11 +136,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()
|
||||
@@ -165,7 +160,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
|
||||
@@ -210,7 +205,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
|
||||
@@ -305,15 +300,19 @@ class QueueManager(QWidget):
|
||||
|
||||
self.setObjectName("调度队列管理")
|
||||
|
||||
self.pivot = Pivot(self)
|
||||
self.pivotArea = PivotArea()
|
||||
self.pivot = self.pivotArea.pivot
|
||||
|
||||
self.stackedWidget = QStackedWidget(self)
|
||||
self.Layout = QVBoxLayout(self)
|
||||
self.stackedWidget.setContentsMargins(0, 0, 0, 0)
|
||||
self.stackedWidget.setStyleSheet("background: transparent; border: none;")
|
||||
|
||||
self.script_list: List[
|
||||
QueueManager.QueueSettingBox.QueueMemberSettingBox
|
||||
] = []
|
||||
|
||||
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
|
||||
self.Layout = QVBoxLayout(self)
|
||||
self.Layout.addWidget(self.pivotArea)
|
||||
self.Layout.addWidget(self.stackedWidget)
|
||||
self.Layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@@ -376,14 +375,6 @@ class QueueManager(QWidget):
|
||||
self.setObjectName(f"调度队列_{uid}")
|
||||
self.config = Config.queue_dict[f"调度队列_{uid}"]["Config"]
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
|
||||
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)
|
||||
@@ -393,18 +384,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):
|
||||
|
||||
@@ -26,7 +26,8 @@ v4.3
|
||||
"""
|
||||
|
||||
from loguru import logger
|
||||
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout
|
||||
from PySide6.QtGui import QIcon
|
||||
from PySide6.QtCore import Qt
|
||||
from qfluentwidgets import (
|
||||
ScrollArea,
|
||||
@@ -50,6 +51,7 @@ from typing import Dict, Union
|
||||
|
||||
from app.core import Config, MainInfoBar, Network
|
||||
from app.services import Crypto, System, Notify
|
||||
from .downloader import DownloadManager
|
||||
from .Widget import (
|
||||
SwitchSettingCard,
|
||||
RangeSettingCard,
|
||||
@@ -68,9 +70,6 @@ class Setting(QWidget):
|
||||
super().__init__(parent)
|
||||
self.setObjectName("设置")
|
||||
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
|
||||
self.function = FunctionSettingCard(self)
|
||||
self.start = StartSettingCard(self)
|
||||
self.ui = UiSettingCard(self)
|
||||
@@ -91,6 +90,9 @@ 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.start)
|
||||
content_layout.addWidget(self.ui)
|
||||
@@ -101,10 +103,12 @@ class Setting(QWidget):
|
||||
|
||||
scrollArea = ScrollArea()
|
||||
scrollArea.setWidgetResizable(True)
|
||||
scrollArea.setContentsMargins(0, 0, 0, 0)
|
||||
scrollArea.setStyleSheet("background: transparent; border: none;")
|
||||
scrollArea.setWidget(content_widget)
|
||||
layout = QVBoxLayout()
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(scrollArea)
|
||||
self.setLayout(layout)
|
||||
|
||||
def agree_bilibili(self) -> None:
|
||||
"""授权bilibili游戏隐私政策"""
|
||||
@@ -248,9 +252,7 @@ class Setting(QWidget):
|
||||
choice.exec()
|
||||
else:
|
||||
choice = MessageBox(
|
||||
"确认",
|
||||
"您没有输入管理密钥,是否取消修改管理密钥?",
|
||||
self.window(),
|
||||
"确认", "您没有输入管理密钥,是否取消修改管理密钥?", self.window()
|
||||
)
|
||||
if choice.exec():
|
||||
break
|
||||
@@ -378,45 +380,72 @@ class Setting(QWidget):
|
||||
else:
|
||||
all_version_info[key] = value.copy()
|
||||
|
||||
version_info = {
|
||||
"更新总览": f"{main_version_info}\n\n{version_info_markdown(update_version_info)}",
|
||||
"ALL~版本信息": version_info_markdown(all_version_info),
|
||||
**{
|
||||
version_text(list(map(int, k.split(".")))): version_info_markdown(v)
|
||||
for k, v in version_info_json.items()
|
||||
},
|
||||
}
|
||||
|
||||
# 询问是否开始版本更新
|
||||
choice = NoticeMessageBox(self.window(), "版本更新", version_info)
|
||||
choice = NoticeMessageBox(
|
||||
self.window(),
|
||||
"版本更新",
|
||||
{
|
||||
"更新总览": f"{main_version_info}\n\n{version_info_markdown(update_version_info)}",
|
||||
"ALL~版本信息": version_info_markdown(all_version_info),
|
||||
**{
|
||||
version_text(
|
||||
list(map(int, k.split(".")))
|
||||
): version_info_markdown(v)
|
||||
for k, v in version_info_json.items()
|
||||
},
|
||||
},
|
||||
)
|
||||
if choice.exec():
|
||||
|
||||
with Config.version_path.open(mode="r", encoding="utf-8") as f:
|
||||
version_info = json.load(f)
|
||||
version_info["main_version"] = Config.VERSION
|
||||
with Config.version_path.open(mode="w", encoding="utf-8") as f:
|
||||
json.dump(version_info, f, ensure_ascii=False, indent=4)
|
||||
|
||||
if (Config.app_path / "AUTO_Updater.exe").exists():
|
||||
shutil.copy(
|
||||
Config.app_path / "AUTO_Updater.exe",
|
||||
Config.app_path / "AUTO_Updater.active.exe",
|
||||
)
|
||||
if "url" in version_info["data"]:
|
||||
download_config = {
|
||||
"mode": "MirrorChyan",
|
||||
"thread_numb": 1,
|
||||
"url": version_info["data"]["url"],
|
||||
}
|
||||
else:
|
||||
logger.error("更新器文件不存在")
|
||||
MainInfoBar.push_info_bar(
|
||||
"error", "更新器不存在", "请手动前往 GitHub 获取最新版本", -1
|
||||
)
|
||||
return None
|
||||
|
||||
subprocess.Popen(
|
||||
[Config.app_path / "AUTO_Updater.active.exe"],
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
|
||||
| subprocess.DETACHED_PROCESS
|
||||
| subprocess.CREATE_NO_WINDOW,
|
||||
# 从远程服务器获取代理信息
|
||||
Network.set_info(
|
||||
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
|
||||
else:
|
||||
logger.warning(f"获取应用列表时出错:{Network.error_message}")
|
||||
MainInfoBar.push_info_bar(
|
||||
"warning",
|
||||
"获取应用列表时出错",
|
||||
f"网络错误:{Network.stutus_code}",
|
||||
5000,
|
||||
)
|
||||
return None
|
||||
|
||||
download_config = {
|
||||
"mode": "Proxy",
|
||||
"thread_numb": Config.get(Config.update_ThreadNumb),
|
||||
"proxy_list": list(
|
||||
set(
|
||||
Config.get(Config.update_ProxyUrlList)
|
||||
+ download_info["proxy_list"]
|
||||
)
|
||||
),
|
||||
"download_dict": download_info["download_dict"],
|
||||
}
|
||||
|
||||
self.downloader = DownloadManager(
|
||||
Config.app_path, "AUTO_MAA", remote_version, download_config
|
||||
)
|
||||
self.window().close()
|
||||
QApplication.quit()
|
||||
self.downloader.setWindowTitle("AUTO_MAA更新器")
|
||||
self.downloader.setWindowIcon(
|
||||
QIcon(str(Config.app_path / "resources/icons/AUTO_MAA_Updater.ico"))
|
||||
)
|
||||
self.downloader.download_accomplish.connect(self.start_setup)
|
||||
self.downloader.show()
|
||||
self.downloader.run()
|
||||
|
||||
elif (
|
||||
if_show
|
||||
@@ -433,10 +462,28 @@ class Setting(QWidget):
|
||||
"发现新版本",
|
||||
f"{version_text(current_version)} --> {version_text(remote_version)}",
|
||||
3600000,
|
||||
if_force=True,
|
||||
)
|
||||
else:
|
||||
MainInfoBar.push_info_bar("success", "更新检查", "已是最新版本~", 3000)
|
||||
|
||||
def start_setup(self) -> None:
|
||||
subprocess.Popen(
|
||||
[
|
||||
Config.app_path / "AUTO_MAA-Setup.exe",
|
||||
"/SP-",
|
||||
"/SILENT",
|
||||
"/NOCANCEL",
|
||||
"/FORCECLOSEAPPLICATIONS",
|
||||
"/LANG=Chinese",
|
||||
f"/DIR={Config.app_path}",
|
||||
],
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
|
||||
| subprocess.DETACHED_PROCESS
|
||||
| subprocess.CREATE_NO_WINDOW,
|
||||
)
|
||||
System.set_power("KillSelf")
|
||||
|
||||
def show_notice(self, if_show: bool = False, if_first: bool = False) -> None:
|
||||
"""显示公告"""
|
||||
|
||||
@@ -499,7 +546,7 @@ class Setting(QWidget):
|
||||
):
|
||||
|
||||
MainInfoBar.push_info_bar(
|
||||
"info", "有新公告", "请前往设置界面查看公告", 3600000
|
||||
"info", "有新公告", "请前往设置界面查看公告", 3600000, if_force=True
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
|
||||
Source: "{#MyAppPath}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#MyAppPath}\app\*"; DestDir: "{app}\app"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "{#MyAppPath}\resources\*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "{#MyAppPath}\AUTO_Updater.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#MyAppPath}\main.py"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#MyAppPath}\requirements.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#MyAppPath}\README.md"; DestDir: "{app}"; Flags: ignoreversion
|
||||
@@ -63,7 +62,7 @@ Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall
|
||||
|
||||
[Code]
|
||||
var
|
||||
|
||||
@@ -29,6 +29,4 @@ __version__ = "4.2.0"
|
||||
__author__ = "DLmaster361 <DLmaster_361@163.com>"
|
||||
__license__ = "GPL-3.0 license"
|
||||
|
||||
from .downloader import DownloadManager
|
||||
|
||||
__all__ = ["DownloadManager"]
|
||||
__all__ = []
|
||||
|
||||
@@ -66,7 +66,6 @@ if __name__ == "__main__":
|
||||
version = json.load(f)
|
||||
|
||||
main_version_numb = list(map(int, version["main_version"].split(".")))
|
||||
updater_version_numb = list(map(int, version["updater_version"].split(".")))
|
||||
|
||||
print("Packaging AUTO_MAA main program ...")
|
||||
|
||||
@@ -86,35 +85,10 @@ if __name__ == "__main__":
|
||||
|
||||
print("AUTO_MAA main program packaging completed !")
|
||||
|
||||
print("Packaging AUTO_MAA update program ...")
|
||||
|
||||
shutil.copy(root_path / "app/utils/downloader.py", root_path)
|
||||
os.system(
|
||||
"powershell -Command python -m nuitka --standalone --onefile --mingw64"
|
||||
" --enable-plugins=pyside6 --windows-console-mode=disable"
|
||||
" --onefile-tempdir-spec='{TEMP}\\AUTO_MAA_Updater'"
|
||||
" --windows-icon-from-ico=resources\\icons\\AUTO_MAA_Updater.ico"
|
||||
" --company-name='AUTO_MAA Team' --product-name=AUTO_MAA"
|
||||
f" --file-version={version["updater_version"]}"
|
||||
f" --product-version={version["main_version"]}"
|
||||
" --file-description='AUTO_MAA Component'"
|
||||
" --copyright='Copyright © 2024-2025 DLmaster361'"
|
||||
" --assume-yes-for-downloads --output-filename=AUTO_Updater"
|
||||
" --remove-output downloader.py"
|
||||
)
|
||||
(root_path / "downloader.py").unlink()
|
||||
|
||||
print("AUTO_MAA update program packaging completed !")
|
||||
print("start to create setup program ...")
|
||||
|
||||
(root_path / "AUTO_MAA").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print("Start to move AUTO_MAA program ...")
|
||||
|
||||
shutil.move(root_path / "AUTO_MAA.exe", root_path / "AUTO_MAA/")
|
||||
shutil.move(root_path / "AUTO_Updater.exe", root_path / "AUTO_MAA/")
|
||||
|
||||
print("Start to copy rescourses ...")
|
||||
|
||||
shutil.copytree(root_path / "app", root_path / "AUTO_MAA/app")
|
||||
shutil.copytree(root_path / "resources", root_path / "AUTO_MAA/resources")
|
||||
shutil.copy(root_path / "main.py", root_path / "AUTO_MAA/")
|
||||
@@ -122,34 +96,8 @@ if __name__ == "__main__":
|
||||
shutil.copy(root_path / "README.md", root_path / "AUTO_MAA/")
|
||||
shutil.copy(root_path / "LICENSE", root_path / "AUTO_MAA/")
|
||||
|
||||
print("Start to compress ...")
|
||||
|
||||
shutil.make_archive(
|
||||
base_name=root_path / f"AUTO_MAA_{version_text(main_version_numb)}",
|
||||
format="zip",
|
||||
root_dir=root_path / "AUTO_MAA",
|
||||
base_dir=".",
|
||||
)
|
||||
shutil.rmtree(root_path / "AUTO_MAA")
|
||||
|
||||
print("compress completed !")
|
||||
|
||||
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{version_text(updater_version_numb)}\n<!--{json.dumps(version["version_info"], ensure_ascii=False)}-->\n{version_info_markdown(all_version_info)}",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with (root_path / "app/utils/AUTO_MAA.iss").open(mode="r", encoding="utf-8") as f:
|
||||
iss = f.read()
|
||||
|
||||
iss = (
|
||||
iss.replace(
|
||||
'#define MyAppVersion ""',
|
||||
@@ -160,6 +108,36 @@ if __name__ == "__main__":
|
||||
)
|
||||
.replace('#define OutputDir ""', f'#define OutputDir "{root_path}"')
|
||||
)
|
||||
|
||||
with (root_path / "AUTO_MAA.iss").open(mode="w", encoding="utf-8") as f:
|
||||
f.write(iss)
|
||||
|
||||
os.system(f'ISCC "{root_path / "AUTO_MAA.iss"}"')
|
||||
|
||||
(root_path / "AUTO_MAA_Setup").mkdir(parents=True, exist_ok=True)
|
||||
shutil.move(root_path / "AUTO_MAA-Setup.exe", root_path / "AUTO_MAA_Setup")
|
||||
|
||||
shutil.make_archive(
|
||||
base_name=root_path / f"AUTO_MAA_{version_text(main_version_numb)}",
|
||||
format="zip",
|
||||
root_dir=root_path / "AUTO_MAA_Setup",
|
||||
base_dir=".",
|
||||
)
|
||||
|
||||
print("setup program created !")
|
||||
|
||||
(root_path / "AUTO_MAA.iss").unlink(missing_ok=True)
|
||||
shutil.rmtree(root_path / "AUTO_MAA")
|
||||
shutil.rmtree(root_path / "AUTO_MAA_Setup")
|
||||
|
||||
all_version_info = {}
|
||||
for v_i in version["version_info"].values():
|
||||
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",
|
||||
)
|
||||
|
||||
2
main.py
2
main.py
@@ -42,7 +42,7 @@ def main():
|
||||
from app.ui.main_window import AUTO_MAA
|
||||
|
||||
window = AUTO_MAA()
|
||||
window.show_ui("显示主窗口")
|
||||
window.show_ui("显示主窗口", if_start=True)
|
||||
window.start_up_task()
|
||||
sys.exit(application.exec())
|
||||
|
||||
|
||||
@@ -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" #更新包标识
|
||||
@@ -1,61 +1,54 @@
|
||||
{
|
||||
"main_version": "4.3.6.0",
|
||||
"updater_version": "1.0.0.0",
|
||||
"announcement": "\n## 新增功能\n- 屏蔽MuMu模拟器开屏广告功能上线\n- 更新器支持多线程下载\n- 添加强制关闭ADB与模拟器等增强任务项\n## 修复BUG\n- 修复统计信息HTML模板公招匹配错误\n- 修复密码显示按钮动画异常\n- 修复`检测到MAA未能实际执行任务`报错被异常屏蔽\n- 修复MAA超时判定异常失效\n## 程序优化\n- 关机等电源操作添加100s倒计时\n- 人工排查弹窗方法优化\n- 人工排查时自动屏蔽静默操作\n- 公告样式优化",
|
||||
"main_version": "4.3.8.0",
|
||||
"version_info": {
|
||||
"4.3.6.0": {
|
||||
"4.3.8.0": {
|
||||
"新增功能": [
|
||||
"适配MAA连战次数`AUTO`模式"
|
||||
"吐司通知在主窗口隐藏时不再弹出"
|
||||
]
|
||||
},
|
||||
"4.3.6.5": {
|
||||
"4.3.8.4": {
|
||||
"新增功能": [
|
||||
"release中添加安装程序"
|
||||
]
|
||||
},
|
||||
"4.3.6.4": {
|
||||
"修复BUG": [
|
||||
"修复详细模式下非定时自定义基建无法正常换班的问题"
|
||||
]
|
||||
},
|
||||
"4.3.6.3": {
|
||||
"新增功能": [
|
||||
"下载器仅支持调用Mirror酱"
|
||||
"支持为每一个用户执行独立通知",
|
||||
"输入文本框适配文本插入操作",
|
||||
"计划表功能上线",
|
||||
"静默控制时长从全任务内缩短至搜索ADB时段内",
|
||||
"UI界面添加自动日常代理任务序列设置项"
|
||||
],
|
||||
"修复BUG": [
|
||||
"修复Mirror酱更新检查报错不显示详细信息的问题"
|
||||
"修复bug": [
|
||||
"修复雷电模拟器静默模式无法正常识别模拟器是否隐藏相关问题"
|
||||
]
|
||||
},
|
||||
"4.3.6.2": {
|
||||
"4.3.8.3": {
|
||||
"新增功能": [
|
||||
"新增`无人值守模式`"
|
||||
"用户仪表盘支持直接控制用户状态"
|
||||
],
|
||||
"修复BUG": [
|
||||
"修复软件窗口最大化异常问题",
|
||||
"修复异常操作导致窗口离开屏幕后难以复原的问题",
|
||||
"修正剩余理智关卡文案",
|
||||
"修复隐藏到托盘时,托盘无法退出主程序的问题",
|
||||
"修复Server酱网络异常导致的卡死问题"
|
||||
"修复bug": [
|
||||
"修复雷电ADB端口号相关问题"
|
||||
]
|
||||
},
|
||||
"4.3.8.2": {
|
||||
"新增功能": [
|
||||
"添加ADB端口号宽幅适配能力"
|
||||
],
|
||||
"修复bug": [
|
||||
"日志分析忽略MAA超时提示"
|
||||
],
|
||||
"程序优化": [
|
||||
"主窗口显示版本号"
|
||||
"配置类定义方法优化"
|
||||
]
|
||||
},
|
||||
"4.3.6.1": {
|
||||
"4.3.8.1": {
|
||||
"新增功能": [
|
||||
"单次自动代理任务中,已完成的子任务在重复执行时不再启用"
|
||||
"自定义基建显示配置名称 #46",
|
||||
"主调度台添加仅一次电源任务"
|
||||
],
|
||||
"修复bug": [
|
||||
"电源相关选项改为所有任务完成后生效",
|
||||
"适配MAAv5.16.3的ADB报错信息更改"
|
||||
],
|
||||
"程序优化": [
|
||||
"UI样式优化,进一步适配win10主题"
|
||||
]
|
||||
}
|
||||
},
|
||||
"proxy_list": [
|
||||
"https://gitproxy.click/",
|
||||
"https://cdn.moran233.xyz/",
|
||||
"https://gh.llkk.cc/",
|
||||
"https://github.akams.cn/",
|
||||
"https://www.ghproxy.cn/",
|
||||
"https://ghfast.top/"
|
||||
],
|
||||
"download_dict": {
|
||||
"官方下载站-jp": "https://jp-download.fearr.xyz/AUTO_MAA/"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user