Compare commits
79 Commits
v4.2.2-bet
...
v4.2.5-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe26f29f93 | ||
|
|
67b8725156 | ||
|
|
2a235b2bc9 | ||
|
|
54b697f2ee | ||
|
|
70df428825 | ||
|
|
8993d66056 | ||
|
|
863e6fb25e | ||
|
|
181856173e | ||
|
|
576fe59bbc | ||
|
|
c73aca71f7 | ||
|
|
ce264de963 | ||
|
|
1feb0cf83f | ||
|
|
6292624d41 | ||
| 4271a07f03 | |||
|
|
254fb6916f | ||
|
|
21857325a2 | ||
|
|
175d6860a3 | ||
|
|
d1c8f98408 | ||
|
|
3499fa9067 | ||
|
|
cca2cd774c | ||
|
|
6d60f8adb8 | ||
|
|
3b406a7974 | ||
| a116b3359c | |||
| 928019390b | |||
| 022b698f54 | |||
| 0228ac8393 | |||
|
|
a99f381f7f | ||
|
|
7c0af24bf5 | ||
|
|
d3aa45cfb9 | ||
|
|
f5461deb81 | ||
|
|
c19068128f | ||
|
|
1367daf1b7 | ||
|
|
5fc6e74cd6 | ||
|
|
5d7227c009 | ||
| 3a9c670172 | |||
|
|
2768faed53 | ||
|
|
85f3d6f09f | ||
|
|
c99707ecb4 | ||
|
|
2b8e648fe6 | ||
|
|
fcf61fd39a | ||
|
|
8e3026f91e | ||
|
|
8e00676faf | ||
|
|
ae293c4c20 | ||
| df4a5f3318 | |||
|
|
1da96c4d1d | ||
|
|
144c7f5db7 | ||
|
|
b3a3ccfea3 | ||
|
|
c3212f0ca1 | ||
|
|
a946999782 | ||
|
|
284c41feb7 | ||
|
|
ddf905cb13 | ||
|
|
d5082d18ef | ||
|
|
af51831062 | ||
|
|
fe4707df84 | ||
|
|
292e7f51e0 | ||
|
|
8936b1c41d | ||
|
|
d45da439bd | ||
|
|
7dc057e30f | ||
|
|
eb2f9d2cea | ||
|
|
fb1895c4eb | ||
|
|
90d3dad8c8 | ||
|
|
de12339c3e | ||
|
|
f07cd2b44a | ||
|
|
c7fbbf6f50 | ||
|
|
de262ee6bd | ||
|
|
0c123e9389 | ||
|
|
d13fbb063d | ||
|
|
5c24eb7d56 | ||
|
|
6c2f19a884 | ||
|
|
4ff632ed2a | ||
|
|
d445c0054f | ||
|
|
748fa7a004 | ||
|
|
c3e710b5cf | ||
|
|
a93a60d125 | ||
|
|
07f24c6168 | ||
|
|
7f5478b098 | ||
|
|
fb7a429ff2 | ||
| 3307793a3d | |||
|
|
0da9f4b7ab |
26
.github/workflows/build-app.yml
vendored
26
.github/workflows/build-app.yml
vendored
@@ -150,4 +150,28 @@ jobs:
|
|||||||
gh release delete "$TAGNAME" --yes
|
gh release delete "$TAGNAME" --yes
|
||||||
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" artifacts/*
|
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" artifacts/*
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
||||||
|
- name: Setup SSH Key
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts
|
||||||
|
- name: Upload Release to Server
|
||||||
|
run: |
|
||||||
|
scp -r artifacts/* ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }}:/home/user/files/AUTO_MAA/
|
||||||
|
- name: Install obsutil
|
||||||
|
run: |
|
||||||
|
wget https://obs-community.obs.cn-north-1.myhuaweicloud.com/obsutil/current/obsutil_linux_amd64.tar.gz
|
||||||
|
tar -xzvf obsutil_linux_amd64.tar.gz --strip-components=1
|
||||||
|
chmod 755 obsutil
|
||||||
|
./obsutil version
|
||||||
|
- name: Upload Release to Huawei OBS
|
||||||
|
env:
|
||||||
|
OBS_AK: ${{ secrets.OBS_AK }}
|
||||||
|
OBS_SK: ${{ secrets.OBS_SK }}
|
||||||
|
OBS_ENDPOINT: ${{ secrets.OBS_ENDPOINT }}
|
||||||
|
OBS_BUCKET: ${{ secrets.OBS_BUCKET }}
|
||||||
|
run: |
|
||||||
|
./obsutil config -i $OBS_AK -k $OBS_SK -e $OBS_ENDPOINT
|
||||||
|
./obsutil cp artifacts/ obs://$OBS_BUCKET/releases/ -r -f
|
||||||
26
.github/workflows/build-pre.yml
vendored
26
.github/workflows/build-pre.yml
vendored
@@ -150,4 +150,28 @@ jobs:
|
|||||||
gh release delete "$TAGNAME" --yes
|
gh release delete "$TAGNAME" --yes
|
||||||
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" --prerelease artifacts/*
|
gh release create "$TAGNAME" --target "main" --title "$NAME" --notes "$NOTES" --prerelease artifacts/*
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
||||||
|
- name: Setup SSH Key
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts
|
||||||
|
- name: Upload Release to Server
|
||||||
|
run: |
|
||||||
|
scp -r artifacts/* ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }}:/home/user/files/AUTO_MAA/
|
||||||
|
- name: Install obsutil
|
||||||
|
run: |
|
||||||
|
wget https://obs-community.obs.cn-north-1.myhuaweicloud.com/obsutil/current/obsutil_linux_amd64.tar.gz
|
||||||
|
tar -xzvf obsutil_linux_amd64.tar.gz --strip-components=1
|
||||||
|
chmod 755 obsutil
|
||||||
|
./obsutil version
|
||||||
|
- name: Upload Release to Huawei OBS
|
||||||
|
env:
|
||||||
|
OBS_AK: ${{ secrets.OBS_AK }}
|
||||||
|
OBS_SK: ${{ secrets.OBS_SK }}
|
||||||
|
OBS_ENDPOINT: ${{ secrets.OBS_ENDPOINT }}
|
||||||
|
OBS_BUCKET: ${{ secrets.OBS_BUCKET }}
|
||||||
|
run: |
|
||||||
|
./obsutil config -i $OBS_AK -k $OBS_SK -e $OBS_ENDPOINT
|
||||||
|
./obsutil cp artifacts/ obs://$OBS_BUCKET/releases/ -r -f
|
||||||
131
README.md
131
README.md
@@ -58,132 +58,9 @@ MAA多账号管理与自动化软件
|
|||||||
|
|
||||||
# 使用方法
|
# 使用方法
|
||||||
|
|
||||||
## 安装软件
|
访问AUTO_MAA官方文档站以获取使用指南和项目相关信息
|
||||||
|
|
||||||
```
|
- [AUTO_MAA官方文档站](https://clozya.github.io/AUTOMAA_docs)
|
||||||
本软件是MAA的外部工具,需要安装MAA后才能使用。
|
|
||||||
```
|
|
||||||
|
|
||||||
### 下载MAA
|
|
||||||
|
|
||||||
- 什么是MAA? [官网](https://maa.plus/)/[GitHub](https://github.com/MaaAssistantArknights/MaaAssistantArknights)
|
|
||||||
- MAA下载地址 [GitHub下载](https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases)
|
|
||||||
|
|
||||||
### 安装MAA
|
|
||||||
|
|
||||||
- 将MAA压缩包解压至任意普通文件夹即可。
|
|
||||||
- 若为首次安装MAA,请双击`MAA.exe`启动MAA程序以生成MAA配置文件。
|
|
||||||
|
|
||||||
### 下载AUTO_MAA [](https://github.com/DLmaster361/AUTO_MAA/releases)
|
|
||||||
|
|
||||||
- GitHub下载地址 [GitHub下载](https://github.com/DLmaster361/AUTO_MAA/releases)
|
|
||||||
|
|
||||||
### 安装AUTO_MAA
|
|
||||||
|
|
||||||
- 将AUTO_MAA压缩包解压至任意普通文件夹即可。
|
|
||||||
|
|
||||||
## 配置AUTO_MAA
|
|
||||||
|
|
||||||
### 启动AUTO_MAA
|
|
||||||
|
|
||||||
- 双击`AUTO_MAA.exe`以启动软件。
|
|
||||||
|
|
||||||
```
|
|
||||||
注意:
|
|
||||||
|
|
||||||
首次启动时会要求设置管理密钥。
|
|
||||||
|
|
||||||
管理密钥是解密用户密码的唯一凭证,与用户数据库绑定。
|
|
||||||
密钥丢失或data/key/目录下任一文件损坏都将导致解密无法正常进行。
|
|
||||||
|
|
||||||
本项目采用自主开发的混合加密模式,项目组也无法找回您的管理密钥或修复data/key/目录下的文件。
|
|
||||||
如果不幸的事发生,建议您删除data/key目录与config目录后重新录入信息。
|
|
||||||
```
|
|
||||||
|
|
||||||
### 配置信息
|
|
||||||
|
|
||||||
#### 设置脚本实例
|
|
||||||
|
|
||||||
1. 单击`+`并选择`MAA`以添加MAA脚本实例。
|
|
||||||
2. 在`MAA目录`选项卡中通过`选择文件夹`打开MAA软件目录以绑定MAA。
|
|
||||||
3. 在`MAA全局配置`选项卡中通过`设置`进行MAA全局设置。在打开的MAA界面完成`性能设置`、`游戏设置`、`连接设置`、`启动设置`、`界面设置`、`软件更新`等基本配置以及代理任务的详细配置。
|
|
||||||
4. 完成基本配置后,关闭MAA页面,AUTO_MAA会自动保存您的配置。
|
|
||||||
|
|
||||||
- 注意:在MAA的设置过程中,若MAA要求`立刻重启应用更改`,请选择`稍后`。否则,MAA重启后的一切更改都不会被程序记录。
|
|
||||||
|
|
||||||
- 特别的,在设置MAA过程中,您需要确保自己:
|
|
||||||
- 在`切换配置`选项卡中选择了`Default`项。
|
|
||||||
- 取消勾选`开机自启动MAA`。
|
|
||||||
- 配置自己模拟器所在的位置并根据实际情况填写`等待模拟器启动时间`(建议预留10s以防意外)。
|
|
||||||
- 如果是模拟器多开用户,还需要填写`附加命令`,具体填写值参见多开模拟器对应快捷方式路径(如`-v 1`)。
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
#### 设置用户配置
|
|
||||||
|
|
||||||
每一个脚本实例都有独立的用户数据库,您可以直接在`用户列表`选项卡配置用户相关信息,页面简介如下:
|
|
||||||
- `新建用户`、`删除用户`:新建一个用户到当前用户配置列表、删除当前所选第一行所对应的用户。
|
|
||||||
- `向上移动`、`向下移动`:移动用户位置,用户位置即代理顺序。
|
|
||||||
- `模式转换`:将当前所选第一行所对应的用户转为高级/简洁配置模式。
|
|
||||||
- `用户选项配置`:选择用户与对应配置项目,执行对应配置流程。
|
|
||||||
- `自定义基建`:选择自定义基建配置文件。
|
|
||||||
- `日常`、`剿灭`:打开MAA界面进行设置,设置方法与MAA全局配置相同。
|
|
||||||
- `显示密码`:输入管理密钥以显示用户密码,仅当管理密钥正确时能够修改`密码栏目`。
|
|
||||||
- `简洁用户配置列表`:仅支持核心代理选项的设置,其它设置选项沿用MAA的全局设置,部分代理核心功能选项由程序托管。
|
|
||||||
- `高级用户配置列表`:支持几乎所有代理选项的设置,通过`用户选项配置`进行MAA自定义,仅部分代理核心功能选项由程序托管。
|
|
||||||
- `用户配置列表栏目`详解如下:
|
|
||||||
- `用户名`:展示在执行界面的用户名,用于区分不同用户。
|
|
||||||
- `账号ID`:MAA进行账号切换所需的凭据,官服用户请输入手机号码、B服请输入B站ID。
|
|
||||||
- `服务器`:当前支持官服、B服。
|
|
||||||
- `代理天数`:剩余需要进行代理的天数,输入`任意负数`可设置为无限代理天数,当剩余天数为0时不再代理或排查。
|
|
||||||
- `状态`:用户的状态,禁用时将不再对其进行代理或排查。
|
|
||||||
- `执行情况`:当日执行情况,不可编辑。
|
|
||||||
- `关卡`、`备选关卡-1`、`备选关卡-2`:关卡号。
|
|
||||||
- `日常`:单独设定是否进行自动代理的日常部分,可进一步配置MAA的具体代理任务,该配置与全局MAA配置相互独立。
|
|
||||||
- `剿灭`:单独设定是否进行自动代理的剿灭部分,高级配置模式下可进一步配置MAA的具体代理任务,该配置与全局MAA配置相互独立。
|
|
||||||
- `自定义基建`:是否启用自定义基建功能,需要进一步配置自定义基建文件,该配置与其他用户相互独立。
|
|
||||||
- `密码`:仅用于登记用户的密码,可留空。
|
|
||||||
- `备注`:用于备注用户信息。
|
|
||||||
|
|
||||||
- 特别的:
|
|
||||||
- 对于`简洁用户配置列表的关卡、备选关卡-1、备选关卡-2栏目`您可以自定义关卡号替换方案。
|
|
||||||
- 程序会读取`data/gameid.txt`中的数据,依据此进行关卡号的替换,便于常用关卡的使用。
|
|
||||||
- `gameid.txt`会在程序首次运行时生成,其中将预置一些常用资源本的替换方案。
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### 设置调度队列
|
|
||||||
|
|
||||||
- 单个调度队列可包含至多10个定时与至多10个任务实例。
|
|
||||||
- 调度队列状态为关闭时,将不会定时启动该调度队列,但仍能在主调度台直接运行该调度队列。
|
|
||||||
- 同一调度队列内任务实例被依次挨个调起运行,非同一调度队列内的不同任务实例可被同时调起。
|
|
||||||
- 同一时间内,任何脚本实例或调度队列都不会被重复调起,若某一任务运行时发现同一任务已在运行,将自动跳过。
|
|
||||||
|
|
||||||
#### 设置AUTO_MAA
|
|
||||||
|
|
||||||
- 详见软件中对应选项卡的注解。
|
|
||||||
|
|
||||||
## 运行代理任务
|
|
||||||
|
|
||||||
### 直接运行
|
|
||||||
|
|
||||||
- 在`调度中心`的`主调度台`选择对应任务与`自动代理`模式,单击`开始任务`即可开始代理。
|
|
||||||
|
|
||||||
### 定时运行
|
|
||||||
|
|
||||||
- 将调度队列状态设为开启,并在`定时`选项卡设置定时启动时间。
|
|
||||||
- 保持软件开启,软件会在设定的时间自动运行。
|
|
||||||
|
|
||||||
## 人工排查代理结果
|
|
||||||
|
|
||||||
### 直接开始人工排查
|
|
||||||
|
|
||||||
- 在`调度中心`的`主调度台`选择对应任务与`人工排查`模式,单击`开始任务`即可开始人工排查。
|
|
||||||
- 软件将调起MAA,依次登录各用户的账号。
|
|
||||||
- 完成PRTS登录后,请人工检查代理情况,可以手动完成未代理的任务。
|
|
||||||
- 在对话框中单击对应账号的代理情况。
|
|
||||||
- 结束人工排查后,相应排查情况将被写入用户管理页的`备注栏目`。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -205,6 +82,8 @@ MAA多账号管理与自动化软件
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
感谢 [AoXuan (@ClozyA)](https://github.com/ClozyA) 为本项目提供的下载服务器
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
[](https://star-history.com/#DLmaster361/AUTO_MAA&Date)
|
[](https://star-history.com/#DLmaster361/AUTO_MAA&Date)
|
||||||
@@ -213,7 +92,7 @@ MAA多账号管理与自动化软件
|
|||||||
|
|
||||||
欢迎加入AUTO_MAA项目组,欢迎反馈bug
|
欢迎加入AUTO_MAA项目组,欢迎反馈bug
|
||||||
|
|
||||||
- QQ群:[957750551](https://qm.qq.com/q/bd9fISNoME)
|
- QQ交流群:[957750551](https://qm.qq.com/q/bd9fISNoME)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ __version__ = "4.2.0"
|
|||||||
__author__ = "DLmaster361 <DLmaster_361@163.com>"
|
__author__ = "DLmaster361 <DLmaster_361@163.com>"
|
||||||
__license__ = "GPL-3.0 license"
|
__license__ = "GPL-3.0 license"
|
||||||
|
|
||||||
from .core import AppConfig, QueueConfig, MaaConfig, Task, Task_manager, Main_timer
|
from .core import AppConfig, QueueConfig, MaaConfig, Task, TaskManager, MainTimer
|
||||||
from .models import MaaManager
|
from .models import MaaManager
|
||||||
from .services import Notify, Crypto, System
|
from .services import Notify, Crypto, System
|
||||||
from .ui import AUTO_MAA
|
from .ui import AUTO_MAA
|
||||||
@@ -40,8 +40,8 @@ __all__ = [
|
|||||||
"QueueConfig",
|
"QueueConfig",
|
||||||
"MaaConfig",
|
"MaaConfig",
|
||||||
"Task",
|
"Task",
|
||||||
"Task_manager",
|
"TaskManager",
|
||||||
"Main_timer",
|
"MainTimer",
|
||||||
"MaaManager",
|
"MaaManager",
|
||||||
"Notify",
|
"Notify",
|
||||||
"Crypto",
|
"Crypto",
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ __license__ = "GPL-3.0 license"
|
|||||||
|
|
||||||
from .config import AppConfig, QueueConfig, MaaConfig, Config
|
from .config import AppConfig, QueueConfig, MaaConfig, Config
|
||||||
from .main_info_bar import MainInfoBar
|
from .main_info_bar import MainInfoBar
|
||||||
from .task_manager import Task, Task_manager
|
from .task_manager import Task, TaskManager
|
||||||
from .timer import Main_timer
|
from .timer import MainTimer
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AppConfig",
|
"AppConfig",
|
||||||
@@ -41,6 +41,6 @@ __all__ = [
|
|||||||
"MaaConfig",
|
"MaaConfig",
|
||||||
"MainInfoBar",
|
"MainInfoBar",
|
||||||
"Task",
|
"Task",
|
||||||
"Task_manager",
|
"TaskManager",
|
||||||
"Main_timer",
|
"MainTimer",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ import sqlite3
|
|||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
from collections import defaultdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from qfluentwidgets import (
|
from qfluentwidgets import (
|
||||||
QConfig,
|
QConfig,
|
||||||
@@ -42,6 +45,7 @@ from qfluentwidgets import (
|
|||||||
OptionsValidator,
|
OptionsValidator,
|
||||||
qconfig,
|
qconfig,
|
||||||
)
|
)
|
||||||
|
from typing import Union, Dict, List, Tuple
|
||||||
|
|
||||||
|
|
||||||
class AppConfig:
|
class AppConfig:
|
||||||
@@ -54,7 +58,7 @@ class AppConfig:
|
|||||||
self.log_path = self.app_path / "debug/AUTO_MAA.log"
|
self.log_path = self.app_path / "debug/AUTO_MAA.log"
|
||||||
self.database_path = self.app_path / "data/data.db"
|
self.database_path = self.app_path / "data/data.db"
|
||||||
self.config_path = self.app_path / "config/config.json"
|
self.config_path = self.app_path / "config/config.json"
|
||||||
self.history_path = self.app_path / "config/history.json"
|
self.history_path = self.app_path / "history/main.json"
|
||||||
self.key_path = self.app_path / "data/key"
|
self.key_path = self.app_path / "data/key"
|
||||||
self.gameid_path = self.app_path / "data/gameid.txt"
|
self.gameid_path = self.app_path / "data/gameid.txt"
|
||||||
self.version_path = self.app_path / "resources/version.json"
|
self.version_path = self.app_path / "resources/version.json"
|
||||||
@@ -74,6 +78,7 @@ class AppConfig:
|
|||||||
(self.app_path / "config").mkdir(parents=True, exist_ok=True)
|
(self.app_path / "config").mkdir(parents=True, exist_ok=True)
|
||||||
(self.app_path / "data").mkdir(parents=True, exist_ok=True)
|
(self.app_path / "data").mkdir(parents=True, exist_ok=True)
|
||||||
(self.app_path / "debug").mkdir(parents=True, exist_ok=True)
|
(self.app_path / "debug").mkdir(parents=True, exist_ok=True)
|
||||||
|
(self.app_path / "history").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# 生成版本信息文件
|
# 生成版本信息文件
|
||||||
if not self.version_path.exists():
|
if not self.version_path.exists():
|
||||||
@@ -127,6 +132,8 @@ class AppConfig:
|
|||||||
self.queue_config = QueueConfig()
|
self.queue_config = QueueConfig()
|
||||||
self.maa_config = MaaConfig()
|
self.maa_config = MaaConfig()
|
||||||
|
|
||||||
|
qconfig.load(self.config_path, self.global_config)
|
||||||
|
|
||||||
config_list = self.search_config()
|
config_list = self.search_config()
|
||||||
for config in config_list:
|
for config in config_list:
|
||||||
if config[0] == "Maa":
|
if config[0] == "Maa":
|
||||||
@@ -459,6 +466,224 @@ class AppConfig:
|
|||||||
cur.close()
|
cur.close()
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
def save_maa_log(self, log_path: Path, logs: list, maa_result: str) -> bool:
|
||||||
|
"""保存MAA日志"""
|
||||||
|
|
||||||
|
data: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = {
|
||||||
|
"recruit_statistics": defaultdict(int),
|
||||||
|
"drop_statistics": defaultdict(dict),
|
||||||
|
"maa_result": maa_result,
|
||||||
|
}
|
||||||
|
|
||||||
|
if_six_star = False
|
||||||
|
|
||||||
|
# 公招统计(仅统计招募到的)
|
||||||
|
confirmed_recruit = False
|
||||||
|
current_star_level = None
|
||||||
|
i = 0
|
||||||
|
while i < len(logs):
|
||||||
|
if "公招识别结果:" in logs[i]:
|
||||||
|
current_star_level = None # 每次识别公招时清空之前的星级
|
||||||
|
i += 1
|
||||||
|
while i < len(logs) and "Tags" not in logs[i]: # 读取所有公招标签
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i < len(logs) and "Tags" in logs[i]: # 识别星级
|
||||||
|
star_match = re.search(r"(\d+)\s*★ Tags", logs[i])
|
||||||
|
if star_match:
|
||||||
|
current_star_level = f"{star_match.group(1)}★"
|
||||||
|
if current_star_level == "6★":
|
||||||
|
if_six_star = True
|
||||||
|
|
||||||
|
if "已确认招募" in logs[i]: # 只有确认招募后才统计
|
||||||
|
confirmed_recruit = True
|
||||||
|
|
||||||
|
if confirmed_recruit and current_star_level:
|
||||||
|
data["recruit_statistics"][current_star_level] += 1
|
||||||
|
confirmed_recruit = False # 重置,等待下一次公招
|
||||||
|
current_star_level = None # 清空已处理的星级
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# 掉落统计
|
||||||
|
current_stage = None
|
||||||
|
stage_drops = {}
|
||||||
|
|
||||||
|
for i, line in enumerate(logs):
|
||||||
|
drop_match = re.search(r"([A-Za-z0-9\-]+) 掉落统计:", line)
|
||||||
|
if drop_match:
|
||||||
|
# 发现新关卡,保存前一个关卡数据
|
||||||
|
if current_stage and stage_drops:
|
||||||
|
data["drop_statistics"][current_stage] = stage_drops
|
||||||
|
|
||||||
|
current_stage = drop_match.group(1)
|
||||||
|
if current_stage == "WE":
|
||||||
|
current_stage = "剿灭模式"
|
||||||
|
stage_drops = {}
|
||||||
|
continue
|
||||||
|
|
||||||
|
if current_stage:
|
||||||
|
item_match: List[str] = re.findall(
|
||||||
|
r"^(?!\[)([\u4e00-\u9fa5A-Za-z0-9\-]+)\s*:\s*([\d,]+)(?:\s*\(\+[\d,]+\))?",
|
||||||
|
line,
|
||||||
|
re.M,
|
||||||
|
)
|
||||||
|
for item, total in item_match:
|
||||||
|
# 解析数值时去掉逗号 (如 2,160 -> 2160)
|
||||||
|
total = int(total.replace(",", ""))
|
||||||
|
|
||||||
|
# 黑名单
|
||||||
|
if item not in [
|
||||||
|
"当前次数",
|
||||||
|
"理智",
|
||||||
|
"最快截图耗时",
|
||||||
|
"专精等级",
|
||||||
|
"剩余时间",
|
||||||
|
]:
|
||||||
|
stage_drops[item] = total
|
||||||
|
|
||||||
|
# 处理最后一个关卡的掉落数据
|
||||||
|
if current_stage and stage_drops:
|
||||||
|
data["drop_statistics"][current_stage] = stage_drops
|
||||||
|
|
||||||
|
# 保存日志
|
||||||
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with log_path.open("w", encoding="utf-8") as f:
|
||||||
|
f.writelines(logs)
|
||||||
|
with log_path.with_suffix(".json").open("w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
logger.info(f"处理完成:{log_path}")
|
||||||
|
|
||||||
|
self.merge_maa_logs("所有项", log_path.parent)
|
||||||
|
|
||||||
|
return if_six_star
|
||||||
|
|
||||||
|
def merge_maa_logs(self, mode: str, logs_path: Union[Path, List[Path]]) -> dict:
|
||||||
|
"""合并指定数据统计信息文件"""
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"recruit_statistics": defaultdict(int),
|
||||||
|
"drop_statistics": defaultdict(dict),
|
||||||
|
"maa_result": defaultdict(str),
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode == "所有项":
|
||||||
|
logs_path_list = list(logs_path.glob("*.json"))
|
||||||
|
elif mode == "指定项":
|
||||||
|
logs_path_list = logs_path
|
||||||
|
|
||||||
|
for json_file in logs_path_list:
|
||||||
|
|
||||||
|
with json_file.open("r", encoding="utf-8") as f:
|
||||||
|
single_data: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = (
|
||||||
|
json.load(f)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 合并公招统计
|
||||||
|
for star_level, count in single_data["recruit_statistics"].items():
|
||||||
|
data["recruit_statistics"][star_level] += count
|
||||||
|
|
||||||
|
# 合并掉落统计
|
||||||
|
for stage, drops in single_data["drop_statistics"].items():
|
||||||
|
if stage not in data["drop_statistics"]:
|
||||||
|
data["drop_statistics"][stage] = {} # 初始化关卡
|
||||||
|
|
||||||
|
for item, count in drops.items():
|
||||||
|
|
||||||
|
if item in data["drop_statistics"][stage]:
|
||||||
|
data["drop_statistics"][stage][item] += count
|
||||||
|
else:
|
||||||
|
data["drop_statistics"][stage][item] = count
|
||||||
|
|
||||||
|
# 合并MAA结果
|
||||||
|
data["maa_result"][
|
||||||
|
json_file.name.replace(".json", "").replace("-", ":")
|
||||||
|
] = single_data["maa_result"]
|
||||||
|
|
||||||
|
# 生成汇总 JSON 文件
|
||||||
|
if mode == "所有项":
|
||||||
|
|
||||||
|
with logs_path.with_suffix(".json").open("w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
logger.info(f"统计完成:{logs_path.with_suffix(".json")}")
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def load_maa_logs(
|
||||||
|
self, mode: str, json_path: Path
|
||||||
|
) -> Dict[str, Union[str, list, Dict[str, list]]]:
|
||||||
|
"""加载MAA日志统计信息"""
|
||||||
|
|
||||||
|
if mode == "总览":
|
||||||
|
|
||||||
|
with json_path.open("r", encoding="utf-8") as f:
|
||||||
|
info: Dict[str, Dict[str, Union[int, dict]]] = json.load(f)
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
data["条目索引"] = [
|
||||||
|
[k, "完成" if v == "Success!" else "异常"]
|
||||||
|
for k, v in info["maa_result"].items()
|
||||||
|
]
|
||||||
|
data["条目索引"].insert(0, ["数据总览", "运行"])
|
||||||
|
data["统计数据"] = {"公招统计": list(info["recruit_statistics"].items())}
|
||||||
|
|
||||||
|
for game_id, drops in info["drop_statistics"].items():
|
||||||
|
data["统计数据"][f"掉落统计:{game_id}"] = list(drops.items())
|
||||||
|
|
||||||
|
data["统计数据"]["报错汇总"] = [
|
||||||
|
[k, v] for k, v in info["maa_result"].items() if v != "Success!"
|
||||||
|
]
|
||||||
|
|
||||||
|
elif mode == "单项":
|
||||||
|
|
||||||
|
with json_path.open("r", encoding="utf-8") as f:
|
||||||
|
info: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = json.load(f)
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
data["统计数据"] = {"公招统计": list(info["recruit_statistics"].items())}
|
||||||
|
|
||||||
|
for game_id, drops in info["drop_statistics"].items():
|
||||||
|
data["统计数据"][f"掉落统计:{game_id}"] = list(drops.items())
|
||||||
|
|
||||||
|
with json_path.with_suffix(".log").open("r", encoding="utf-8") as f:
|
||||||
|
log = f.read()
|
||||||
|
|
||||||
|
data["日志信息"] = log
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def search_history(self) -> dict:
|
||||||
|
"""搜索所有历史记录"""
|
||||||
|
|
||||||
|
history_dict = {}
|
||||||
|
|
||||||
|
for date_folder in (Config.app_path / "history").iterdir():
|
||||||
|
if not date_folder.is_dir():
|
||||||
|
continue # 只处理日期文件夹
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
date = datetime.strptime(date_folder.name, "%Y-%m-%d")
|
||||||
|
|
||||||
|
history_dict[date.strftime("%Y年 %m月 %d日")] = list(
|
||||||
|
date_folder.glob("*.json")
|
||||||
|
)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
logger.warning(f"非日期格式的目录: {date_folder}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
k: v
|
||||||
|
for k, v in sorted(
|
||||||
|
history_dict.items(),
|
||||||
|
key=lambda x: datetime.strptime(x[0], "%Y年 %m月 %d日"),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def save_history(self, key: str, content: dict) -> None:
|
def save_history(self, key: str, content: dict) -> None:
|
||||||
"""保存历史记录"""
|
"""保存历史记录"""
|
||||||
|
|
||||||
@@ -486,6 +711,7 @@ class AppConfig:
|
|||||||
|
|
||||||
self.maa_config.set(self.maa_config.MaaSet_Name, "")
|
self.maa_config.set(self.maa_config.MaaSet_Name, "")
|
||||||
self.maa_config.set(self.maa_config.MaaSet_Path, ".")
|
self.maa_config.set(self.maa_config.MaaSet_Path, ".")
|
||||||
|
self.maa_config.set(self.maa_config.RunSet_TaskTransitionMethod, "ExitEmulator")
|
||||||
self.maa_config.set(self.maa_config.RunSet_ProxyTimesLimit, 0)
|
self.maa_config.set(self.maa_config.RunSet_ProxyTimesLimit, 0)
|
||||||
self.maa_config.set(self.maa_config.RunSet_AnnihilationTimeLimit, 40)
|
self.maa_config.set(self.maa_config.RunSet_AnnihilationTimeLimit, 40)
|
||||||
self.maa_config.set(self.maa_config.RunSet_RoutineTimeLimit, 10)
|
self.maa_config.set(self.maa_config.RunSet_RoutineTimeLimit, 10)
|
||||||
@@ -499,6 +725,7 @@ class AppConfig:
|
|||||||
|
|
||||||
self.queue_config.set(self.queue_config.queueSet_Name, "")
|
self.queue_config.set(self.queue_config.queueSet_Name, "")
|
||||||
self.queue_config.set(self.queue_config.queueSet_Enabled, False)
|
self.queue_config.set(self.queue_config.queueSet_Enabled, False)
|
||||||
|
self.queue_config.set(self.queue_config.queueSet_AfterAccomplish, "None")
|
||||||
|
|
||||||
self.queue_config.set(self.queue_config.time_TimeEnabled_0, False)
|
self.queue_config.set(self.queue_config.time_TimeEnabled_0, False)
|
||||||
self.queue_config.set(self.queue_config.time_TimeSet_0, "00:00")
|
self.queue_config.set(self.queue_config.time_TimeSet_0, "00:00")
|
||||||
@@ -536,14 +763,29 @@ class AppConfig:
|
|||||||
class GlobalConfig(QConfig):
|
class GlobalConfig(QConfig):
|
||||||
"""全局配置"""
|
"""全局配置"""
|
||||||
|
|
||||||
|
function_HomeImageMode = OptionsConfigItem(
|
||||||
|
"Function",
|
||||||
|
"HomeImageMode",
|
||||||
|
"默认",
|
||||||
|
OptionsValidator(["默认", "自定义", "主题图像"]),
|
||||||
|
)
|
||||||
|
function_HistoryRetentionTime = OptionsConfigItem(
|
||||||
|
"Function", "HistoryRetentionTime", 0, OptionsValidator([7, 15, 30, 60, 0])
|
||||||
|
)
|
||||||
function_IfAllowSleep = ConfigItem(
|
function_IfAllowSleep = ConfigItem(
|
||||||
"Function", "IfAllowSleep", False, BoolValidator()
|
"Function", "IfAllowSleep", False, BoolValidator()
|
||||||
)
|
)
|
||||||
function_IfSilence = ConfigItem("Function", "IfSilence", False, BoolValidator())
|
function_IfSilence = ConfigItem("Function", "IfSilence", False, BoolValidator())
|
||||||
function_BossKey = ConfigItem("Function", "BossKey", "")
|
function_BossKey = ConfigItem("Function", "BossKey", "")
|
||||||
|
function_IfAgreeBilibili = ConfigItem(
|
||||||
|
"Function", "IfAgreeBilibili", False, BoolValidator()
|
||||||
|
)
|
||||||
|
|
||||||
start_IfSelfStart = ConfigItem("Start", "IfSelfStart", False, BoolValidator())
|
start_IfSelfStart = ConfigItem("Start", "IfSelfStart", False, BoolValidator())
|
||||||
start_IfRunDirectly = ConfigItem("Start", "IfRunDirectly", False, BoolValidator())
|
start_IfRunDirectly = ConfigItem("Start", "IfRunDirectly", False, BoolValidator())
|
||||||
|
start_IfMinimizeDirectly = ConfigItem(
|
||||||
|
"Start", "IfMinimizeDirectly", False, BoolValidator()
|
||||||
|
)
|
||||||
|
|
||||||
ui_IfShowTray = ConfigItem("UI", "IfShowTray", False, BoolValidator())
|
ui_IfShowTray = ConfigItem("UI", "IfShowTray", False, BoolValidator())
|
||||||
ui_IfToTray = ConfigItem("UI", "IfToTray", False, BoolValidator())
|
ui_IfToTray = ConfigItem("UI", "IfToTray", False, BoolValidator())
|
||||||
@@ -551,12 +793,22 @@ class GlobalConfig(QConfig):
|
|||||||
ui_location = ConfigItem("UI", "location", "100x100")
|
ui_location = ConfigItem("UI", "location", "100x100")
|
||||||
ui_maximized = ConfigItem("UI", "maximized", False, BoolValidator())
|
ui_maximized = ConfigItem("UI", "maximized", False, BoolValidator())
|
||||||
|
|
||||||
|
notify_SendTaskResultTime = OptionsConfigItem(
|
||||||
|
"Notify",
|
||||||
|
"SendTaskResultTime",
|
||||||
|
"不推送",
|
||||||
|
OptionsValidator(["不推送", "任何时刻", "仅失败时"]),
|
||||||
|
)
|
||||||
|
notify_IfSendStatistic = ConfigItem(
|
||||||
|
"Notify", "IfSendStatistic", False, BoolValidator()
|
||||||
|
)
|
||||||
|
notify_IfSendSixStar = ConfigItem("Notify", "IfSendSixStar", False, BoolValidator())
|
||||||
notify_IfPushPlyer = ConfigItem("Notify", "IfPushPlyer", False, BoolValidator())
|
notify_IfPushPlyer = ConfigItem("Notify", "IfPushPlyer", False, BoolValidator())
|
||||||
notify_IfSendMail = ConfigItem("Notify", "IfSendMail", False, BoolValidator())
|
notify_IfSendMail = ConfigItem("Notify", "IfSendMail", False, BoolValidator())
|
||||||
notify_IfSendErrorOnly = ConfigItem(
|
notify_SMTPServerAddress = ConfigItem("Notify", "SMTPServerAddress", "")
|
||||||
"Notify", "IfSendErrorOnly", False, BoolValidator()
|
notify_AuthorizationCode = ConfigItem("Notify", "AuthorizationCode", "")
|
||||||
)
|
notify_FromAddress = ConfigItem("Notify", "FromAddress", "")
|
||||||
notify_MailAddress = ConfigItem("Notify", "MailAddress", "")
|
notify_ToAddress = ConfigItem("Notify", "ToAddress", "")
|
||||||
notify_IfServerChan = ConfigItem("Notify", "IfServerChan", False, BoolValidator())
|
notify_IfServerChan = ConfigItem("Notify", "IfServerChan", False, BoolValidator())
|
||||||
notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
||||||
notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
||||||
@@ -579,6 +831,12 @@ class QueueConfig(QConfig):
|
|||||||
|
|
||||||
queueSet_Name = ConfigItem("QueueSet", "Name", "")
|
queueSet_Name = ConfigItem("QueueSet", "Name", "")
|
||||||
queueSet_Enabled = ConfigItem("QueueSet", "Enabled", False, BoolValidator())
|
queueSet_Enabled = ConfigItem("QueueSet", "Enabled", False, BoolValidator())
|
||||||
|
queueSet_AfterAccomplish = OptionsConfigItem(
|
||||||
|
"QueueSet",
|
||||||
|
"AfterAccomplish",
|
||||||
|
"None",
|
||||||
|
OptionsValidator(["None", "KillSelf", "Sleep", "Hibernate", "Shutdown"]),
|
||||||
|
)
|
||||||
|
|
||||||
time_TimeEnabled_0 = ConfigItem("Time", "TimeEnabled_0", False, BoolValidator())
|
time_TimeEnabled_0 = ConfigItem("Time", "TimeEnabled_0", False, BoolValidator())
|
||||||
time_TimeSet_0 = ConfigItem("Time", "TimeSet_0", "00:00")
|
time_TimeSet_0 = ConfigItem("Time", "TimeSet_0", "00:00")
|
||||||
@@ -628,6 +886,12 @@ class MaaConfig(QConfig):
|
|||||||
MaaSet_Name = ConfigItem("MaaSet", "Name", "")
|
MaaSet_Name = ConfigItem("MaaSet", "Name", "")
|
||||||
MaaSet_Path = ConfigItem("MaaSet", "Path", ".", FolderValidator())
|
MaaSet_Path = ConfigItem("MaaSet", "Path", ".", FolderValidator())
|
||||||
|
|
||||||
|
RunSet_TaskTransitionMethod = OptionsConfigItem(
|
||||||
|
"RunSet",
|
||||||
|
"TaskTransitionMethod",
|
||||||
|
"ExitEmulator",
|
||||||
|
OptionsValidator(["NoAction", "ExitGame", "ExitEmulator"]),
|
||||||
|
)
|
||||||
RunSet_ProxyTimesLimit = RangeConfigItem(
|
RunSet_ProxyTimesLimit = RangeConfigItem(
|
||||||
"RunSet", "ProxyTimesLimit", 0, RangeValidator(0, 1024)
|
"RunSet", "ProxyTimesLimit", 0, RangeValidator(0, 1024)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ from qfluentwidgets import (
|
|||||||
class _MainInfoBar:
|
class _MainInfoBar:
|
||||||
"""信息通知栏"""
|
"""信息通知栏"""
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, main_window=None):
|
||||||
|
|
||||||
self.parent = parent
|
self.main_window = main_window
|
||||||
|
|
||||||
def push_info_bar(self, mode: str, title: str, content: str, time: int):
|
def push_info_bar(self, mode: str, title: str, content: str, time: int):
|
||||||
"""推送到信息通知栏"""
|
"""推送到信息通知栏"""
|
||||||
|
|
||||||
if self.parent is None:
|
if self.main_window is None:
|
||||||
logger.error("信息通知栏未设置父窗口")
|
logger.error("信息通知栏未设置父窗口")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ class _MainInfoBar:
|
|||||||
isClosable=True,
|
isClosable=True,
|
||||||
position=InfoBarPosition.TOP_RIGHT,
|
position=InfoBarPosition.TOP_RIGHT,
|
||||||
duration=time,
|
duration=time,
|
||||||
parent=self.parent,
|
parent=self.main_window,
|
||||||
)
|
)
|
||||||
elif mode == "warning":
|
elif mode == "warning":
|
||||||
InfoBar.warning(
|
InfoBar.warning(
|
||||||
@@ -65,7 +65,7 @@ class _MainInfoBar:
|
|||||||
isClosable=True,
|
isClosable=True,
|
||||||
position=InfoBarPosition.TOP_RIGHT,
|
position=InfoBarPosition.TOP_RIGHT,
|
||||||
duration=time,
|
duration=time,
|
||||||
parent=self.parent,
|
parent=self.main_window,
|
||||||
)
|
)
|
||||||
elif mode == "error":
|
elif mode == "error":
|
||||||
InfoBar.error(
|
InfoBar.error(
|
||||||
@@ -75,7 +75,7 @@ class _MainInfoBar:
|
|||||||
isClosable=True,
|
isClosable=True,
|
||||||
position=InfoBarPosition.TOP_RIGHT,
|
position=InfoBarPosition.TOP_RIGHT,
|
||||||
duration=time,
|
duration=time,
|
||||||
parent=self.parent,
|
parent=self.main_window,
|
||||||
)
|
)
|
||||||
elif mode == "info":
|
elif mode == "info":
|
||||||
InfoBar.info(
|
InfoBar.info(
|
||||||
@@ -85,7 +85,7 @@ class _MainInfoBar:
|
|||||||
isClosable=True,
|
isClosable=True,
|
||||||
position=InfoBarPosition.TOP_RIGHT,
|
position=InfoBarPosition.TOP_RIGHT,
|
||||||
duration=time,
|
duration=time,
|
||||||
parent=self.parent,
|
parent=self.main_window,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ v4.2
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
from PySide6.QtCore import QThread, QObject, Signal
|
from PySide6.QtCore import QThread, QObject, Signal
|
||||||
from qfluentwidgets import Dialog
|
from qfluentwidgets import Dialog
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, Union
|
from typing import Dict, Union
|
||||||
@@ -35,6 +36,7 @@ from typing import Dict, Union
|
|||||||
from .config import Config
|
from .config import Config
|
||||||
from .main_info_bar import MainInfoBar
|
from .main_info_bar import MainInfoBar
|
||||||
from app.models import MaaManager
|
from app.models import MaaManager
|
||||||
|
from app.services import System
|
||||||
|
|
||||||
|
|
||||||
class Task(QThread):
|
class Task(QThread):
|
||||||
@@ -52,10 +54,7 @@ class Task(QThread):
|
|||||||
accomplish = Signal(list)
|
accomplish = Signal(list)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, mode: str, name: str, info: Dict[str, Dict[str, Union[str, int, bool]]]
|
||||||
mode: str,
|
|
||||||
name: str,
|
|
||||||
info: Dict[str, Dict[str, Union[str, int, bool]]],
|
|
||||||
):
|
):
|
||||||
super(Task, self).__init__()
|
super(Task, self).__init__()
|
||||||
|
|
||||||
@@ -92,41 +91,41 @@ class Task(QThread):
|
|||||||
else:
|
else:
|
||||||
|
|
||||||
self.member_dict = self.search_member()
|
self.member_dict = self.search_member()
|
||||||
self.task_list = [
|
self.task_dict = [
|
||||||
[value, "等待"]
|
[value, "等待"]
|
||||||
for _, value in self.info["Queue"].items()
|
for _, value in self.info["Queue"].items()
|
||||||
if value != "禁用"
|
if value != "禁用"
|
||||||
]
|
]
|
||||||
|
|
||||||
self.create_task_list.emit(self.task_list)
|
self.create_task_list.emit(self.task_dict)
|
||||||
|
|
||||||
for i in range(len(self.task_list)):
|
for i in range(len(self.task_dict)):
|
||||||
|
|
||||||
if self.isInterruptionRequested():
|
if self.isInterruptionRequested():
|
||||||
break
|
break
|
||||||
|
|
||||||
self.task_list[i][1] = "运行"
|
self.task_dict[i][1] = "运行"
|
||||||
self.update_task_list.emit(self.task_list)
|
self.update_task_list.emit(self.task_dict)
|
||||||
|
|
||||||
if self.task_list[i][0] in Config.running_list:
|
if self.task_dict[i][0] in Config.running_list:
|
||||||
|
|
||||||
self.task_list[i][1] = "跳过"
|
self.task_dict[i][1] = "跳过"
|
||||||
self.update_task_list.emit(self.task_list)
|
self.update_task_list.emit(self.task_dict)
|
||||||
logger.info(f"跳过任务:{self.task_list[i][0]}")
|
logger.info(f"跳过任务:{self.task_dict[i][0]}")
|
||||||
self.push_info_bar.emit(
|
self.push_info_bar.emit(
|
||||||
"info", "跳过任务", self.task_list[i][0], 3000
|
"info", "跳过任务", self.task_dict[i][0], 3000
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
Config.running_list.append(self.task_list[i][0])
|
Config.running_list.append(self.task_dict[i][0])
|
||||||
logger.info(f"任务开始:{self.task_list[i][0]}")
|
logger.info(f"任务开始:{self.task_dict[i][0]}")
|
||||||
self.push_info_bar.emit("info", "任务开始", self.task_list[i][0], 3000)
|
self.push_info_bar.emit("info", "任务开始", self.task_dict[i][0], 3000)
|
||||||
|
|
||||||
if self.member_dict[self.task_list[i][0]][0] == "Maa":
|
if self.member_dict[self.task_dict[i][0]][0] == "Maa":
|
||||||
|
|
||||||
self.task = MaaManager(
|
self.task = MaaManager(
|
||||||
self.mode[0:4],
|
self.mode[0:4],
|
||||||
self.member_dict[self.task_list[i][0]][1],
|
self.member_dict[self.task_dict[i][0]][1],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.task.question.connect(self.question.emit)
|
self.task.question.connect(self.question.emit)
|
||||||
@@ -138,7 +137,7 @@ class Task(QThread):
|
|||||||
self.task.update_log_text.connect(self.update_log_text.emit)
|
self.task.update_log_text.connect(self.update_log_text.emit)
|
||||||
self.task.update_user_info.connect(
|
self.task.update_user_info.connect(
|
||||||
lambda modes, uids, days, lasts, notes, numbs: self.update_user_info.emit(
|
lambda modes, uids, days, lasts, notes, numbs: self.update_user_info.emit(
|
||||||
self.member_dict[self.task_list[i][0]][1],
|
self.member_dict[self.task_dict[i][0]][1],
|
||||||
modes,
|
modes,
|
||||||
uids,
|
uids,
|
||||||
days,
|
days,
|
||||||
@@ -148,16 +147,16 @@ class Task(QThread):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.task.accomplish.connect(
|
self.task.accomplish.connect(
|
||||||
lambda log: self.save_log(self.task_list[i][0], log)
|
lambda log: self.task_accomplish(self.task_dict[i][0], log)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.task.run()
|
self.task.run()
|
||||||
|
|
||||||
Config.running_list.remove(self.task_list[i][0])
|
Config.running_list.remove(self.task_dict[i][0])
|
||||||
|
|
||||||
self.task_list[i][1] = "完成"
|
self.task_dict[i][1] = "完成"
|
||||||
logger.info(f"任务完成:{self.task_list[i][0]}")
|
logger.info(f"任务完成:{self.task_dict[i][0]}")
|
||||||
self.push_info_bar.emit("info", "任务完成", self.task_list[i][0], 3000)
|
self.push_info_bar.emit("info", "任务完成", self.task_dict[i][0], 3000)
|
||||||
|
|
||||||
self.accomplish.emit(self.logs)
|
self.accomplish.emit(self.logs)
|
||||||
|
|
||||||
@@ -174,13 +173,14 @@ class Task(QThread):
|
|||||||
|
|
||||||
return member_dict
|
return member_dict
|
||||||
|
|
||||||
def save_log(self, name: str, log: dict):
|
def task_accomplish(self, name: str, log: dict):
|
||||||
"""保存保存任务结果"""
|
"""保存保存任务结果"""
|
||||||
|
|
||||||
self.logs.append([name, log])
|
self.logs.append([name, log])
|
||||||
|
self.task.deleteLater()
|
||||||
|
|
||||||
|
|
||||||
class TaskManager(QObject):
|
class _TaskManager(QObject):
|
||||||
"""业务调度器"""
|
"""业务调度器"""
|
||||||
|
|
||||||
create_gui = Signal(Task)
|
create_gui = Signal(Task)
|
||||||
@@ -188,16 +188,16 @@ class TaskManager(QObject):
|
|||||||
push_info_bar = Signal(str, str, str, int)
|
push_info_bar = Signal(str, str, str, int)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(TaskManager, self).__init__()
|
super(_TaskManager, self).__init__()
|
||||||
|
|
||||||
self.task_list: Dict[str, Task] = {}
|
self.task_dict: Dict[str, Task] = {}
|
||||||
|
|
||||||
def add_task(
|
def add_task(
|
||||||
self, mode: str, name: str, info: Dict[str, Dict[str, Union[str, int, bool]]]
|
self, mode: str, name: str, info: Dict[str, Dict[str, Union[str, int, bool]]]
|
||||||
):
|
):
|
||||||
"""添加任务"""
|
"""添加任务"""
|
||||||
|
|
||||||
if name in Config.running_list or name in self.task_list:
|
if name in Config.running_list or name in self.task_dict:
|
||||||
|
|
||||||
logger.warning(f"任务已存在:{name}")
|
logger.warning(f"任务已存在:{name}")
|
||||||
MainInfoBar.push_info_bar("warning", "任务已存在", name, 5000)
|
MainInfoBar.push_info_bar("warning", "任务已存在", name, 5000)
|
||||||
@@ -207,23 +207,23 @@ class TaskManager(QObject):
|
|||||||
MainInfoBar.push_info_bar("info", "任务开始", name, 3000)
|
MainInfoBar.push_info_bar("info", "任务开始", name, 3000)
|
||||||
|
|
||||||
Config.running_list.append(name)
|
Config.running_list.append(name)
|
||||||
self.task_list[name] = Task(mode, name, info)
|
self.task_dict[name] = Task(mode, name, info)
|
||||||
self.task_list[name].question.connect(
|
self.task_dict[name].question.connect(
|
||||||
lambda title, content: self.push_dialog(name, title, content)
|
lambda title, content: self.push_dialog(name, title, content)
|
||||||
)
|
)
|
||||||
self.task_list[name].push_info_bar.connect(MainInfoBar.push_info_bar)
|
self.task_dict[name].push_info_bar.connect(MainInfoBar.push_info_bar)
|
||||||
self.task_list[name].update_user_info.connect(Config.change_user_info)
|
self.task_dict[name].update_user_info.connect(Config.change_user_info)
|
||||||
self.task_list[name].accomplish.connect(
|
self.task_dict[name].accomplish.connect(
|
||||||
lambda logs: self.remove_task(name, logs)
|
lambda logs: self.remove_task(mode, name, logs)
|
||||||
)
|
)
|
||||||
|
|
||||||
if "新调度台" in mode:
|
if "新调度台" in mode:
|
||||||
self.create_gui.emit(self.task_list[name])
|
self.create_gui.emit(self.task_dict[name])
|
||||||
|
|
||||||
elif "主调度台" in mode:
|
elif "主调度台" in mode:
|
||||||
self.connect_gui.emit(self.task_list[name])
|
self.connect_gui.emit(self.task_dict[name])
|
||||||
|
|
||||||
self.task_list[name].start()
|
self.task_dict[name].start()
|
||||||
|
|
||||||
def stop_task(self, name: str):
|
def stop_task(self, name: str):
|
||||||
"""中止任务"""
|
"""中止任务"""
|
||||||
@@ -233,26 +233,28 @@ class TaskManager(QObject):
|
|||||||
|
|
||||||
if name == "ALL":
|
if name == "ALL":
|
||||||
|
|
||||||
for name in self.task_list:
|
for name in self.task_dict:
|
||||||
|
|
||||||
self.task_list[name].task.requestInterruption()
|
self.task_dict[name].task.requestInterruption()
|
||||||
self.task_list[name].requestInterruption()
|
self.task_dict[name].requestInterruption()
|
||||||
self.task_list[name].quit()
|
self.task_dict[name].quit()
|
||||||
self.task_list[name].wait()
|
self.task_dict[name].wait()
|
||||||
|
|
||||||
elif name in self.task_list:
|
elif name in self.task_dict:
|
||||||
|
|
||||||
self.task_list[name].task.requestInterruption()
|
self.task_dict[name].task.requestInterruption()
|
||||||
self.task_list[name].requestInterruption()
|
self.task_dict[name].requestInterruption()
|
||||||
self.task_list[name].quit()
|
self.task_dict[name].quit()
|
||||||
self.task_list[name].wait()
|
self.task_dict[name].wait()
|
||||||
|
|
||||||
def remove_task(self, name: str, logs: str):
|
def remove_task(self, mode: str, name: str, logs: str):
|
||||||
"""移除任务标记"""
|
"""任务结束后的处理"""
|
||||||
|
|
||||||
logger.info(f"任务结束:{name}")
|
logger.info(f"任务结束:{name}")
|
||||||
MainInfoBar.push_info_bar("info", "任务结束", name, 3000)
|
MainInfoBar.push_info_bar("info", "任务结束", name, 3000)
|
||||||
|
|
||||||
|
self.task_dict[name].deleteLater()
|
||||||
|
|
||||||
if len(logs) > 0:
|
if len(logs) > 0:
|
||||||
time = logs[0][1]["Time"]
|
time = logs[0][1]["Time"]
|
||||||
history = ""
|
history = ""
|
||||||
@@ -271,9 +273,16 @@ class TaskManager(QObject):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
self.task_list.pop(name)
|
self.task_dict.pop(name)
|
||||||
Config.running_list.remove(name)
|
Config.running_list.remove(name)
|
||||||
|
|
||||||
|
if "调度队列" in name and "人工排查" not in mode:
|
||||||
|
with (Config.app_path / f"config/QueueConfig/{name}.json").open(
|
||||||
|
"r", encoding="utf-8"
|
||||||
|
) as f:
|
||||||
|
info = json.load(f)
|
||||||
|
System.set_power(info["QueueSet"]["AfterAccomplish"])
|
||||||
|
|
||||||
def push_dialog(self, name: str, title: str, content: str):
|
def push_dialog(self, name: str, title: str, content: str):
|
||||||
"""推送对话框"""
|
"""推送对话框"""
|
||||||
|
|
||||||
@@ -281,7 +290,7 @@ class TaskManager(QObject):
|
|||||||
choice.yesButton.setText("是")
|
choice.yesButton.setText("是")
|
||||||
choice.cancelButton.setText("否")
|
choice.cancelButton.setText("否")
|
||||||
|
|
||||||
self.task_list[name].question_response.emit(bool(choice.exec_()))
|
self.task_dict[name].question_response.emit(bool(choice.exec_()))
|
||||||
|
|
||||||
|
|
||||||
Task_manager = TaskManager()
|
TaskManager = _TaskManager()
|
||||||
|
|||||||
@@ -33,16 +33,13 @@ from datetime import datetime
|
|||||||
import pyautogui
|
import pyautogui
|
||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .task_manager import Task_manager
|
from .task_manager import TaskManager
|
||||||
from app.services import System
|
from app.services import System
|
||||||
|
|
||||||
|
|
||||||
class MainTimer(QWidget):
|
class _MainTimer(QWidget):
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, parent=None):
|
||||||
self,
|
|
||||||
parent=None,
|
|
||||||
):
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.if_FailSafeException = False
|
self.if_FailSafeException = False
|
||||||
@@ -81,30 +78,35 @@ class MainTimer(QWidget):
|
|||||||
):
|
):
|
||||||
|
|
||||||
logger.info(f"定时任务:{name}")
|
logger.info(f"定时任务:{name}")
|
||||||
Task_manager.add_task("自动代理_新调度台", name, info)
|
TaskManager.add_task("自动代理_新调度台", name, info)
|
||||||
|
|
||||||
def set_silence(self):
|
def set_silence(self):
|
||||||
"""设置静默模式"""
|
"""设置静默模式"""
|
||||||
|
|
||||||
windows = System.get_window_info()
|
if (
|
||||||
if any(
|
Config.global_config.get(Config.global_config.function_IfSilence)
|
||||||
str(emulator_path) in window
|
and Config.global_config.get(Config.global_config.function_BossKey) != ""
|
||||||
for window in windows
|
|
||||||
for emulator_path in Config.silence_list
|
|
||||||
):
|
):
|
||||||
try:
|
|
||||||
pyautogui.hotkey(
|
windows = System.get_window_info()
|
||||||
*[
|
if any(
|
||||||
_.strip().lower()
|
str(emulator_path) in window
|
||||||
for _ in Config.global_config.get(
|
for window in windows
|
||||||
Config.global_config.function_BossKey
|
for emulator_path in Config.silence_list
|
||||||
).split("+")
|
):
|
||||||
]
|
try:
|
||||||
)
|
pyautogui.hotkey(
|
||||||
except pyautogui.FailSafeException as e:
|
*[
|
||||||
if not self.if_FailSafeException:
|
_.strip().lower()
|
||||||
logger.warning(f"FailSafeException: {e}")
|
for _ in Config.global_config.get(
|
||||||
self.if_FailSafeException = True
|
Config.global_config.function_BossKey
|
||||||
|
).split("+")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
except pyautogui.FailSafeException as e:
|
||||||
|
if not self.if_FailSafeException:
|
||||||
|
logger.warning(f"FailSafeException: {e}")
|
||||||
|
self.if_FailSafeException = True
|
||||||
|
|
||||||
def search_queue(self) -> list:
|
def search_queue(self) -> list:
|
||||||
"""搜索所有调度队列实例"""
|
"""搜索所有调度队列实例"""
|
||||||
@@ -120,4 +122,4 @@ class MainTimer(QWidget):
|
|||||||
return queue_list
|
return queue_list
|
||||||
|
|
||||||
|
|
||||||
Main_timer = MainTimer()
|
MainTimer = _MainTimer()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -24,22 +24,33 @@ AUTO_MAA通知服务
|
|||||||
v4.2
|
v4.2
|
||||||
作者:DLmaster_361
|
作者:DLmaster_361
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QWidget
|
||||||
|
from PySide6.QtCore import Signal
|
||||||
import requests
|
import requests
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from plyer import notification
|
from plyer import notification
|
||||||
|
import re
|
||||||
import smtplib
|
import smtplib
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.header import Header
|
from email.header import Header
|
||||||
from email.utils import formataddr
|
from email.utils import formataddr
|
||||||
|
|
||||||
from serverchan_sdk import sc_send
|
from serverchan_sdk import sc_send
|
||||||
|
|
||||||
from app.core import Config
|
from app.core import Config
|
||||||
|
from app.services.security import Crypto
|
||||||
|
|
||||||
|
|
||||||
class Notification:
|
class Notification(QWidget):
|
||||||
|
|
||||||
def push_notification(self, title, message, ticker, t):
|
push_info_bar = Signal(str, str, str, int)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
def push_plyer(self, title, message, ticker, t):
|
||||||
"""推送系统通知"""
|
"""推送系统通知"""
|
||||||
|
|
||||||
if Config.global_config.get(Config.global_config.notify_IfPushPlyer):
|
if Config.global_config.get(Config.global_config.notify_IfPushPlyer):
|
||||||
@@ -56,74 +67,139 @@ class Notification:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def send_mail(self, title, content):
|
def send_mail(self, mode, title, content) -> None:
|
||||||
"""使用官方专用邮箱推送邮件通知"""
|
"""推送邮件通知"""
|
||||||
|
|
||||||
# 声明:此邮箱为AUTO_MAA项目组资产,未经授权不得私自使用
|
|
||||||
# 注意:此声明注释只有使用者更换发信邮箱时才能删除,本条规则优先级高于GPLv3
|
|
||||||
|
|
||||||
if Config.global_config.get(Config.global_config.notify_IfSendMail):
|
if Config.global_config.get(Config.global_config.notify_IfSendMail):
|
||||||
|
|
||||||
# 第三方 SMTP 服务配置
|
if (
|
||||||
mail_host = "smtp.163.com" # 设置服务器
|
Config.global_config.get(Config.global_config.notify_SMTPServerAddress)
|
||||||
mail_sender = "AUTO_MAA_server@163.com" # 用户名
|
== ""
|
||||||
mail_key = "SYrq87nDLD4RNB5T" # 授权码 24/11/15
|
or Config.global_config.get(
|
||||||
|
Config.global_config.notify_AuthorizationCode
|
||||||
# 定义邮件正文
|
|
||||||
message = MIMEText(content, "plain", "utf-8")
|
|
||||||
message["From"] = formataddr(
|
|
||||||
(
|
|
||||||
Header("AUTO_MAA通知服务", "utf-8").encode(),
|
|
||||||
"AUTO_MAA_server@163.com",
|
|
||||||
)
|
)
|
||||||
) # 发件人显示的名字
|
== ""
|
||||||
message["To"] = formataddr(
|
or not bool(
|
||||||
(
|
re.match(
|
||||||
Header("AUTO_MAA用户", "utf-8").encode(),
|
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||||
Config.global_config.get(Config.global_config.notify_MailAddress),
|
Config.global_config.get(
|
||||||
|
Config.global_config.notify_FromAddress
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) # 收件人显示的名字
|
or not bool(
|
||||||
message["Subject"] = Header(title, "utf-8")
|
re.match(
|
||||||
|
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||||
|
Config.global_config.get(Config.global_config.notify_ToAddress),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
):
|
||||||
|
logger.error(
|
||||||
|
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址"
|
||||||
|
)
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"error",
|
||||||
|
"邮件通知推送异常",
|
||||||
|
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址",
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 465为SMTP_SSL默认端口
|
# 定义邮件正文
|
||||||
smtpObj.login(mail_sender, mail_key)
|
if mode == "文本":
|
||||||
|
message = MIMEText(content, "plain", "utf-8")
|
||||||
|
elif mode == "网页":
|
||||||
|
message = MIMEMultipart("alternative")
|
||||||
|
message["From"] = formataddr(
|
||||||
|
(
|
||||||
|
Header("AUTO_MAA通知服务", "utf-8").encode(),
|
||||||
|
Config.global_config.get(
|
||||||
|
Config.global_config.notify_FromAddress
|
||||||
|
),
|
||||||
|
)
|
||||||
|
) # 发件人显示的名字
|
||||||
|
message["To"] = formataddr(
|
||||||
|
(
|
||||||
|
Header("AUTO_MAA用户", "utf-8").encode(),
|
||||||
|
Config.global_config.get(Config.global_config.notify_ToAddress),
|
||||||
|
)
|
||||||
|
) # 收件人显示的名字
|
||||||
|
message["Subject"] = Header(title, "utf-8")
|
||||||
|
|
||||||
|
if mode == "网页":
|
||||||
|
message.attach(MIMEText(content, "html", "utf-8"))
|
||||||
|
|
||||||
|
smtpObj = smtplib.SMTP_SSL(
|
||||||
|
Config.global_config.get(
|
||||||
|
Config.global_config.notify_SMTPServerAddress
|
||||||
|
),
|
||||||
|
465,
|
||||||
|
)
|
||||||
|
smtpObj.login(
|
||||||
|
Config.global_config.get(Config.global_config.notify_FromAddress),
|
||||||
|
Crypto.win_decryptor(
|
||||||
|
Config.global_config.get(
|
||||||
|
Config.global_config.notify_AuthorizationCode
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
smtpObj.sendmail(
|
smtpObj.sendmail(
|
||||||
mail_sender,
|
Config.global_config.get(Config.global_config.notify_FromAddress),
|
||||||
Config.global_config.get(Config.global_config.notify_MailAddress),
|
Config.global_config.get(Config.global_config.notify_ToAddress),
|
||||||
message.as_string(),
|
message.as_string(),
|
||||||
)
|
)
|
||||||
return True
|
|
||||||
except smtplib.SMTPException as e:
|
|
||||||
return f"发送邮件时出错:\n{e}"
|
|
||||||
finally:
|
|
||||||
smtpObj.quit()
|
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):
|
def ServerChanPush(self, title, content):
|
||||||
"""使用Server酱推送通知"""
|
"""使用Server酱推送通知"""
|
||||||
|
|
||||||
if Config.global_config.get(Config.global_config.notify_IfServerChan):
|
if Config.global_config.get(Config.global_config.notify_IfServerChan):
|
||||||
send_key = Config.global_config.get(Config.global_config.notify_ServerChanKey)
|
send_key = Config.global_config.get(
|
||||||
|
Config.global_config.notify_ServerChanKey
|
||||||
|
)
|
||||||
option = {}
|
option = {}
|
||||||
is_valid = lambda s: s == "" or (s == '|'.join(s.split('|')) and (s.count('|') == 0 or all(s.split('|'))))
|
is_valid = lambda s: s == "" or (
|
||||||
|
s == "|".join(s.split("|")) and (s.count("|") == 0 or all(s.split("|")))
|
||||||
|
)
|
||||||
"""
|
"""
|
||||||
is_valid => True, 如果启用的话需要正确设置Tag和Channel。
|
is_valid => True, 如果启用的话需要正确设置Tag和Channel。
|
||||||
允许空的Tag和Channel即不启用,但不允许例如a||b,|a|b,a|b|,||||
|
允许空的Tag和Channel即不启用,但不允许例如a||b,|a|b,a|b|,||||
|
||||||
"""
|
"""
|
||||||
send_tag = Config.global_config.get(Config.global_config.notify_ServerChanTag)
|
send_tag = Config.global_config.get(
|
||||||
send_channel = Config.global_config.get(Config.global_config.notify_ServerChanChannel)
|
Config.global_config.notify_ServerChanTag
|
||||||
|
)
|
||||||
|
send_channel = Config.global_config.get(
|
||||||
|
Config.global_config.notify_ServerChanChannel
|
||||||
|
)
|
||||||
|
|
||||||
if is_valid(send_tag):
|
if is_valid(send_tag):
|
||||||
option['tags'] = send_tag
|
option["tags"] = send_tag
|
||||||
else:
|
else:
|
||||||
option['tags'] = ''
|
option["tags"] = ""
|
||||||
logger.warning('请正确设置Auto_MAA中ServerChan的Tag。')
|
logger.warning("请正确设置Auto_MAA中ServerChan的Tag。")
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"warning",
|
||||||
|
"Server酱通知推送异常",
|
||||||
|
"请正确设置Auto_MAA中ServerChan的Tag。",
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
|
||||||
if is_valid(send_channel):
|
if is_valid(send_channel):
|
||||||
option['channel'] = send_channel
|
option["channel"] = send_channel
|
||||||
else:
|
else:
|
||||||
option['channel'] = ''
|
option["channel"] = ""
|
||||||
logger.warning('请正确设置Auto_MAA中ServerChan的Channel。')
|
logger.warning("请正确设置Auto_MAA中ServerChan的Channel。")
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"warning",
|
||||||
|
"Server酱通知推送异常",
|
||||||
|
"请正确设置Auto_MAA中ServerChan的Channel。",
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
|
||||||
response = sc_send(send_key, title, content, option)
|
response = sc_send(send_key, title, content, option)
|
||||||
if response["code"] == 0:
|
if response["code"] == 0:
|
||||||
@@ -132,21 +208,24 @@ class Notification:
|
|||||||
else:
|
else:
|
||||||
logger.info("Server酱推送通知失败")
|
logger.info("Server酱推送通知失败")
|
||||||
logger.error(response)
|
logger.error(response)
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"error",
|
||||||
|
"Server酱通知推送失败",
|
||||||
|
f'使用Server酱推送通知时出错:\n{response["data"]['error']}',
|
||||||
|
-1,
|
||||||
|
)
|
||||||
return f'使用Server酱推送通知时出错:\n{response["data"]['error']}'
|
return f'使用Server酱推送通知时出错:\n{response["data"]['error']}'
|
||||||
|
|
||||||
def CompanyWebHookBotPush(self, title, content):
|
def CompanyWebHookBotPush(self, title, content):
|
||||||
"""使用企业微信群机器人推送通知"""
|
"""使用企业微信群机器人推送通知"""
|
||||||
if Config.global_config.get(Config.global_config.notify_IfCompanyWebHookBot):
|
if Config.global_config.get(Config.global_config.notify_IfCompanyWebHookBot):
|
||||||
content = f'{title}\n{content}'
|
content = f"{title}\n{content}"
|
||||||
data = {
|
data = {"msgtype": "text", "text": {"content": content}}
|
||||||
"msgtype": "text",
|
|
||||||
"text": {
|
|
||||||
"content": content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url=Config.global_config.get(Config.global_config.notify_CompanyWebHookBotUrl),
|
url=Config.global_config.get(
|
||||||
json=data
|
Config.global_config.notify_CompanyWebHookBotUrl
|
||||||
|
),
|
||||||
|
json=data,
|
||||||
)
|
)
|
||||||
if response.json()["errcode"] == 0:
|
if response.json()["errcode"] == 0:
|
||||||
logger.info("企业微信群机器人推送通知成功")
|
logger.info("企业微信群机器人推送通知成功")
|
||||||
@@ -154,7 +233,15 @@ class Notification:
|
|||||||
else:
|
else:
|
||||||
logger.info("企业微信群机器人推送通知失败")
|
logger.info("企业微信群机器人推送通知失败")
|
||||||
logger.error(response.json())
|
logger.error(response.json())
|
||||||
return f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}'
|
self.push_info_bar.emit(
|
||||||
|
"error",
|
||||||
|
"企业微信群机器人通知推送失败",
|
||||||
|
f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}',
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
f'使用企业微信群机器人推送通知时出错:\n{response.json()["errmsg"]}'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
Notify = Notification()
|
Notify = Notification()
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import sqlite3
|
|||||||
import hashlib
|
import hashlib
|
||||||
import random
|
import random
|
||||||
import secrets
|
import secrets
|
||||||
|
import base64
|
||||||
|
import win32crypt
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
@@ -83,8 +85,8 @@ class CryptoHandler:
|
|||||||
private_key_local = AES_key.encrypt(pad(private_key.exportKey(), 32))
|
private_key_local = AES_key.encrypt(pad(private_key.exportKey(), 32))
|
||||||
(Config.app_path / "data/key/private_key.bin").write_bytes(private_key_local)
|
(Config.app_path / "data/key/private_key.bin").write_bytes(private_key_local)
|
||||||
|
|
||||||
def encryptx(self, note: str) -> bytes:
|
def AUTO_encryptor(self, note: str) -> bytes:
|
||||||
"""加密数据"""
|
"""使用AUTO_MAA的算法加密数据"""
|
||||||
|
|
||||||
# 读取RSA公钥
|
# 读取RSA公钥
|
||||||
public_key_local = RSA.import_key(
|
public_key_local = RSA.import_key(
|
||||||
@@ -95,8 +97,8 @@ class CryptoHandler:
|
|||||||
encrypted = cipher.encrypt(note.encode("utf-8"))
|
encrypted = cipher.encrypt(note.encode("utf-8"))
|
||||||
return encrypted
|
return encrypted
|
||||||
|
|
||||||
def decryptx(self, note: bytes, PASSWORD: str) -> str:
|
def AUTO_decryptor(self, note: bytes, PASSWORD: str) -> str:
|
||||||
"""解密数据"""
|
"""使用AUTO_MAA的算法解密数据"""
|
||||||
|
|
||||||
# 读入RSA私钥密文、盐与校验哈希值
|
# 读入RSA私钥密文、盐与校验哈希值
|
||||||
private_key_local = (
|
private_key_local = (
|
||||||
@@ -150,7 +152,9 @@ class CryptoHandler:
|
|||||||
# 使用旧管理密钥解密
|
# 使用旧管理密钥解密
|
||||||
user_data["Password"] = []
|
user_data["Password"] = []
|
||||||
for i in range(len(data)):
|
for i in range(len(data)):
|
||||||
user_data["Password"].append(self.decryptx(data[i][12], PASSWORD_old))
|
user_data["Password"].append(
|
||||||
|
self.AUTO_decryptor(data[i][12], PASSWORD_old)
|
||||||
|
)
|
||||||
cur.close()
|
cur.close()
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
@@ -169,7 +173,7 @@ class CryptoHandler:
|
|||||||
cur.execute(
|
cur.execute(
|
||||||
"UPDATE adminx SET password = ? WHERE mode = ? AND uid = ?",
|
"UPDATE adminx SET password = ? WHERE mode = ? AND uid = ?",
|
||||||
(
|
(
|
||||||
self.encryptx(user_data["Password"][i]),
|
self.AUTO_encryptor(user_data["Password"][i]),
|
||||||
data[i][15],
|
data[i][15],
|
||||||
data[i][16],
|
data[i][16],
|
||||||
),
|
),
|
||||||
@@ -181,6 +185,27 @@ class CryptoHandler:
|
|||||||
cur.close()
|
cur.close()
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
def win_encryptor(
|
||||||
|
self, note: str, description: str = None, entropy: bytes = None
|
||||||
|
) -> str:
|
||||||
|
"""使用Windows DPAPI加密数据"""
|
||||||
|
|
||||||
|
encrypted = win32crypt.CryptProtectData(
|
||||||
|
note.encode("utf-8"), description, entropy, None, None, 0
|
||||||
|
)
|
||||||
|
return base64.b64encode(encrypted).decode("utf-8")
|
||||||
|
|
||||||
|
def win_decryptor(self, note: str, entropy: bytes = None) -> str:
|
||||||
|
"""使用Windows DPAPI解密数据"""
|
||||||
|
|
||||||
|
if note == "":
|
||||||
|
return ""
|
||||||
|
|
||||||
|
decrypted = win32crypt.CryptUnprotectData(
|
||||||
|
base64.b64decode(note), entropy, None, None, 0
|
||||||
|
)
|
||||||
|
return decrypted[1].decode("utf-8")
|
||||||
|
|
||||||
def search_member(self) -> List[Dict[str, Union[Path, list]]]:
|
def search_member(self) -> List[Dict[str, Union[Path, list]]]:
|
||||||
"""搜索所有脚本实例及其用户数据库路径"""
|
"""搜索所有脚本实例及其用户数据库路径"""
|
||||||
|
|
||||||
@@ -197,7 +222,9 @@ class CryptoHandler:
|
|||||||
def check_PASSWORD(self, PASSWORD: str) -> bool:
|
def check_PASSWORD(self, PASSWORD: str) -> bool:
|
||||||
"""验证管理密钥"""
|
"""验证管理密钥"""
|
||||||
|
|
||||||
return bool(self.decryptx(self.encryptx(""), PASSWORD) != "管理密钥错误")
|
return bool(
|
||||||
|
self.AUTO_decryptor(self.AUTO_encryptor(""), PASSWORD) != "管理密钥错误"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
Crypto = CryptoHandler()
|
Crypto = CryptoHandler()
|
||||||
|
|||||||
@@ -25,26 +25,33 @@ v4.2
|
|||||||
作者:DLmaster_361
|
作者:DLmaster_361
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
from PySide6.QtWidgets import QWidget
|
||||||
|
import sys
|
||||||
import ctypes
|
import ctypes
|
||||||
import win32gui
|
import win32gui
|
||||||
import win32process
|
import win32process
|
||||||
import winreg
|
import winreg
|
||||||
import psutil
|
import psutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from app.core import Config
|
from app.core import Config
|
||||||
|
|
||||||
|
|
||||||
class SystemHandler:
|
class _SystemHandler:
|
||||||
|
|
||||||
ES_CONTINUOUS = 0x80000000
|
ES_CONTINUOUS = 0x80000000
|
||||||
ES_SYSTEM_REQUIRED = 0x00000001
|
ES_SYSTEM_REQUIRED = 0x00000001
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, main_window: QWidget = None):
|
||||||
|
|
||||||
|
self.main_window = main_window
|
||||||
|
|
||||||
self.set_Sleep()
|
self.set_Sleep()
|
||||||
self.set_SelfStart()
|
self.set_SelfStart()
|
||||||
|
|
||||||
def set_Sleep(self):
|
def set_Sleep(self) -> None:
|
||||||
"""同步系统休眠状态"""
|
"""同步系统休眠状态"""
|
||||||
|
|
||||||
if Config.global_config.get(Config.global_config.function_IfAllowSleep):
|
if Config.global_config.get(Config.global_config.function_IfAllowSleep):
|
||||||
@@ -56,7 +63,7 @@ class SystemHandler:
|
|||||||
# 恢复系统电源状态
|
# 恢复系统电源状态
|
||||||
ctypes.windll.kernel32.SetThreadExecutionState(self.ES_CONTINUOUS)
|
ctypes.windll.kernel32.SetThreadExecutionState(self.ES_CONTINUOUS)
|
||||||
|
|
||||||
def set_SelfStart(self):
|
def set_SelfStart(self) -> None:
|
||||||
"""同步开机自启"""
|
"""同步开机自启"""
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -84,7 +91,61 @@ class SystemHandler:
|
|||||||
winreg.DeleteValue(key, "AUTO_MAA")
|
winreg.DeleteValue(key, "AUTO_MAA")
|
||||||
winreg.CloseKey(key)
|
winreg.CloseKey(key)
|
||||||
|
|
||||||
def is_startup(self):
|
def set_power(self, mode) -> None:
|
||||||
|
|
||||||
|
if sys.platform.startswith("win"):
|
||||||
|
|
||||||
|
if mode == "None":
|
||||||
|
|
||||||
|
logger.info("不执行系统电源操作")
|
||||||
|
|
||||||
|
elif mode == "Shutdown":
|
||||||
|
|
||||||
|
logger.info("执行关机操作")
|
||||||
|
subprocess.run(["shutdown", "/s", "/t", "0"])
|
||||||
|
|
||||||
|
elif mode == "Hibernate":
|
||||||
|
|
||||||
|
logger.info("执行休眠操作")
|
||||||
|
subprocess.run(["shutdown", "/h"])
|
||||||
|
|
||||||
|
elif mode == "Sleep":
|
||||||
|
|
||||||
|
logger.info("执行睡眠操作")
|
||||||
|
subprocess.run(
|
||||||
|
["rundll32.exe", "powrprof.dll,SetSuspendState", "0,1,0"]
|
||||||
|
)
|
||||||
|
|
||||||
|
elif mode == "KillSelf":
|
||||||
|
|
||||||
|
self.main_window.close()
|
||||||
|
|
||||||
|
elif sys.platform.startswith("linux"):
|
||||||
|
|
||||||
|
if mode == "None":
|
||||||
|
|
||||||
|
logger.info("不执行系统电源操作")
|
||||||
|
|
||||||
|
elif mode == "Shutdown":
|
||||||
|
|
||||||
|
logger.info("执行关机操作")
|
||||||
|
subprocess.run(["shutdown", "-h", "now"])
|
||||||
|
|
||||||
|
elif mode == "Hibernate":
|
||||||
|
|
||||||
|
logger.info("执行休眠操作")
|
||||||
|
subprocess.run(["systemctl", "hibernate"])
|
||||||
|
|
||||||
|
elif mode == "Sleep":
|
||||||
|
|
||||||
|
logger.info("执行睡眠操作")
|
||||||
|
subprocess.run(["systemctl", "suspend"])
|
||||||
|
|
||||||
|
elif mode == "KillSelf":
|
||||||
|
|
||||||
|
self.main_window.close()
|
||||||
|
|
||||||
|
def is_startup(self) -> bool:
|
||||||
"""判断程序是否已经开机自启"""
|
"""判断程序是否已经开机自启"""
|
||||||
|
|
||||||
key = winreg.OpenKey(
|
key = winreg.OpenKey(
|
||||||
@@ -102,7 +163,7 @@ class SystemHandler:
|
|||||||
winreg.CloseKey(key)
|
winreg.CloseKey(key)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_window_info(self):
|
def get_window_info(self) -> list:
|
||||||
"""获取当前窗口信息"""
|
"""获取当前窗口信息"""
|
||||||
|
|
||||||
def callback(hwnd, window_info):
|
def callback(hwnd, window_info):
|
||||||
@@ -116,5 +177,29 @@ class SystemHandler:
|
|||||||
win32gui.EnumWindows(callback, window_info)
|
win32gui.EnumWindows(callback, window_info)
|
||||||
return window_info
|
return window_info
|
||||||
|
|
||||||
|
def kill_process(self, path: Path) -> None:
|
||||||
|
"""根据路径中止进程"""
|
||||||
|
|
||||||
System = SystemHandler()
|
for pid in self.search_pids(path):
|
||||||
|
killprocess = subprocess.Popen(
|
||||||
|
f"taskkill /F /T /PID {pid}",
|
||||||
|
shell=True,
|
||||||
|
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||||
|
)
|
||||||
|
killprocess.wait()
|
||||||
|
|
||||||
|
def search_pids(self, path: Path) -> list:
|
||||||
|
"""根据路径查找进程PID"""
|
||||||
|
|
||||||
|
pids = []
|
||||||
|
for proc in psutil.process_iter(["pid", "exe"]):
|
||||||
|
try:
|
||||||
|
if proc.info["exe"] and proc.info["exe"].lower() == str(path).lower():
|
||||||
|
pids.append(proc.info["pid"])
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
# 进程可能在此期间已结束或无法访问,忽略这些异常
|
||||||
|
pass
|
||||||
|
return pids
|
||||||
|
|
||||||
|
|
||||||
|
System = _SystemHandler()
|
||||||
|
|||||||
276
app/ui/Widget.py
276
app/ui/Widget.py
@@ -25,9 +25,9 @@ v4.2
|
|||||||
作者:DLmaster_361
|
作者:DLmaster_361
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from PySide6.QtCore import Qt, QTime
|
|
||||||
from PySide6.QtGui import QIcon
|
|
||||||
from PySide6.QtWidgets import QWidget, QHBoxLayout
|
from PySide6.QtWidgets import QWidget, QHBoxLayout
|
||||||
|
from PySide6.QtCore import Qt, QTime, QEvent
|
||||||
|
from PySide6.QtGui import QIcon, QPixmap, QPainter, QPainterPath
|
||||||
from qfluentwidgets import (
|
from qfluentwidgets import (
|
||||||
LineEdit,
|
LineEdit,
|
||||||
PasswordLineEdit,
|
PasswordLineEdit,
|
||||||
@@ -39,19 +39,29 @@ from qfluentwidgets import (
|
|||||||
Signal,
|
Signal,
|
||||||
ComboBox,
|
ComboBox,
|
||||||
CheckBox,
|
CheckBox,
|
||||||
|
IconWidget,
|
||||||
|
FluentIcon,
|
||||||
|
CardWidget,
|
||||||
|
BodyLabel,
|
||||||
qconfig,
|
qconfig,
|
||||||
ConfigItem,
|
ConfigItem,
|
||||||
TimeEdit,
|
TimeEdit,
|
||||||
OptionsConfigItem,
|
OptionsConfigItem,
|
||||||
|
TeachingTip,
|
||||||
|
TransparentToolButton,
|
||||||
|
TeachingTipTailPosition,
|
||||||
)
|
)
|
||||||
|
from qfluentwidgets.common.overload import singledispatchmethod
|
||||||
|
import os
|
||||||
|
from typing import Optional, Union, List
|
||||||
|
|
||||||
from typing import Union, List
|
from app.services import Crypto
|
||||||
|
|
||||||
|
|
||||||
class InputMessageBox(MessageBoxBase):
|
class LineEditMessageBox(MessageBoxBase):
|
||||||
"""输入对话框"""
|
"""输入对话框"""
|
||||||
|
|
||||||
def __init__(self, parent, title: str, content: str, mode: str, list: list = None):
|
def __init__(self, parent, title: str, content: str, mode: str):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.title = SubtitleLabel(title)
|
self.title = SubtitleLabel(title)
|
||||||
|
|
||||||
@@ -60,10 +70,6 @@ class InputMessageBox(MessageBoxBase):
|
|||||||
self.input.setClearButtonEnabled(True)
|
self.input.setClearButtonEnabled(True)
|
||||||
elif mode == "密码":
|
elif mode == "密码":
|
||||||
self.input = PasswordLineEdit()
|
self.input = PasswordLineEdit()
|
||||||
elif mode == "选择":
|
|
||||||
self.input = ComboBox()
|
|
||||||
self.input.addItems(list)
|
|
||||||
self.input.setCurrentIndex(-1)
|
|
||||||
|
|
||||||
self.input.setPlaceholderText(content)
|
self.input.setPlaceholderText(content)
|
||||||
|
|
||||||
@@ -72,8 +78,8 @@ class InputMessageBox(MessageBoxBase):
|
|||||||
self.viewLayout.addWidget(self.input)
|
self.viewLayout.addWidget(self.input)
|
||||||
|
|
||||||
|
|
||||||
class SetMessageBox(MessageBoxBase):
|
class ComboBoxMessageBox(MessageBoxBase):
|
||||||
"""输入对话框"""
|
"""选择对话框"""
|
||||||
|
|
||||||
def __init__(self, parent, title: str, content: List[str], list: List[List[str]]):
|
def __init__(self, parent, title: str, content: List[str], list: List[List[str]]):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -98,7 +104,7 @@ class SetMessageBox(MessageBoxBase):
|
|||||||
|
|
||||||
|
|
||||||
class LineEditSettingCard(SettingCard):
|
class LineEditSettingCard(SettingCard):
|
||||||
"""Setting card with switch button"""
|
"""Setting card with LineEdit"""
|
||||||
|
|
||||||
textChanged = Signal(str)
|
textChanged = Signal(str)
|
||||||
|
|
||||||
@@ -138,7 +144,49 @@ class LineEditSettingCard(SettingCard):
|
|||||||
self.LineEdit.setText(content)
|
self.LineEdit.setText(content)
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordLineEditSettingCard(SettingCard):
|
||||||
|
"""Setting card with PasswordLineEdit"""
|
||||||
|
|
||||||
|
textChanged = Signal(str)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
text,
|
||||||
|
icon: Union[str, QIcon, FluentIconBase],
|
||||||
|
title,
|
||||||
|
content=None,
|
||||||
|
configItem: ConfigItem = None,
|
||||||
|
parent=None,
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(icon, title, content, parent)
|
||||||
|
self.configItem = configItem
|
||||||
|
self.LineEdit = PasswordLineEdit(self)
|
||||||
|
self.LineEdit.setMinimumWidth(250)
|
||||||
|
self.LineEdit.setPlaceholderText(text)
|
||||||
|
|
||||||
|
if configItem:
|
||||||
|
self.setValue(qconfig.get(configItem))
|
||||||
|
configItem.valueChanged.connect(self.setValue)
|
||||||
|
|
||||||
|
self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight)
|
||||||
|
self.hBoxLayout.addSpacing(16)
|
||||||
|
|
||||||
|
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||||
|
|
||||||
|
def __textChanged(self, content: str):
|
||||||
|
self.setValue(Crypto.win_encryptor(content))
|
||||||
|
self.textChanged.emit(content)
|
||||||
|
|
||||||
|
def setValue(self, content: str):
|
||||||
|
if self.configItem:
|
||||||
|
qconfig.set(self.configItem, content)
|
||||||
|
|
||||||
|
self.LineEdit.setText(Crypto.win_decryptor(content))
|
||||||
|
|
||||||
|
|
||||||
class SpinBoxSettingCard(SettingCard):
|
class SpinBoxSettingCard(SettingCard):
|
||||||
|
"""Setting card with SpinBox"""
|
||||||
|
|
||||||
textChanged = Signal(int)
|
textChanged = Signal(int)
|
||||||
|
|
||||||
@@ -280,3 +328,207 @@ class TimeEditSettingCard(SettingCard):
|
|||||||
qconfig.set(self.configItem_time, value)
|
qconfig.set(self.configItem_time, value)
|
||||||
|
|
||||||
self.TimeEdit.setTime(QTime.fromString(value, "HH:mm"))
|
self.TimeEdit.setTime(QTime.fromString(value, "HH:mm"))
|
||||||
|
|
||||||
|
|
||||||
|
class StatefulItemCard(CardWidget):
|
||||||
|
|
||||||
|
def __init__(self, item: list, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.Layout = QHBoxLayout(self)
|
||||||
|
|
||||||
|
self.Label = BodyLabel(item[0], self)
|
||||||
|
self.icon = IconWidget(FluentIcon.MORE, self)
|
||||||
|
self.icon.setFixedSize(16, 16)
|
||||||
|
self.update_status(item[1])
|
||||||
|
|
||||||
|
self.Layout.addWidget(self.icon)
|
||||||
|
self.Layout.addWidget(self.Label)
|
||||||
|
self.Layout.addStretch(1)
|
||||||
|
|
||||||
|
def update_status(self, status: str):
|
||||||
|
|
||||||
|
if status == "完成":
|
||||||
|
self.icon.setIcon(FluentIcon.ACCEPT)
|
||||||
|
self.Label.setTextColor("#0eb840", "#0eb840")
|
||||||
|
elif status == "等待":
|
||||||
|
self.icon.setIcon(FluentIcon.MORE)
|
||||||
|
self.Label.setTextColor("#161823", "#e3f9fd")
|
||||||
|
elif status == "运行":
|
||||||
|
self.icon.setIcon(FluentIcon.PLAY)
|
||||||
|
self.Label.setTextColor("#177cb0", "#70f3ff")
|
||||||
|
elif status == "跳过":
|
||||||
|
self.icon.setIcon(FluentIcon.REMOVE)
|
||||||
|
self.Label.setTextColor("#75878a", "#7397ab")
|
||||||
|
elif status == "异常":
|
||||||
|
self.icon.setIcon(FluentIcon.CLOSE)
|
||||||
|
self.Label.setTextColor("#ff2121", "#ff2121")
|
||||||
|
|
||||||
|
|
||||||
|
class QuantifiedItemCard(CardWidget):
|
||||||
|
|
||||||
|
def __init__(self, item: list, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.Layout = QHBoxLayout(self)
|
||||||
|
|
||||||
|
self.Name = BodyLabel(item[0], self)
|
||||||
|
self.Numb = BodyLabel(str(item[1]), self)
|
||||||
|
|
||||||
|
self.Layout.addWidget(self.Name)
|
||||||
|
self.Layout.addStretch(1)
|
||||||
|
self.Layout.addWidget(self.Numb)
|
||||||
|
|
||||||
|
|
||||||
|
class IconButton(TransparentToolButton):
|
||||||
|
"""包含下拉框的自定义设置卡片类。"""
|
||||||
|
|
||||||
|
@singledispatchmethod
|
||||||
|
def __init__(self, parent: QWidget = None):
|
||||||
|
TransparentToolButton.__init__(self, parent)
|
||||||
|
|
||||||
|
self._tooltip: Optional[TeachingTip] = None
|
||||||
|
|
||||||
|
@__init__.register
|
||||||
|
def _(self, icon: Union[str, QIcon, FluentIconBase], parent: QWidget = None):
|
||||||
|
self.__init__(parent)
|
||||||
|
self.setIcon(icon)
|
||||||
|
|
||||||
|
@__init__.register
|
||||||
|
def _(
|
||||||
|
self,
|
||||||
|
icon: Union[str, QIcon, FluentIconBase],
|
||||||
|
isTooltip: bool,
|
||||||
|
tip_title: str,
|
||||||
|
tip_content: str,
|
||||||
|
parent: QWidget = None,
|
||||||
|
):
|
||||||
|
self.__init__(parent)
|
||||||
|
self.setIcon(icon)
|
||||||
|
|
||||||
|
# 处理工具提示
|
||||||
|
if isTooltip:
|
||||||
|
self.installEventFilter(self)
|
||||||
|
|
||||||
|
self.tip_title: str = tip_title
|
||||||
|
self.tip_content: str = tip_content
|
||||||
|
|
||||||
|
def eventFilter(self, obj, event: QEvent) -> bool:
|
||||||
|
"""处理鼠标事件。"""
|
||||||
|
if event.type() == QEvent.Type.Enter:
|
||||||
|
self._show_tooltip()
|
||||||
|
elif event.type() == QEvent.Type.Leave:
|
||||||
|
self._hide_tooltip()
|
||||||
|
return super().eventFilter(obj, event)
|
||||||
|
|
||||||
|
def _show_tooltip(self) -> None:
|
||||||
|
"""显示工具提示。"""
|
||||||
|
self._tooltip = TeachingTip.create(
|
||||||
|
target=self,
|
||||||
|
title=self.tip_title,
|
||||||
|
content=self.tip_content,
|
||||||
|
tailPosition=TeachingTipTailPosition.RIGHT,
|
||||||
|
isClosable=False,
|
||||||
|
duration=-1,
|
||||||
|
parent=self,
|
||||||
|
)
|
||||||
|
# 设置偏移
|
||||||
|
if self._tooltip:
|
||||||
|
tooltip_pos = self.mapToGlobal(self.rect().topRight())
|
||||||
|
|
||||||
|
tooltip_pos.setX(
|
||||||
|
tooltip_pos.x() - self._tooltip.size().width() - 40
|
||||||
|
) # 水平偏移
|
||||||
|
tooltip_pos.setY(
|
||||||
|
tooltip_pos.y() - self._tooltip.size().height() / 2 + 35
|
||||||
|
) # 垂直偏移
|
||||||
|
|
||||||
|
self._tooltip.move(tooltip_pos)
|
||||||
|
|
||||||
|
def _hide_tooltip(self) -> None:
|
||||||
|
"""隐藏工具提示。"""
|
||||||
|
if self._tooltip:
|
||||||
|
self._tooltip.close()
|
||||||
|
self._tooltip = None
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return id(self)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return NotImplemented
|
||||||
|
return self is other
|
||||||
|
|
||||||
|
|
||||||
|
class Banner(QWidget):
|
||||||
|
"""展示带有圆角的固定大小横幅小部件"""
|
||||||
|
|
||||||
|
def __init__(self, image_path: str = None, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.image_path = None
|
||||||
|
self.banner_image = None
|
||||||
|
self.scaled_image = None
|
||||||
|
|
||||||
|
if image_path:
|
||||||
|
self.set_banner_image(image_path)
|
||||||
|
|
||||||
|
def set_banner_image(self, image_path: str):
|
||||||
|
"""设置横幅图片"""
|
||||||
|
self.image_path = image_path
|
||||||
|
self.banner_image = self.load_banner_image(image_path)
|
||||||
|
self.update_scaled_image()
|
||||||
|
|
||||||
|
def load_banner_image(self, image_path: str) -> QPixmap:
|
||||||
|
"""加载横幅图片,或创建渐变备用图片"""
|
||||||
|
if os.path.isfile(image_path):
|
||||||
|
return QPixmap(image_path)
|
||||||
|
return self._create_fallback_image()
|
||||||
|
|
||||||
|
def _create_fallback_image(self):
|
||||||
|
"""创建渐变备用图片"""
|
||||||
|
fallback_image = QPixmap(2560, 1280) # 使用原始图片的大小
|
||||||
|
fallback_image.fill(Qt.GlobalColor.gray)
|
||||||
|
return fallback_image
|
||||||
|
|
||||||
|
def update_scaled_image(self):
|
||||||
|
"""按高度缩放图片,宽度保持比例,超出裁剪"""
|
||||||
|
if self.banner_image:
|
||||||
|
self.scaled_image = self.banner_image.scaled(
|
||||||
|
self.size(),
|
||||||
|
Qt.AspectRatioMode.KeepAspectRatioByExpanding,
|
||||||
|
Qt.TransformationMode.SmoothTransformation,
|
||||||
|
)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
"""重载 paintEvent 以绘制缩放后的图片"""
|
||||||
|
if self.scaled_image:
|
||||||
|
painter = QPainter(self)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||||
|
painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
|
||||||
|
|
||||||
|
# 创建圆角路径
|
||||||
|
path = QPainterPath()
|
||||||
|
path.addRoundedRect(self.rect(), 20, 20)
|
||||||
|
painter.setClipPath(path)
|
||||||
|
|
||||||
|
# 计算绘制位置,使图片居中
|
||||||
|
x = (self.width() - self.scaled_image.width()) // 2
|
||||||
|
y = (self.height() - self.scaled_image.height()) // 2
|
||||||
|
|
||||||
|
# 绘制缩放后的图片
|
||||||
|
painter.drawPixmap(x, y, self.scaled_image)
|
||||||
|
|
||||||
|
def resizeEvent(self, event):
|
||||||
|
"""重载 resizeEvent 以更新缩放后的图片"""
|
||||||
|
self.update_scaled_image()
|
||||||
|
QWidget.resizeEvent(self, event)
|
||||||
|
|
||||||
|
def set_percentage_size(self, width_percentage, height_percentage):
|
||||||
|
"""设置 Banner 的大小为窗口大小的百分比"""
|
||||||
|
parent = self.parentWidget()
|
||||||
|
if parent:
|
||||||
|
new_width = int(parent.width() * width_percentage)
|
||||||
|
new_height = int(parent.height() * height_percentage)
|
||||||
|
self.setFixedSize(new_width, new_height)
|
||||||
|
self.update_scaled_image()
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ from PySide6.QtWidgets import (
|
|||||||
)
|
)
|
||||||
from qfluentwidgets import (
|
from qfluentwidgets import (
|
||||||
CardWidget,
|
CardWidget,
|
||||||
IconWidget,
|
|
||||||
BodyLabel,
|
|
||||||
Pivot,
|
Pivot,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
FluentIcon,
|
FluentIcon,
|
||||||
@@ -52,7 +50,8 @@ from typing import List, Dict
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
from app.core import Config, Task_manager, Task, MainInfoBar
|
from app.core import Config, TaskManager, Task, MainInfoBar
|
||||||
|
from .Widget import StatefulItemCard
|
||||||
|
|
||||||
|
|
||||||
class DispatchCenter(QWidget):
|
class DispatchCenter(QWidget):
|
||||||
@@ -92,7 +91,7 @@ class DispatchCenter(QWidget):
|
|||||||
dispatch_box = DispatchBox(task.name, self)
|
dispatch_box = DispatchBox(task.name, self)
|
||||||
|
|
||||||
dispatch_box.top_bar.button.clicked.connect(
|
dispatch_box.top_bar.button.clicked.connect(
|
||||||
lambda: Task_manager.stop_task(task.name)
|
lambda: TaskManager.stop_task(task.name)
|
||||||
)
|
)
|
||||||
|
|
||||||
task.create_task_list.connect(dispatch_box.info.task.create_task)
|
task.create_task_list.connect(dispatch_box.info.task.create_task)
|
||||||
@@ -128,7 +127,7 @@ class DispatchCenter(QWidget):
|
|||||||
self.script_list["主调度台"].top_bar.button.clicked.disconnect()
|
self.script_list["主调度台"].top_bar.button.clicked.disconnect()
|
||||||
self.script_list["主调度台"].top_bar.button.setText("中止任务")
|
self.script_list["主调度台"].top_bar.button.setText("中止任务")
|
||||||
self.script_list["主调度台"].top_bar.button.clicked.connect(
|
self.script_list["主调度台"].top_bar.button.clicked.connect(
|
||||||
lambda: Task_manager.stop_task(task.name)
|
lambda: TaskManager.stop_task(task.name)
|
||||||
)
|
)
|
||||||
task.create_task_list.connect(
|
task.create_task_list.connect(
|
||||||
self.script_list["主调度台"].info.task.create_task
|
self.script_list["主调度台"].info.task.create_task
|
||||||
@@ -166,20 +165,31 @@ class DispatchCenter(QWidget):
|
|||||||
"""更新顶栏"""
|
"""更新顶栏"""
|
||||||
|
|
||||||
list = []
|
list = []
|
||||||
|
queue_numb, member_numb = 0, 0
|
||||||
|
|
||||||
if (Config.app_path / "config/QueueConfig").exists():
|
if (Config.app_path / "config/QueueConfig").exists():
|
||||||
for json_file in (Config.app_path / "config/QueueConfig").glob("*.json"):
|
for json_file in (Config.app_path / "config/QueueConfig").glob("*.json"):
|
||||||
list.append(f"队列 - {json_file.stem}")
|
list.append(f"队列 - {json_file.stem}")
|
||||||
|
queue_numb += 1
|
||||||
|
|
||||||
if (Config.app_path / "config/MaaConfig").exists():
|
if (Config.app_path / "config/MaaConfig").exists():
|
||||||
for subdir in (Config.app_path / "config/MaaConfig").iterdir():
|
for subdir in (Config.app_path / "config/MaaConfig").iterdir():
|
||||||
if subdir.is_dir():
|
if subdir.is_dir():
|
||||||
list.append(f"实例 - Maa - {subdir.name}")
|
list.append(f"实例 - Maa - {subdir.name}")
|
||||||
|
member_numb += 1
|
||||||
|
|
||||||
self.script_list["主调度台"].top_bar.object.clear()
|
self.script_list["主调度台"].top_bar.object.clear()
|
||||||
self.script_list["主调度台"].top_bar.object.addItems(list)
|
self.script_list["主调度台"].top_bar.object.addItems(list)
|
||||||
self.script_list["主调度台"].top_bar.object.setCurrentIndex(-1)
|
self.script_list["主调度台"].top_bar.mode.clear()
|
||||||
self.script_list["主调度台"].top_bar.mode.setCurrentIndex(-1)
|
self.script_list["主调度台"].top_bar.mode.addItems(["自动代理", "人工排查"])
|
||||||
|
|
||||||
|
if queue_numb == 1:
|
||||||
|
self.script_list["主调度台"].top_bar.object.setCurrentIndex(0)
|
||||||
|
elif member_numb == 1:
|
||||||
|
self.script_list["主调度台"].top_bar.object.setCurrentIndex(queue_numb)
|
||||||
|
else:
|
||||||
|
self.script_list["主调度台"].top_bar.object.setCurrentIndex(-1)
|
||||||
|
self.script_list["主调度台"].top_bar.mode.setCurrentIndex(0)
|
||||||
|
|
||||||
|
|
||||||
class DispatchBox(QWidget):
|
class DispatchBox(QWidget):
|
||||||
@@ -223,7 +233,6 @@ class DispatchBox(QWidget):
|
|||||||
self.object = ComboBox()
|
self.object = ComboBox()
|
||||||
self.object.setPlaceholderText("请选择调度对象")
|
self.object.setPlaceholderText("请选择调度对象")
|
||||||
self.mode = ComboBox()
|
self.mode = ComboBox()
|
||||||
self.mode.addItems(["自动代理", "人工排查"])
|
|
||||||
self.mode.setPlaceholderText("请选择调度模式")
|
self.mode.setPlaceholderText("请选择调度模式")
|
||||||
|
|
||||||
self.button = PushButton("开始任务")
|
self.button = PushButton("开始任务")
|
||||||
@@ -276,7 +285,7 @@ class DispatchBox(QWidget):
|
|||||||
info = json.load(f)
|
info = json.load(f)
|
||||||
|
|
||||||
logger.info(f"用户添加任务:{name}")
|
logger.info(f"用户添加任务:{name}")
|
||||||
Task_manager.add_task(f"{self.mode.currentText()}_主调度台", name, info)
|
TaskManager.add_task(f"{self.mode.currentText()}_主调度台", name, info)
|
||||||
|
|
||||||
elif self.object.currentText().split(" - ")[0] == "实例":
|
elif self.object.currentText().split(" - ")[0] == "实例":
|
||||||
|
|
||||||
@@ -285,8 +294,8 @@ class DispatchBox(QWidget):
|
|||||||
info = {"Queue": {"Member_1": name}}
|
info = {"Queue": {"Member_1": name}}
|
||||||
|
|
||||||
logger.info(f"用户添加任务:{name}")
|
logger.info(f"用户添加任务:{name}")
|
||||||
Task_manager.add_task(
|
TaskManager.add_task(
|
||||||
f"{self.mode.currentText()}_主调度台", "用户自定义队列", info
|
f"{self.mode.currentText()}_主调度台", "自定义队列", info
|
||||||
)
|
)
|
||||||
|
|
||||||
class DispatchInfoCard(HeaderCardWidget):
|
class DispatchInfoCard(HeaderCardWidget):
|
||||||
@@ -325,7 +334,7 @@ class DispatchBox(QWidget):
|
|||||||
self.viewLayout.addLayout(self.Layout)
|
self.viewLayout.addLayout(self.Layout)
|
||||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||||
|
|
||||||
self.task_cards: List[ItemCard] = []
|
self.task_cards: List[StatefulItemCard] = []
|
||||||
|
|
||||||
def create_task(self, task_list: list):
|
def create_task(self, task_list: list):
|
||||||
"""创建任务队列"""
|
"""创建任务队列"""
|
||||||
@@ -341,7 +350,7 @@ class DispatchBox(QWidget):
|
|||||||
|
|
||||||
for task in task_list:
|
for task in task_list:
|
||||||
|
|
||||||
self.task_cards.append(ItemCard(task))
|
self.task_cards.append(StatefulItemCard(task))
|
||||||
self.Layout.addWidget(self.task_cards[-1])
|
self.Layout.addWidget(self.task_cards[-1])
|
||||||
|
|
||||||
self.Layout.addStretch(1)
|
self.Layout.addStretch(1)
|
||||||
@@ -363,7 +372,7 @@ class DispatchBox(QWidget):
|
|||||||
self.viewLayout.addLayout(self.Layout)
|
self.viewLayout.addLayout(self.Layout)
|
||||||
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||||
|
|
||||||
self.user_cards: List[ItemCard] = []
|
self.user_cards: List[StatefulItemCard] = []
|
||||||
|
|
||||||
def create_user(self, user_list: list):
|
def create_user(self, user_list: list):
|
||||||
"""创建用户队列"""
|
"""创建用户队列"""
|
||||||
@@ -379,7 +388,7 @@ class DispatchBox(QWidget):
|
|||||||
|
|
||||||
for user in user_list:
|
for user in user_list:
|
||||||
|
|
||||||
self.user_cards.append(ItemCard(user))
|
self.user_cards.append(StatefulItemCard(user))
|
||||||
self.Layout.addWidget(self.user_cards[-1])
|
self.Layout.addWidget(self.user_cards[-1])
|
||||||
|
|
||||||
self.Layout.addStretch(1)
|
self.Layout.addStretch(1)
|
||||||
@@ -409,38 +418,3 @@ class DispatchBox(QWidget):
|
|||||||
|
|
||||||
self.text.moveCursor(QTextCursor.End)
|
self.text.moveCursor(QTextCursor.End)
|
||||||
self.text.ensureCursorVisible()
|
self.text.ensureCursorVisible()
|
||||||
|
|
||||||
|
|
||||||
class ItemCard(CardWidget):
|
|
||||||
|
|
||||||
def __init__(self, task_item: list, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.Layout = QHBoxLayout(self)
|
|
||||||
|
|
||||||
self.Label = BodyLabel(task_item[0], self)
|
|
||||||
self.icon = IconWidget(FluentIcon.MORE, self)
|
|
||||||
self.icon.setFixedSize(16, 16)
|
|
||||||
self.update_status(task_item[1])
|
|
||||||
|
|
||||||
self.Layout.addWidget(self.icon)
|
|
||||||
self.Layout.addWidget(self.Label)
|
|
||||||
self.Layout.addStretch(1)
|
|
||||||
|
|
||||||
def update_status(self, status: str):
|
|
||||||
|
|
||||||
if status == "完成":
|
|
||||||
self.icon.setIcon(FluentIcon.ACCEPT)
|
|
||||||
self.Label.setTextColor("#0eb840", "#0eb840")
|
|
||||||
elif status == "等待":
|
|
||||||
self.icon.setIcon(FluentIcon.MORE)
|
|
||||||
self.Label.setTextColor("#7397ab", "#7397ab")
|
|
||||||
elif status == "运行":
|
|
||||||
self.icon.setIcon(FluentIcon.PLAY)
|
|
||||||
self.Label.setTextColor("#2e4e7e", "#2e4e7e")
|
|
||||||
elif status == "跳过":
|
|
||||||
self.icon.setIcon(FluentIcon.REMOVE)
|
|
||||||
self.Label.setTextColor("#606060", "#d2d2d2")
|
|
||||||
elif status == "异常":
|
|
||||||
self.icon.setIcon(FluentIcon.CLOSE)
|
|
||||||
self.Label.setTextColor("#ff2121", "#ff2121")
|
|
||||||
|
|||||||
258
app/ui/history.py
Normal file
258
app/ui/history.py
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
|
||||||
|
# Copyright © <2024> <DLmaster361>
|
||||||
|
|
||||||
|
# This file is part of AUTO_MAA.
|
||||||
|
|
||||||
|
# AUTO_MAA is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation, either version 3 of the License,
|
||||||
|
# or (at your option) any later version.
|
||||||
|
|
||||||
|
# AUTO_MAA is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||||
|
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||||
|
# the GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# DLmaster_361@163.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
AUTO_MAA
|
||||||
|
AUTO_MAA历史记录界面
|
||||||
|
v4.2
|
||||||
|
作者:DLmaster_361
|
||||||
|
"""
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QWidget,
|
||||||
|
QVBoxLayout,
|
||||||
|
QHBoxLayout,
|
||||||
|
)
|
||||||
|
from qfluentwidgets import (
|
||||||
|
ScrollArea,
|
||||||
|
FluentIcon,
|
||||||
|
HeaderCardWidget,
|
||||||
|
PushButton,
|
||||||
|
ExpandGroupSettingCard,
|
||||||
|
TextBrowser,
|
||||||
|
)
|
||||||
|
from PySide6.QtCore import Signal
|
||||||
|
import os
|
||||||
|
from functools import partial
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
from app.core import Config
|
||||||
|
from .Widget import StatefulItemCard, QuantifiedItemCard
|
||||||
|
|
||||||
|
|
||||||
|
class History(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("历史记录")
|
||||||
|
|
||||||
|
content_widget = QWidget()
|
||||||
|
self.content_layout = QVBoxLayout(content_widget)
|
||||||
|
|
||||||
|
scrollArea = ScrollArea()
|
||||||
|
scrollArea.setWidgetResizable(True)
|
||||||
|
scrollArea.setWidget(content_widget)
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(scrollArea)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
self.history_card_list = []
|
||||||
|
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
"""刷新脚本实例界面"""
|
||||||
|
|
||||||
|
while self.content_layout.count() > 0:
|
||||||
|
item = self.content_layout.takeAt(0)
|
||||||
|
if item.spacerItem():
|
||||||
|
self.content_layout.removeItem(item.spacerItem())
|
||||||
|
elif item.widget():
|
||||||
|
item.widget().deleteLater()
|
||||||
|
|
||||||
|
self.history_card_list = []
|
||||||
|
|
||||||
|
history_dict = Config.search_history()
|
||||||
|
|
||||||
|
for date, user_list in history_dict.items():
|
||||||
|
|
||||||
|
self.history_card_list.append(HistoryCard(date, user_list, self))
|
||||||
|
self.content_layout.addWidget(self.history_card_list[-1])
|
||||||
|
|
||||||
|
self.content_layout.addStretch(1)
|
||||||
|
|
||||||
|
|
||||||
|
class HistoryCard(ExpandGroupSettingCard):
|
||||||
|
|
||||||
|
def __init__(self, date: str, user_list: List[Path], parent=None):
|
||||||
|
super().__init__(
|
||||||
|
FluentIcon.HISTORY, date, f"{date}的历史运行记录与统计信息", parent
|
||||||
|
)
|
||||||
|
|
||||||
|
widget = QWidget()
|
||||||
|
Layout = QVBoxLayout(widget)
|
||||||
|
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.viewLayout.setSpacing(0)
|
||||||
|
self.addGroupWidget(widget)
|
||||||
|
|
||||||
|
self.user_history_card_list = []
|
||||||
|
|
||||||
|
for user_path in user_list:
|
||||||
|
|
||||||
|
self.user_history_card_list.append(self.UserHistoryCard(user_path, self))
|
||||||
|
Layout.addWidget(self.user_history_card_list[-1])
|
||||||
|
|
||||||
|
class UserHistoryCard(HeaderCardWidget):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
user_history_path: Path,
|
||||||
|
parent=None,
|
||||||
|
):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.setTitle(user_history_path.name.replace(".json", ""))
|
||||||
|
|
||||||
|
self.user_history_path = user_history_path
|
||||||
|
self.main_history = Config.load_maa_logs("总览", user_history_path)
|
||||||
|
|
||||||
|
self.index_card = self.IndexCard(self.main_history["条目索引"], self)
|
||||||
|
self.statistics_card = QHBoxLayout()
|
||||||
|
self.log_card = self.LogCard(self)
|
||||||
|
|
||||||
|
self.index_card.index_changed.connect(self.update_info)
|
||||||
|
|
||||||
|
self.viewLayout.addWidget(self.index_card)
|
||||||
|
self.viewLayout.addLayout(self.statistics_card)
|
||||||
|
self.viewLayout.addWidget(self.log_card)
|
||||||
|
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.viewLayout.setSpacing(0)
|
||||||
|
self.viewLayout.setStretch(0, 1)
|
||||||
|
self.viewLayout.setStretch(2, 4)
|
||||||
|
|
||||||
|
self.update_info("数据总览")
|
||||||
|
|
||||||
|
def update_info(self, index: str) -> None:
|
||||||
|
"""更新信息"""
|
||||||
|
|
||||||
|
if index == "数据总览":
|
||||||
|
|
||||||
|
while self.statistics_card.count() > 0:
|
||||||
|
item = self.statistics_card.takeAt(0)
|
||||||
|
if item.spacerItem():
|
||||||
|
self.statistics_card.removeItem(item.spacerItem())
|
||||||
|
elif item.widget():
|
||||||
|
item.widget().deleteLater()
|
||||||
|
|
||||||
|
for name, item_list in self.main_history["统计数据"].items():
|
||||||
|
|
||||||
|
statistics_card = self.StatisticsCard(name, item_list, self)
|
||||||
|
self.statistics_card.addWidget(statistics_card)
|
||||||
|
|
||||||
|
self.log_card.hide()
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
single_history = Config.load_maa_logs(
|
||||||
|
"单项",
|
||||||
|
self.user_history_path.with_suffix("")
|
||||||
|
/ f"{index.replace(":","-")}.json",
|
||||||
|
)
|
||||||
|
|
||||||
|
while self.statistics_card.count() > 0:
|
||||||
|
item = self.statistics_card.takeAt(0)
|
||||||
|
if item.spacerItem():
|
||||||
|
self.statistics_card.removeItem(item.spacerItem())
|
||||||
|
elif item.widget():
|
||||||
|
item.widget().deleteLater()
|
||||||
|
|
||||||
|
for name, item_list in single_history["统计数据"].items():
|
||||||
|
|
||||||
|
statistics_card = self.StatisticsCard(name, item_list, self)
|
||||||
|
self.statistics_card.addWidget(statistics_card)
|
||||||
|
|
||||||
|
self.log_card.text.setText(single_history["日志信息"])
|
||||||
|
self.log_card.button.clicked.disconnect()
|
||||||
|
self.log_card.button.clicked.connect(
|
||||||
|
lambda: os.startfile(
|
||||||
|
self.user_history_path.with_suffix("")
|
||||||
|
/ f"{index.replace(":","-")}.log"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.log_card.show()
|
||||||
|
|
||||||
|
self.viewLayout.setStretch(1, self.statistics_card.count())
|
||||||
|
|
||||||
|
self.setMinimumHeight(300)
|
||||||
|
|
||||||
|
class IndexCard(HeaderCardWidget):
|
||||||
|
|
||||||
|
index_changed = Signal(str)
|
||||||
|
|
||||||
|
def __init__(self, index_list: list, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setTitle("记录条目")
|
||||||
|
|
||||||
|
self.Layout = QVBoxLayout()
|
||||||
|
self.viewLayout.addLayout(self.Layout)
|
||||||
|
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||||
|
|
||||||
|
self.index_cards: List[StatefulItemCard] = []
|
||||||
|
|
||||||
|
for index in index_list:
|
||||||
|
|
||||||
|
self.index_cards.append(StatefulItemCard(index))
|
||||||
|
self.index_cards[-1].clicked.connect(
|
||||||
|
partial(self.index_changed.emit, index[0])
|
||||||
|
)
|
||||||
|
self.Layout.addWidget(self.index_cards[-1])
|
||||||
|
|
||||||
|
self.Layout.addStretch(1)
|
||||||
|
|
||||||
|
class StatisticsCard(HeaderCardWidget):
|
||||||
|
|
||||||
|
def __init__(self, name: str, item_list: list, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setTitle(name)
|
||||||
|
|
||||||
|
self.Layout = QVBoxLayout()
|
||||||
|
self.viewLayout.addLayout(self.Layout)
|
||||||
|
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||||
|
|
||||||
|
self.item_cards: List[QuantifiedItemCard] = []
|
||||||
|
|
||||||
|
for item in item_list:
|
||||||
|
|
||||||
|
self.item_cards.append(QuantifiedItemCard(item))
|
||||||
|
self.Layout.addWidget(self.item_cards[-1])
|
||||||
|
|
||||||
|
if len(item_list) == 0:
|
||||||
|
self.Layout.addWidget(QuantifiedItemCard(["暂无记录", ""]))
|
||||||
|
|
||||||
|
self.Layout.addStretch(1)
|
||||||
|
|
||||||
|
class LogCard(HeaderCardWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setTitle("日志")
|
||||||
|
|
||||||
|
self.text = TextBrowser(self)
|
||||||
|
self.button = PushButton("打开日志文件", self)
|
||||||
|
self.button.clicked.connect(lambda: print("打开日志文件"))
|
||||||
|
|
||||||
|
Layout = QVBoxLayout()
|
||||||
|
Layout.addWidget(self.text)
|
||||||
|
Layout.addWidget(self.button)
|
||||||
|
self.viewLayout.setContentsMargins(3, 0, 3, 3)
|
||||||
|
self.viewLayout.addLayout(Layout)
|
||||||
423
app/ui/home.py
Normal file
423
app/ui/home.py
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
# <AUTO_MAA:A MAA Multi Account Management and Automation Tool>
|
||||||
|
# Copyright © <2024> <DLmaster361>
|
||||||
|
|
||||||
|
# This file is part of AUTO_MAA.
|
||||||
|
|
||||||
|
# AUTO_MAA is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation, either version 3 of the License,
|
||||||
|
# or (at your option) any later version.
|
||||||
|
|
||||||
|
# AUTO_MAA is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||||
|
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||||
|
# the GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# DLmaster_361@163.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
AUTO_MAA
|
||||||
|
AUTO_MAA主界面
|
||||||
|
v4.2
|
||||||
|
作者:DLmaster_361
|
||||||
|
"""
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
from PySide6.QtWidgets import (
|
||||||
|
QWidget,
|
||||||
|
QVBoxLayout,
|
||||||
|
QHBoxLayout,
|
||||||
|
QSpacerItem,
|
||||||
|
QSizePolicy,
|
||||||
|
QFileDialog,
|
||||||
|
)
|
||||||
|
from PySide6.QtCore import Qt, QSize, QUrl
|
||||||
|
from PySide6.QtGui import QDesktopServices, QColor
|
||||||
|
from qfluentwidgets import (
|
||||||
|
FluentIcon,
|
||||||
|
ScrollArea,
|
||||||
|
SimpleCardWidget,
|
||||||
|
PrimaryToolButton,
|
||||||
|
TextBrowser,
|
||||||
|
)
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from app.core import Config, MainInfoBar
|
||||||
|
from .Widget import Banner, IconButton
|
||||||
|
|
||||||
|
|
||||||
|
class Home(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("主页")
|
||||||
|
|
||||||
|
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)
|
||||||
|
v_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||||
|
|
||||||
|
# 空白占位符
|
||||||
|
v_layout.addItem(
|
||||||
|
QSpacerItem(10, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 顶部部分 (按钮组)
|
||||||
|
h1_layout = QHBoxLayout()
|
||||||
|
h1_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||||
|
|
||||||
|
# 左边留白区域
|
||||||
|
h1_layout.addStretch()
|
||||||
|
|
||||||
|
# 按钮组
|
||||||
|
buttonGroup = ButtonGroup()
|
||||||
|
buttonGroup.setMaximumHeight(320)
|
||||||
|
h1_layout.addWidget(buttonGroup)
|
||||||
|
|
||||||
|
# 空白占位符
|
||||||
|
h1_layout.addItem(
|
||||||
|
QSpacerItem(20, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 将顶部水平布局添加到垂直布局
|
||||||
|
v_layout.addLayout(h1_layout)
|
||||||
|
|
||||||
|
# 中间留白区域
|
||||||
|
v_layout.addItem(
|
||||||
|
QSpacerItem(10, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
|
||||||
|
)
|
||||||
|
v_layout.addStretch()
|
||||||
|
|
||||||
|
# 中间留白区域
|
||||||
|
v_layout.addItem(
|
||||||
|
QSpacerItem(10, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
|
||||||
|
)
|
||||||
|
v_layout.addStretch()
|
||||||
|
|
||||||
|
# 底部部分 (图片切换按钮)
|
||||||
|
h2_layout = QHBoxLayout()
|
||||||
|
h2_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||||
|
|
||||||
|
# 左边留白区域
|
||||||
|
h2_layout.addItem(
|
||||||
|
QSpacerItem(20, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
|
||||||
|
)
|
||||||
|
|
||||||
|
# # 公告卡片
|
||||||
|
# noticeCard = NoticeCard()
|
||||||
|
# h2_layout.addWidget(noticeCard)
|
||||||
|
|
||||||
|
h2_layout.addStretch()
|
||||||
|
|
||||||
|
# 自定义图像按钮布局
|
||||||
|
self.imageButton = PrimaryToolButton(FluentIcon.IMAGE_EXPORT)
|
||||||
|
self.imageButton.setFixedSize(56, 56)
|
||||||
|
self.imageButton.setIconSize(QSize(32, 32))
|
||||||
|
self.imageButton.clicked.connect(self.get_home_image)
|
||||||
|
|
||||||
|
v1_layout = QVBoxLayout()
|
||||||
|
v1_layout.addWidget(self.imageButton, alignment=Qt.AlignmentFlag.AlignBottom)
|
||||||
|
|
||||||
|
h2_layout.addLayout(v1_layout)
|
||||||
|
|
||||||
|
# 空白占位符
|
||||||
|
h2_layout.addItem(
|
||||||
|
QSpacerItem(25, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 将底部水平布局添加到垂直布局
|
||||||
|
v_layout.addLayout(h2_layout)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
scrollArea = ScrollArea()
|
||||||
|
scrollArea.setWidgetResizable(True)
|
||||||
|
scrollArea.setWidget(widget)
|
||||||
|
layout.addWidget(scrollArea)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
self.set_banner()
|
||||||
|
|
||||||
|
def get_home_image(self) -> None:
|
||||||
|
"""获取主页图片"""
|
||||||
|
|
||||||
|
if (
|
||||||
|
Config.global_config.get(Config.global_config.function_HomeImageMode)
|
||||||
|
== "默认"
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
elif (
|
||||||
|
Config.global_config.get(Config.global_config.function_HomeImageMode)
|
||||||
|
== "自定义"
|
||||||
|
):
|
||||||
|
|
||||||
|
file_path, _ = QFileDialog.getOpenFileName(
|
||||||
|
self, "打开自定义主页图片", "", "图片文件 (*.png *.jpg *.bmp)"
|
||||||
|
)
|
||||||
|
if file_path:
|
||||||
|
|
||||||
|
for file in Config.app_path.glob(
|
||||||
|
"resources/images/Home/BannerCustomize.*"
|
||||||
|
):
|
||||||
|
file.unlink()
|
||||||
|
|
||||||
|
shutil.copy(
|
||||||
|
file_path,
|
||||||
|
Config.app_path
|
||||||
|
/ f"resources/images/Home/BannerCustomize{Path(file_path).suffix}",
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"自定义主页图片更换成功:{file_path}")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"success",
|
||||||
|
"主页图片更换成功",
|
||||||
|
"自定义主页图片更换成功!",
|
||||||
|
3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.warning("自定义主页图片更换失败:未选择图片文件")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"warning",
|
||||||
|
"主页图片更换失败",
|
||||||
|
"未选择图片文件!",
|
||||||
|
5000,
|
||||||
|
)
|
||||||
|
elif (
|
||||||
|
Config.global_config.get(Config.global_config.function_HomeImageMode)
|
||||||
|
== "主题图像"
|
||||||
|
):
|
||||||
|
|
||||||
|
# 从远程服务器获取最新主题图像
|
||||||
|
for _ in range(3):
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
"https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/theme_image.json"
|
||||||
|
)
|
||||||
|
theme_image = response.json()
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
err = e
|
||||||
|
time.sleep(0.1)
|
||||||
|
else:
|
||||||
|
logger.error(f"获取最新主题图像时出错:\n{err}")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"error",
|
||||||
|
"主题图像获取失败",
|
||||||
|
f"获取最新主题图像信息时出错:\n{err}",
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if (Config.app_path / "resources/theme_image.json").exists():
|
||||||
|
with (Config.app_path / "resources/theme_image.json").open(
|
||||||
|
mode="r", encoding="utf-8"
|
||||||
|
) as f:
|
||||||
|
theme_image_local = json.load(f)
|
||||||
|
time_local = datetime.strptime(
|
||||||
|
theme_image_local["time"], "%Y-%m-%d %H:%M"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
time_local = datetime.strptime("2000-01-01 00:00", "%Y-%m-%d %H:%M")
|
||||||
|
|
||||||
|
if not (
|
||||||
|
Config.app_path / "resources/images/Home/BannerTheme.jpg"
|
||||||
|
).exists() or (
|
||||||
|
datetime.now()
|
||||||
|
> datetime.strptime(theme_image["time"], "%Y-%m-%d %H:%M")
|
||||||
|
and datetime.strptime(theme_image["time"], "%Y-%m-%d %H:%M")
|
||||||
|
> time_local
|
||||||
|
):
|
||||||
|
|
||||||
|
response = requests.get(theme_image["url"])
|
||||||
|
if response.status_code == 200:
|
||||||
|
|
||||||
|
with open(
|
||||||
|
Config.app_path / "resources/images/Home/BannerTheme.jpg", "wb"
|
||||||
|
) as file:
|
||||||
|
file.write(response.content)
|
||||||
|
|
||||||
|
logger.info(f"主题图像「{theme_image["name"]}」下载成功")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"success",
|
||||||
|
"主题图像下载成功",
|
||||||
|
f"「{theme_image["name"]}」下载成功!",
|
||||||
|
3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
logger.error("主题图像下载失败")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"error",
|
||||||
|
"主题图像下载失败",
|
||||||
|
f"主题图像下载失败:{response.status_code}",
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
|
||||||
|
with (Config.app_path / "resources/theme_image.json").open(
|
||||||
|
mode="w", encoding="utf-8"
|
||||||
|
) as f:
|
||||||
|
json.dump(theme_image, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
logger.info("主题图像已是最新")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"info",
|
||||||
|
"主题图像已是最新",
|
||||||
|
"主题图像已是最新!",
|
||||||
|
3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.set_banner()
|
||||||
|
|
||||||
|
def set_banner(self):
|
||||||
|
"""设置主页图像"""
|
||||||
|
if (
|
||||||
|
Config.global_config.get(Config.global_config.function_HomeImageMode)
|
||||||
|
== "默认"
|
||||||
|
):
|
||||||
|
self.banner.set_banner_image(
|
||||||
|
str(Config.app_path / "resources/images/Home/BannerDefault.png")
|
||||||
|
)
|
||||||
|
self.imageButton.hide()
|
||||||
|
self.banner_text.setVisible(False)
|
||||||
|
elif (
|
||||||
|
Config.global_config.get(Config.global_config.function_HomeImageMode)
|
||||||
|
== "自定义"
|
||||||
|
):
|
||||||
|
for file in Config.app_path.glob("resources/images/Home/BannerCustomize.*"):
|
||||||
|
self.banner.set_banner_image(str(file))
|
||||||
|
break
|
||||||
|
self.imageButton.show()
|
||||||
|
self.banner_text.setVisible(False)
|
||||||
|
elif (
|
||||||
|
Config.global_config.get(Config.global_config.function_HomeImageMode)
|
||||||
|
== "主题图像"
|
||||||
|
):
|
||||||
|
self.banner.set_banner_image(
|
||||||
|
str(Config.app_path / "resources/images/Home/BannerTheme.jpg")
|
||||||
|
)
|
||||||
|
self.imageButton.show()
|
||||||
|
self.banner_text.setVisible(True)
|
||||||
|
|
||||||
|
if (Config.app_path / "resources/theme_image.json").exists():
|
||||||
|
with (Config.app_path / "resources/theme_image.json").open(
|
||||||
|
mode="r", encoding="utf-8"
|
||||||
|
) as f:
|
||||||
|
theme_image = json.load(f)
|
||||||
|
html_content = theme_image["html"]
|
||||||
|
else:
|
||||||
|
html_content = "<h1>主题图像</h1><p>主题图像信息未知</p>"
|
||||||
|
|
||||||
|
self.banner_text.setHtml(re.sub(r"<img[^>]*>", "", html_content))
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonGroup(SimpleCardWidget):
|
||||||
|
"""显示主页和 GitHub 按钮的竖直按钮组"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
self.setFixedSize(56, 180)
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||||
|
|
||||||
|
# 创建主页按钮
|
||||||
|
home_button = IconButton(
|
||||||
|
FluentIcon.HOME.icon(color=QColor("#fff")),
|
||||||
|
tip_title="AUTO_MAA官网",
|
||||||
|
tip_content="AUTO_MAA官方文档站",
|
||||||
|
isTooltip=True,
|
||||||
|
)
|
||||||
|
home_button.setIconSize(QSize(32, 32))
|
||||||
|
home_button.clicked.connect(self.open_home)
|
||||||
|
layout.addWidget(home_button)
|
||||||
|
|
||||||
|
# 创建 GitHub 按钮
|
||||||
|
github_button = IconButton(
|
||||||
|
FluentIcon.GITHUB.icon(color=QColor("#fff")),
|
||||||
|
tip_title="Github仓库",
|
||||||
|
tip_content="如果本项目有帮助到您~\n不妨给项目点一个Star⭐",
|
||||||
|
isTooltip=True,
|
||||||
|
)
|
||||||
|
github_button.setIconSize(QSize(32, 32))
|
||||||
|
github_button.clicked.connect(self.open_github)
|
||||||
|
layout.addWidget(github_button)
|
||||||
|
|
||||||
|
# # 创建 文档 按钮
|
||||||
|
# doc_button = IconButton(
|
||||||
|
# FluentIcon.DICTIONARY.icon(color=QColor("#fff")),
|
||||||
|
# tip_title="自助排障文档",
|
||||||
|
# tip_content="点击打开自助排障文档,好孩子都能看懂",
|
||||||
|
# isTooltip=True,
|
||||||
|
# )
|
||||||
|
# doc_button.setIconSize(QSize(32, 32))
|
||||||
|
# doc_button.clicked.connect(self.open_doc)
|
||||||
|
# layout.addWidget(doc_button)
|
||||||
|
|
||||||
|
# 创建 Q群 按钮
|
||||||
|
doc_button = IconButton(
|
||||||
|
FluentIcon.CHAT.icon(color=QColor("#fff")),
|
||||||
|
tip_title="官方社群",
|
||||||
|
tip_content="加入官方群聊【AUTO_MAA绝赞DeBug中!】",
|
||||||
|
isTooltip=True,
|
||||||
|
)
|
||||||
|
doc_button.setIconSize(QSize(32, 32))
|
||||||
|
doc_button.clicked.connect(self.open_chat)
|
||||||
|
layout.addWidget(doc_button)
|
||||||
|
|
||||||
|
# 创建 官方店铺 按钮 (当然没有)
|
||||||
|
doc_button = IconButton(
|
||||||
|
FluentIcon.SHOPPING_CART.icon(color=QColor("#fff")),
|
||||||
|
tip_title="官方店铺",
|
||||||
|
tip_content="暂时没有官方店铺,但是可以加入官方群聊哦~",
|
||||||
|
isTooltip=True,
|
||||||
|
)
|
||||||
|
doc_button.setIconSize(QSize(32, 32))
|
||||||
|
doc_button.clicked.connect(self.open_sales)
|
||||||
|
layout.addWidget(doc_button)
|
||||||
|
|
||||||
|
def _normalBackgroundColor(self):
|
||||||
|
return QColor(0, 0, 0, 96)
|
||||||
|
|
||||||
|
def open_home(self):
|
||||||
|
"""打开主页链接"""
|
||||||
|
QDesktopServices.openUrl(QUrl("https://clozya.github.io/AUTOMAA_docs"))
|
||||||
|
|
||||||
|
def open_github(self):
|
||||||
|
"""打开 GitHub 链接"""
|
||||||
|
QDesktopServices.openUrl(QUrl("https://github.com/DLmaster361/AUTO_MAA"))
|
||||||
|
|
||||||
|
def open_chat(self):
|
||||||
|
"""打开 Q群 链接"""
|
||||||
|
QDesktopServices.openUrl(QUrl("https://qm.qq.com/q/bd9fISNoME"))
|
||||||
|
|
||||||
|
def open_doc(self):
|
||||||
|
"""打开 文档 链接"""
|
||||||
|
QDesktopServices.openUrl(QUrl("https://clozya.github.io/AUTOMAA_docs"))
|
||||||
|
|
||||||
|
def open_sales(self):
|
||||||
|
"""其实还是打开 Q群 链接"""
|
||||||
|
QDesktopServices.openUrl(QUrl("https://qm.qq.com/q/bd9fISNoME"))
|
||||||
@@ -26,10 +26,7 @@ v4.2
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import QSystemTrayIcon
|
||||||
QApplication,
|
|
||||||
QSystemTrayIcon,
|
|
||||||
)
|
|
||||||
from qfluentwidgets import (
|
from qfluentwidgets import (
|
||||||
Action,
|
Action,
|
||||||
PushButton,
|
PushButton,
|
||||||
@@ -39,20 +36,27 @@ from qfluentwidgets import (
|
|||||||
InfoBar,
|
InfoBar,
|
||||||
InfoBarPosition,
|
InfoBarPosition,
|
||||||
setTheme,
|
setTheme,
|
||||||
|
isDarkTheme,
|
||||||
|
SystemThemeListener,
|
||||||
Theme,
|
Theme,
|
||||||
MSFluentWindow,
|
MSFluentWindow,
|
||||||
NavigationItemPosition,
|
NavigationItemPosition,
|
||||||
qconfig,
|
qconfig,
|
||||||
)
|
)
|
||||||
from PySide6.QtGui import QIcon, QCloseEvent
|
from PySide6.QtGui import QIcon, QCloseEvent
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt, QTimer
|
||||||
|
import json
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import shutil
|
||||||
|
|
||||||
from app.core import Config, Task_manager, Main_timer, MainInfoBar
|
from app.core import Config, TaskManager, MainTimer, MainInfoBar
|
||||||
from app.services import Notify, Crypto, System
|
from app.services import Notify, Crypto, System
|
||||||
from .setting import Setting
|
from .home import Home
|
||||||
from .member_manager import MemberManager
|
from .member_manager import MemberManager
|
||||||
from .queue_manager import QueueManager
|
from .queue_manager import QueueManager
|
||||||
from .dispatch_center import DispatchCenter
|
from .dispatch_center import DispatchCenter
|
||||||
|
from .history import History
|
||||||
|
from .setting import Setting
|
||||||
|
|
||||||
|
|
||||||
class AUTO_MAA(MSFluentWindow):
|
class AUTO_MAA(MSFluentWindow):
|
||||||
@@ -63,25 +67,28 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
self.setWindowIcon(QIcon(str(Config.app_path / "resources/icons/AUTO_MAA.ico")))
|
self.setWindowIcon(QIcon(str(Config.app_path / "resources/icons/AUTO_MAA.ico")))
|
||||||
self.setWindowTitle("AUTO_MAA")
|
self.setWindowTitle("AUTO_MAA")
|
||||||
|
|
||||||
setTheme(Theme.AUTO)
|
setTheme(Theme.AUTO, lazy=True)
|
||||||
|
|
||||||
self.splashScreen = SplashScreen(self.windowIcon(), self)
|
self.splashScreen = SplashScreen(self.windowIcon(), self)
|
||||||
self.show_ui("显示主窗口", if_quick=True)
|
self.show_ui("显示主窗口", if_quick=True)
|
||||||
|
|
||||||
MainInfoBar.parent = self
|
MainInfoBar.main_window = self.window()
|
||||||
|
System.main_window = self.window()
|
||||||
|
|
||||||
# 创建主窗口
|
# 创建主窗口
|
||||||
self.setting = Setting(self)
|
self.home = Home(self)
|
||||||
self.member_manager = MemberManager(self)
|
self.member_manager = MemberManager(self)
|
||||||
self.queue_manager = QueueManager(self)
|
self.queue_manager = QueueManager(self)
|
||||||
self.dispatch_center = DispatchCenter(self)
|
self.dispatch_center = DispatchCenter(self)
|
||||||
|
self.history = History(self)
|
||||||
|
self.setting = Setting(self)
|
||||||
|
|
||||||
self.addSubInterface(
|
self.addSubInterface(
|
||||||
self.setting,
|
self.home,
|
||||||
FluentIcon.SETTING,
|
FluentIcon.HOME,
|
||||||
"设置",
|
"主页",
|
||||||
FluentIcon.SETTING,
|
FluentIcon.HOME,
|
||||||
NavigationItemPosition.BOTTOM,
|
NavigationItemPosition.TOP,
|
||||||
)
|
)
|
||||||
self.addSubInterface(
|
self.addSubInterface(
|
||||||
self.member_manager,
|
self.member_manager,
|
||||||
@@ -104,6 +111,20 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
FluentIcon.IOT,
|
FluentIcon.IOT,
|
||||||
NavigationItemPosition.TOP,
|
NavigationItemPosition.TOP,
|
||||||
)
|
)
|
||||||
|
self.addSubInterface(
|
||||||
|
self.history,
|
||||||
|
FluentIcon.HISTORY,
|
||||||
|
"历史记录",
|
||||||
|
FluentIcon.HISTORY,
|
||||||
|
NavigationItemPosition.BOTTOM,
|
||||||
|
)
|
||||||
|
self.addSubInterface(
|
||||||
|
self.setting,
|
||||||
|
FluentIcon.SETTING,
|
||||||
|
"设置",
|
||||||
|
FluentIcon.SETTING,
|
||||||
|
NavigationItemPosition.BOTTOM,
|
||||||
|
)
|
||||||
self.stackedWidget.currentChanged.connect(
|
self.stackedWidget.currentChanged.connect(
|
||||||
lambda index: (self.member_manager.refresh() if index == 1 else None)
|
lambda index: (self.member_manager.refresh() if index == 1 else None)
|
||||||
)
|
)
|
||||||
@@ -122,11 +143,13 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
self.dispatch_center.update_top_bar() if index == 3 else None
|
self.dispatch_center.update_top_bar() if index == 3 else None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.stackedWidget.currentChanged.connect(
|
||||||
|
lambda index: (self.history.refresh() if index == 4 else None)
|
||||||
|
)
|
||||||
|
|
||||||
# 创建系统托盘及其菜单
|
# 创建系统托盘及其菜单
|
||||||
self.tray = QSystemTrayIcon(
|
self.tray = QSystemTrayIcon(
|
||||||
QIcon(str(Config.app_path / "resources/icons/AUTO_MAA.ico")),
|
QIcon(str(Config.app_path / "resources/icons/AUTO_MAA.ico")), self
|
||||||
self,
|
|
||||||
)
|
)
|
||||||
self.tray.setToolTip("AUTO_MAA")
|
self.tray.setToolTip("AUTO_MAA")
|
||||||
self.tray_menu = SystemTrayMenu("AUTO_MAA", self)
|
self.tray_menu = SystemTrayMenu("AUTO_MAA", self)
|
||||||
@@ -142,41 +165,59 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
self.tray_menu.addSeparator()
|
self.tray_menu.addSeparator()
|
||||||
|
|
||||||
# 开始任务菜单项
|
# 开始任务菜单项
|
||||||
# self.tray_menu.addActions(
|
self.tray_menu.addActions(
|
||||||
# [
|
[
|
||||||
# Action(
|
Action(FluentIcon.PLAY, "运行自动代理", triggered=self.start_main_task),
|
||||||
# FluentIcon.PLAY,
|
Action(
|
||||||
# "运行自动代理",
|
FluentIcon.PAUSE,
|
||||||
# triggered=lambda: self.start_task("自动代理"),
|
"中止所有任务",
|
||||||
# ),
|
triggered=lambda: TaskManager.stop_task("ALL"),
|
||||||
# Action(
|
),
|
||||||
# FluentIcon.PLAY,
|
]
|
||||||
# "运行人工排查",
|
)
|
||||||
# triggered=lambda: self.start_task("人工排查"),
|
self.tray_menu.addSeparator()
|
||||||
# ),
|
|
||||||
# Action(FluentIcon.PAUSE, "中止当前任务", triggered=self.stop_task),
|
|
||||||
# ]
|
|
||||||
# )
|
|
||||||
# self.tray_menu.addSeparator()
|
|
||||||
|
|
||||||
# 退出主程序菜单项
|
# 退出主程序菜单项
|
||||||
self.tray_menu.addAction(
|
self.tray_menu.addAction(
|
||||||
Action(FluentIcon.POWER_BUTTON, "退出主程序", triggered=self.kill_main)
|
Action(FluentIcon.POWER_BUTTON, "退出主程序", triggered=self.window().close)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 设置托盘菜单
|
# 设置托盘菜单
|
||||||
self.tray.setContextMenu(self.tray_menu)
|
self.tray.setContextMenu(self.tray_menu)
|
||||||
self.tray.activated.connect(self.on_tray_activated)
|
self.tray.activated.connect(self.on_tray_activated)
|
||||||
|
|
||||||
Task_manager.create_gui.connect(self.dispatch_center.add_board)
|
TaskManager.create_gui.connect(self.dispatch_center.add_board)
|
||||||
Task_manager.connect_gui.connect(self.dispatch_center.connect_main_board)
|
TaskManager.connect_gui.connect(self.dispatch_center.connect_main_board)
|
||||||
|
Notify.push_info_bar.connect(MainInfoBar.push_info_bar)
|
||||||
self.setting.ui.card_IfShowTray.checkedChanged.connect(
|
self.setting.ui.card_IfShowTray.checkedChanged.connect(
|
||||||
lambda: self.show_ui("配置托盘")
|
lambda: self.show_ui("配置托盘")
|
||||||
)
|
)
|
||||||
self.setting.ui.card_IfToTray.checkedChanged.connect(self.set_min_method)
|
self.setting.ui.card_IfToTray.checkedChanged.connect(self.set_min_method)
|
||||||
|
self.setting.function.card_HomeImageMode.comboBox.currentIndexChanged.connect(
|
||||||
|
lambda index: (
|
||||||
|
self.home.get_home_image() if index == 2 else self.home.set_banner()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.splashScreen.finish()
|
self.splashScreen.finish()
|
||||||
|
|
||||||
|
self.themeListener = SystemThemeListener(self)
|
||||||
|
self.themeListener.systemThemeChanged.connect(self.switch_theme)
|
||||||
|
self.themeListener.start()
|
||||||
|
|
||||||
|
def switch_theme(self):
|
||||||
|
"""切换主题"""
|
||||||
|
|
||||||
|
setTheme(Theme.AUTO, lazy=True)
|
||||||
|
QTimer.singleShot(100, lambda: setTheme(Theme.AUTO, lazy=True))
|
||||||
|
|
||||||
|
# 云母特效启用时需要增加重试机制
|
||||||
|
if self.isMicaEffectEnabled():
|
||||||
|
QTimer.singleShot(
|
||||||
|
100,
|
||||||
|
lambda: self.windowEffect.setMicaEffect(self.winId(), isDarkTheme()),
|
||||||
|
)
|
||||||
|
|
||||||
def start_up_task(self) -> None:
|
def start_up_task(self) -> None:
|
||||||
"""启动时任务"""
|
"""启动时任务"""
|
||||||
|
|
||||||
@@ -184,9 +225,19 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
qconfig.load(Config.config_path, Config.global_config)
|
qconfig.load(Config.config_path, Config.global_config)
|
||||||
Config.global_config.save()
|
Config.global_config.save()
|
||||||
|
|
||||||
|
# 清理旧日志
|
||||||
|
self.clean_old_logs()
|
||||||
|
|
||||||
# 检查密码
|
# 检查密码
|
||||||
self.setting.check_PASSWORD()
|
self.setting.check_PASSWORD()
|
||||||
|
|
||||||
|
# 获取主题图像
|
||||||
|
if (
|
||||||
|
Config.global_config.get(Config.global_config.function_HomeImageMode)
|
||||||
|
== "主题图像"
|
||||||
|
):
|
||||||
|
self.home.get_home_image()
|
||||||
|
|
||||||
# 获取公告
|
# 获取公告
|
||||||
self.setting.show_notice(if_show=False)
|
self.setting.show_notice(if_show=False)
|
||||||
|
|
||||||
@@ -211,6 +262,16 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
info.addWidget(Up)
|
info.addWidget(Up)
|
||||||
info.show()
|
info.show()
|
||||||
|
|
||||||
|
# 直接运行主任务
|
||||||
|
if Config.global_config.get(Config.global_config.start_IfRunDirectly):
|
||||||
|
|
||||||
|
self.start_main_task()
|
||||||
|
|
||||||
|
# 直接最小化
|
||||||
|
if Config.global_config.get(Config.global_config.start_IfMinimizeDirectly):
|
||||||
|
|
||||||
|
self.titleBar.minBtn.click()
|
||||||
|
|
||||||
def set_min_method(self) -> None:
|
def set_min_method(self) -> None:
|
||||||
"""设置最小化方法"""
|
"""设置最小化方法"""
|
||||||
|
|
||||||
@@ -222,52 +283,73 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
else:
|
else:
|
||||||
|
|
||||||
self.titleBar.minBtn.clicked.disconnect()
|
self.titleBar.minBtn.clicked.disconnect()
|
||||||
self.titleBar.minBtn.clicked.connect(self.showMinimized)
|
self.titleBar.minBtn.clicked.connect(self.window().showMinimized)
|
||||||
|
|
||||||
def on_tray_activated(self, reason):
|
def on_tray_activated(self, reason):
|
||||||
"""双击返回主界面"""
|
"""双击返回主界面"""
|
||||||
if reason == QSystemTrayIcon.DoubleClick:
|
if reason == QSystemTrayIcon.DoubleClick:
|
||||||
self.show_ui("显示主窗口")
|
self.show_ui("显示主窗口")
|
||||||
|
|
||||||
# def start_task(self, mode):
|
def clean_old_logs(self):
|
||||||
# """调起对应任务"""
|
"""
|
||||||
# if self.main.MaaManager.isRunning():
|
删除超过用户设定天数的日志文件(基于目录日期)
|
||||||
# Notify.push_notification(
|
"""
|
||||||
# f"无法运行{mode}!",
|
|
||||||
# "当前已有任务正在运行,请在该任务结束后重试",
|
|
||||||
# "当前已有任务正在运行,请在该任务结束后重试",
|
|
||||||
# 3,
|
|
||||||
# )
|
|
||||||
# else:
|
|
||||||
# self.main.maa_starter(mode)
|
|
||||||
|
|
||||||
# def stop_task(self):
|
if (
|
||||||
# """中止当前任务"""
|
Config.global_config.get(Config.global_config.function_HistoryRetentionTime)
|
||||||
# if self.main.MaaManager.isRunning():
|
== 0
|
||||||
# if (
|
):
|
||||||
# self.main.MaaManager.mode == "自动代理"
|
logger.info("由于用户设置日志永久保留,跳过日志清理")
|
||||||
# or self.main.MaaManager.mode == "人工排查"
|
return
|
||||||
# ):
|
|
||||||
# self.main.maa_ender(f"{self.main.MaaManager.mode}_结束")
|
|
||||||
# elif "设置MAA" in self.main.MaaManager.mode:
|
|
||||||
# Notify.push_notification(
|
|
||||||
# "正在设置MAA!",
|
|
||||||
# "正在运行设置MAA任务,无法中止",
|
|
||||||
# "正在运行设置MAA任务,无法中止",
|
|
||||||
# 3,
|
|
||||||
# )
|
|
||||||
# else:
|
|
||||||
# Notify.push_notification(
|
|
||||||
# "无任务运行!",
|
|
||||||
# "当前无任务正在运行,无需中止",
|
|
||||||
# "当前无任务正在运行,无需中止",
|
|
||||||
# 3,
|
|
||||||
# )
|
|
||||||
|
|
||||||
def kill_main(self) -> None:
|
deleted_count = 0
|
||||||
"""退出主程序"""
|
|
||||||
self.close()
|
for date_folder in (Config.app_path / "history").iterdir():
|
||||||
QApplication.quit()
|
if not date_folder.is_dir():
|
||||||
|
continue # 只处理日期文件夹
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 只检查 `YYYY-MM-DD` 格式的文件夹
|
||||||
|
folder_date = datetime.strptime(date_folder.name, "%Y-%m-%d")
|
||||||
|
if datetime.now() - folder_date > timedelta(
|
||||||
|
days=Config.global_config.get(
|
||||||
|
Config.global_config.function_HistoryRetentionTime
|
||||||
|
)
|
||||||
|
):
|
||||||
|
shutil.rmtree(date_folder, ignore_errors=True)
|
||||||
|
deleted_count += 1
|
||||||
|
logger.info(f"已删除超期日志目录: {date_folder}")
|
||||||
|
except ValueError:
|
||||||
|
logger.warning(f"非日期格式的目录: {date_folder}")
|
||||||
|
|
||||||
|
logger.info(f"清理完成: {deleted_count} 个日期目录")
|
||||||
|
|
||||||
|
def start_main_task(self) -> None:
|
||||||
|
"""启动主任务"""
|
||||||
|
|
||||||
|
if (Config.app_path / "config/QueueConfig/调度队列_1.json").exists():
|
||||||
|
|
||||||
|
with (Config.app_path / "config/QueueConfig/调度队列_1.json").open(
|
||||||
|
mode="r", encoding="utf-8"
|
||||||
|
) as f:
|
||||||
|
info = json.load(f)
|
||||||
|
|
||||||
|
logger.info("自动添加任务:调度队列_1")
|
||||||
|
TaskManager.add_task("自动代理_主调度台", "主任务队列", info)
|
||||||
|
|
||||||
|
elif (Config.app_path / "config/MaaConfig/脚本_1").exists():
|
||||||
|
|
||||||
|
info = {"Queue": {"Member_1": "脚本_1"}}
|
||||||
|
|
||||||
|
logger.info("自动添加任务:脚本_1")
|
||||||
|
TaskManager.add_task("自动代理_主调度台", "主任务队列", info)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
logger.worning("启动主任务失败:未找到有效的主任务配置文件")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"warning", "启动主任务失败", "“调度队列_1”与“脚本_1”均不存在", -1
|
||||||
|
)
|
||||||
|
|
||||||
def show_ui(self, mode: str, if_quick: bool = False) -> None:
|
def show_ui(self, mode: str, if_quick: bool = False) -> None:
|
||||||
"""配置窗口状态"""
|
"""配置窗口状态"""
|
||||||
@@ -289,11 +371,13 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.setGeometry(location[0], location[1], size[0], size[1])
|
self.window().setGeometry(location[0], location[1], size[0], size[1])
|
||||||
self.show()
|
self.window().show()
|
||||||
|
self.window().raise_()
|
||||||
|
self.window().activateWindow()
|
||||||
if not if_quick:
|
if not if_quick:
|
||||||
if Config.global_config.get(Config.global_config.ui_maximized):
|
if Config.global_config.get(Config.global_config.ui_maximized):
|
||||||
self.showMaximized()
|
self.window().showMaximized()
|
||||||
self.set_min_method()
|
self.set_min_method()
|
||||||
self.show_ui("配置托盘")
|
self.show_ui("配置托盘")
|
||||||
|
|
||||||
@@ -307,7 +391,7 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
elif mode == "隐藏到托盘":
|
elif mode == "隐藏到托盘":
|
||||||
|
|
||||||
# 保存窗口相关属性
|
# 保存窗口相关属性
|
||||||
if not self.isMaximized():
|
if not self.window().isMaximized():
|
||||||
|
|
||||||
Config.global_config.set(
|
Config.global_config.set(
|
||||||
Config.global_config.ui_size,
|
Config.global_config.ui_size,
|
||||||
@@ -318,14 +402,14 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
f"{self.geometry().x()}x{self.geometry().y()}",
|
f"{self.geometry().x()}x{self.geometry().y()}",
|
||||||
)
|
)
|
||||||
Config.global_config.set(
|
Config.global_config.set(
|
||||||
Config.global_config.ui_maximized, self.isMaximized()
|
Config.global_config.ui_maximized, self.window().isMaximized()
|
||||||
)
|
)
|
||||||
Config.global_config.save()
|
Config.global_config.save()
|
||||||
|
|
||||||
# 隐藏主窗口
|
# 隐藏主窗口
|
||||||
if not if_quick:
|
if not if_quick:
|
||||||
|
|
||||||
self.hide()
|
self.window().hide()
|
||||||
self.tray.show()
|
self.tray.show()
|
||||||
|
|
||||||
def closeEvent(self, event: QCloseEvent):
|
def closeEvent(self, event: QCloseEvent):
|
||||||
@@ -334,14 +418,18 @@ class AUTO_MAA(MSFluentWindow):
|
|||||||
self.show_ui("隐藏到托盘", if_quick=True)
|
self.show_ui("隐藏到托盘", if_quick=True)
|
||||||
|
|
||||||
# 清理各功能线程
|
# 清理各功能线程
|
||||||
Main_timer.Timer.stop()
|
MainTimer.Timer.stop()
|
||||||
Main_timer.Timer.deleteLater()
|
MainTimer.Timer.deleteLater()
|
||||||
Task_manager.stop_task("ALL")
|
TaskManager.stop_task("ALL")
|
||||||
|
|
||||||
# 关闭数据库连接
|
# 关闭数据库连接
|
||||||
Config.close_database()
|
Config.close_database()
|
||||||
|
|
||||||
|
# 关闭主题监听
|
||||||
|
self.themeListener.terminate()
|
||||||
|
self.themeListener.deleteLater()
|
||||||
|
|
||||||
logger.info("AUTO_MAA主程序关闭")
|
logger.info("AUTO_MAA主程序关闭")
|
||||||
logger.info("===================================")
|
logger.info("----------------END----------------")
|
||||||
|
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|||||||
@@ -46,9 +46,12 @@ from qfluentwidgets import (
|
|||||||
HeaderCardWidget,
|
HeaderCardWidget,
|
||||||
CommandBar,
|
CommandBar,
|
||||||
ExpandGroupSettingCard,
|
ExpandGroupSettingCard,
|
||||||
|
ComboBoxSettingCard,
|
||||||
PushSettingCard,
|
PushSettingCard,
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
@@ -56,22 +59,20 @@ from datetime import datetime, timedelta
|
|||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from app.core import Config, MainInfoBar, Task_manager
|
from app.core import Config, MainInfoBar, TaskManager
|
||||||
from app.services import Crypto
|
from app.services import Crypto
|
||||||
|
from app.utils import Updater
|
||||||
from .Widget import (
|
from .Widget import (
|
||||||
InputMessageBox,
|
LineEditMessageBox,
|
||||||
LineEditSettingCard,
|
LineEditSettingCard,
|
||||||
SpinBoxSettingCard,
|
SpinBoxSettingCard,
|
||||||
SetMessageBox,
|
ComboBoxMessageBox,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MemberManager(QWidget):
|
class MemberManager(QWidget):
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, parent=None):
|
||||||
self,
|
|
||||||
parent=None,
|
|
||||||
):
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.setObjectName("脚本管理")
|
self.setObjectName("脚本管理")
|
||||||
@@ -107,14 +108,21 @@ class MemberManager(QWidget):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.tools.addSeparator()
|
self.tools.addSeparator()
|
||||||
self.key = Action(
|
|
||||||
FluentIcon.HIDE,
|
|
||||||
"显示/隐藏密码",
|
|
||||||
checkable=True,
|
|
||||||
triggered=self.show_password,
|
|
||||||
)
|
|
||||||
self.tools.addAction(
|
self.tools.addAction(
|
||||||
self.key,
|
Action(
|
||||||
|
FluentIcon.DOWNLOAD,
|
||||||
|
"脚本下载器",
|
||||||
|
triggered=self.member_downloader,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.tools.addSeparator()
|
||||||
|
self.tools.addAction(
|
||||||
|
Action(
|
||||||
|
FluentIcon.HIDE,
|
||||||
|
"显示/隐藏密码",
|
||||||
|
checkable=True,
|
||||||
|
triggered=self.show_password,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
layout.addWidget(self.tools)
|
layout.addWidget(self.tools)
|
||||||
@@ -123,16 +131,15 @@ class MemberManager(QWidget):
|
|||||||
def add_setting_box(self):
|
def add_setting_box(self):
|
||||||
"""添加一个脚本实例"""
|
"""添加一个脚本实例"""
|
||||||
|
|
||||||
choice = InputMessageBox(
|
choice = ComboBoxMessageBox(
|
||||||
self,
|
self.window(),
|
||||||
"选择一个脚本类型并添加相应脚本实例",
|
"选择一个脚本类型以添加相应脚本实例",
|
||||||
"选择脚本类型",
|
["选择脚本类型"],
|
||||||
"选择",
|
[["MAA"]],
|
||||||
["MAA"],
|
|
||||||
)
|
)
|
||||||
if choice.exec() and choice.input.currentIndex() != -1:
|
if choice.exec() and choice.input[0].currentIndex() != -1:
|
||||||
|
|
||||||
if choice.input.currentText() == "MAA":
|
if choice.input[0].currentText() == "MAA":
|
||||||
|
|
||||||
index = len(self.member_manager.search_member()) + 1
|
index = len(self.member_manager.search_member()) + 1
|
||||||
|
|
||||||
@@ -170,7 +177,7 @@ class MemberManager(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"确认",
|
"确认",
|
||||||
f"确定要删除 {name} 实例吗?",
|
f"确定要删除 {name} 实例吗?",
|
||||||
self,
|
self.window(),
|
||||||
)
|
)
|
||||||
if choice.exec():
|
if choice.exec():
|
||||||
|
|
||||||
@@ -292,11 +299,70 @@ class MemberManager(QWidget):
|
|||||||
|
|
||||||
self.member_manager.show_SettingBox(index + 1)
|
self.member_manager.show_SettingBox(index + 1)
|
||||||
|
|
||||||
|
def member_downloader(self):
|
||||||
|
"""脚本下载器"""
|
||||||
|
|
||||||
|
choice = ComboBoxMessageBox(
|
||||||
|
self.window(),
|
||||||
|
"选择一个脚本类型以下载相应脚本",
|
||||||
|
["选择脚本类型"],
|
||||||
|
[["MAA"]],
|
||||||
|
)
|
||||||
|
if choice.exec() and choice.input[0].currentIndex() != -1:
|
||||||
|
|
||||||
|
if choice.input[0].currentText() == "MAA":
|
||||||
|
|
||||||
|
(Config.app_path / "script/MAA").mkdir(parents=True, exist_ok=True)
|
||||||
|
folder = QFileDialog.getExistingDirectory(
|
||||||
|
self, "选择MAA下载目录", str(Config.app_path / "script/MAA")
|
||||||
|
)
|
||||||
|
if not folder:
|
||||||
|
logger.warning("选择MAA下载目录时未选择文件夹")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"warning", "警告", "未选择MAA下载目录", 5000
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 从mirrorc服务器获取最新版本信息
|
||||||
|
for _ in range(3):
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
"https://mirrorc.top/api/resources/MAA/latest?user_agent=MaaWpfGui&os=win&arch=x64&channel=stable"
|
||||||
|
)
|
||||||
|
maa_info = response.json()
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
err = e
|
||||||
|
time.sleep(0.1)
|
||||||
|
else:
|
||||||
|
choice = MessageBox(
|
||||||
|
"错误",
|
||||||
|
f"获取版本信息时出错:\n{err}",
|
||||||
|
self.window(),
|
||||||
|
)
|
||||||
|
choice.cancelButton.hide()
|
||||||
|
choice.buttonLayout.insertStretch(1)
|
||||||
|
if choice.exec():
|
||||||
|
return None
|
||||||
|
maa_version = list(
|
||||||
|
map(
|
||||||
|
int,
|
||||||
|
maa_info["data"]["version_name"][1:]
|
||||||
|
.replace("-beta", "")
|
||||||
|
.split("."),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
while len(maa_version) < 4:
|
||||||
|
maa_version.append(0)
|
||||||
|
|
||||||
|
self.downloader = Updater(Path(folder), "MAA", maa_version, [])
|
||||||
|
self.downloader.show()
|
||||||
|
|
||||||
def show_password(self):
|
def show_password(self):
|
||||||
|
|
||||||
if Config.PASSWORD == "":
|
if Config.PASSWORD == "":
|
||||||
choice = InputMessageBox(
|
choice = LineEditMessageBox(
|
||||||
self,
|
self.window(),
|
||||||
"请输入管理密钥",
|
"请输入管理密钥",
|
||||||
"管理密钥",
|
"管理密钥",
|
||||||
"密码",
|
"密码",
|
||||||
@@ -523,7 +589,7 @@ class MaaSettingBox(QWidget):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.card_Set.clicked.connect(
|
self.card_Set.clicked.connect(
|
||||||
lambda: Task_manager.add_task("设置MAA_全局", self.name, None)
|
lambda: TaskManager.add_task("设置MAA_全局", self.name, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
Layout.addWidget(self.card_Name)
|
Layout.addWidget(self.card_Name)
|
||||||
@@ -535,7 +601,11 @@ class MaaSettingBox(QWidget):
|
|||||||
|
|
||||||
def PathClicked(self):
|
def PathClicked(self):
|
||||||
|
|
||||||
folder = QFileDialog.getExistingDirectory(self, "选择MAA目录", "./")
|
folder = QFileDialog.getExistingDirectory(
|
||||||
|
self,
|
||||||
|
"选择MAA目录",
|
||||||
|
Config.maa_config.get(Config.maa_config.MaaSet_Path),
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
not folder
|
not folder
|
||||||
or Config.maa_config.get(Config.maa_config.MaaSet_Path) == folder
|
or Config.maa_config.get(Config.maa_config.MaaSet_Path) == folder
|
||||||
@@ -568,16 +638,15 @@ class MaaSettingBox(QWidget):
|
|||||||
class RunSetSettingCard(ExpandGroupSettingCard):
|
class RunSetSettingCard(ExpandGroupSettingCard):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(
|
super().__init__(FluentIcon.SETTING, "运行", "MAA运行调控选项", parent)
|
||||||
FluentIcon.SETTING,
|
|
||||||
"运行",
|
self.card_TaskTransitionMethod = ComboBoxSettingCard(
|
||||||
"MAA运行调控选项",
|
configItem=Config.maa_config.RunSet_TaskTransitionMethod,
|
||||||
parent,
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="任务切换方式",
|
||||||
|
content="简洁用户列表下相邻两个任务间的切换方式",
|
||||||
|
texts=["直接切换账号", "重启明日方舟", "重启模拟器"],
|
||||||
)
|
)
|
||||||
|
|
||||||
widget = QWidget()
|
|
||||||
Layout = QVBoxLayout(widget)
|
|
||||||
|
|
||||||
self.ProxyTimesLimit = SpinBoxSettingCard(
|
self.ProxyTimesLimit = SpinBoxSettingCard(
|
||||||
(0, 1024),
|
(0, 1024),
|
||||||
FluentIcon.PAGE_RIGHT,
|
FluentIcon.PAGE_RIGHT,
|
||||||
@@ -585,7 +654,6 @@ class MaaSettingBox(QWidget):
|
|||||||
"当用户本日代理成功次数超过该阈值时跳过代理,阈值为“0”时视为无代理次数上限",
|
"当用户本日代理成功次数超过该阈值时跳过代理,阈值为“0”时视为无代理次数上限",
|
||||||
Config.maa_config.RunSet_ProxyTimesLimit,
|
Config.maa_config.RunSet_ProxyTimesLimit,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.AnnihilationTimeLimit = SpinBoxSettingCard(
|
self.AnnihilationTimeLimit = SpinBoxSettingCard(
|
||||||
(1, 1024),
|
(1, 1024),
|
||||||
FluentIcon.PAGE_RIGHT,
|
FluentIcon.PAGE_RIGHT,
|
||||||
@@ -593,7 +661,6 @@ class MaaSettingBox(QWidget):
|
|||||||
"MAA日志无变化时间超过该阈值视为超时,单位为分钟",
|
"MAA日志无变化时间超过该阈值视为超时,单位为分钟",
|
||||||
Config.maa_config.RunSet_AnnihilationTimeLimit,
|
Config.maa_config.RunSet_AnnihilationTimeLimit,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.RoutineTimeLimit = SpinBoxSettingCard(
|
self.RoutineTimeLimit = SpinBoxSettingCard(
|
||||||
(1, 1024),
|
(1, 1024),
|
||||||
FluentIcon.PAGE_RIGHT,
|
FluentIcon.PAGE_RIGHT,
|
||||||
@@ -601,7 +668,6 @@ class MaaSettingBox(QWidget):
|
|||||||
"MAA日志无变化时间超过该阈值视为超时,单位为分钟",
|
"MAA日志无变化时间超过该阈值视为超时,单位为分钟",
|
||||||
Config.maa_config.RunSet_RoutineTimeLimit,
|
Config.maa_config.RunSet_RoutineTimeLimit,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.RunTimesLimit = SpinBoxSettingCard(
|
self.RunTimesLimit = SpinBoxSettingCard(
|
||||||
(1, 1024),
|
(1, 1024),
|
||||||
FluentIcon.PAGE_RIGHT,
|
FluentIcon.PAGE_RIGHT,
|
||||||
@@ -610,14 +676,15 @@ class MaaSettingBox(QWidget):
|
|||||||
Config.maa_config.RunSet_RunTimesLimit,
|
Config.maa_config.RunSet_RunTimesLimit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
widget = QWidget()
|
||||||
|
Layout = QVBoxLayout(widget)
|
||||||
|
Layout.addWidget(self.card_TaskTransitionMethod)
|
||||||
Layout.addWidget(self.ProxyTimesLimit)
|
Layout.addWidget(self.ProxyTimesLimit)
|
||||||
Layout.addWidget(self.AnnihilationTimeLimit)
|
Layout.addWidget(self.AnnihilationTimeLimit)
|
||||||
Layout.addWidget(self.RoutineTimeLimit)
|
Layout.addWidget(self.RoutineTimeLimit)
|
||||||
Layout.addWidget(self.RunTimesLimit)
|
Layout.addWidget(self.RunTimesLimit)
|
||||||
|
|
||||||
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.viewLayout.setSpacing(0)
|
self.viewLayout.setSpacing(0)
|
||||||
|
|
||||||
self.addGroupWidget(widget)
|
self.addGroupWidget(widget)
|
||||||
|
|
||||||
class UserSettingCard(HeaderCardWidget):
|
class UserSettingCard(HeaderCardWidget):
|
||||||
@@ -690,8 +757,8 @@ class MaaSettingBox(QWidget):
|
|||||||
user_list = [_[0] for _ in data if _[15] == "simple"]
|
user_list = [_[0] for _ in data if _[15] == "simple"]
|
||||||
set_list = ["自定义基建"]
|
set_list = ["自定义基建"]
|
||||||
|
|
||||||
choice = SetMessageBox(
|
choice = ComboBoxMessageBox(
|
||||||
self.parent().parent().parent().parent().parent().parent().parent(),
|
self.window(),
|
||||||
"用户选项配置",
|
"用户选项配置",
|
||||||
["选择要配置的用户", "选择要配置的选项"],
|
["选择要配置的用户", "选择要配置的选项"],
|
||||||
[user_list, set_list],
|
[user_list, set_list],
|
||||||
@@ -730,8 +797,8 @@ class MaaSettingBox(QWidget):
|
|||||||
user_list = [_[0] for _ in data if _[15] == "beta"]
|
user_list = [_[0] for _ in data if _[15] == "beta"]
|
||||||
set_list = ["MAA日常配置", "MAA剿灭配置"]
|
set_list = ["MAA日常配置", "MAA剿灭配置"]
|
||||||
|
|
||||||
choice = SetMessageBox(
|
choice = ComboBoxMessageBox(
|
||||||
self.parent().parent().parent().parent().parent().parent().parent(),
|
self.window(),
|
||||||
"用户选项配置",
|
"用户选项配置",
|
||||||
["选择要配置的用户", "选择要配置的选项"],
|
["选择要配置的用户", "选择要配置的选项"],
|
||||||
[user_list, set_list],
|
[user_list, set_list],
|
||||||
@@ -743,7 +810,7 @@ class MaaSettingBox(QWidget):
|
|||||||
):
|
):
|
||||||
|
|
||||||
set_book = ["routine", "annihilation"]
|
set_book = ["routine", "annihilation"]
|
||||||
Task_manager.add_task(
|
TaskManager.add_task(
|
||||||
"设置MAA_用户",
|
"设置MAA_用户",
|
||||||
self.name,
|
self.name,
|
||||||
{
|
{
|
||||||
@@ -985,7 +1052,7 @@ class MaaSettingBox(QWidget):
|
|||||||
item = QTableWidgetItem("******")
|
item = QTableWidgetItem("******")
|
||||||
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
||||||
else:
|
else:
|
||||||
result = Crypto.decryptx(value, Config.PASSWORD)
|
result = Crypto.AUTO_decryptor(value, Config.PASSWORD)
|
||||||
item = QTableWidgetItem(result)
|
item = QTableWidgetItem(result)
|
||||||
if result == "管理密钥错误":
|
if result == "管理密钥错误":
|
||||||
item.setFlags(
|
item.setFlags(
|
||||||
@@ -1052,7 +1119,7 @@ class MaaSettingBox(QWidget):
|
|||||||
item = QTableWidgetItem("******")
|
item = QTableWidgetItem("******")
|
||||||
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
||||||
else:
|
else:
|
||||||
result = Crypto.decryptx(value, Config.PASSWORD)
|
result = Crypto.AUTO_decryptor(value, Config.PASSWORD)
|
||||||
item = QTableWidgetItem(result)
|
item = QTableWidgetItem(result)
|
||||||
if result == "管理密钥错误":
|
if result == "管理密钥错误":
|
||||||
item.setFlags(
|
item.setFlags(
|
||||||
@@ -1123,7 +1190,7 @@ class MaaSettingBox(QWidget):
|
|||||||
games[game_in.strip()] = game_out.strip()
|
games[game_in.strip()] = game_out.strip()
|
||||||
text = games.get(text, text)
|
text = games.get(text, text)
|
||||||
if item.column() == 11: # 密码
|
if item.column() == 11: # 密码
|
||||||
text = Crypto.encryptx(text)
|
text = Crypto.AUTO_encryptor(text)
|
||||||
|
|
||||||
# 保存至本地数据库
|
# 保存至本地数据库
|
||||||
if text != "":
|
if text != "":
|
||||||
@@ -1141,7 +1208,7 @@ class MaaSettingBox(QWidget):
|
|||||||
self.update_user_info("normal")
|
self.update_user_info("normal")
|
||||||
return None
|
return None
|
||||||
if item.column() == 6: # 密码
|
if item.column() == 6: # 密码
|
||||||
text = Crypto.encryptx(text)
|
text = Crypto.AUTO_encryptor(text)
|
||||||
|
|
||||||
# 保存至本地数据库
|
# 保存至本地数据库
|
||||||
if text != "":
|
if text != "":
|
||||||
@@ -1223,7 +1290,7 @@ class MaaSettingBox(QWidget):
|
|||||||
Config.cur.execute(
|
Config.cur.execute(
|
||||||
"INSERT INTO adminx VALUES('新用户','手机号码(官服)/B站ID(B服)','Official',-1,'y','2000-01-01','1-7','-','-','n','n','n',?,'无',0,?,?)",
|
"INSERT INTO adminx VALUES('新用户','手机号码(官服)/B站ID(B服)','Official',-1,'y','2000-01-01','1-7','-','-','n','n','n',?,'无',0,?,?)",
|
||||||
(
|
(
|
||||||
Crypto.encryptx("未设置"),
|
Crypto.AUTO_encryptor("未设置"),
|
||||||
set_book[0],
|
set_book[0],
|
||||||
set_book[1],
|
set_book[1],
|
||||||
),
|
),
|
||||||
@@ -1273,15 +1340,7 @@ class MaaSettingBox(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"确认",
|
"确认",
|
||||||
f"确定要删除用户 {data[0][0]} 吗?",
|
f"确定要删除用户 {data[0][0]} 吗?",
|
||||||
self.parent()
|
self.window(),
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 删除用户
|
# 删除用户
|
||||||
@@ -1570,15 +1629,7 @@ class MaaSettingBox(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"确认",
|
"确认",
|
||||||
f"确定要将用户 {data[0][0]} 转为{mode_list[1 - mode]}配置模式吗?",
|
f"确定要将用户 {data[0][0]} 转为{mode_list[1 - mode]}配置模式吗?",
|
||||||
self.parent()
|
self.window(),
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent()
|
|
||||||
.parent(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 切换用户
|
# 切换用户
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ from qfluentwidgets import (
|
|||||||
TextBrowser,
|
TextBrowser,
|
||||||
CommandBar,
|
CommandBar,
|
||||||
SwitchSettingCard,
|
SwitchSettingCard,
|
||||||
|
ComboBoxSettingCard,
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
from typing import List
|
from typing import List
|
||||||
@@ -59,10 +60,7 @@ from .Widget import (
|
|||||||
|
|
||||||
class QueueManager(QWidget):
|
class QueueManager(QWidget):
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, parent=None):
|
||||||
self,
|
|
||||||
parent=None,
|
|
||||||
):
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.setObjectName("调度队列")
|
self.setObjectName("调度队列")
|
||||||
@@ -138,7 +136,7 @@ class QueueManager(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"确认",
|
"确认",
|
||||||
f"确定要删除 {name} 吗?",
|
f"确定要删除 {name} 吗?",
|
||||||
self,
|
self.window(),
|
||||||
)
|
)
|
||||||
if choice.exec():
|
if choice.exec():
|
||||||
|
|
||||||
@@ -412,9 +410,23 @@ class QueueMemberSettingBox(QWidget):
|
|||||||
"调度队列状态",
|
"调度队列状态",
|
||||||
Config.queue_config.queueSet_Enabled,
|
Config.queue_config.queueSet_Enabled,
|
||||||
)
|
)
|
||||||
|
self.card_AfterAccomplish = ComboBoxSettingCard(
|
||||||
|
configItem=Config.queue_config.queueSet_AfterAccomplish,
|
||||||
|
icon=FluentIcon.POWER_BUTTON,
|
||||||
|
title="调度队列结束后",
|
||||||
|
content="选择调度队列结束后的操作",
|
||||||
|
texts=[
|
||||||
|
"无动作",
|
||||||
|
"退出AUTO_MAA",
|
||||||
|
"睡眠(win系统需禁用休眠)",
|
||||||
|
"休眠",
|
||||||
|
"关机",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
Layout.addWidget(self.card_Name)
|
Layout.addWidget(self.card_Name)
|
||||||
Layout.addWidget(self.card_Enable)
|
Layout.addWidget(self.card_Enable)
|
||||||
|
Layout.addWidget(self.card_AfterAccomplish)
|
||||||
|
|
||||||
self.viewLayout.addLayout(Layout)
|
self.viewLayout.addLayout(Layout)
|
||||||
|
|
||||||
|
|||||||
@@ -53,24 +53,15 @@ import requests
|
|||||||
from app.core import Config, MainInfoBar
|
from app.core import Config, MainInfoBar
|
||||||
from app.services import Crypto, System
|
from app.services import Crypto, System
|
||||||
from app.utils import Updater
|
from app.utils import Updater
|
||||||
from .Widget import InputMessageBox, LineEditSettingCard
|
from .Widget import LineEditMessageBox, LineEditSettingCard, PasswordLineEditSettingCard
|
||||||
|
|
||||||
|
|
||||||
class Setting(QWidget):
|
class Setting(QWidget):
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, parent=None):
|
||||||
self,
|
|
||||||
parent=None,
|
|
||||||
):
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.setObjectName("设置")
|
self.setObjectName("设置")
|
||||||
|
|
||||||
layout = QVBoxLayout()
|
|
||||||
|
|
||||||
scrollArea = ScrollArea()
|
|
||||||
scrollArea.setWidgetResizable(True)
|
|
||||||
|
|
||||||
content_widget = QWidget()
|
content_widget = QWidget()
|
||||||
content_layout = QVBoxLayout(content_widget)
|
content_layout = QVBoxLayout(content_widget)
|
||||||
|
|
||||||
@@ -83,6 +74,7 @@ class Setting(QWidget):
|
|||||||
self.other = OtherSettingCard(self)
|
self.other = OtherSettingCard(self)
|
||||||
|
|
||||||
self.function.card_IfAllowSleep.checkedChanged.connect(System.set_Sleep)
|
self.function.card_IfAllowSleep.checkedChanged.connect(System.set_Sleep)
|
||||||
|
self.function.card_IfAgreeBilibili.checkedChanged.connect(self.agree_bilibili)
|
||||||
self.start.card_IfSelfStart.checkedChanged.connect(System.set_SelfStart)
|
self.start.card_IfSelfStart.checkedChanged.connect(System.set_SelfStart)
|
||||||
self.security.card_changePASSWORD.clicked.connect(self.change_PASSWORD)
|
self.security.card_changePASSWORD.clicked.connect(self.change_PASSWORD)
|
||||||
self.updater.card_CheckUpdate.clicked.connect(self.get_update)
|
self.updater.card_CheckUpdate.clicked.connect(self.get_update)
|
||||||
@@ -96,12 +88,38 @@ class Setting(QWidget):
|
|||||||
content_layout.addWidget(self.updater)
|
content_layout.addWidget(self.updater)
|
||||||
content_layout.addWidget(self.other)
|
content_layout.addWidget(self.other)
|
||||||
|
|
||||||
|
scrollArea = ScrollArea()
|
||||||
|
scrollArea.setWidgetResizable(True)
|
||||||
scrollArea.setWidget(content_widget)
|
scrollArea.setWidget(content_widget)
|
||||||
|
layout = QVBoxLayout()
|
||||||
layout.addWidget(scrollArea)
|
layout.addWidget(scrollArea)
|
||||||
|
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def agree_bilibili(self) -> None:
|
||||||
|
"""授权bilibili游戏隐私政策"""
|
||||||
|
|
||||||
|
if not Config.global_config.get(Config.global_config.function_IfAgreeBilibili):
|
||||||
|
logger.info("取消授权bilibili游戏隐私政策")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"info", "操作成功", "已取消授权bilibili游戏隐私政策", 3000
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
choice = MessageBox(
|
||||||
|
"授权声明",
|
||||||
|
"开启“托管bilibili游戏隐私政策”功能,即代表您已完整阅读并同意《哔哩哔哩弹幕网用户使用协议》、《哔哩哔哩隐私政策》和《哔哩哔哩游戏中心用户协议》,并授权AUTO_MAA在其认定需要时以其认定合适的方法替您处理相关弹窗\n\n是否同意授权?",
|
||||||
|
self.window(),
|
||||||
|
)
|
||||||
|
if choice.exec():
|
||||||
|
logger.success("确认授权bilibili游戏隐私政策")
|
||||||
|
MainInfoBar.push_info_bar(
|
||||||
|
"success", "操作成功", "已确认授权bilibili游戏隐私政策", 3000
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
Config.global_config.set(
|
||||||
|
Config.global_config.function_IfAgreeBilibili, False
|
||||||
|
)
|
||||||
|
|
||||||
def check_PASSWORD(self) -> None:
|
def check_PASSWORD(self) -> None:
|
||||||
"""检查并配置管理密钥"""
|
"""检查并配置管理密钥"""
|
||||||
|
|
||||||
@@ -110,8 +128,8 @@ class Setting(QWidget):
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
choice = InputMessageBox(
|
choice = LineEditMessageBox(
|
||||||
self.parent().parent().parent(),
|
self.window(),
|
||||||
"未检测到管理密钥,请设置您的管理密钥",
|
"未检测到管理密钥,请设置您的管理密钥",
|
||||||
"管理密钥",
|
"管理密钥",
|
||||||
"密码",
|
"密码",
|
||||||
@@ -123,12 +141,11 @@ class Setting(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"警告",
|
"警告",
|
||||||
"您没有设置管理密钥,无法使用本软件,请先设置管理密钥",
|
"您没有设置管理密钥,无法使用本软件,请先设置管理密钥",
|
||||||
self.parent().parent().parent(),
|
self.window(),
|
||||||
)
|
)
|
||||||
choice.cancelButton.hide()
|
choice.cancelButton.hide()
|
||||||
choice.buttonLayout.insertStretch(1)
|
choice.buttonLayout.insertStretch(1)
|
||||||
if choice.exec():
|
choice.exec()
|
||||||
pass
|
|
||||||
|
|
||||||
def change_PASSWORD(self) -> None:
|
def change_PASSWORD(self) -> None:
|
||||||
"""修改管理密钥"""
|
"""修改管理密钥"""
|
||||||
@@ -137,8 +154,8 @@ class Setting(QWidget):
|
|||||||
|
|
||||||
while if_change:
|
while if_change:
|
||||||
|
|
||||||
choice = InputMessageBox(
|
choice = LineEditMessageBox(
|
||||||
self,
|
self.window(),
|
||||||
"请输入旧的管理密钥",
|
"请输入旧的管理密钥",
|
||||||
"旧管理密钥",
|
"旧管理密钥",
|
||||||
"密码",
|
"密码",
|
||||||
@@ -152,8 +169,8 @@ class Setting(QWidget):
|
|||||||
# 获取新的管理密钥
|
# 获取新的管理密钥
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
choice = InputMessageBox(
|
choice = LineEditMessageBox(
|
||||||
self,
|
self.window(),
|
||||||
"请输入新的管理密钥",
|
"请输入新的管理密钥",
|
||||||
"新管理密钥",
|
"新管理密钥",
|
||||||
"密码",
|
"密码",
|
||||||
@@ -173,23 +190,22 @@ class Setting(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"确认",
|
"确认",
|
||||||
"您没有输入新的管理密钥,是否取消修改管理密钥?",
|
"您没有输入新的管理密钥,是否取消修改管理密钥?",
|
||||||
self,
|
self.window(),
|
||||||
)
|
)
|
||||||
if choice.exec():
|
if choice.exec():
|
||||||
if_change = False
|
if_change = False
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
choice = MessageBox("错误", "管理密钥错误", self)
|
choice = MessageBox("错误", "管理密钥错误", self.window())
|
||||||
choice.cancelButton.hide()
|
choice.cancelButton.hide()
|
||||||
choice.buttonLayout.insertStretch(1)
|
choice.buttonLayout.insertStretch(1)
|
||||||
if choice.exec():
|
choice.exec()
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"确认",
|
"确认",
|
||||||
"您没有输入管理密钥,是否取消修改管理密钥?",
|
"您没有输入管理密钥,是否取消修改管理密钥?",
|
||||||
self,
|
self.window(),
|
||||||
)
|
)
|
||||||
if choice.exec():
|
if choice.exec():
|
||||||
break
|
break
|
||||||
@@ -261,7 +277,7 @@ class Setting(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"错误",
|
"错误",
|
||||||
f"获取版本信息时出错:\n{err}",
|
f"获取版本信息时出错:\n{err}",
|
||||||
self,
|
self.window(),
|
||||||
)
|
)
|
||||||
choice.cancelButton.hide()
|
choice.cancelButton.hide()
|
||||||
choice.buttonLayout.insertStretch(1)
|
choice.buttonLayout.insertStretch(1)
|
||||||
@@ -297,7 +313,7 @@ class Setting(QWidget):
|
|||||||
choice = MessageBox(
|
choice = MessageBox(
|
||||||
"版本更新",
|
"版本更新",
|
||||||
f"发现新版本:\n{main_version_info}{updater_version_info} 更新说明:\n{version_remote['announcement'].replace("\n# ","\n !").replace("\n## ","\n - ").replace("\n- ","\n · ")}\n\n是否开始更新?\n\n 注意:主程序更新时AUTO_MAA将自动关闭",
|
f"发现新版本:\n{main_version_info}{updater_version_info} 更新说明:\n{version_remote['announcement'].replace("\n# ","\n !").replace("\n## ","\n - ").replace("\n- ","\n · ")}\n\n是否开始更新?\n\n 注意:主程序更新时AUTO_MAA将自动关闭",
|
||||||
self,
|
self.window(),
|
||||||
)
|
)
|
||||||
if not choice.exec():
|
if not choice.exec():
|
||||||
return None
|
return None
|
||||||
@@ -315,7 +331,7 @@ class Setting(QWidget):
|
|||||||
if main_version_remote > main_version_current:
|
if main_version_remote > main_version_current:
|
||||||
self.updater.update_process.accomplish.connect(self.update_main)
|
self.updater.update_process.accomplish.connect(self.update_main)
|
||||||
# 显示更新页面
|
# 显示更新页面
|
||||||
self.updater.ui.show()
|
self.updater.show()
|
||||||
|
|
||||||
# 更新主程序
|
# 更新主程序
|
||||||
elif main_version_remote > main_version_current:
|
elif main_version_remote > main_version_current:
|
||||||
@@ -339,7 +355,7 @@ class Setting(QWidget):
|
|||||||
def show_notice(self, if_show: bool = True):
|
def show_notice(self, if_show: bool = True):
|
||||||
"""显示公告"""
|
"""显示公告"""
|
||||||
|
|
||||||
# 从远程服务器获取最新版本信息
|
# 从远程服务器获取最新公告
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
try:
|
try:
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
@@ -393,6 +409,20 @@ class FunctionSettingCard(HeaderCardWidget):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setTitle("功能")
|
self.setTitle("功能")
|
||||||
|
|
||||||
|
self.card_HomeImageMode = ComboBoxSettingCard(
|
||||||
|
configItem=Config.global_config.function_HomeImageMode,
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="主页背景图模式",
|
||||||
|
content="选择主页背景图的来源",
|
||||||
|
texts=["默认", "自定义", "主题图像"],
|
||||||
|
)
|
||||||
|
self.card_HistoryRetentionTime = ComboBoxSettingCard(
|
||||||
|
configItem=Config.global_config.function_HistoryRetentionTime,
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="历史记录保留时间",
|
||||||
|
content="选择历史记录的保留时间,超期自动清理",
|
||||||
|
texts=["7 天", "15 天", "30 天", "60 天", "永久"],
|
||||||
|
)
|
||||||
self.card_IfAllowSleep = SwitchSettingCard(
|
self.card_IfAllowSleep = SwitchSettingCard(
|
||||||
icon=FluentIcon.PAGE_RIGHT,
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
title="启动时阻止系统休眠",
|
title="启动时阻止系统休眠",
|
||||||
@@ -400,10 +430,19 @@ class FunctionSettingCard(HeaderCardWidget):
|
|||||||
configItem=Config.global_config.function_IfAllowSleep,
|
configItem=Config.global_config.function_IfAllowSleep,
|
||||||
)
|
)
|
||||||
self.card_IfSilence = self.SilenceSettingCard(self)
|
self.card_IfSilence = self.SilenceSettingCard(self)
|
||||||
|
self.card_IfAgreeBilibili = SwitchSettingCard(
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="托管bilibili游戏隐私政策",
|
||||||
|
content="授权AUTO_MAA同意bilibili游戏隐私政策",
|
||||||
|
configItem=Config.global_config.function_IfAgreeBilibili,
|
||||||
|
)
|
||||||
|
|
||||||
Layout = QVBoxLayout()
|
Layout = QVBoxLayout()
|
||||||
|
Layout.addWidget(self.card_HomeImageMode)
|
||||||
|
Layout.addWidget(self.card_HistoryRetentionTime)
|
||||||
Layout.addWidget(self.card_IfAllowSleep)
|
Layout.addWidget(self.card_IfAllowSleep)
|
||||||
Layout.addWidget(self.card_IfSilence)
|
Layout.addWidget(self.card_IfSilence)
|
||||||
|
Layout.addWidget(self.card_IfAgreeBilibili)
|
||||||
self.viewLayout.addLayout(Layout)
|
self.viewLayout.addLayout(Layout)
|
||||||
|
|
||||||
class SilenceSettingCard(ExpandGroupSettingCard):
|
class SilenceSettingCard(ExpandGroupSettingCard):
|
||||||
@@ -423,10 +462,10 @@ class FunctionSettingCard(HeaderCardWidget):
|
|||||||
configItem=Config.global_config.function_IfSilence,
|
configItem=Config.global_config.function_IfSilence,
|
||||||
)
|
)
|
||||||
self.card_BossKey = LineEditSettingCard(
|
self.card_BossKey = LineEditSettingCard(
|
||||||
text="请输入安卓模拟器老版键",
|
text="请输入安卓模拟器老板键",
|
||||||
icon=FluentIcon.PAGE_RIGHT,
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
title="模拟器老版键",
|
title="模拟器老板键",
|
||||||
content="输入模拟器老版快捷键,以“+”分隔",
|
content="输入模拟器老板快捷键,以“+”分隔",
|
||||||
configItem=Config.global_config.function_BossKey,
|
configItem=Config.global_config.function_BossKey,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -453,14 +492,21 @@ class StartSettingCard(HeaderCardWidget):
|
|||||||
)
|
)
|
||||||
self.card_IfRunDirectly = SwitchSettingCard(
|
self.card_IfRunDirectly = SwitchSettingCard(
|
||||||
icon=FluentIcon.PAGE_RIGHT,
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
title="启动后直接运行",
|
title="启动后直接运行主任务",
|
||||||
content="启动AUTO_MAA后自动运行任务(暂不可用)",
|
content="启动AUTO_MAA后自动运行自动代理任务,优先级:调度队列 1 > 脚本 1",
|
||||||
configItem=Config.global_config.start_IfRunDirectly,
|
configItem=Config.global_config.start_IfRunDirectly,
|
||||||
)
|
)
|
||||||
|
self.card_IfMinimizeDirectly = SwitchSettingCard(
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="启动后直接最小化",
|
||||||
|
content="启动AUTO_MAA后直接最小化",
|
||||||
|
configItem=Config.global_config.start_IfMinimizeDirectly,
|
||||||
|
)
|
||||||
|
|
||||||
Layout = QVBoxLayout()
|
Layout = QVBoxLayout()
|
||||||
Layout.addWidget(self.card_IfSelfStart)
|
Layout.addWidget(self.card_IfSelfStart)
|
||||||
Layout.addWidget(self.card_IfRunDirectly)
|
Layout.addWidget(self.card_IfRunDirectly)
|
||||||
|
Layout.addWidget(self.card_IfMinimizeDirectly)
|
||||||
self.viewLayout.addLayout(Layout)
|
self.viewLayout.addLayout(Layout)
|
||||||
|
|
||||||
|
|
||||||
@@ -496,38 +542,82 @@ class NotifySettingCard(HeaderCardWidget):
|
|||||||
|
|
||||||
self.setTitle("通知")
|
self.setTitle("通知")
|
||||||
|
|
||||||
self.card_IfSendErrorOnly = SwitchSettingCard(
|
self.card_NotifyContent = self.NotifyContentSettingCard(self)
|
||||||
icon=FluentIcon.PAGE_RIGHT,
|
self.card_Plyer = self.PlyerSettingCard(self)
|
||||||
title="仅推送异常信息",
|
self.card_EMail = self.EMailSettingCard(self)
|
||||||
content="仅在任务出现异常时推送通知",
|
|
||||||
configItem=Config.global_config.notify_IfSendErrorOnly,
|
|
||||||
)
|
|
||||||
self.card_IfPushPlyer = SwitchSettingCard(
|
|
||||||
icon=FluentIcon.PAGE_RIGHT,
|
|
||||||
title="推送系统通知",
|
|
||||||
content="推送系统级通知,不会在通知中心停留",
|
|
||||||
configItem=Config.global_config.notify_IfPushPlyer,
|
|
||||||
)
|
|
||||||
self.card_SendMail = self.SendMailSettingCard(self)
|
|
||||||
self.card_ServerChan = self.ServerChanSettingCard(self)
|
self.card_ServerChan = self.ServerChanSettingCard(self)
|
||||||
self.card_CompanyWebhookBot = self.CompanyWechatPushSettingCard(self)
|
self.card_CompanyWebhookBot = self.CompanyWechatPushSettingCard(self)
|
||||||
|
|
||||||
Layout = QVBoxLayout()
|
Layout = QVBoxLayout()
|
||||||
Layout.addWidget(self.card_IfSendErrorOnly)
|
Layout.addWidget(self.card_NotifyContent)
|
||||||
Layout.addWidget(self.card_IfPushPlyer)
|
Layout.addWidget(self.card_Plyer)
|
||||||
Layout.addWidget(self.card_SendMail)
|
Layout.addWidget(self.card_EMail)
|
||||||
Layout.addWidget(self.card_ServerChan)
|
Layout.addWidget(self.card_ServerChan)
|
||||||
Layout.addWidget(self.card_CompanyWebhookBot)
|
Layout.addWidget(self.card_CompanyWebhookBot)
|
||||||
self.viewLayout.addLayout(Layout)
|
self.viewLayout.addLayout(Layout)
|
||||||
|
|
||||||
class SendMailSettingCard(ExpandGroupSettingCard):
|
class NotifyContentSettingCard(ExpandGroupSettingCard):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
FluentIcon.SETTING,
|
FluentIcon.SETTING, "通知内容选项", "选择需要推送的通知内容", parent
|
||||||
"推送邮件通知",
|
)
|
||||||
"通过AUTO_MAA官方通知服务邮箱推送任务结果",
|
|
||||||
parent,
|
self.card_SendTaskResultTime = ComboBoxSettingCard(
|
||||||
|
configItem=Config.global_config.notify_SendTaskResultTime,
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="推送任务结果选项",
|
||||||
|
content="选择推送自动代理与人工排查任务结果的时机",
|
||||||
|
texts=["不推送", "任何时刻", "仅失败时"],
|
||||||
|
)
|
||||||
|
self.card_IfSendStatistic = SwitchSettingCard(
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="推送统计信息",
|
||||||
|
content="推送自动代理统计信息的通知",
|
||||||
|
configItem=Config.global_config.notify_IfSendStatistic,
|
||||||
|
)
|
||||||
|
self.card_IfSendSixStar = SwitchSettingCard(
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="推送公招高资喜报",
|
||||||
|
content="公招出现六星词条时推送喜报",
|
||||||
|
configItem=Config.global_config.notify_IfSendSixStar,
|
||||||
|
)
|
||||||
|
|
||||||
|
widget = QWidget()
|
||||||
|
Layout = QVBoxLayout(widget)
|
||||||
|
Layout.addWidget(self.card_SendTaskResultTime)
|
||||||
|
Layout.addWidget(self.card_IfSendStatistic)
|
||||||
|
Layout.addWidget(self.card_IfSendSixStar)
|
||||||
|
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.viewLayout.setSpacing(0)
|
||||||
|
self.addGroupWidget(widget)
|
||||||
|
|
||||||
|
class PlyerSettingCard(ExpandGroupSettingCard):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(
|
||||||
|
FluentIcon.SETTING, "推送系统通知", "Plyer系统通知推送渠道", parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self.card_IfPushPlyer = SwitchSettingCard(
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="推送系统通知",
|
||||||
|
content="使用Plyer推送系统级通知,不会在通知中心停留",
|
||||||
|
configItem=Config.global_config.notify_IfPushPlyer,
|
||||||
|
)
|
||||||
|
|
||||||
|
widget = QWidget()
|
||||||
|
Layout = QVBoxLayout(widget)
|
||||||
|
Layout.addWidget(self.card_IfPushPlyer)
|
||||||
|
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.viewLayout.setSpacing(0)
|
||||||
|
self.addGroupWidget(widget)
|
||||||
|
|
||||||
|
class EMailSettingCard(ExpandGroupSettingCard):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(
|
||||||
|
FluentIcon.SETTING, "推送邮件通知", "电子邮箱通知推送渠道", parent
|
||||||
)
|
)
|
||||||
|
|
||||||
self.card_IfSendMail = SwitchSettingCard(
|
self.card_IfSendMail = SwitchSettingCard(
|
||||||
@@ -536,18 +626,42 @@ class NotifySettingCard(HeaderCardWidget):
|
|||||||
content="是否启用邮件通知功能",
|
content="是否启用邮件通知功能",
|
||||||
configItem=Config.global_config.notify_IfSendMail,
|
configItem=Config.global_config.notify_IfSendMail,
|
||||||
)
|
)
|
||||||
self.card_MailAddress = LineEditSettingCard(
|
self.card_SMTPServerAddress = LineEditSettingCard(
|
||||||
text="请输入邮箱地址",
|
text="请输入SMTP服务器地址",
|
||||||
icon=FluentIcon.PAGE_RIGHT,
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
title="邮箱地址",
|
title="SMTP服务器地址",
|
||||||
|
content="发信邮箱的SMTP服务器地址",
|
||||||
|
configItem=Config.global_config.notify_SMTPServerAddress,
|
||||||
|
)
|
||||||
|
self.card_FromAddress = LineEditSettingCard(
|
||||||
|
text="请输入发信邮箱地址",
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="发信邮箱地址",
|
||||||
|
content="发送通知的邮箱地址",
|
||||||
|
configItem=Config.global_config.notify_FromAddress,
|
||||||
|
)
|
||||||
|
self.card_AuthorizationCode = PasswordLineEditSettingCard(
|
||||||
|
text="请输入发信邮箱授权码",
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="发信邮箱授权码",
|
||||||
|
content="发送通知的邮箱授权码",
|
||||||
|
configItem=Config.global_config.notify_AuthorizationCode,
|
||||||
|
)
|
||||||
|
self.card_ToAddress = LineEditSettingCard(
|
||||||
|
text="请输入收信邮箱地址",
|
||||||
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
|
title="收信邮箱地址",
|
||||||
content="接收通知的邮箱地址",
|
content="接收通知的邮箱地址",
|
||||||
configItem=Config.global_config.notify_MailAddress,
|
configItem=Config.global_config.notify_ToAddress,
|
||||||
)
|
)
|
||||||
|
|
||||||
widget = QWidget()
|
widget = QWidget()
|
||||||
Layout = QVBoxLayout(widget)
|
Layout = QVBoxLayout(widget)
|
||||||
Layout.addWidget(self.card_IfSendMail)
|
Layout.addWidget(self.card_IfSendMail)
|
||||||
Layout.addWidget(self.card_MailAddress)
|
Layout.addWidget(self.card_SMTPServerAddress)
|
||||||
|
Layout.addWidget(self.card_FromAddress)
|
||||||
|
Layout.addWidget(self.card_AuthorizationCode)
|
||||||
|
Layout.addWidget(self.card_ToAddress)
|
||||||
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.viewLayout.setSpacing(0)
|
self.viewLayout.setSpacing(0)
|
||||||
self.addGroupWidget(widget)
|
self.addGroupWidget(widget)
|
||||||
@@ -557,7 +671,7 @@ class NotifySettingCard(HeaderCardWidget):
|
|||||||
super().__init__(
|
super().__init__(
|
||||||
FluentIcon.SETTING,
|
FluentIcon.SETTING,
|
||||||
"推送ServerChan通知",
|
"推送ServerChan通知",
|
||||||
"通过ServerChan通知推送任务结果",
|
"ServerChan通知推送渠道",
|
||||||
parent,
|
parent,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -604,7 +718,7 @@ class NotifySettingCard(HeaderCardWidget):
|
|||||||
super().__init__(
|
super().__init__(
|
||||||
FluentIcon.SETTING,
|
FluentIcon.SETTING,
|
||||||
"推送企业微信机器人通知",
|
"推送企业微信机器人通知",
|
||||||
"通过企业微信机器人Webhook通知推送任务结果",
|
"企业微信机器人Webhook通知推送渠道",
|
||||||
parent,
|
parent,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -695,11 +809,11 @@ class OtherSettingCard(HeaderCardWidget):
|
|||||||
content="查看AUTO_MAA的最新公告",
|
content="查看AUTO_MAA的最新公告",
|
||||||
)
|
)
|
||||||
self.card_UserDocs = HyperlinkCard(
|
self.card_UserDocs = HyperlinkCard(
|
||||||
url="https://docs.qq.com/aio/DQ2NwUHRiWGtMWHBy",
|
url="https://clozya.github.io/AUTOMAA_docs",
|
||||||
text="查看使用指南",
|
text="访问",
|
||||||
icon=FluentIcon.PAGE_RIGHT,
|
icon=FluentIcon.PAGE_RIGHT,
|
||||||
title="使用指南",
|
title="AUTO_MAA官方文档站",
|
||||||
content="查看AUTO_MAA的使用教程和文档",
|
content="访问AUTO_MAA的官方文档站,获取使用指南和项目相关信息",
|
||||||
)
|
)
|
||||||
self.card_Association = self.AssociationSettingCard()
|
self.card_Association = self.AssociationSettingCard()
|
||||||
|
|
||||||
|
|||||||
@@ -33,14 +33,16 @@ import subprocess
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout, QHBoxLayout
|
||||||
QApplication,
|
from qfluentwidgets import (
|
||||||
QDialog,
|
ProgressBar,
|
||||||
QVBoxLayout,
|
IndeterminateProgressBar,
|
||||||
|
BodyLabel,
|
||||||
|
PushButton,
|
||||||
|
EditableComboBox,
|
||||||
)
|
)
|
||||||
from qfluentwidgets import ProgressBar, IndeterminateProgressBar, BodyLabel
|
from PySide6.QtGui import QIcon, QCloseEvent
|
||||||
from PySide6.QtGui import QIcon
|
from PySide6.QtCore import QThread, Signal, QEventLoop
|
||||||
from PySide6.QtCore import QObject, QThread, Signal
|
|
||||||
|
|
||||||
|
|
||||||
def version_text(version_numb: list) -> str:
|
def version_text(version_numb: list) -> str:
|
||||||
@@ -59,6 +61,8 @@ class UpdateProcess(QThread):
|
|||||||
|
|
||||||
info = Signal(str)
|
info = Signal(str)
|
||||||
progress = Signal(int, int, int)
|
progress = Signal(int, int, int)
|
||||||
|
question = Signal(dict)
|
||||||
|
question_response = Signal(str)
|
||||||
accomplish = Signal()
|
accomplish = Signal()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -70,8 +74,11 @@ class UpdateProcess(QThread):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.main_version = main_version
|
self.main_version = main_version
|
||||||
self.updater_version = updater_version
|
self.updater_version = updater_version
|
||||||
self.download_path = app_path / "AUTO_MAA_Update.zip" # 临时下载文件的路径
|
self.download_path = app_path / "DOWNLOAD_TEMP.zip" # 临时下载文件的路径
|
||||||
self.version_path = app_path / "resources/version.json"
|
self.version_path = app_path / "resources/version.json"
|
||||||
|
self.response = None
|
||||||
|
|
||||||
|
self.question_response.connect(self._capture_response)
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
|
|
||||||
@@ -81,25 +88,41 @@ class UpdateProcess(QThread):
|
|||||||
|
|
||||||
self.info.emit("正在获取下载链接")
|
self.info.emit("正在获取下载链接")
|
||||||
url_list = self.get_download_url()
|
url_list = self.get_download_url()
|
||||||
|
url_dict = {}
|
||||||
|
|
||||||
|
# 验证下载地址
|
||||||
|
for i, url in enumerate(url_list):
|
||||||
|
|
||||||
|
if self.isInterruptionRequested():
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.progress.emit(0, len(url_list), i)
|
||||||
|
|
||||||
# 验证下载地址并获取文件大小
|
|
||||||
for i in range(len(url_list)):
|
|
||||||
try:
|
try:
|
||||||
self.info.emit(f"正在验证下载地址:{url_list[i]}")
|
self.info.emit(f"正在验证下载地址:{url}")
|
||||||
response = requests.get(url_list[i], stream=True)
|
response = requests.get(url, stream=True)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
self.info.emit(
|
self.info.emit(f"连接失败,错误代码 {response.status_code}")
|
||||||
f"连接失败,错误代码 {response.status_code} ,正在切换代理({i+1}/{len(url_list)})"
|
|
||||||
)
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
continue
|
||||||
file_size = response.headers.get("Content-Length")
|
url_dict[url] = response.elapsed.total_seconds()
|
||||||
break
|
|
||||||
except requests.RequestException:
|
except requests.RequestException:
|
||||||
self.info.emit(f"请求超时,正在切换代理({i+1}/{len(url_list)})")
|
self.info.emit(f"请求超时")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
else:
|
|
||||||
self.info.emit(f"连接失败,已尝试所有{len(url_list)}个代理")
|
download_url = self.push_question(url_dict)
|
||||||
|
|
||||||
|
# 获取文件大小
|
||||||
|
try:
|
||||||
|
self.info.emit(f"正在连接下载地址:{download_url}")
|
||||||
|
self.progress.emit(0, 0, 0)
|
||||||
|
response = requests.get(download_url, stream=True)
|
||||||
|
if response.status_code != 200:
|
||||||
|
self.info.emit(f"连接失败,错误代码 {response.status_code}")
|
||||||
|
return None
|
||||||
|
file_size = response.headers.get("Content-Length")
|
||||||
|
except requests.RequestException:
|
||||||
|
self.info.emit(f"请求超时")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if file_size is None:
|
if file_size is None:
|
||||||
@@ -118,6 +141,9 @@ class UpdateProcess(QThread):
|
|||||||
|
|
||||||
for chunk in response.iter_content(chunk_size=8192):
|
for chunk in response.iter_content(chunk_size=8192):
|
||||||
|
|
||||||
|
if self.isInterruptionRequested():
|
||||||
|
break
|
||||||
|
|
||||||
# 写入已下载数据
|
# 写入已下载数据
|
||||||
f.write(chunk)
|
f.write(chunk)
|
||||||
downloaded_size += len(chunk)
|
downloaded_size += len(chunk)
|
||||||
@@ -143,6 +169,10 @@ class UpdateProcess(QThread):
|
|||||||
)
|
)
|
||||||
self.progress.emit(0, 100, int(downloaded_size / file_size * 100))
|
self.progress.emit(0, 100, int(downloaded_size / file_size * 100))
|
||||||
|
|
||||||
|
if self.isInterruptionRequested() and self.download_path.exists():
|
||||||
|
self.download_path.unlink()
|
||||||
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
e = str(e)
|
e = str(e)
|
||||||
e = "\n".join([e[_ : _ + 75] for _ in range(0, len(e), 75)])
|
e = "\n".join([e[_ : _ + 75] for _ in range(0, len(e), 75)])
|
||||||
@@ -153,6 +183,9 @@ class UpdateProcess(QThread):
|
|||||||
try:
|
try:
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
if self.isInterruptionRequested():
|
||||||
|
self.download_path.unlink()
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
self.info.emit("正在解压更新文件")
|
self.info.emit("正在解压更新文件")
|
||||||
self.progress.emit(0, 0, 0)
|
self.progress.emit(0, 0, 0)
|
||||||
@@ -160,7 +193,7 @@ class UpdateProcess(QThread):
|
|||||||
zip_ref.extractall(self.app_path)
|
zip_ref.extractall(self.app_path)
|
||||||
break
|
break
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
self.info.emit("解压出错:AUTO_MAA正在运行,正在等待其关闭")
|
self.info.emit(f"解压出错:{self.name}正在运行,正在等待其关闭")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
self.info.emit("正在删除临时文件")
|
self.info.emit("正在删除临时文件")
|
||||||
@@ -178,22 +211,34 @@ class UpdateProcess(QThread):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# 更新version文件
|
# 更新version文件
|
||||||
with open(self.version_path, "r", encoding="utf-8") as f:
|
if not self.isInterruptionRequested and self.name in [
|
||||||
version_info = json.load(f)
|
"AUTO_MAA主程序",
|
||||||
if self.name == "AUTO_MAA更新器":
|
"AUTO_MAA更新器",
|
||||||
version_info["updater_version"] = ".".join(map(str, self.updater_version))
|
]:
|
||||||
elif self.name == "AUTO_MAA主程序":
|
with open(self.version_path, "r", encoding="utf-8") as f:
|
||||||
version_info["main_version"] = ".".join(map(str, self.main_version))
|
version_info = json.load(f)
|
||||||
with open(self.version_path, "w", encoding="utf-8") as f:
|
if self.name == "AUTO_MAA主程序":
|
||||||
json.dump(version_info, f, ensure_ascii=False, indent=4)
|
version_info["main_version"] = ".".join(map(str, self.main_version))
|
||||||
|
elif self.name == "AUTO_MAA更新器":
|
||||||
|
version_info["updater_version"] = ".".join(
|
||||||
|
map(str, self.updater_version)
|
||||||
|
)
|
||||||
|
with open(self.version_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(version_info, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
# 主程序更新完成后打开AUTO_MAA
|
# 主程序更新完成后打开AUTO_MAA
|
||||||
if self.name == "AUTO_MAA主程序":
|
if not self.isInterruptionRequested and self.name == "AUTO_MAA主程序":
|
||||||
subprocess.Popen(
|
subprocess.Popen(
|
||||||
str(self.app_path / "AUTO_MAA.exe"),
|
str(self.app_path / "AUTO_MAA.exe"),
|
||||||
shell=True,
|
shell=True,
|
||||||
creationflags=subprocess.CREATE_NO_WINDOW,
|
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||||
)
|
)
|
||||||
|
elif not self.isInterruptionRequested and self.name == "MAA":
|
||||||
|
subprocess.Popen(
|
||||||
|
str(self.app_path / "MAA.exe"),
|
||||||
|
shell=True,
|
||||||
|
creationflags=subprocess.CREATE_NO_WINDOW,
|
||||||
|
)
|
||||||
|
|
||||||
self.accomplish.emit()
|
self.accomplish.emit()
|
||||||
|
|
||||||
@@ -239,6 +284,9 @@ class UpdateProcess(QThread):
|
|||||||
url_list.append(
|
url_list.append(
|
||||||
f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
|
f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
|
||||||
)
|
)
|
||||||
|
url_list.append(
|
||||||
|
f"https://jp-download.fearr.xyz/AUTO_MAA/AUTO_MAA_{version_text(self.main_version)}.zip"
|
||||||
|
)
|
||||||
for i in range(len(PROXY_list)):
|
for i in range(len(PROXY_list)):
|
||||||
url_list.append(
|
url_list.append(
|
||||||
f"{PROXY_list[i]}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
|
f"{PROXY_list[i]}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/AUTO_MAA_{version_text(self.main_version)}.zip"
|
||||||
@@ -247,33 +295,63 @@ class UpdateProcess(QThread):
|
|||||||
url_list.append(
|
url_list.append(
|
||||||
f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/Updater_{version_text(self.updater_version)}.zip"
|
f"https://gitee.com/DLmaster_361/AUTO_MAA/releases/download/{version_text(self.main_version)}/Updater_{version_text(self.updater_version)}.zip"
|
||||||
)
|
)
|
||||||
|
url_list.append(
|
||||||
|
f"https://jp-download.fearr.xyz/AUTO_MAA/Updater_{version_text(self.updater_version)}.zip"
|
||||||
|
)
|
||||||
for i in range(len(PROXY_list)):
|
for i in range(len(PROXY_list)):
|
||||||
url_list.append(
|
url_list.append(
|
||||||
f"{PROXY_list[i]}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/Updater_{version_text(self.updater_version)}.zip"
|
f"{PROXY_list[i]}https://github.com/DLmaster361/AUTO_MAA/releases/download/{version_text(self.main_version)}/Updater_{version_text(self.updater_version)}.zip"
|
||||||
)
|
)
|
||||||
|
elif self.name == "MAA":
|
||||||
|
url_list.append(
|
||||||
|
f"https://jp-download.fearr.xyz/MAA/MAA-{version_text(self.main_version)}-win-x64.zip"
|
||||||
|
)
|
||||||
|
for i in range(len(PROXY_list)):
|
||||||
|
url_list.append(
|
||||||
|
f"{PROXY_list[i]}https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases/download/{version_text(self.main_version)}/MAA-{version_text(self.main_version)}-win-x64.zip"
|
||||||
|
)
|
||||||
return url_list
|
return url_list
|
||||||
|
|
||||||
|
def push_question(self, url_dict: dict) -> str:
|
||||||
|
self.question.emit(url_dict)
|
||||||
|
loop = QEventLoop()
|
||||||
|
self.question_response.connect(loop.quit)
|
||||||
|
loop.exec()
|
||||||
|
return self.response
|
||||||
|
|
||||||
class Updater(QObject):
|
def _capture_response(self, response: str) -> None:
|
||||||
|
self.response = response
|
||||||
|
|
||||||
|
|
||||||
|
class Updater(QDialog):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, app_path: Path, name: str, main_version: list, updater_version: list
|
self, app_path: Path, name: str, main_version: list, updater_version: list
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.ui = QDialog()
|
self.setWindowTitle("AUTO_MAA更新器")
|
||||||
self.ui.setWindowTitle("AUTO_MAA更新器")
|
self.setWindowIcon(
|
||||||
self.ui.resize(700, 70)
|
QIcon(
|
||||||
self.ui.setWindowIcon(
|
str(
|
||||||
QIcon(str(app_path / "resources/icons/AUTO_MAA_Updater.ico"))
|
Path(sys.argv[0]).resolve().parent
|
||||||
|
/ "resources/icons/AUTO_MAA_Updater.ico"
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 创建垂直布局
|
# 创建垂直布局
|
||||||
self.Layout = QVBoxLayout(self.ui)
|
self.Layout = QVBoxLayout(self)
|
||||||
|
|
||||||
self.info = BodyLabel("正在初始化", self.ui)
|
self.info = BodyLabel("正在初始化", self)
|
||||||
self.progress_1 = IndeterminateProgressBar(self.ui)
|
self.progress_1 = IndeterminateProgressBar(self)
|
||||||
self.progress_2 = ProgressBar(self.ui)
|
self.progress_2 = ProgressBar(self)
|
||||||
|
self.combo_box = EditableComboBox(self)
|
||||||
|
|
||||||
|
self.button = PushButton("继续", self)
|
||||||
|
self.h_layout = QHBoxLayout()
|
||||||
|
self.h_layout.addStretch(1)
|
||||||
|
self.h_layout.addWidget(self.button)
|
||||||
|
|
||||||
self.update_progress(0, 0, 0)
|
self.update_progress(0, 0, 0)
|
||||||
|
|
||||||
@@ -281,6 +359,8 @@ class Updater(QObject):
|
|||||||
self.Layout.addStretch(1)
|
self.Layout.addStretch(1)
|
||||||
self.Layout.addWidget(self.progress_1)
|
self.Layout.addWidget(self.progress_1)
|
||||||
self.Layout.addWidget(self.progress_2)
|
self.Layout.addWidget(self.progress_2)
|
||||||
|
self.Layout.addWidget(self.combo_box)
|
||||||
|
self.Layout.addLayout(self.h_layout)
|
||||||
self.Layout.addStretch(1)
|
self.Layout.addStretch(1)
|
||||||
|
|
||||||
self.update_process = UpdateProcess(
|
self.update_process = UpdateProcess(
|
||||||
@@ -289,21 +369,59 @@ class Updater(QObject):
|
|||||||
|
|
||||||
self.update_process.info.connect(self.update_info)
|
self.update_process.info.connect(self.update_info)
|
||||||
self.update_process.progress.connect(self.update_progress)
|
self.update_process.progress.connect(self.update_progress)
|
||||||
|
self.update_process.question.connect(self.question)
|
||||||
|
|
||||||
self.update_process.start()
|
self.update_process.start()
|
||||||
|
|
||||||
def update_info(self, text: str) -> None:
|
def update_info(self, text: str) -> None:
|
||||||
self.info.setText(text)
|
self.info.setText(text)
|
||||||
|
|
||||||
def update_progress(self, begin: int, end: int, current: int) -> None:
|
def update_progress(
|
||||||
if begin == 0 and end == 0:
|
self, begin: int, end: int, current: int, if_show_combo_box: bool = False
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
self.combo_box.setVisible(if_show_combo_box)
|
||||||
|
self.button.setVisible(if_show_combo_box)
|
||||||
|
|
||||||
|
if if_show_combo_box:
|
||||||
|
self.progress_1.setVisible(False)
|
||||||
|
self.progress_2.setVisible(False)
|
||||||
|
self.resize(1000, 90)
|
||||||
|
elif begin == 0 and end == 0:
|
||||||
self.progress_2.setVisible(False)
|
self.progress_2.setVisible(False)
|
||||||
self.progress_1.setVisible(True)
|
self.progress_1.setVisible(True)
|
||||||
|
self.resize(700, 70)
|
||||||
else:
|
else:
|
||||||
self.progress_1.setVisible(False)
|
self.progress_1.setVisible(False)
|
||||||
self.progress_2.setVisible(True)
|
self.progress_2.setVisible(True)
|
||||||
self.progress_2.setRange(begin, end)
|
self.progress_2.setRange(begin, end)
|
||||||
self.progress_2.setValue(current)
|
self.progress_2.setValue(current)
|
||||||
|
self.resize(700, 70)
|
||||||
|
|
||||||
|
def question(self, url_dict: dict) -> None:
|
||||||
|
|
||||||
|
self.update_info("测速完成,请选择或自行输入一个合适下载地址:")
|
||||||
|
self.update_progress(0, 0, 0, True)
|
||||||
|
|
||||||
|
url_dict = dict(sorted(url_dict.items(), key=lambda item: item[1]))
|
||||||
|
|
||||||
|
for url, time in url_dict.items():
|
||||||
|
self.combo_box.addItem(f"{url} | 响应时间:{time:.3f}秒")
|
||||||
|
|
||||||
|
self.button.clicked.connect(
|
||||||
|
lambda: self.update_process.question_response.emit(
|
||||||
|
self.combo_box.currentText().split(" | ")[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def closeEvent(self, event: QCloseEvent):
|
||||||
|
"""清理残余进程"""
|
||||||
|
|
||||||
|
self.update_process.requestInterruption()
|
||||||
|
self.update_process.quit()
|
||||||
|
self.update_process.wait()
|
||||||
|
|
||||||
|
event.accept()
|
||||||
|
|
||||||
|
|
||||||
class AUTO_MAA_Updater(QApplication):
|
class AUTO_MAA_Updater(QApplication):
|
||||||
@@ -313,7 +431,7 @@ class AUTO_MAA_Updater(QApplication):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.main = Updater(app_path, name, main_version, updater_version)
|
self.main = Updater(app_path, name, main_version, updater_version)
|
||||||
self.main.ui.show()
|
self.main.show()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ pywin32
|
|||||||
pyautogui
|
pyautogui
|
||||||
pycryptodome
|
pycryptodome
|
||||||
requests
|
requests
|
||||||
|
Jinja2
|
||||||
serverchan_sdk
|
serverchan_sdk
|
||||||
nuitka==2.6
|
nuitka==2.6
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#主界面
|
#主界面
|
||||||
"MainFunction.PostActions": "12" #完成后
|
"MainFunction.PostActions": "8" #完成后退出MAA
|
||||||
|
"MainFunction.PostActions": "9" #完成后退出MAA和游戏
|
||||||
|
"MainFunction.PostActions": "12" #完成后退出MAA和模拟器
|
||||||
"TaskQueue.WakeUp.IsChecked": "True" #开始唤醒
|
"TaskQueue.WakeUp.IsChecked": "True" #开始唤醒
|
||||||
"TaskQueue.Recruiting.IsChecked": "True" #自动公招
|
"TaskQueue.Recruiting.IsChecked": "True" #自动公招
|
||||||
"TaskQueue.Base.IsChecked": "True" #基建换班
|
"TaskQueue.Base.IsChecked": "True" #基建换班
|
||||||
|
|||||||
160
resources/html/MAA_result.html
Normal file
160
resources/html/MAA_result.html
Normal file
File diff suppressed because one or more lines are too long
129
resources/html/MAA_six_star.html
Normal file
129
resources/html/MAA_six_star.html
Normal file
File diff suppressed because one or more lines are too long
233
resources/html/MAA_statistics.html
Normal file
233
resources/html/MAA_statistics.html
Normal file
File diff suppressed because one or more lines are too long
BIN
resources/images/Home.png
Normal file
BIN
resources/images/Home.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
BIN
resources/images/Home/BannerDefault.png
Normal file
BIN
resources/images/Home/BannerDefault.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"main_version": "4.2.2.2",
|
"main_version": "4.2.5.1",
|
||||||
"updater_version": "1.1.1.3",
|
"updater_version": "1.1.2.1",
|
||||||
"announcement": "\n## 新增功能\n- 添加用户每日代理次数上限功能 #15\n- 新增代理成功消息推送渠道Server酱与企业微信群机器人推送\n- 添加更新类别可选项\n## 修复BUG\n- 修复自定义基建无法正常使用的问题\n- 修正人工排查文案\n- 修复高级MAA配置序号错位\n- 修复高级用户列表无法配置问题\n- 修复主调度台选项乱动问题\n- 修复更新器文件夹定位问题\n## 程序优化\n- 无",
|
"announcement": "\n## 新增功能\n- 暂无\n## 修复BUG\n- 修复统计信息HTML模板公招匹配错误\n## 程序优化\n- 暂无",
|
||||||
"proxy_list": [
|
"proxy_list": [
|
||||||
"",
|
"",
|
||||||
"https://gitproxy.click/",
|
"https://gitproxy.click/",
|
||||||
|
|||||||
Reference in New Issue
Block a user