Merge branch 'dev' into DLMS_dev

This commit is contained in:
DLmaster361
2025-05-24 19:56:16 +08:00
6 changed files with 462 additions and 320 deletions

View File

@@ -22,6 +22,11 @@
本软件是明日方舟第三方软件`MAA`的第三方工具即第3<sup>3</sup>方软件。旨在优化MAA多账号功能体验并通过一些方法解决MAA项目未能解决的部分问题提高代理的稳定性。
- **集中管理**一站式管理多个MAA脚本与多个用户配置和凌乱的散装脚本窗口说再见
- **无人值守**自动处理MAA相关报错再也不用为代理任务卡死时自己不在电脑旁烦恼啦
- **配置灵活**:通过调度队列与脚本的组合,自由实现您能想到的所有调度需求!
- **信息统计**:自动统计用户的公招与关卡掉落物,看看这个月您的收益是多少!
### 原理
本软件可以存储多个明日方舟账号数据,并通过以下流程实现代理功能:
@@ -32,11 +37,9 @@
### 优势
- **节省运行开销:** 只需要一份MAA软件与一个模拟器无需多开就能完成多账号代理羸弱的电脑也能代理日常
- **自定义空间大:** 依靠高级用户配置模式支持MAA几乎所有设置选项自定义支持模拟器多开
- **调度方法自由:** 通过调度队列功能自由实现MAA多开等多种调度方式
- **一键代理无忧:** 无须中途手动修改MAA配置将繁琐交给AUTO_MAA把游戏留给自己。
- **代理结果复核:** 通过人工排查功能核实各用户代理情况,堵住自动代理的最后一丝风险。
- **高效稳定**:通过日志监测、异常处理等机制,保障代理任务顺利完成
- **简洁易用**:无需手动修改配置文件,实现自动化调度与多开管理
- **兼容扩展**:支持 MAA 几乎所有的配置选项,满足不同用户需求
## 重要声明

View File

@@ -517,7 +517,7 @@ class MaaPlanConfig(LQConfig):
class AppConfig(GlobalConfig):
VERSION = "4.3.8.3"
VERSION = "4.3.8.4"
gameid_refreshed = Signal()
PASSWORD_refreshed = Signal()

View File

