Files
AUTO-MAS-test/app/ui/member_manager.py
2025-05-06 17:42:24 +08:00

1647 lines
73 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
# AUTO_MAA is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
# AUTO_MAA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
AUTO_MAA脚本管理界面
v4.3
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtWidgets import (
QWidget,
QFileDialog,
QHBoxLayout,
QVBoxLayout,
QStackedWidget,
QTableWidgetItem,
QHeaderView,
)
from PySide6.QtGui import QIcon
from qfluentwidgets import (
Action,
Pivot,
ScrollArea,
FluentIcon,
MessageBox,
HeaderCardWidget,
CommandBar,
ExpandGroupSettingCard,
PushSettingCard,
TableWidget,
PrimaryToolButton,
)
from PySide6.QtCore import Qt, Signal
from datetime import datetime
from functools import partial
from pathlib import Path
from typing import List
import shutil
from app.core import Config, MainInfoBar, TaskManager, MaaConfig, MaaUserConfig, Network
from app.services import Crypto
from app.utils import DownloadManager
from .Widget import (
LineEditMessageBox,
LineEditSettingCard,
SpinBoxSettingCard,
ComboBoxMessageBox,
EditableComboBoxSettingCard,
PasswordLineEditSettingCard,
UserLableSettingCard,
ComboBoxSettingCard,
SwitchSettingCard,
PushAndSwitchButtonSettingCard,
PushAndComboBoxSettingCard,
)
class MemberManager(QWidget):
"""脚本管理父界面"""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("脚本管理")
layout = QVBoxLayout(self)
self.tools = CommandBar()
self.member_manager = self.MemberSettingBox(self)
# 逐个添加动作
self.tools.addActions(
[
Action(
FluentIcon.ADD_TO, "新建脚本实例", triggered=self.add_setting_box
),
Action(
FluentIcon.REMOVE_FROM,
"删除脚本实例",
triggered=self.del_setting_box,
),
]
)
self.tools.addSeparator()
self.tools.addActions(
[
Action(
FluentIcon.LEFT_ARROW, "向左移动", triggered=self.left_setting_box
),
Action(
FluentIcon.RIGHT_ARROW, "向右移动", triggered=self.right_setting_box
),
]
)
self.tools.addSeparator()
self.tools.addAction(
Action(
FluentIcon.DOWNLOAD,
"脚本下载器",
triggered=self.member_downloader,
)
)
self.tools.addSeparator()
self.key = Action(
FluentIcon.HIDE,
"显示/隐藏密码",
checkable=True,
triggered=self.show_password,
)
self.tools.addAction(self.key)
layout.addWidget(self.tools)
layout.addWidget(self.member_manager)
def add_setting_box(self):
"""添加一个脚本实例"""
choice = ComboBoxMessageBox(
self.window(),
"选择一个脚本类型以添加相应脚本实例",
["选择脚本类型"],
[["MAA"]],
)
if choice.exec() and choice.input[0].currentIndex() != -1:
if choice.input[0].currentText() == "MAA":
index = len(Config.member_dict) + 1
maa_config = MaaConfig()
maa_config.load(
Config.app_path / f"config/MaaConfig/脚本_{index}/config.json",
maa_config,
)
maa_config.save()
(Config.app_path / f"config/MaaConfig/脚本_{index}/UserData").mkdir(
parents=True, exist_ok=True
)
Config.member_dict[f"脚本_{index}"] = {
"Type": "Maa",
"Path": Config.app_path / f"config/MaaConfig/脚本_{index}",
"Config": maa_config,
"UserData": {},
}
self.member_manager.add_MaaSettingBox(index)
self.member_manager.switch_SettingBox(index)
logger.success(f"脚本实例 脚本_{index} 添加成功")
MainInfoBar.push_info_bar(
"success", "操作成功", f"添加脚本实例 脚本_{index}", 3000
)
def del_setting_box(self):
"""删除一个脚本实例"""
name = self.member_manager.pivot.currentRouteKey()
if name == None:
logger.warning("删除脚本实例时未选择脚本实例")
MainInfoBar.push_info_bar(
"warning", "未选择脚本实例", "请选择一个脚本实例", 5000
)
return None
if len(Config.running_list) > 0:
logger.warning("删除脚本实例时调度队列未停止运行")
MainInfoBar.push_info_bar(
"warning", "调度中心正在执行任务", "请等待或手动中止任务", 5000
)
return None
choice = MessageBox(
"确认",
f"确定要删除 {name} 实例吗?",
self.window(),
)
if choice.exec():
self.member_manager.clear_SettingBox()
shutil.rmtree(Config.member_dict[name]["Path"])
Config.change_queue(name, "禁用")
for i in range(int(name[3:]) + 1, len(Config.member_dict) + 1):
if Config.member_dict[f"脚本_{i}"]["Path"].exists():
Config.member_dict[f"脚本_{i}"]["Path"].rename(
Config.member_dict[f"脚本_{i}"]["Path"].with_name(f"脚本_{i-1}")
)
Config.change_queue(f"脚本_{i}", f"脚本_{i-1}")
self.member_manager.show_SettingBox(max(int(name[3:]) - 1, 1))
logger.success(f"脚本实例 {name} 删除成功")
MainInfoBar.push_info_bar(
"success", "操作成功", f"删除脚本实例 {name}", 3000
)
def left_setting_box(self):
"""向左移动脚本实例"""
name = self.member_manager.pivot.currentRouteKey()
if name == None:
logger.warning("向左移动脚本实例时未选择脚本实例")
MainInfoBar.push_info_bar(
"warning", "未选择脚本实例", "请选择一个脚本实例", 5000
)
return None
index = int(name[3:])
if index == 1:
logger.warning("向左移动脚本实例时已到达最左端")
MainInfoBar.push_info_bar(
"warning", "已经是第一个脚本实例", "无法向左移动", 5000
)
return None
if len(Config.running_list) > 0:
logger.warning("向左移动脚本实例时调度队列未停止运行")
MainInfoBar.push_info_bar(
"warning", "调度中心正在执行任务", "请等待或手动中止任务", 5000
)
return None
self.member_manager.clear_SettingBox()
Config.member_dict[name]["Path"].rename(
Config.member_dict[name]["Path"].with_name("脚本_0")
)
Config.change_queue(name, "脚本_0")
Config.member_dict[f"脚本_{index-1}"]["Path"].rename(
Config.member_dict[name]["Path"]
)
Config.change_queue(f"脚本_{index-1}", name)
Config.member_dict[name]["Path"].with_name("脚本_0").rename(
Config.member_dict[f"脚本_{index-1}"]["Path"]
)
Config.change_queue("脚本_0", f"脚本_{index-1}")
self.member_manager.show_SettingBox(index - 1)
logger.success(f"脚本实例 {name} 左移成功")
MainInfoBar.push_info_bar("success", "操作成功", f"左移脚本实例 {name}", 3000)
def right_setting_box(self):
"""向右移动脚本实例"""
name = self.member_manager.pivot.currentRouteKey()
if name == None:
logger.warning("向右移动脚本实例时未选择脚本实例")
MainInfoBar.push_info_bar(
"warning", "未选择脚本实例", "请选择一个脚本实例", 5000
)
return None
index = int(name[3:])
if index == len(Config.member_dict):
logger.warning("向右移动脚本实例时已到达最右端")
MainInfoBar.push_info_bar(
"warning", "已经是最后一个脚本实例", "无法向右移动", 5000
)
return None
if len(Config.running_list) > 0:
logger.warning("向右移动脚本实例时调度队列未停止运行")
MainInfoBar.push_info_bar(
"warning", "调度中心正在执行任务", "请等待或手动中止任务", 5000
)
return None
self.member_manager.clear_SettingBox()
Config.member_dict[name]["Path"].rename(
Config.member_dict[name]["Path"].with_name("脚本_0")
)
Config.change_queue(name, "脚本_0")
Config.member_dict[f"脚本_{index+1}"]["Path"].rename(
Config.member_dict[name]["Path"]
)
Config.change_queue(f"脚本_{index+1}", name)
Config.member_dict[name]["Path"].with_name("脚本_0").rename(
Config.member_dict[f"脚本_{index+1}"]["Path"]
)
Config.change_queue("脚本_0", f"脚本_{index+1}")
self.member_manager.show_SettingBox(index + 1)
logger.success(f"脚本实例 {name} 右移成功")
MainInfoBar.push_info_bar("success", "操作成功", f"右移脚本实例 {name}", 3000)
def member_downloader(self):
"""脚本下载器"""
if not Config.get(Config.update_MirrorChyanCDK):
logger.warning("脚本下载器未设置CDK")
MainInfoBar.push_info_bar(
"warning",
"未设置Mirror酱CDK",
"下载器依赖于Mirror酱未设置CDK时无法使用",
5000,
)
return None
choice = ComboBoxMessageBox(
self.window(),
"选择一个脚本类型以下载相应脚本",
["选择脚本类型"],
[["MAA", "StarRailAssistant"]],
)
if choice.exec() and choice.input[0].currentIndex() != -1:
app_name = choice.input[0].currentText()
(Config.app_path / f"script/{app_name}").mkdir(parents=True, exist_ok=True)
folder = QFileDialog.getExistingDirectory(
self,
f"选择{app_name}下载目录",
str(Config.app_path / f"script/{app_name}"),
)
if not folder:
logger.warning(f"选择{app_name}下载目录时未选择文件夹")
MainInfoBar.push_info_bar(
"warning", "警告", f"未选择{app_name}下载目录", 5000
)
return None
if app_name in ["MAA"]:
url = f"https://mirrorchyan.com/api/resources/{app_name}/latest?user_agent=AutoMaaGui&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&os=win&arch=x64&channel=stable"
elif app_name in ["StarRailAssistant"]:
url = f"https://mirrorchyan.com/api/resources/{app_name}/latest?user_agent=AutoMaaGui&cdk={Crypto.win_decryptor(Config.get(Config.update_MirrorChyanCDK))}&channel=stable"
# 从mirrorc服务器获取最新版本信息
Network.set_info(mode="get", url=url)
Network.start()
Network.loop.exec()
if Network.stutus_code == 200:
app_info = Network.response_json
else:
if Network.response_json:
app_info = Network.response_json
if app_info["code"] != 0:
logger.error(f"获取版本信息时出错:{app_info["msg"]}")
error_remark_dict = {
1001: "获取版本信息的URL参数不正确",
7001: "填入的 CDK 已过期",
7002: "填入的 CDK 错误",
7003: "填入的 CDK 今日下载次数已达上限",
7004: "填入的 CDK 类型和待下载的资源不匹配",
7005: "填入的 CDK 已被封禁",
8001: "对应架构和系统下的资源不存在",
8002: "错误的系统参数",
8003: "错误的架构参数",
8004: "错误的更新通道参数",
1: app_info["msg"],
}
if app_info["code"] in error_remark_dict:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
error_remark_dict[app_info["code"]],
-1,
)
else:
MainInfoBar.push_info_bar(
"error",
"获取版本信息时出错",
"意料之外的错误,请及时联系项目组以获取来自 Mirror 酱的技术支持",
-1,
)
return None
logger.warning(f"获取版本信息时出错:{Network.error_message}")
MainInfoBar.push_info_bar(
"warning",
"获取版本信息时出错",
f"网络错误:{Network.stutus_code}",
5000,
)
return None
self.downloader = DownloadManager(
Path(folder),
app_name,
None,
{
"mode": "MirrorChyan",
"thread_numb": 1,
"url": app_info["data"]["url"],
},
)
self.downloader.setWindowTitle("AUTO_MAA下载器 - Mirror酱渠道")
self.downloader.setWindowIcon(
QIcon(str(Config.app_path / "resources/icons/MirrorChyan.ico"))
)
self.downloader.show()
self.downloader.run()
def show_password(self):
if Config.PASSWORD == "":
choice = LineEditMessageBox(
self.window(),
"请输入管理密钥",
"管理密钥",
"密码",
)
if choice.exec() and choice.input.text() != "":
Config.PASSWORD = choice.input.text()
Config.PASSWORD_refreshed.emit()
self.key.setIcon(FluentIcon.VIEW)
self.key.setChecked(True)
else:
Config.PASSWORD = ""
Config.PASSWORD_refreshed.emit()
self.key.setIcon(FluentIcon.HIDE)
self.key.setChecked(False)
else:
Config.PASSWORD = ""
Config.PASSWORD_refreshed.emit()
self.key.setIcon(FluentIcon.HIDE)
self.key.setChecked(False)
def refresh_dashboard(self):
"""刷新所有脚本实例的用户仪表盘"""
for script in self.member_manager.script_list:
script.user_setting.user_manager.user_dashboard.load_info()
class MemberSettingBox(QWidget):
"""脚本管理子页面组"""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("脚本管理页面组")
self.pivot = Pivot(self)
self.stackedWidget = QStackedWidget(self)
self.Layout = QVBoxLayout(self)
self.script_list: List[MemberManager.MemberSettingBox.MaaSettingBox] = []
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
self.Layout.addWidget(self.stackedWidget)
self.Layout.setContentsMargins(0, 0, 0, 0)
self.pivot.currentItemChanged.connect(
lambda index: self.switch_SettingBox(
int(index[3:]), if_chang_pivot=False
)
)
self.show_SettingBox(1)
def show_SettingBox(self, index) -> None:
"""加载所有子界面"""
Config.search_member()
for name, info in Config.member_dict.items():
if info["Type"] == "Maa":
self.add_MaaSettingBox(int(name[3:]))
self.switch_SettingBox(index)
def switch_SettingBox(self, index: int, if_chang_pivot: bool = True) -> None:
"""切换到指定的子界面"""
if len(Config.member_dict) == 0:
return None
if index > len(Config.member_dict):
return None
if if_chang_pivot:
self.pivot.setCurrentItem(self.script_list[index - 1].objectName())
self.stackedWidget.setCurrentWidget(self.script_list[index - 1])
self.script_list[index - 1].user_setting.user_manager.switch_SettingBox(
"用户仪表盘"
)
def clear_SettingBox(self) -> None:
"""清空所有子界面"""
for sub_interface in self.script_list:
self.stackedWidget.removeWidget(sub_interface)
sub_interface.deleteLater()
self.script_list.clear()
self.pivot.clear()
def add_MaaSettingBox(self, uid: int) -> None:
"""添加一个MAA设置界面"""
maa_setting_box = self.MaaSettingBox(uid, self)
self.script_list.append(maa_setting_box)
self.stackedWidget.addWidget(self.script_list[-1])
self.pivot.addItem(routeKey=f"脚本_{uid}", text=f"脚本 {uid}")
class MaaSettingBox(QWidget):
"""MAA类脚本设置界面"""
def __init__(self, uid: int, parent=None):
super().__init__(parent)
self.setObjectName(f"脚本_{uid}")
self.config = Config.member_dict[f"脚本_{uid}"]["Config"]
layout = QVBoxLayout()
scrollArea = ScrollArea()
scrollArea.setWidgetResizable(True)
content_widget = QWidget()
content_layout = QVBoxLayout(content_widget)
self.app_setting = self.AppSettingCard(f"脚本_{uid}", self.config, self)
self.user_setting = self.UserManager(f"脚本_{uid}", self)
content_layout.addWidget(self.app_setting)
content_layout.addWidget(self.user_setting)
content_layout.addStretch(1)
scrollArea.setWidget(content_widget)
layout.addWidget(scrollArea)
self.setLayout(layout)
class AppSettingCard(HeaderCardWidget):
def __init__(self, name: str, config: MaaConfig, parent=None):
super().__init__(parent)
self.setTitle("MAA实例")
self.name = name
self.config = config
Layout = QVBoxLayout()
self.card_Name = LineEditSettingCard(
icon=FluentIcon.EDIT,
title="实例名称",
content="用于标识MAA实例的名称",
text="请输入实例名称",
qconfig=self.config,
configItem=self.config.MaaSet_Name,
parent=self,
)
self.card_Path = PushSettingCard(
text="选择文件夹",
icon=FluentIcon.FOLDER,
title="MAA目录",
content=self.config.get(self.config.MaaSet_Path),
parent=self,
)
self.card_Set = PushSettingCard(
text="设置",
icon=FluentIcon.HOME,
title="MAA全局配置",
content="简洁模式下MAA将继承全局配置",
parent=self,
)
self.RunSet = self.RunSetSettingCard(self.config, self)
self.card_Path.clicked.connect(self.PathClicked)
self.config.MaaSet_Path.valueChanged.connect(
lambda: self.card_Path.setContent(
self.config.get(self.config.MaaSet_Path)
)
)
self.card_Set.clicked.connect(
lambda: TaskManager.add_task("设置MAA_全局", self.name, None)
)
Layout.addWidget(self.card_Name)
Layout.addWidget(self.card_Path)
Layout.addWidget(self.card_Set)
Layout.addWidget(self.RunSet)
self.viewLayout.addLayout(Layout)
def PathClicked(self):
folder = QFileDialog.getExistingDirectory(
self,
"选择MAA目录",
self.config.get(self.config.MaaSet_Path),
)
if not folder or self.config.get(self.config.MaaSet_Path) == folder:
logger.warning("选择MAA目录时未选择文件夹或未更改文件夹")
MainInfoBar.push_info_bar(
"warning", "警告", "未选择文件夹或未更改文件夹", 5000
)
return None
elif (
not (Path(folder) / "config/gui.json").exists()
or not (Path(folder) / "MAA.exe").exists()
):
logger.warning("选择MAA目录时未找到MAA程序或配置文件")
MainInfoBar.push_info_bar(
"warning", "警告", "未找到MAA程序或配置文件", 5000
)
return None
(Config.member_dict[self.name]["Path"] / "Default").mkdir(
parents=True, exist_ok=True
)
shutil.copy(
Path(folder) / "config/gui.json",
Config.member_dict[self.name]["Path"] / "Default/gui.json",
)
self.config.set(self.config.MaaSet_Path, folder)
class RunSetSettingCard(ExpandGroupSettingCard):
def __init__(self, config: MaaConfig, parent=None):
super().__init__(
FluentIcon.SETTING, "运行", "MAA运行调控选项", parent
)
self.config = config
self.card_TaskTransitionMethod = ComboBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="任务切换方式",
content="相邻两个任务间的切换方式,使用“详细”配置的用户固定为“重启模拟器”",
texts=["直接切换账号", "重启明日方舟", "重启模拟器"],
qconfig=self.config,
configItem=self.config.RunSet_TaskTransitionMethod,
parent=self,
)
self.card_ProxyTimesLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="用户单日代理次数上限",
content="当用户本日代理成功次数达到该阈值时跳过代理阈值为“0”时视为无代理次数上限",
range=(0, 1024),
qconfig=self.config,
configItem=self.config.RunSet_ProxyTimesLimit,
parent=self,
)
self.card_RunTimesLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="代理重试次数限制",
content="若超过该次数限制仍未完成代理,视为代理失败",
range=(1, 1024),
qconfig=self.config,
configItem=self.config.RunSet_RunTimesLimit,
parent=self,
)
self.card_AnnihilationTimeLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="剿灭代理超时限制",
content="MAA日志无变化时间超过该阈值视为超时单位为分钟",
range=(1, 1024),
qconfig=self.config,
configItem=self.config.RunSet_AnnihilationTimeLimit,
parent=self,
)
self.card_RoutineTimeLimit = SpinBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="自动代理超时限制",
content="MAA日志无变化时间超过该阈值视为超时单位为分钟",
range=(1, 1024),
qconfig=self.config,
configItem=self.config.RunSet_RoutineTimeLimit,
parent=self,
)
self.card_AnnihilationWeeklyLimit = SwitchSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="每周剿灭仅执行到上限",
content="每周剿灭模式执行到上限,本周剩下时间不再执行剿灭任务",
qconfig=self.config,
configItem=self.config.RunSet_AnnihilationWeeklyLimit,
parent=self,
)
self.card_AutoUpdateMaa = SwitchSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="自动代理时自动更新MAA",
content="执行自动代理任务时自动更新MAA关闭后仍会进行MAA版本检查",
qconfig=self.config,
configItem=self.config.RunSet_AutoUpdateMaa,
parent=self,
)
widget = QWidget()
Layout = QVBoxLayout(widget)
Layout.addWidget(self.card_TaskTransitionMethod)
Layout.addWidget(self.card_ProxyTimesLimit)
Layout.addWidget(self.card_RunTimesLimit)
Layout.addWidget(self.card_AnnihilationTimeLimit)
Layout.addWidget(self.card_RoutineTimeLimit)
Layout.addWidget(self.card_AnnihilationWeeklyLimit)
Layout.addWidget(self.card_AutoUpdateMaa)
self.viewLayout.setContentsMargins(0, 0, 0, 0)
self.viewLayout.setSpacing(0)
self.addGroupWidget(widget)
class UserManager(HeaderCardWidget):
"""用户管理父页面"""
def __init__(self, name: str, parent=None):
super().__init__(parent)
self.setObjectName(f"{name}_用户管理")
self.setTitle("下属用户")
self.name = name
self.tools = CommandBar()
self.user_manager = self.UserSettingBox(self.name, self)
# 逐个添加动作
self.tools.addActions(
[
Action(
FluentIcon.ADD_TO, "新建用户", triggered=self.add_user
),
Action(
FluentIcon.REMOVE_FROM,
"删除用户",
triggered=self.del_user,
),
]
)
self.tools.addSeparator()
self.tools.addActions(
[
Action(
FluentIcon.LEFT_ARROW,
"向前移动",
triggered=self.left_user,
),
Action(
FluentIcon.RIGHT_ARROW,
"向后移动",
triggered=self.right_user,
),
]
)
layout = QVBoxLayout()
layout.addWidget(self.tools)
layout.addWidget(self.user_manager)
self.viewLayout.addLayout(layout)
def add_user(self):
"""添加一个用户"""
index = len(Config.member_dict[self.name]["UserData"]) + 1
user_config = MaaUserConfig()
user_config.load(
Config.member_dict[self.name]["Path"]
/ f"UserData/用户_{index}/config.json",
user_config,
)
user_config.save()
Config.member_dict[self.name]["UserData"][f"用户_{index}"] = {
"Path": Config.member_dict[self.name]["Path"]
/ f"UserData/用户_{index}",
"Config": user_config,
}
self.user_manager.add_userSettingBox(index)
self.user_manager.switch_SettingBox(f"用户_{index}")
logger.success(f"{self.name} 用户_{index} 添加成功")
MainInfoBar.push_info_bar(
"success", "操作成功", f"{self.name} 添加 用户_{index}", 3000
)
def del_user(self):
"""删除一个用户"""
name = self.user_manager.pivot.currentRouteKey()
if name == None:
logger.warning("未选择用户")
MainInfoBar.push_info_bar(
"warning", "未选择用户", "请先选择一个用户", 5000
)
return None
if name == "用户仪表盘":
logger.warning("试图删除用户仪表盘")
MainInfoBar.push_info_bar(
"warning", "未选择用户", "请勿尝试删除用户仪表盘", 5000
)
return None
if self.name in Config.running_list:
logger.warning("所属脚本正在运行")
MainInfoBar.push_info_bar(
"warning", "所属脚本正在运行", "请先停止任务", 5000
)
return None
choice = MessageBox(
"确认",
f"确定要删除 {name} 吗?",
self.window(),
)
if choice.exec():
self.user_manager.clear_SettingBox()
shutil.rmtree(
Config.member_dict[self.name]["UserData"][name]["Path"]
)
for i in range(
int(name[3:]) + 1,
len(Config.member_dict[self.name]["UserData"]) + 1,
):
if Config.member_dict[self.name]["UserData"][f"用户_{i}"][
"Path"
].exists():
Config.member_dict[self.name]["UserData"][f"用户_{i}"][
"Path"
].rename(
Config.member_dict[self.name]["UserData"][
f"用户_{i}"
]["Path"].with_name(f"用户_{i-1}")
)
self.user_manager.show_SettingBox(
f"用户_{max(int(name[3:]) - 1, 1)}"
)
logger.success(f"{self.name} {name} 删除成功")
MainInfoBar.push_info_bar(
"success", "操作成功", f"{self.name} 删除 {name}", 3000
)
def left_user(self):
"""向前移动用户"""
name = self.user_manager.pivot.currentRouteKey()
if name == None:
logger.warning("未选择用户")
MainInfoBar.push_info_bar(
"warning", "未选择用户", "请先选择一个用户", 5000
)
return None
if name == "用户仪表盘":
logger.warning("试图移动用户仪表盘")
MainInfoBar.push_info_bar(
"warning", "未选择用户", "请勿尝试移动用户仪表盘", 5000
)
return None
index = int(name[3:])
if index == 1:
logger.warning("向前移动用户时已到达最左端")
MainInfoBar.push_info_bar(
"warning", "已经是第一个用户", "无法向前移动", 5000
)
return None
if self.name in Config.running_list:
logger.warning("所属脚本正在运行")
MainInfoBar.push_info_bar(
"warning", "所属脚本正在运行", "请先停止任务", 5000
)
return None
self.user_manager.clear_SettingBox()
Config.member_dict[self.name]["UserData"][name]["Path"].rename(
Config.member_dict[self.name]["UserData"][name][
"Path"
].with_name("用户_0")
)
Config.member_dict[self.name]["UserData"][f"用户_{index-1}"][
"Path"
].rename(Config.member_dict[self.name]["UserData"][name]["Path"])
Config.member_dict[self.name]["UserData"][name]["Path"].with_name(
"用户_0"
).rename(
Config.member_dict[self.name]["UserData"][f"用户_{index-1}"][
"Path"
]
)
self.user_manager.show_SettingBox(f"用户_{index - 1}")
logger.success(f"{self.name} {name} 前移成功")
MainInfoBar.push_info_bar(
"success", "操作成功", f"{self.name} 前移 {name}", 3000
)
def right_user(self):
"""向后移动用户"""
name = self.user_manager.pivot.currentRouteKey()
if name == None:
logger.warning("未选择用户")
MainInfoBar.push_info_bar(
"warning", "未选择用户", "请先选择一个用户", 5000
)
return None
if name == "用户仪表盘":
logger.warning("试图删除用户仪表盘")
MainInfoBar.push_info_bar(
"warning", "未选择用户", "请勿尝试移动用户仪表盘", 5000
)
return None
index = int(name[3:])
if index == len(Config.member_dict[self.name]["UserData"]):
logger.warning("向后移动用户时已到达最右端")
MainInfoBar.push_info_bar(
"warning", "已经是最后一个用户", "无法向后移动", 5000
)
return None
if self.name in Config.running_list:
logger.warning("所属脚本正在运行")
MainInfoBar.push_info_bar(
"warning", "所属脚本正在运行", "请先停止任务", 5000
)
return None
self.user_manager.clear_SettingBox()
Config.member_dict[self.name]["UserData"][name]["Path"].rename(
Config.member_dict[self.name]["UserData"][name][
"Path"
].with_name("用户_0")
)
Config.member_dict[self.name]["UserData"][f"用户_{index+1}"][
"Path"
].rename(Config.member_dict[self.name]["UserData"][name]["Path"])
Config.member_dict[self.name]["UserData"][name]["Path"].with_name(
"用户_0"
).rename(
Config.member_dict[self.name]["UserData"][f"用户_{index+1}"][
"Path"
]
)
self.user_manager.show_SettingBox(f"用户_{index + 1}")
logger.success(f"{self.name} {name} 后移成功")
MainInfoBar.push_info_bar(
"success", "操作成功", f"{self.name} 后移 {name}", 3000
)
class UserSettingBox(QWidget):
"""用户管理子页面组"""
def __init__(self, name: str, parent=None):
super().__init__(parent)
self.setObjectName("用户管理")
self.name = name
self.pivot = Pivot(self)
self.stackedWidget = QStackedWidget(self)
self.Layout = QVBoxLayout(self)
self.script_list: List[
MemberManager.MemberSettingBox.MaaSettingBox.UserManager.UserSettingBox.UserMemberSettingBox
] = []
self.user_dashboard = self.UserDashboard(self.name, self)
self.user_dashboard.switch_to.connect(self.switch_SettingBox)
self.stackedWidget.addWidget(self.user_dashboard)
self.pivot.addItem(routeKey="用户仪表盘", text="用户仪表盘")
self.Layout.addWidget(self.pivot, 0, Qt.AlignHCenter)
self.Layout.addWidget(self.stackedWidget)
self.Layout.setContentsMargins(0, 0, 0, 0)
self.pivot.currentItemChanged.connect(
lambda index: self.switch_SettingBox(
index, if_change_pivot=False
)
)
self.show_SettingBox("用户仪表盘")
def show_SettingBox(self, index: str) -> None:
"""加载所有子界面"""
Config.search_maa_user(self.name)
for name in Config.member_dict[self.name]["UserData"].keys():
self.add_userSettingBox(name[3:])
self.switch_SettingBox(index)
def switch_SettingBox(
self, index: str, if_change_pivot: bool = True
) -> None:
"""切换到指定的子界面"""
if len(Config.member_dict[self.name]["UserData"]) == 0:
index = "用户仪表盘"
if index != "用户仪表盘" and int(index[3:]) > len(
Config.member_dict[self.name]["UserData"]
):
return None
if index == "用户仪表盘":
self.user_dashboard.load_info()
if if_change_pivot:
self.pivot.setCurrentItem(index)
self.stackedWidget.setCurrentWidget(
self.user_dashboard
if index == "用户仪表盘"
else self.script_list[int(index[3:]) - 1]
)
def clear_SettingBox(self) -> None:
"""清空所有子界面"""
for sub_interface in self.script_list:
Config.gameid_refreshed.disconnect(
sub_interface.refresh_gameid
)
Config.PASSWORD_refreshed.disconnect(
sub_interface.refresh_password
)
self.stackedWidget.removeWidget(sub_interface)
sub_interface.deleteLater()
self.script_list.clear()
self.pivot.clear()
self.user_dashboard.dashboard.setRowCount(0)
self.stackedWidget.addWidget(self.user_dashboard)
self.pivot.addItem(routeKey="用户仪表盘", text="用户仪表盘")
def add_userSettingBox(self, uid: int) -> None:
"""添加一个用户设置界面"""
maa_setting_box = self.UserMemberSettingBox(
self.name, uid, self
)
self.script_list.append(maa_setting_box)
self.stackedWidget.addWidget(self.script_list[-1])
self.pivot.addItem(routeKey=f"用户_{uid}", text=f"用户 {uid}")
class UserDashboard(HeaderCardWidget):
"""用户仪表盘页面"""
switch_to = Signal(str)
def __init__(self, name: str, parent=None):
super().__init__(parent)
self.setObjectName("用户仪表盘")
self.setTitle("用户仪表盘")
self.name = name
self.dashboard = TableWidget(self)
self.dashboard.setColumnCount(11)
self.dashboard.setHorizontalHeaderLabels(
[
"用户名",
"账号ID",
"密码",
"状态",
"代理情况",
"给药量",
"关卡选择",
"备选 - 1",
"备选 - 2",
"剩余理智",
"",
]
)
self.dashboard.setEditTriggers(TableWidget.NoEditTriggers)
self.dashboard.verticalHeader().setVisible(False)
for col in range(6):
self.dashboard.horizontalHeader().setSectionResizeMode(
col, QHeaderView.ResizeMode.ResizeToContents
)
for col in range(6, 10):
self.dashboard.horizontalHeader().setSectionResizeMode(
col, QHeaderView.ResizeMode.Stretch
)
self.dashboard.horizontalHeader().setSectionResizeMode(
10, QHeaderView.ResizeMode.Fixed
)
self.dashboard.setColumnWidth(10, 32)
self.viewLayout.addWidget(self.dashboard)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
Config.PASSWORD_refreshed.connect(self.load_info)
def load_info(self):
self.user_data = Config.member_dict[self.name]["UserData"]
self.dashboard.setRowCount(len(self.user_data))
for name, info in self.user_data.items():
config = info["Config"]
text_list = []
if not config.get(config.Data_IfPassCheck):
text_list.append("未通过人工排查")
text_list.append(
f"今日已代理{config.get(config.Data_ProxyTimes)}"
if Config.server_date().strftime("%Y-%m-%d")
== config.get(config.Data_LastProxyDate)
else "今日未进行代理"
)
text_list.append(
"本周剿灭已完成"
if datetime.strptime(
config.get(config.Data_LastAnnihilationDate),
"%Y-%m-%d",
).isocalendar()[:2]
== Config.server_date().isocalendar()[:2]
else "本周剿灭未完成"
)
button = PrimaryToolButton(
FluentIcon.CHEVRON_RIGHT, self
)
button.setFixedSize(32, 32)
button.clicked.connect(
partial(self.switch_to.emit, name)
)
self.dashboard.setItem(
int(name[3:]) - 1,
0,
QTableWidgetItem(config.get(config.Info_Name)),
)
self.dashboard.setItem(
int(name[3:]) - 1,
1,
QTableWidgetItem(config.get(config.Info_Id)),
)
self.dashboard.setItem(
int(name[3:]) - 1,
2,
QTableWidgetItem(
Crypto.AUTO_decryptor(
config.get(config.Info_Password),
Config.PASSWORD,
)
if Config.PASSWORD
else "******"
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
3,
QTableWidgetItem(
"启用"
if config.get(config.Info_Status)
and config.get(config.Info_RemainedDay) != 0
else "禁用"
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
4,
QTableWidgetItem(" | ".join(text_list)),
)
self.dashboard.setItem(
int(name[3:]) - 1,
5,
QTableWidgetItem(
str(config.get(config.Info_MedicineNumb))
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
6,
QTableWidgetItem(
Config.gameid_dict["ALL"]["text"][
Config.gameid_dict["ALL"]["value"].index(
config.get(config.Info_GameId)
)
]
if config.get(config.Info_GameId)
in Config.gameid_dict["ALL"]["value"]
else config.get(config.Info_GameId)
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
7,
QTableWidgetItem(
Config.gameid_dict["ALL"]["text"][
Config.gameid_dict["ALL"]["value"].index(
config.get(config.Info_GameId_1)
)
]
if config.get(config.Info_GameId_1)
in Config.gameid_dict["ALL"]["value"]
else config.get(config.Info_GameId_1)
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
8,
QTableWidgetItem(
Config.gameid_dict["ALL"]["text"][
Config.gameid_dict["ALL"]["value"].index(
config.get(config.Info_GameId_2)
)
]
if config.get(config.Info_GameId_2)
in Config.gameid_dict["ALL"]["value"]
else config.get(config.Info_GameId_2)
),
)
self.dashboard.setItem(
int(name[3:]) - 1,
9,
QTableWidgetItem(
"不使用"
if config.get(config.Info_GameId_Remain) == "-"
else (
(
Config.gameid_dict["ALL"]["text"][
Config.gameid_dict["ALL"][
"value"
].index(
config.get(
config.Info_GameId_Remain
)
)
]
)
if config.get(config.Info_GameId_Remain)
in Config.gameid_dict["ALL"]["value"]
else config.get(config.Info_GameId_Remain)
)
),
)
self.dashboard.setCellWidget(
int(name[3:]) - 1, 10, button
)
class UserMemberSettingBox(HeaderCardWidget):
"""用户管理子页面"""
def __init__(self, name: str, uid: int, parent=None):
super().__init__(parent)
self.setObjectName(f"用户_{uid}")
self.setTitle(f"用户 {uid}")
self.name = name
self.config = Config.member_dict[self.name]["UserData"][
f"用户_{uid}"
]["Config"]
self.user_path = Config.member_dict[self.name]["UserData"][
f"用户_{uid}"
]["Path"]
self.card_Name = LineEditSettingCard(
icon=FluentIcon.PEOPLE,
title="用户名",
content="用户的昵称",
text="请输入用户名",
qconfig=self.config,
configItem=self.config.Info_Name,
parent=self,
)
self.card_Id = LineEditSettingCard(
icon=FluentIcon.PEOPLE,
title="账号ID",
content="官服输入手机号B服输入B站ID",
text="请输入账号ID",
qconfig=self.config,
configItem=self.config.Info_Id,
parent=self,
)
self.card_Mode = ComboBoxSettingCard(
icon=FluentIcon.DICTIONARY,
title="用户配置模式",
content="用户信息配置模式",
texts=["简洁", "详细"],
qconfig=self.config,
configItem=self.config.Info_Mode,
parent=self,
)
self.card_GameIdMode = ComboBoxSettingCard(
icon=FluentIcon.DICTIONARY,
title="关卡配置模式",
content="刷理智关卡号的配置模式",
texts=["固定"],
qconfig=self.config,
configItem=self.config.Info_GameIdMode,
parent=self,
)
self.card_Server = ComboBoxSettingCard(
icon=FluentIcon.PROJECTOR,
title="服务器",
content="选择服务器类型",
texts=["官服", "B服"],
qconfig=self.config,
configItem=self.config.Info_Server,
parent=self,
)
self.card_Status = SwitchSettingCard(
icon=FluentIcon.CHECKBOX,
title="用户状态",
content="启用或禁用该用户",
qconfig=self.config,
configItem=self.config.Info_Status,
parent=self,
)
self.card_RemainedDay = SpinBoxSettingCard(
icon=FluentIcon.CALENDAR,
title="剩余天数",
content="剩余代理天数,-1表示无限代理",
range=(-1, 1024),
qconfig=self.config,
configItem=self.config.Info_RemainedDay,
parent=self,
)
self.card_Annihilation = PushAndSwitchButtonSettingCard(
icon=FluentIcon.CAFE,
title="剿灭代理",
content="剿灭代理子任务相关设置",
text="设置具体配置",
qconfig=self.config,
configItem=self.config.Info_Annihilation,
parent=self,
)
self.card_Routine = PushAndSwitchButtonSettingCard(
icon=FluentIcon.CAFE,
title="日常代理",
content="日常代理子任务相关设置",
text="设置具体配置",
qconfig=self.config,
configItem=self.config.Info_Routine,
parent=self,
)
self.card_InfrastMode = PushAndComboBoxSettingCard(
icon=FluentIcon.CAFE,
title="基建模式",
content="配置文件仅在自定义基建中生效",
text="选择配置文件",
texts=[
"常规模式",
"一键轮休",
"自定义基建",
],
qconfig=self.config,
configItem=self.config.Info_InfrastMode,
parent=self,
)
self.card_Password = PasswordLineEditSettingCard(
icon=FluentIcon.VPN,
title="密码",
content="仅用于用户密码记录",
text="请输入用户密码",
algorithm="AUTO",
qconfig=self.config,
configItem=self.config.Info_Password,
parent=self,
)
self.card_Notes = LineEditSettingCard(
icon=FluentIcon.PENCIL_INK,
title="备注",
content="用户备注信息",
text="请输入备注",
qconfig=self.config,
configItem=self.config.Info_Notes,
parent=self,
)
self.card_MedicineNumb = SpinBoxSettingCard(
icon=FluentIcon.GAME,
title="吃理智药",
content="吃理智药次数输入0以关闭",
range=(0, 1024),
qconfig=self.config,
configItem=self.config.Info_MedicineNumb,
parent=self,
)
self.card_SeriesNumb = ComboBoxSettingCard(
icon=FluentIcon.GAME,
title="连战次数",
content="连战次数较大时建议搭配剩余理智关卡使用",
texts=["AUTO", "6", "5", "4", "3", "2", "1", "不选择"],
qconfig=self.config,
configItem=self.config.Info_SeriesNumb,
parent=self,
)
self.card_SeriesNumb.comboBox.setMinimumWidth(150)
self.card_GameId = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="关卡选择",
content="按下回车以添加自定义关卡号",
value=Config.gameid_dict["ALL"]["value"],
texts=Config.gameid_dict["ALL"]["text"],
qconfig=self.config,
configItem=self.config.Info_GameId,
parent=self,
)
self.card_GameId_1 = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="备选关卡 - 1",
content="按下回车以添加自定义关卡号",
value=Config.gameid_dict["ALL"]["value"],
texts=Config.gameid_dict["ALL"]["text"],
qconfig=self.config,
configItem=self.config.Info_GameId_1,
parent=self,
)
self.card_GameId_2 = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="备选关卡 - 2",
content="按下回车以添加自定义关卡号",
value=Config.gameid_dict["ALL"]["value"],
texts=Config.gameid_dict["ALL"]["text"],
qconfig=self.config,
configItem=self.config.Info_GameId_2,
parent=self,
)
self.card_GameId_Remain = EditableComboBoxSettingCard(
icon=FluentIcon.GAME,
title="剩余理智关卡",
content="按下回车以添加自定义关卡号",
value=Config.gameid_dict["ALL"]["value"],
texts=[
"不使用" if _ == "当前/上次" else _
for _ in Config.gameid_dict["ALL"]["text"]
],
qconfig=self.config,
configItem=self.config.Info_GameId_Remain,
parent=self,
)
self.card_UserLable = UserLableSettingCard(
icon=FluentIcon.INFO,
title="状态信息",
content="用户的代理情况汇总",
qconfig=self.config,
configItems={
"LastProxyDate": self.config.Data_LastProxyDate,
"LastAnnihilationDate": self.config.Data_LastAnnihilationDate,
"ProxyTimes": self.config.Data_ProxyTimes,
"IfPassCheck": self.config.Data_IfPassCheck,
},
parent=self,
)
h1_layout = QHBoxLayout()
h1_layout.addWidget(self.card_Name)
h1_layout.addWidget(self.card_Id)
h2_layout = QHBoxLayout()
h2_layout.addWidget(self.card_Mode)
h2_layout.addWidget(self.card_GameIdMode)
h2_layout.addWidget(self.card_Server)
h3_layout = QHBoxLayout()
h3_layout.addWidget(self.card_Status)
h3_layout.addWidget(self.card_RemainedDay)
h4_layout = QHBoxLayout()
h4_layout.addWidget(self.card_Annihilation)
h4_layout.addWidget(self.card_Routine)
h4_layout.addWidget(self.card_InfrastMode)
h5_layout = QHBoxLayout()
h5_layout.addWidget(self.card_Password)
h5_layout.addWidget(self.card_Notes)
h6_layout = QHBoxLayout()
h6_layout.addWidget(self.card_MedicineNumb)
h6_layout.addWidget(self.card_SeriesNumb)
h7_layout = QHBoxLayout()
h7_layout.addWidget(self.card_GameId)
h7_layout.addWidget(self.card_GameId_1)
h8_layout = QHBoxLayout()
h8_layout.addWidget(self.card_GameId_2)
h8_layout.addWidget(self.card_GameId_Remain)
Layout = QVBoxLayout()
Layout.addLayout(h1_layout)
Layout.addLayout(h2_layout)
Layout.addLayout(h3_layout)
Layout.addWidget(self.card_UserLable)
Layout.addLayout(h4_layout)
Layout.addLayout(h5_layout)
Layout.addLayout(h6_layout)
Layout.addLayout(h7_layout)
Layout.addLayout(h8_layout)
self.viewLayout.addLayout(Layout)
self.viewLayout.setContentsMargins(3, 0, 3, 3)
self.card_Mode.comboBox.currentIndexChanged.connect(
self.switch_mode
)
self.card_Annihilation.clicked.connect(
lambda: self.set_maa("Annihilation")
)
self.card_Routine.clicked.connect(
lambda: self.set_maa("Routine")
)
self.card_InfrastMode.clicked.connect(
self.set_infrastructure
)
Config.gameid_refreshed.connect(self.refresh_gameid)
Config.PASSWORD_refreshed.connect(self.refresh_password)
self.switch_mode()
def switch_mode(self) -> None:
if self.config.get(self.config.Info_Mode) == "简洁":
self.card_Routine.setVisible(False)
self.card_Server.setVisible(True)
self.card_Annihilation.button.setVisible(False)
self.card_InfrastMode.setVisible(True)
elif self.config.get(self.config.Info_Mode) == "详细":
self.card_Server.setVisible(False)
self.card_InfrastMode.setVisible(False)
self.card_Annihilation.button.setVisible(True)
self.card_Routine.setVisible(True)
def refresh_gameid(self):
self.card_GameId.reLoadOptions(
Config.gameid_dict["ALL"]["value"],
Config.gameid_dict["ALL"]["text"],
)
self.card_GameId_1.reLoadOptions(
Config.gameid_dict["ALL"]["value"],
Config.gameid_dict["ALL"]["text"],
)
self.card_GameId_2.reLoadOptions(
Config.gameid_dict["ALL"]["value"],
Config.gameid_dict["ALL"]["text"],
)
self.card_GameId_Remain.reLoadOptions(
Config.gameid_dict["ALL"]["value"],
[
"不使用" if _ == "当前/上次" else _
for _ in Config.gameid_dict["ALL"]["text"]
],
)
def refresh_password(self):
self.card_Password.setValue(
self.card_Password.qconfig.get(
self.card_Password.configItem
)
)
def set_infrastructure(self) -> None:
"""配置自定义基建"""
if self.name in Config.running_list:
logger.warning("所属脚本正在运行")
MainInfoBar.push_info_bar(
"warning", "所属脚本正在运行", "请先停止任务", 5000
)
return None
file_path, _ = QFileDialog.getOpenFileName(
self,
"选择自定义基建文件",
".",
"JSON 文件 (*.json)",
)
if file_path != "":
(self.user_path / "Infrastructure").mkdir(
parents=True, exist_ok=True
)
shutil.copy(
file_path,
self.user_path
/ "Infrastructure/infrastructure.json",
)
else:
logger.warning("未选择自定义基建文件")
MainInfoBar.push_info_bar(
"warning", "警告", "未选择自定义基建文件", 5000
)
def set_maa(self, mode: str) -> None:
"""配置MAA子配置"""
if self.name in Config.running_list:
logger.warning("所属脚本正在运行")
MainInfoBar.push_info_bar(
"warning", "所属脚本正在运行", "请先停止任务", 5000
)
return None
TaskManager.add_task(
"设置MAA_用户",
self.name,
{
"SetMaaInfo": {
"Path": self.user_path / mode,
}
},
)