@@ -608,13 +608,14 @@ class MaaManager(QObject):
Config.app_path
/ f"history/{curdate}/{user_data["Info"]["Name"]}/{start_time.strftime("%H-%M-%S")}.json",
)
if Config.get(Config.notify_IfSendSixStar) and if_six_star:
if if_six_star:
self.push_notification(
"公招六星",
f"喜报:用户 {user[0]} 公招出六星啦!",
{"user_name": user_data["Info"]["Name"]},
{
"user_name": user_data["Info"]["Name"],
},
user_data,
)
# 执行MAA解压更新动作
@@ -640,16 +641,12 @@ class MaaManager(QObject):
logger.info(f"{self.name} | 更新动作结束")
if Config.get(Config.notify_IfSendStatistic):
# 发送统计信息
statistics = Config.merge_maa_logs("指定项", user_logs_list)
statistics["user_index"] = user[2]
statistics["user_info"] = user[0]
statistics["start_time"] = user_start_time.strftime(
"%Y-%m-%d %H:%M:%S"
)
statistics["end_time"] = datetime.now().strftime(
"%Y-%m-%d %H:%M:%S"
)
statistics["start_time"] = user_start_time.strftime("%Y-%m-%d %H:%M:%S")
statistics["end_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
statistics["maa_result"] = (
"代理任务全部完成"
if (run_book["Annihilation"] and run_book["Routine"])
@@ -659,6 +656,7 @@ class MaaManager(QObject):
"统计信息",
f"{current_date} | 用户 {user[0]} 的自动代理统计报告",
statistics,
user_data,
)
if run_book["Annihilation"] and run_book["Routine"]:
@@ -852,6 +850,17 @@ class MaaManager(QObject):
self.data[_]["Config"]["Info"]["Name"] for _ in wait_index
],
}
# 生成结果文本
result_text = (
f"任务开始时间:{result["start_time"]},结束时间:{result["end_time"]}\n"
f"已完成数:{result["completed_count"]},未完成数:{result["uncompleted_count"]}\n\n"
)
if len(result["failed_user"]) > 0:
result_text += f"{self.mode[2:4]}未成功的用户:\n{"\n".join(result["failed_user"])}\n"
if len(result["waiting_user"]) > 0:
result_text += f"\n未开始{self.mode[2:4]}的用户:\n{"\n".join(result["waiting_user"])}\n"
# 推送代理结果通知
Notify.push_plyer(
title.replace("报告", "已完成!"),
@@ -859,15 +868,7 @@ class MaaManager(QObject):
f"已完成用户数:{len(over_index)},未完成用户数:{len(error_index) + len(wait_index)}",
10,
)
if Config.get(Config.notify_SendTaskResultTime) == "任何时刻" or (
Config.get(Config.notify_SendTaskResultTime) == "仅失败时"
and len(error_index) + len(wait_index) != 0
):
result_text = self.push_notification("代理结果", title, result)
else:
result_text = self.push_notification(
"代理结果", title, result, if_get_text_only=True
)
self.push_notification("代理结果", title, result)
self.agree_bilibili(False)
self.log_monitor.deleteLater()
@@ -1741,15 +1742,21 @@ class MaaManager(QObject):
mode: str,
title: str,
message: Union[str, dict],
if_get_text_only: bool = False,
) -> str:
user_data: Dict[str, Dict[str, Union[str, int, bool]]] = None,
) -> None:
"""通过所有渠道推送通知"""
env = Environment(
loader=FileSystemLoader(str(Config.app_path / "resources/html"))
)
if mode == "代理结果":
if mode == "代理结果" and (
Config.get(Config.notify_SendTaskResultTime) == "任何时刻"
or (
Config.get(Config.notify_SendTaskResultTime) == "仅失败时"
and message["uncompleted_count"] != 0
)
):
# 生成文本通知内容
message_text = (
f"任务开始时间:{message["start_time"]},结束时间:{message["end_time"]}\n"
@@ -1761,9 +1768,6 @@ class MaaManager(QObject):
if len(message["waiting_user"]) > 0:
message_text += f"\n未开始{self.mode[2:4]}的用户:\n{"\n".join(message["waiting_user"])}\n"
if if_get_text_only:
return message_text
# 生成HTML通知内容
message["failed_user"] = "".join(message["failed_user"])
message["waiting_user"] = "".join(message["waiting_user"])
@@ -1771,27 +1775,34 @@ class MaaManager(QObject):
template = env.get_template("MAA_result.html")
message_html = template.render(message)
# 发送全局通知
Notify.send_mail("网页", title, message_html,Config.get(Config.notify_ToAddress))
# ServerChan的换行是两个换行符。故而将\n替换为\n\n
serverchan_message = message_text.replace("\n", "\n\n")
Notify.ServerChanPush(title, f"{serverchan_message}\n\nAUTO_MAA 敬上",Config.get(Config.notify_ServerChanKey),Config.get(Config.notify_ServerChanTag),Config.get(Config.notify_ServerChanChannel))
Notify.CompanyWebHookBotPush(title, f"{message_text}\n\nAUTO_MAA 敬上",Config.get(Config.notify_CompanyWebHookBotUrl))
# # 发送用户单独通知
# for user_name in message["failed_user"].split("、") + message["waiting_user"].split("、"):
# if not user_name: # 跳过空字符串
# continue
# user_config = Config.member_dict.get(user_name)
# if user_config and user_config.get(user_config.Notify_Enable):
# user_notify = UserNotification(user_config)
# user_notify.send_notification(
# f"{self.mode[2:4]}任务未完成通知",
# f"您的{self.mode[2:4]}任务未完成,请检查相关设置。\n\n{message_text}"
# )
# 发送全局通知
return message_text
if Config.get(Config.notify_IfSendMail):
Notify.send_mail(
"网页", title, message_html, Config.get(Config.notify_ToAddress)
)
if Config.get(Config.notify_IfServerChan):
Notify.ServerChanPush(
title,
f"{serverchan_message}\n\nAUTO_MAA 敬上",
Config.get(Config.notify_ServerChanKey),
Config.get(Config.notify_ServerChanTag),
Config.get(Config.notify_ServerChanChannel),
)
if Config.get(Config.notify_IfCompanyWebHookBot):
Notify.CompanyWebHookBotPush(
title,
f"{message_text}\n\nAUTO_MAA 敬上",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
elif mode == "统计信息":
# 生成文本通知内容
formatted = []
for stage, items in message["drop_statistics"].items():
@@ -1817,41 +1828,155 @@ class MaaManager(QObject):
template = env.get_template("MAA_statistics.html")
message_html = template.render(message)
# 发送全局通知
Notify.send_mail("网页", title, message_html,Config.get(Config.notify_ToAddress))
# ServerChan的换行是两个换行符。故而将\n替换为\n\n
serverchan_message = message_text.replace("\n", "\n\n")
Notify.ServerChanPush(title, f"{serverchan_message}\n\nAUTO_MAA 敬上",Config.get(Config.notify_ServerChanKey),Config.get(Config.notify_ServerChanTag),Config.get(Config.notify_ServerChanChannel))
Notify.CompanyWebHookBotPush(title, f"{message_text}\n\nAUTO_MAA 敬上",Config.get(Config.notify_CompanyWebHookBotUrl))
# # 发送用户单独通知
# user_name = message.get("user_info")
# if user_name:
# user_config = Config.member_dict.get(user_name)
# if user_config and user_config.get(user_config.Notify_Enable):
# user_notify = UserNotification(user_config)
# user_notify.send_notification(
# f"{self.mode[2:4]}任务统计报告",
# f"您的{self.mode[2:4]}任务统计报告如下:\n\n{message_text}"
# )
# 发送全局通知
if Config.get(Config.notify_IfSendStatistic):
if Config.get(Config.notify_IfSendMail):
Notify.send_mail(
"网页", title, message_html, Config.get(Config.notify_ToAddress)
)
if Config.get(Config.notify_IfServerChan):
Notify.ServerChanPush(
title,
f"{serverchan_message}\n\nAUTO_MAA 敬上",
Config.get(Config.notify_ServerChanKey),
Config.get(Config.notify_ServerChanTag),
Config.get(Config.notify_ServerChanChannel),
)
if Config.get(Config.notify_IfCompanyWebHookBot):
Notify.CompanyWebHookBotPush(
title,
f"{message_text}\n\nAUTO_MAA 敬上",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
# 发送用户单独通知
if (
user_data["Notify"]["Enabled"]
and user_data["Notify"]["IfSendStatistic"]
):
# 发送邮件通知
if user_data["Notify"]["IfSendMail"]:
if user_data["Notify"]["ToAddress"]:
Notify.send_mail(
"网页",
title,
message_html,
user_data["Notify"]["ToAddress"],
)
else:
logger.error(
f"{self.name} | 用户邮箱地址为空,无法发送用户单独的邮件通知"
)
# 发送ServerChan通知
if user_data["Notify"]["IfServerChan"]:
if user_data["Notify"]["ServerChanKey"]:
Notify.ServerChanPush(
title,
f"{serverchan_message}\n\nAUTO_MAA 敬上",
user_data["Notify"]["ServerChanKey"],
user_data["Notify"]["ServerChanTag"],
user_data["Notify"]["ServerChanChannel"],
)
else:
logger.error(
f"{self.name} |用户ServerChan密钥为空无法发送用户单独的ServerChan通知"
)
# 推送CompanyWebHookBot通知
if user_data["Notify"]["IfCompanyWebHookBot"]:
if user_data["Notify"]["CompanyWebHookBotUrl"]:
Notify.CompanyWebHookBotPush(
title,
f"{message_text}\n\nAUTO_MAA 敬上",
user_data["Notify"]["CompanyWebHookBotUrl"],
)
else:
logger.error(
f"{self.name} |用户CompanyWebHookBot密钥为空无法发送用户单独的CompanyWebHookBot通知"
)
elif mode == "公招六星":
# 生成HTML通知内容
template = env.get_template("MAA_six_star.html")
message_html = template.render(message)
# 发送全局通知
Notify.send_mail("网页", title, message_html,Config.get(Config.notify_ToAddress))
Notify.ServerChanPush(title, "好羡慕~\n\nAUTO_MAA 敬上",Config.get(Config.notify_ServerChanKey),Config.get(Config.notify_ServerChanTag),Config.get(Config.notify_ServerChanChannel))
Notify.CompanyWebHookBotPush(title, "好羡慕~\n\nAUTO_MAA 敬上",Config.get(Config.notify_CompanyWebHookBotUrl))
if Config.get(Config.notify_IfSendSixStar):
# # 发送用户单独通知
# user_name = message.get("user_name")
# if user_name:
# user_config = Config.member_dict.get(user_name)
# if user_config and user_config.get(user_config.Notify_Enable):
# user_notify = UserNotification(user_config)
# user_notify.send_notification(
# "公招六星通知",
# "恭喜您在公招中获得了六星干员!"
# )
if Config.get(Config.notify_IfSendMail):
Notify.send_mail(
"网页", title, message_html, Config.get(Config.notify_ToAddress)
)
if Config.get(Config.notify_IfServerChan):
Notify.ServerChanPush(
title,
"好羡慕~\n\nAUTO_MAA 敬上",
Config.get(Config.notify_ServerChanKey),
Config.get(Config.notify_ServerChanTag),
Config.get(Config.notify_ServerChanChannel),
)
if Config.get(Config.notify_IfCompanyWebHookBot):
Notify.CompanyWebHookBotPush(
title,
"好羡慕~\n\nAUTO_MAA 敬上",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
# 发送用户单独通知
if user_data["Notify"]["Enabled"] and user_data["Notify"]["IfSendSixStar"]:
# 发送邮件通知
if user_data["Notify"]["IfSendMail"]:
if user_data["Notify"]["ToAddress"]:
Notify.send_mail(
"网页",
title,
message_html,
user_data["Notify"]["ToAddress"],
)
else:
logger.error(
f"{self.name} | 用户邮箱地址为空,无法发送用户单独的邮件通知"
)
# 发送ServerChan通知
if user_data["Notify"]["IfServerChan"]:
if user_data["Notify"]["ServerChanKey"]:
Notify.ServerChanPush(
title,
"好羡慕~\n\nAUTO_MAA 敬上",
user_data["Notify"]["ServerChanKey"],
user_data["Notify"]["ServerChanTag"],
user_data["Notify"]["ServerChanChannel"],
)
else:
logger.error(
f"{self.name} |用户ServerChan密钥为空无法发送用户单独的ServerChan通知"
)
# 推送CompanyWebHookBot通知
if user_data["Notify"]["IfCompanyWebHookBot"]:
if user_data["Notify"]["CompanyWebHookBotUrl"]:
Notify.CompanyWebHookBotPush(
title,
"好羡慕~\n\nAUTO_MAA 敬上",
user_data["Notify"]["CompanyWebHookBotUrl"],
)
else:
logger.error(
f"{self.name} |用户CompanyWebHookBot密钥为空无法发送用户单独的CompanyWebHookBot通知"
)
return None

View File

@@ -69,9 +69,6 @@ class Notification(QWidget):
def send_mail(self, mode, title, content, to_address) -> None:
"""推送邮件通知"""
if Config.get(Config.notify_IfSendMail):
if (
Config.get(Config.notify_SMTPServerAddress) == ""
or Config.get(Config.notify_AuthorizationCode) == ""
@@ -146,7 +143,6 @@ class Notification(QWidget):
def ServerChanPush(self, title, content, send_key, tag, channel):
"""使用Server酱推送通知"""
if Config.get(Config.notify_IfServerChan):
if not send_key:
logger.error("请正确设置Server酱的SendKey")
self.push_info_bar.emit(
@@ -172,14 +168,8 @@ class Notification(QWidget):
and (s.count("|") == 0 or all(s.split("|")))
)
tags = "|".join(
_.strip()
for _ in tag.split("|")
)
channels = "|".join(
_.strip()
for _ in channel.split("|")
)
tags = "|".join(_.strip() for _ in tag.split("|"))
channels = "|".join(_.strip() for _ in channel.split("|"))
options = {}
if is_valid(tags):
@@ -225,15 +215,15 @@ class Notification(QWidget):
except Exception as e:
logger.exception("Server酱通知推送异常")
self.push_info_bar.emit(
"error", "Server酱通知推送异常", f"请检查相关设置,如还有问题可联系开发者", -1
"error",
"Server酱通知推送异常",
"请检查相关设置和网络连接。如全部配置正确,请稍后再试。",
-1,
)
return f"Server酱通知推送异常{str(e)}"
return None
def CompanyWebHookBotPush(self, title, content,webhook_url):
def CompanyWebHookBotPush(self, title, content, webhook_url):
"""使用企业微信群机器人推送通知"""
if Config.get(Config.notify_IfCompanyWebHookBot):
if webhook_url == "":
logger.error("请正确设置企业微信群机器人的WebHook地址")
self.push_info_bar.emit(
@@ -281,7 +271,6 @@ class Notification(QWidget):
-1,
)
return f'使用企业微信群机器人推送通知时出错:{info["errmsg"]}'
return None
def send_test_notification(self):
"""发送测试通知到所有已启用的通知渠道"""

View File

@@ -25,17 +25,24 @@ v4.3
作者DLmaster_361
"""
import os
import re
from datetime import datetime
from functools import partial
from typing import Optional, Union, List, Dict
from urllib.parse import urlparse
import markdown
from PySide6.QtCore import Qt, QTime, QTimer, QEvent, QSize
from PySide6.QtGui import QIcon, QPixmap, QPainter, QPainterPath
from PySide6.QtWidgets import (
QApplication,
QWidget,
QWidget,
QLabel,
QHBoxLayout,
QVBoxLayout,
QSizePolicy,
)
from PySide6.QtCore import Qt, QTime, QTimer, QEvent, QSize
from PySide6.QtGui import QIcon, QPixmap, QPainter, QPainterPath
from qfluentwidgets import (
LineEdit,
PasswordLineEdit,
@@ -77,13 +84,6 @@ from qfluentwidgets import (
FlyoutViewBase,
)
from qfluentwidgets.common.overload import singledispatchmethod
import os
import re
import markdown
from datetime import datetime
from urllib.parse import urlparse
from functools import partial
from typing import Optional, Union, List, Dict
from app.core import Config
from app.services import Crypto
@@ -109,6 +109,8 @@ class LineEditMessageBox(MessageBoxBase):
self.viewLayout.addWidget(self.title)
self.viewLayout.addWidget(self.input)
self.input.setFocus()
class ComboBoxMessageBox(MessageBoxBase):
"""选择对话框"""
@@ -465,24 +467,27 @@ class LineEditSettingCard(SettingCard):
self.LineEdit.setMinimumWidth(250)
self.LineEdit.setPlaceholderText(text)
if configItem:
self.setValue(self.qconfig.get(configItem))
configItem.valueChanged.connect(self.setValue)
self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight)
self.hBoxLayout.addSpacing(16)
self.configItem.valueChanged.connect(self.setValue)
self.LineEdit.textChanged.connect(self.__textChanged)
self.setValue(self.qconfig.get(configItem))
def __textChanged(self, content: str):
self.setValue(content.strip())
self.configItem.valueChanged.disconnect()
self.qconfig.set(self.configItem, content.strip())
self.configItem.valueChanged.connect(self.setValue)
self.textChanged.emit(content.strip())
def setValue(self, content: str):
if self.configItem:
self.qconfig.set(self.configItem, content.strip())
self.LineEdit.textChanged.disconnect()
self.LineEdit.setText(content.strip())
self.LineEdit.textChanged.connect(self.__textChanged)
class PasswordLineEditSettingCard(SettingCard):
@@ -511,35 +516,29 @@ class PasswordLineEditSettingCard(SettingCard):
self.LineEdit.setPlaceholderText(text)
if algorithm == "AUTO":
self.LineEdit.setViewPasswordButtonVisible(False)
self.if_setValue = False
if configItem:
self.setValue(self.qconfig.get(configItem))
configItem.valueChanged.connect(self.setValue)
self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight)
self.hBoxLayout.addSpacing(16)
self.configItem.valueChanged.connect(self.setValue)
self.LineEdit.textChanged.connect(self.__textChanged)
self.setValue(self.qconfig.get(configItem))
def __textChanged(self, content: str):
if self.if_setValue:
return None
self.configItem.valueChanged.disconnect()
if self.algorithm == "DPAPI":
self.setValue(Crypto.win_encryptor(content))
self.qconfig.set(self.configItem, Crypto.win_encryptor(content))
elif self.algorithm == "AUTO":
self.setValue(Crypto.AUTO_encryptor(content))
self.qconfig.set(self.configItem, Crypto.AUTO_encryptor(content))
self.configItem.valueChanged.connect(self.setValue)
self.textChanged.emit()
def setValue(self, content: str):
self.if_setValue = True
if self.configItem:
self.qconfig.set(self.configItem, content)
self.LineEdit.textChanged.disconnect()
if self.algorithm == "DPAPI":
self.LineEdit.setText(Crypto.win_decryptor(content))
elif self.algorithm == "AUTO":
@@ -555,8 +554,7 @@ class PasswordLineEditSettingCard(SettingCard):
self.LineEdit.setText("************")
self.LineEdit.setPasswordVisible(False)
self.LineEdit.setReadOnly(True)
self.if_setValue = False
self.LineEdit.textChanged.connect(self.__textChanged)
class UserLableSettingCard(SettingCard):
@@ -998,9 +996,30 @@ class UserNoticeSettingCard(PushAndSwitchButtonSettingCard):
def setValues(self):
def short_str(s: str) -> str:
if len(s) <= 10:
return s
return s[:10] + "..."
if s.startswith(("SC", "sc")):
# SendKey首4 + 末4
return f"{s[:4]}***{s[-4:]}" if len(s) > 8 else s
elif s.startswith(("http://", "https://")):
# Webhook URL域名 + 路径尾3
parsed_url = urlparse(s)
domain = parsed_url.netloc
path_tail = (
parsed_url.path[-3:]
if len(parsed_url.path) > 3
else parsed_url.path
)
return f"{domain}***{path_tail}"
elif "@" in s:
# 邮箱:@前3/6 + 域名
username, domain = s.split("@", 1)
displayed_name = f"{username[:3]}***" if len(username) > 6 else username
return f"{displayed_name}@{domain}"
else:
# 普通字符串末尾3字符
return f"***{s[-3:]}" if len(s) > 3 else s
content_list = []

View File

@@ -1,6 +1,12 @@
{
"main_version": "4.3.8.3",
"main_version": "4.3.8.4",
"version_info": {
"4.3.8.4": {
"新增功能": [
"支持为每一个用户执行独立通知",
"输入文本框适配文本插入操作"
]
},
"4.3.8.3": {
"新增功能": [
"用户仪表盘支持直接控制用户状态"