feat(core): 重构日志记录,载入更多日志记录项

This commit is contained in:
DLmaster361
2025-07-18 18:12:47 +08:00
parent 8427bd9f6b
commit 9b492b5e0d
26 changed files with 2217 additions and 800 deletions

View File

@@ -38,6 +38,7 @@ from .config import (
GeneralSubConfig,
Config,
)
from .logger import logger
from .main_info_bar import MainInfoBar
from .network import Network
from .sound_player import SoundPlayer
@@ -52,6 +53,7 @@ __all__ = [
"MaaPlanConfig",
"GeneralConfig",
"GeneralSubConfig",
"logger",
"MainInfoBar",
"Network",
"SoundPlayer",

View File

@@ -25,7 +25,6 @@ v4.4
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import Signal
import argparse
import sqlite3
@@ -53,6 +52,7 @@ from qfluentwidgets import (
from urllib.parse import urlparse
from typing import Union, Dict, List
from .logger import logger
from .network import Network
@@ -707,7 +707,7 @@ class GeneralSubConfig(LQConfig):
class AppConfig(GlobalConfig):
VERSION = "4.4.0.0"
VERSION = "4.4.1.1"
stage_refreshed = Signal()
PASSWORD_refreshed = Signal()
@@ -717,8 +717,8 @@ class AppConfig(GlobalConfig):
def __init__(self) -> None:
super().__init__()
self.app_path = Path(sys.argv[0]).resolve().parent # 获取软件根目录
self.app_path_sys = Path(sys.argv[0]).resolve() # 获取软件自身的路径
self.app_path = Path(sys.argv[0]).resolve().parent
self.app_path_sys = Path(sys.argv[0]).resolve()
self.log_path = self.app_path / "debug/AUTO_MAA.log"
self.database_path = self.app_path / "data/data.db"
@@ -781,7 +781,7 @@ class AppConfig(GlobalConfig):
self.init_logger()
self.check_data()
logger.info("程序初始化完成")
logger.info("程序初始化完成", module="配置管理")
def init_logger(self) -> None:
"""初始化日志记录器"""
@@ -789,7 +789,7 @@ class AppConfig(GlobalConfig):
logger.add(
sink=self.log_path,
level="DEBUG",
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <level>{message}</level>",
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <cyan>{extra[module]}</cyan> | <level>{message}</level>",
enqueue=True,
backtrace=True,
diagnose=True,
@@ -797,102 +797,28 @@ class AppConfig(GlobalConfig):
retention="1 month",
compression="zip",
)
logger.info("")
logger.info("===================================")
logger.info("AUTO_MAA 主程序")
logger.info(f"版本号: v{self.VERSION}")
logger.info(f"根目录: {self.app_path}")
logger.add(
sink=sys.stderr,
level="DEBUG",
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <cyan>{extra[module]}</cyan> | <level>{message}</level>",
enqueue=True,
backtrace=True,
diagnose=True,
colorize=True,
)
logger.info("", module="配置管理")
logger.info("===================================", module="配置管理")
logger.info("AUTO_MAA 主程序", module="配置管理")
logger.info(f"版本号: v{self.VERSION}", module="配置管理")
logger.info(f"根目录: {self.app_path}", module="配置管理")
logger.info(
f"运行模式: {'图形化界面' if self.args.mode == 'gui' else '命令行界面'}"
f"运行模式: {'图形化界面' if self.args.mode == 'gui' else '命令行界面'}",
module="配置管理",
)
logger.info("===================================")
logger.info("===================================", module="配置管理")
logger.info("日志记录器初始化完成")
def get_stage(self) -> None:
"""从MAA服务器获取活动关卡信息"""
network = Network.add_task(
mode="get",
url="https://api.maa.plus/MaaAssistantArknights/api/gui/StageActivity.json",
)
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
stage_infos: List[Dict[str, Union[str, Dict[str, Union[str, int]]]]] = (
network_result["response_json"]["Official"]["sideStoryStage"]
)
else:
logger.warning(
f"无法从MAA服务器获取活动关卡信息:{network_result['error_message']}"
)
stage_infos = []
ss_stage_dict = {"value": [], "text": []}
for stage_info in stage_infos:
if (
datetime.strptime(
stage_info["Activity"]["UtcStartTime"], "%Y/%m/%d %H:%M:%S"
)
< datetime.now()
< datetime.strptime(
stage_info["Activity"]["UtcExpireTime"], "%Y/%m/%d %H:%M:%S"
)
):
ss_stage_dict["value"].append(stage_info["Value"])
ss_stage_dict["text"].append(stage_info["Value"])
# 生成每日关卡信息
stage_daily_info = [
{"value": "-", "text": "当前/上次", "days": [1, 2, 3, 4, 5, 6, 7]},
{"value": "1-7", "text": "1-7", "days": [1, 2, 3, 4, 5, 6, 7]},
{"value": "R8-11", "text": "R8-11", "days": [1, 2, 3, 4, 5, 6, 7]},
{
"value": "12-17-HARD",
"text": "12-17-HARD",
"days": [1, 2, 3, 4, 5, 6, 7],
},
{"value": "CE-6", "text": "龙门币-6/5", "days": [2, 4, 6, 7]},
{"value": "AP-5", "text": "红票-5", "days": [1, 4, 6, 7]},
{"value": "CA-5", "text": "技能-5", "days": [2, 3, 5, 7]},
{"value": "LS-6", "text": "经验-6/5", "days": [1, 2, 3, 4, 5, 6, 7]},
{"value": "SK-5", "text": "碳-5", "days": [1, 3, 5, 6]},
{"value": "PR-A-1", "text": "奶/盾芯片", "days": [1, 4, 5, 7]},
{"value": "PR-A-2", "text": "奶/盾芯片组", "days": [1, 4, 5, 7]},
{"value": "PR-B-1", "text": "术/狙芯片", "days": [1, 2, 5, 6]},
{"value": "PR-B-2", "text": "术/狙芯片组", "days": [1, 2, 5, 6]},
{"value": "PR-C-1", "text": "先/辅芯片", "days": [3, 4, 6, 7]},
{"value": "PR-C-2", "text": "先/辅芯片组", "days": [3, 4, 6, 7]},
{"value": "PR-D-1", "text": "近/特芯片", "days": [2, 3, 6, 7]},
{"value": "PR-D-2", "text": "近/特芯片组", "days": [2, 3, 6, 7]},
]
for day in range(0, 8):
today_stage_dict = {"value": [], "text": []}
for stage_info in stage_daily_info:
if day in stage_info["days"] or day == 0:
today_stage_dict["value"].append(stage_info["value"])
today_stage_dict["text"].append(stage_info["text"])
self.stage_dict[calendar.day_name[day - 1] if day > 0 else "ALL"] = {
"value": today_stage_dict["value"] + ss_stage_dict["value"],
"text": today_stage_dict["text"] + ss_stage_dict["text"],
}
self.stage_refreshed.emit()
def server_date(self) -> date:
"""获取当前的服务器日期"""
dt = datetime.now()
if dt.time() < datetime.min.time().replace(hour=4):
dt = dt - timedelta(days=1)
return dt.date()
logger.info("日志记录器初始化完成", module="配置管理")
def check_data(self) -> None:
"""检查用户数据文件并处理数据文件版本更新"""
@@ -914,11 +840,11 @@ class AppConfig(GlobalConfig):
version = cur.fetchall()
if version[0][0] != "v1.7":
logger.info("数据文件版本更新开始")
logger.info("数据文件版本更新开始", module="配置管理")
if_streaming = False
# v1.4-->v1.5
if version[0][0] == "v1.4" or if_streaming:
logger.info("数据文件版本更新v1.4-->v1.5")
logger.info("数据文件版本更新v1.4-->v1.5", module="配置管理")
if_streaming = True
member_dict: Dict[str, Dict[str, Union[str, Path]]] = {}
@@ -1046,7 +972,7 @@ class AppConfig(GlobalConfig):
# v1.5-->v1.6
if version[0][0] == "v1.5" or if_streaming:
logger.info("数据文件版本更新v1.5-->v1.6")
logger.info("数据文件版本更新v1.5-->v1.6", module="配置管理")
if_streaming = True
cur.execute("DELETE FROM version WHERE v = ?", ("v1.5",))
cur.execute("INSERT INTO version VALUES(?)", ("v1.6",))
@@ -1078,7 +1004,7 @@ class AppConfig(GlobalConfig):
pass
# v1.6-->v1.7
if version[0][0] == "v1.6" or if_streaming:
logger.info("数据文件版本更新v1.6-->v1.7")
logger.info("数据文件版本更新v1.6-->v1.7", module="配置管理")
if_streaming = True
if (self.app_path / "config/MaaConfig").exists():
@@ -1148,11 +1074,106 @@ class AppConfig(GlobalConfig):
cur.close()
db.close()
logger.info("数据文件版本更新完成")
logger.success("数据文件版本更新完成", module="配置管理")
def get_stage(self) -> None:
"""从MAA服务器更新活动关卡信息"""
logger.info("开始获取活动关卡信息", module="配置管理")
network = Network.add_task(
mode="get",
url="https://api.maa.plus/MaaAssistantArknights/api/gui/StageActivity.json",
)
network.loop.exec()
network_result = Network.get_result(network)
if network_result["status_code"] == 200:
stage_infos: List[Dict[str, Union[str, Dict[str, Union[str, int]]]]] = (
network_result["response_json"]["Official"]["sideStoryStage"]
)
else:
logger.warning(
f"无法从MAA服务器获取活动关卡信息:{network_result['error_message']}",
module="配置管理",
)
stage_infos = []
ss_stage_dict = {"value": [], "text": []}
for stage_info in stage_infos:
if (
datetime.strptime(
stage_info["Activity"]["UtcStartTime"], "%Y/%m/%d %H:%M:%S"
)
< datetime.now()
< datetime.strptime(
stage_info["Activity"]["UtcExpireTime"], "%Y/%m/%d %H:%M:%S"
)
):
ss_stage_dict["value"].append(stage_info["Value"])
ss_stage_dict["text"].append(stage_info["Value"])
# 生成每日关卡信息
stage_daily_info = [
{"value": "-", "text": "当前/上次", "days": [1, 2, 3, 4, 5, 6, 7]},
{"value": "1-7", "text": "1-7", "days": [1, 2, 3, 4, 5, 6, 7]},
{"value": "R8-11", "text": "R8-11", "days": [1, 2, 3, 4, 5, 6, 7]},
{
"value": "12-17-HARD",
"text": "12-17-HARD",
"days": [1, 2, 3, 4, 5, 6, 7],
},
{"value": "CE-6", "text": "龙门币-6/5", "days": [2, 4, 6, 7]},
{"value": "AP-5", "text": "红票-5", "days": [1, 4, 6, 7]},
{"value": "CA-5", "text": "技能-5", "days": [2, 3, 5, 7]},
{"value": "LS-6", "text": "经验-6/5", "days": [1, 2, 3, 4, 5, 6, 7]},
{"value": "SK-5", "text": "碳-5", "days": [1, 3, 5, 6]},
{"value": "PR-A-1", "text": "奶/盾芯片", "days": [1, 4, 5, 7]},
{"value": "PR-A-2", "text": "奶/盾芯片组", "days": [1, 4, 5, 7]},
{"value": "PR-B-1", "text": "术/狙芯片", "days": [1, 2, 5, 6]},
{"value": "PR-B-2", "text": "术/狙芯片组", "days": [1, 2, 5, 6]},
{"value": "PR-C-1", "text": "先/辅芯片", "days": [3, 4, 6, 7]},
{"value": "PR-C-2", "text": "先/辅芯片组", "days": [3, 4, 6, 7]},
{"value": "PR-D-1", "text": "近/特芯片", "days": [2, 3, 6, 7]},
{"value": "PR-D-2", "text": "近/特芯片组", "days": [2, 3, 6, 7]},
]
for day in range(0, 8):
today_stage_dict = {"value": [], "text": []}
for stage_info in stage_daily_info:
if day in stage_info["days"] or day == 0:
today_stage_dict["value"].append(stage_info["value"])
today_stage_dict["text"].append(stage_info["text"])
self.stage_dict[calendar.day_name[day - 1] if day > 0 else "ALL"] = {
"value": today_stage_dict["value"] + ss_stage_dict["value"],
"text": today_stage_dict["text"] + ss_stage_dict["text"],
}
self.stage_refreshed.emit()
logger.success("活动关卡信息更新完成", module="配置管理")
def server_date(self) -> date:
"""
获取当前的服务器日期
:return: 当前的服务器日期
:rtype: date
"""
dt = datetime.now()
if dt.time() < datetime.min.time().replace(hour=4):
dt = dt - timedelta(days=1)
return dt.date()
def search_member(self) -> None:
"""搜索所有脚本实例"""
"""更新脚本实例配置信息"""
logger.info("开始搜索并读入脚本实例配置", module="配置管理")
self.member_dict: Dict[
str,
Dict[
@@ -1198,7 +1219,20 @@ class AppConfig(GlobalConfig):
sorted(self.member_dict.items(), key=lambda x: int(x[0][3:]))
)
logger.success(
f"脚本实例配置搜索完成,共找到 {len(self.member_dict)} 个实例",
module="配置管理",
)
def search_maa_user(self, name: str) -> None:
"""
更新指定 MAA 脚本实例的用户信息
:param name: 脚本实例名称
:type name: str
"""
logger.info(f"开始搜索并读入 MAA 脚本实例 {name} 的用户信息", module="配置管理")
user_dict: Dict[str, Dict[str, Union[Path, MaaUserConfig]]] = {}
for user_dir in (Config.member_dict[name]["Path"] / "UserData").iterdir():
@@ -1214,7 +1248,22 @@ class AppConfig(GlobalConfig):
sorted(user_dict.items(), key=lambda x: int(x[0][3:]))
)
logger.success(
f"MAA 脚本实例 {name} 的用户信息搜索完成,共找到 {len(user_dict)} 个用户",
module="配置管理",
)
def search_general_sub(self, name: str) -> None:
"""
更新指定通用脚本实例的子配置信息
:param name: 脚本实例名称
:type name: str
"""
logger.info(
f"开始搜索并读入通用脚本实例 {name} 的子配置信息", module="配置管理"
)
user_dict: Dict[str, Dict[str, Union[Path, GeneralSubConfig]]] = {}
for sub_dir in (Config.member_dict[name]["Path"] / "SubData").iterdir():
@@ -1230,8 +1279,15 @@ class AppConfig(GlobalConfig):
sorted(user_dict.items(), key=lambda x: int(x[0][3:]))
)
logger.success(
f"通用脚本实例 {name} 的子配置信息搜索完成,共找到 {len(user_dict)} 个子配置",
module="配置管理",
)
def search_plan(self) -> None:
"""搜索所有计划表"""
"""更新计划表配置信息"""
logger.info("开始搜索并读入计划表配置", module="配置管理")
self.plan_dict: Dict[str, Dict[str, Union[str, Path, MaaPlanConfig]]] = {}
if (self.app_path / "config/MaaPlanConfig").exists():
@@ -1252,8 +1308,15 @@ class AppConfig(GlobalConfig):
sorted(self.plan_dict.items(), key=lambda x: int(x[0][3:]))
)
logger.success(
f"计划表配置搜索完成,共找到 {len(self.plan_dict)} 个计划表",
module="配置管理",
)
def search_queue(self):
"""搜索所有调度队列实例"""
"""更新调度队列实例配置信息"""
logger.info("开始搜索并读入调度队列配置", module="配置管理")
self.queue_dict: Dict[str, Dict[str, Union[Path, QueueConfig]]] = {}
@@ -1273,8 +1336,20 @@ class AppConfig(GlobalConfig):
sorted(self.queue_dict.items(), key=lambda x: int(x[0][5:]))
)
logger.success(
f"调度队列配置搜索完成,共找到 {len(self.queue_dict)} 个调度队列",
module="配置管理",
)
def change_queue(self, old: str, new: str) -> None:
"""修改调度队列配置文件的队列参数"""
"""
修改调度队列配置文件的队列参数
:param old: 旧脚本名
:param new: 新脚本名
"""
logger.info(f"开始修改调度队列参数:{old} -> {new}", module="配置管理")
for queue in self.queue_dict.values():
@@ -1299,8 +1374,17 @@ class AppConfig(GlobalConfig):
if queue["Config"].get(queue["Config"].queue_Member_10) == old:
queue["Config"].set(queue["Config"].queue_Member_10, new)
logger.success(f"调度队列参数修改完成:{old} -> {new}", module="配置管理")
def change_plan(self, old: str, new: str) -> None:
"""修改脚本管理所有下属用户的计划表配置参数"""
"""
修改脚本管理所有下属用户的计划表配置参数
:param old: 旧计划表名
:param new: 新计划表名
"""
logger.info(f"开始修改计划表参数:{old} -> {new}", module="配置管理")
for member in self.member_dict.values():
@@ -1309,10 +1393,21 @@ class AppConfig(GlobalConfig):
if user["Config"].get(user["Config"].Info_StageMode) == old:
user["Config"].set(user["Config"].Info_StageMode, new)
logger.success(f"计划表参数修改完成:{old} -> {new}", module="配置管理")
def change_maa_user_info(
self, name: str, user_data: Dict[str, Dict[str, Union[str, Path, dict]]]
) -> None:
"""代理完成后保存改动的用户信息"""
"""
保存代理完成后发生改动的用户信息
:param name: 脚本实例名称
:type name: str
:param user_data: 用户信息字典,包含用户名称和对应的配置信息
:type user_data: Dict[str, Dict[str, Union[str, Path, dict]]]
"""
logger.info(f"开始保存 MAA 脚本实例 {name} 的用户信息变动", module="配置管理")
for user, info in user_data.items():
@@ -1345,10 +1440,21 @@ class AppConfig(GlobalConfig):
self.sub_info_changed.emit()
logger.success(f"MAA 脚本实例 {name} 的用户信息变动保存完成", module="配置管理")
def change_general_sub_info(
self, name: str, sub_data: Dict[str, Dict[str, Union[str, Path, dict]]]
) -> None:
"""代理完成后保存改动的配置信息"""
"""
保存代理完成后发生改动的配置信息
:param name: 脚本实例名称
:type name: str
:param sub_data: 子配置信息字典,包含子配置名称和对应的配置信息
:type sub_data: Dict[str, Dict[str, Union[str, Path, dict]]]
"""
logger.info(f"开始保存通用脚本实例 {name} 的子配置信息变动", module="配置管理")
for sub, info in sub_data.items():
@@ -1366,27 +1472,62 @@ class AppConfig(GlobalConfig):
self.sub_info_changed.emit()
logger.success(
f"通用脚本实例 {name} 的子配置信息变动保存完成", module="配置管理"
)
def set_power_sign(self, sign: str) -> None:
"""设置当前电源状态"""
"""
设置当前电源状态
:param sign: 电源状态标志
"""
self.power_sign = sign
self.power_sign_changed.emit()
logger.info(f"电源状态已更改为: {sign}", module="配置管理")
def save_history(self, key: str, content: dict) -> None:
"""保存历史记录"""
"""
保存历史记录
:param key: 调度队列的键
:type key: str
:param content: 包含时间和历史记录内容的字典
:type content: dict
"""
if key in self.queue_dict:
logger.info(f"保存调度队列 {key} 的历史记录", module="配置管理")
self.queue_dict[key]["Config"].set(
self.queue_dict[key]["Config"].Data_LastProxyTime, content["Time"]
)
self.queue_dict[key]["Config"].set(
self.queue_dict[key]["Config"].Data_LastProxyHistory, content["History"]
)
logger.success(f"调度队列 {key} 的历史记录已保存", module="配置管理")
else:
logger.warning(f"保存历史记录时未找到调度队列: {key}")
def save_maa_log(self, log_path: Path, logs: list, maa_result: str) -> bool:
"""保存MAA日志并生成对应统计数据"""
"""
保存MAA日志并生成对应统计数据
:param log_path: 日志文件保存路径
:type log_path: Path
:param logs: 日志内容列表
:type logs: list
:param maa_result: MAA 结果
:type maa_result: str
:return: 是否包含6★招募
:rtype: bool
"""
logger.info(
f"开始处理 MAA 日志,日志长度: {len(logs)},日志标记:{maa_result}",
module="配置管理",
)
data: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = {
"recruit_statistics": defaultdict(int),
@@ -1431,15 +1572,17 @@ class AppConfig(GlobalConfig):
# 查找所有Fight任务的开始和结束位置
fight_tasks = []
for i, line in enumerate(logs):
if "开始任务: Fight" in line:
if "开始任务: Fight" in line or "开始任务: 刷理智" in line:
# 查找对应的任务结束位置
end_index = -1
for j in range(i + 1, len(logs)):
if "完成任务: Fight" in logs[j]:
if "完成任务: Fight" in logs[j] or "完成任务: 刷理智" in logs[j]:
end_index = j
break
# 如果遇到新的Fight任务开始则当前任务没有正常结束
if j < len(logs) and "开始任务: Fight" in logs[j]:
if j < len(logs) and (
"开始任务: Fight" in logs[j] or "开始任务: 刷理智" in logs[j]
):
break
# 如果找到了结束位置,记录这个任务的范围
@@ -1505,12 +1648,23 @@ class AppConfig(GlobalConfig):
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}")
logger.success(f"MAA 日志统计完成,日志路径{log_path}", module="配置管理")
return if_six_star
def save_general_log(self, log_path: Path, logs: list, general_result: str) -> None:
"""保存通用日志并生成对应统计数据"""
"""
保存通用日志并生成对应统计数据
:param log_path: 日志文件保存路径
:param logs: 日志内容列表
:param general_result: 待保存的日志结果信息
"""
logger.info(
f"开始处理通用日志,日志长度: {len(logs)},日志标记:{general_result}",
module="配置管理",
)
data: Dict[str, str] = {"general_result": general_result}
@@ -1521,10 +1675,23 @@ class AppConfig(GlobalConfig):
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}")
logger.success(
f"通用日志统计完成,日志路径:{log_path.with_suffix('.log')}",
module="配置管理",
)
def merge_statistic_info(self, statistic_path_list: List[Path]) -> dict:
"""合并指定数据统计信息文件"""
"""
合并指定数据统计信息文件
:param statistic_path_list: 需要合并的统计信息文件路径列表
:return: 合并后的统计信息字典
"""
logger.info(
f"开始合并统计信息文件,共计 {len(statistic_path_list)} 个文件",
module="配置管理",
)
data = {"index": {}}
@@ -1591,12 +1758,28 @@ class AppConfig(GlobalConfig):
data["index"] = [data["index"][_] for _ in sorted(data["index"])]
logger.success(
f"统计信息合并完成,共计 {len(data['index'])} 条记录", module="配置管理"
)
return {k: v for k, v in data.items() if v}
def search_history(
self, mode: str, start_date: datetime, end_date: datetime
) -> dict:
"""搜索所有历史记录"""
"""
搜索指定范围内的历史记录
:param mode: 合并模式(按日合并、按周合并、按月合并)
:param start_date: 开始日期
:param end_date: 结束日期
:return: 搜索到的历史记录字典
"""
logger.info(
f"开始搜索历史记录,合并模式:{mode},日期范围:{start_date}{end_date}",
module="配置管理",
)
history_dict = {}
@@ -1638,10 +1821,43 @@ class AppConfig(GlobalConfig):
except ValueError:
logger.warning(f"非日期格式的目录: {date_folder}")
logger.success(
f"历史记录搜索完成,共计 {len(history_dict)} 条记录", module="配置管理"
)
return {
k: v
for k, v in sorted(history_dict.items(), key=lambda x: x[0], reverse=True)
}
def clean_old_history(self):
"""删除超过用户设定天数的历史记录文件(基于目录日期)"""
if self.get(self.function_HistoryRetentionTime) == 0:
logger.info("历史记录永久保留,跳过历史记录清理", module="配置管理")
return
logger.info("开始清理超过设定天数的历史记录", module="配置管理")
deleted_count = 0
for date_folder in (self.app_path / "history").iterdir():
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=self.get(self.function_HistoryRetentionTime)
):
shutil.rmtree(date_folder, ignore_errors=True)
deleted_count += 1
logger.info(f"已删除超期日志目录: {date_folder}", module="配置管理")
except ValueError:
logger.warning(f"非日期格式的目录: {date_folder}", module="配置管理")
logger.success(f"清理完成: {deleted_count} 个日期目录", module="配置管理")
Config = AppConfig()

34
app/core/logger.py Normal file
View File

@@ -0,0 +1,34 @@
# 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.4
作者DLmaster_361
"""
from loguru import logger as _logger
# 设置日志 module 字段默认值
logger = _logger.patch(
lambda record: record["extra"].setdefault("module", "未知模块") or True
)
logger.remove(0)

View File

@@ -25,10 +25,10 @@ v4.4
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import Qt
from qfluentwidgets import InfoBar, InfoBarPosition
from .logger import logger
from .config import Config
from .sound_player import SoundPlayer
@@ -46,20 +46,34 @@ class _MainInfoBar:
def push_info_bar(
self, mode: str, title: str, content: str, time: int, if_force: bool = False
):
"""推送到信息通知栏"""
) -> None:
"""
推送消息到吐司通知栏
:param mode: 通知栏模式,支持 "success", "warning", "error", "info"
:param title: 通知栏标题
:type title: str
:param content: 通知栏内容
:type content: str
:param time: 显示时长,单位为毫秒
:type time: int
:param if_force: 是否强制推送
:type if_force: bool
"""
if Config.main_window is None:
logger.error("信息通知栏未设置父窗口")
logger.error("信息通知栏未设置父窗口", module="吐司通知栏")
return None
# 根据 mode 获取对应的 InfoBar 方法
info_bar_method = self.mode_mapping.get(mode)
if not info_bar_method:
logger.error(f"未知的通知栏模式: {mode}")
logger.error(f"未知的通知栏模式: {mode}", module="吐司通知栏")
return None
if Config.main_window.isVisible():
# 主窗口可见时直接推送通知
info_bar_method(
title=title,
content=content,
@@ -69,6 +83,7 @@ class _MainInfoBar:
duration=time,
parent=Config.main_window,
)
elif if_force:
# 如果主窗口不可见且强制推送,则录入消息队列等待窗口显示后推送
info_bar_item = {
@@ -80,6 +95,11 @@ class _MainInfoBar:
if info_bar_item not in Config.info_bar_list:
Config.info_bar_list.append(info_bar_item)
logger.info(
f"主窗口不可见,已将通知栏消息录入队列: {info_bar_item}",
module="吐司通知栏",
)
if mode == "warning":
SoundPlayer.play("发生异常")
if mode == "error":

View File

@@ -25,7 +25,6 @@ v4.4
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import QObject, QThread, QEventLoop
import re
import time
@@ -33,6 +32,8 @@ import requests
import truststore
from pathlib import Path
from .logger import logger
class NetworkThread(QThread):
"""网络请求线程类"""
@@ -48,6 +49,8 @@ class NetworkThread(QThread):
f"NetworkThread-{mode}-{re.sub(r'(&cdk=)[^&]+(&)', r'\1******\2', url)}"
)
logger.info(f"创建网络请求线程: {self.objectName()}", module="网络请求子线程")
self.mode = mode
self.url = url
self.path = path
@@ -65,7 +68,7 @@ class NetworkThread(QThread):
self.loop = QEventLoop()
truststore.inject_into_ssl()
truststore.inject_into_ssl() # 信任系统证书
@logger.catch
def run(self) -> None:
@@ -77,7 +80,13 @@ class NetworkThread(QThread):
self.get_file(self.url, self.path)
def get_json(self, url: str) -> None:
"""通过get方法获取json数据"""
"""
通过get方法获取json数据
:param url: 请求的URL
"""
logger.info(f"子线程 {self.objectName()} 开始网络请求", module="网络请求子线程")
response = None
@@ -92,12 +101,23 @@ class NetworkThread(QThread):
self.status_code = response.status_code if response else None
self.response_json = None
self.error_message = str(e)
logger.exception(
f"子线程 {self.objectName()} 网络请求失败:{e}",
module="网络请求子线程",
)
time.sleep(self.backoff_factor)
self.loop.quit()
def get_file(self, url: str, path: Path) -> None:
"""通过get方法下载文件"""
"""
通过get方法下载文件到指定路径
:param url: 请求的URL
:param path: 下载文件的保存路径
"""
logger.info(f"子线程 {self.objectName()} 开始下载文件", module="网络请求子线程")
response = None
@@ -114,12 +134,15 @@ class NetworkThread(QThread):
except Exception as e:
self.status_code = response.status_code if response else None
self.error_message = str(e)
logger.exception(
f"子线程 {self.objectName()} 网络请求失败:{e}", module="网络请求子线程"
)
self.loop.quit()
class _Network(QObject):
"""网络请求线程类"""
"""网络请求线程管理"""
def __init__(self) -> None:
super().__init__()
@@ -127,7 +150,16 @@ class _Network(QObject):
self.task_queue = []
def add_task(self, mode: str, url: str, path: Path = None) -> NetworkThread:
"""添加网络请求任务"""
"""
添加网络请求任务
:param mode: 请求模式,支持 "get", "get_file"
:param url: 请求的URL
:param path: 下载文件的保存路径,仅在 mode 为 "get_file" 时有效
:return: 返回创建的 NetworkThread 实例
"""
logger.info(f"添加网络请求任务: {mode} {url} {path}", module="网络请求")
network_thread = NetworkThread(mode, url, path)
@@ -138,7 +170,12 @@ class _Network(QObject):
return network_thread
def get_result(self, network_thread: NetworkThread) -> dict:
"""获取网络请求结果"""
"""
获取网络请求结果
:param network_thread: 网络请求线程实例
:return: 包含状态码、响应JSON和错误信息的字典
"""
result = {
"status_code": network_thread.status_code,
@@ -155,6 +192,11 @@ class _Network(QObject):
self.task_queue.remove(network_thread)
network_thread.deleteLater()
logger.info(
f"网络请求结果: {result['status_code']},请求子线程已结束",
module="网络请求",
)
return result

View File

@@ -25,12 +25,12 @@ v4.4
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import QObject, QUrl
from PySide6.QtMultimedia import QSoundEffect
from pathlib import Path
from .logger import logger
from .config import Config
@@ -42,6 +42,11 @@ class _SoundPlayer(QObject):
self.sounds_path = Config.app_path / "resources/sounds"
def play(self, sound_name: str):
"""
播放指定名称的音效
:param sound_name: 音效文件名(不带扩展名)
"""
if not Config.get(Config.voice_Enabled):
return
@@ -59,6 +64,11 @@ class _SoundPlayer(QObject):
)
def play_voice(self, sound_path: Path):
"""
播放音效文件
:param sound_path: 音效文件的完整路径
"""
effect = QSoundEffect(self)
effect.setVolume(1)

View File

@@ -25,19 +25,18 @@ v4.4
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import QThread, QObject, Signal
from qfluentwidgets import MessageBox
from datetime import datetime
from packaging import version
from typing import Dict, Union
from .logger import logger
from .config import Config
from .main_info_bar import MainInfoBar
from .network import Network
from .sound_player import SoundPlayer
from app.models import MaaManager, GeneralManager
from app.services import System
class Task(QThread):
@@ -77,7 +76,7 @@ class Task(QThread):
if "设置MAA" in self.mode:
logger.info(f"任务开始:设置{self.name}")
logger.info(f"任务开始:设置{self.name}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "设置MAA", self.name, 3000)
self.task = MaaManager(
@@ -93,12 +92,14 @@ class Task(QThread):
try:
self.task.run()
except Exception as e:
logger.exception(f"任务异常:{self.name},错误信息:{e}")
logger.exception(
f"任务异常:{self.name},错误信息:{e}", module=f"业务 {self.name}"
)
self.push_info_bar.emit("error", "任务异常", self.name, -1)
elif self.mode == "设置通用脚本":
logger.info(f"任务开始:设置{self.name}")
logger.info(f"任务开始:设置{self.name}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "设置通用脚本", self.name, 3000)
self.task = GeneralManager(
@@ -113,11 +114,14 @@ class Task(QThread):
try:
self.task.run()
except Exception as e:
logger.exception(f"任务异常:{self.name},错误信息:{e}")
logger.exception(
f"任务异常:{self.name},错误信息:{e}", module=f"业务 {self.name}"
)
self.push_info_bar.emit("error", "任务异常", self.name, -1)
else:
logger.info(f"任务开始:{self.name}", module=f"业务 {self.name}")
self.task_list = [
[
(
@@ -144,16 +148,21 @@ class Task(QThread):
task[1] = "运行"
self.update_task_list.emit(self.task_list)
# 检查任务是否在运行列表中
if task[2] in Config.running_list:
task[1] = "跳过"
self.update_task_list.emit(self.task_list)
logger.info(f"跳过任务:{task[0]}")
logger.info(
f"跳过任务:{task[0]},该任务已在运行列表中",
module=f"业务 {self.name}",
)
self.push_info_bar.emit("info", "跳过任务", task[0], 3000)
continue
# 标记为运行中
Config.running_list.append(task[2])
logger.info(f"任务开始:{task[0]}")
logger.info(f"任务开始:{task[0]}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "任务开始", task[0], 3000)
if Config.member_dict[task[2]]["Type"] == "Maa":
@@ -198,11 +207,11 @@ class Task(QThread):
)
try:
self.task.run()
self.task.run() # 运行任务业务
task[1] = "完成"
self.update_task_list.emit(self.task_list)
logger.info(f"任务完成:{task[0]}")
logger.info(f"任务完成:{task[0]}", module=f"业务 {self.name}")
self.push_info_bar.emit("info", "任务完成", task[0], 3000)
except Exception as e:
@@ -217,15 +226,29 @@ class Task(QThread):
task[1] = "异常"
self.update_task_list.emit(self.task_list)
logger.exception(f"任务异常:{task[0]},错误信息:{e}")
logger.exception(
f"任务异常:{task[0]},错误信息:{e}",
module=f"业务 {self.name}",
)
self.push_info_bar.emit("error", "任务异常", task[0], -1)
# 任务结束后从运行列表中移除
Config.running_list.remove(task[2])
self.accomplish.emit(self.logs)
def task_accomplish(self, name: str, log: dict):
"""保存任务结果"""
"""
销毁任务线程并保存任务结果
:param name: 任务名称
:param log: 任务日志记录
"""
logger.info(
f"任务完成:{name},日志记录:{list(log.values())}",
module=f"业务 {self.name}",
)
self.logs.append([name, log])
self.task.deleteLater()
@@ -245,7 +268,13 @@ class _TaskManager(QObject):
def add_task(
self, mode: str, name: str, info: Dict[str, Dict[str, Union[str, int, bool]]]
):
"""添加任务"""
"""
添加任务
:param mode: 任务模式
:param name: 任务名称
:param info: 任务信息
"""
if name in Config.running_list or name in self.task_dict:
@@ -253,11 +282,14 @@ class _TaskManager(QObject):
MainInfoBar.push_info_bar("warning", "任务已存在", name, 5000)
return None
logger.info(f"任务开始:{name}")
logger.info(f"任务开始:{name},模式:{mode}", module="业务调度")
MainInfoBar.push_info_bar("info", "任务开始", name, 3000)
SoundPlayer.play("任务开始")
# 标记任务为运行中
Config.running_list.append(name)
# 创建任务实例并连接信号
self.task_dict[name] = Task(mode, name, info)
self.task_dict[name].check_maa_version.connect(self.check_maa_version)
self.task_dict[name].question.connect(
@@ -273,18 +305,24 @@ class _TaskManager(QObject):
lambda logs: self.remove_task(mode, name, logs)
)
# 向UI发送信号以创建或连接GUI
if "新调度台" in mode:
self.create_gui.emit(self.task_dict[name])
elif "主调度台" in mode:
self.connect_gui.emit(self.task_dict[name])
# 启动任务线程
self.task_dict[name].start()
def stop_task(self, name: str):
"""中止任务"""
def stop_task(self, name: str) -> None:
"""
中止任务
logger.info(f"中止任务:{name}")
:param name: 任务名称
"""
logger.info(f"中止任务:{name}", module="业务调度")
MainInfoBar.push_info_bar("info", "中止任务", name, 3000)
if name == "ALL":
@@ -303,19 +341,27 @@ class _TaskManager(QObject):
self.task_dict[name].quit()
self.task_dict[name].wait()
def remove_task(self, mode: str, name: str, logs: list):
"""任务结束后的处理"""
def remove_task(self, mode: str, name: str, logs: list) -> None:
"""
处理任务结束后的收尾工作
logger.info(f"任务结束:{name}")
:param mode: 任务模式
:param name: 任务名称
:param logs: 任务日志
"""
logger.info(f"任务结束:{name}", module="业务调度")
MainInfoBar.push_info_bar("info", "任务结束", name, 3000)
SoundPlayer.play("任务结束")
# 删除任务线程,移除运行中标记
self.task_dict[name].deleteLater()
self.task_dict.pop(name)
Config.running_list.remove(name)
if "调度队列" in name and "人工排查" not in mode:
# 保存调度队列历史记录
if len(logs) > 0:
time = logs[0][1]["Time"]
history = ""
@@ -331,6 +377,7 @@ class _TaskManager(QObject):
},
)
# 根据调度队列情况设置电源状态
if (
Config.queue_dict[name]["Config"].get(
Config.queue_dict[name]["Config"].queueSet_AfterAccomplish
@@ -347,9 +394,14 @@ class _TaskManager(QObject):
if Config.args.mode == "cli" and Config.power_sign == "NoAction":
Config.set_power_sign("KillSelf")
def check_maa_version(self, v: str):
"""检查MAA版本"""
def check_maa_version(self, v: str) -> None:
"""
检查MAA版本如果版本过低则推送通知
:param v: 当前MAA版本
"""
logger.info(f"检查MAA版本{v}", module="业务调度")
network = Network.add_task(
mode="get",
url="https://mirrorchyan.com/api/resources/MAA/latest?user_agent=AutoMaaGui&os=win&arch=x64&channel=stable",
@@ -359,7 +411,10 @@ class _TaskManager(QObject):
if network_result["status_code"] == 200:
maa_info = network_result["response_json"]
else:
logger.warning(f"获取MAA版本信息时出错{network_result['error_message']}")
logger.warning(
f"获取MAA版本信息时出错{network_result['error_message']}",
module="业务调度",
)
MainInfoBar.push_info_bar(
"warning",
"获取MAA版本信息时出错",
@@ -371,7 +426,8 @@ class _TaskManager(QObject):
if version.parse(maa_info["data"]["version_name"]) > version.parse(v):
logger.info(
f"检测到MAA版本过低{v},最新版本:{maa_info['data']['version_name']}"
f"检测到MAA版本过低{v},最新版本:{maa_info['data']['version_name']}",
module="业务调度",
)
MainInfoBar.push_info_bar(
"info",
@@ -380,8 +436,19 @@ class _TaskManager(QObject):
-1,
)
logger.success(
f"MAA版本检查完成{v},最新版本:{maa_info['data']['version_name']}",
module="业务调度",
)
def push_dialog(self, name: str, title: str, content: str):
"""推送对话框"""
"""
推送来自任务线程的对话框
:param name: 任务名称
:param title: 对话框标题
:param content: 对话框内容
"""
choice = MessageBox(title, content, Config.main_window)
choice.yesButton.setText("")

View File

@@ -25,12 +25,11 @@ v4.4
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import QObject, QTimer
from datetime import datetime
from pathlib import Path
import keyboard
from .logger import logger
from .config import Config
from .task_manager import TaskManager
from app.services import System
@@ -45,14 +44,31 @@ class _MainTimer(QObject):
self.Timer.timeout.connect(self.timed_start)
self.Timer.timeout.connect(self.set_silence)
self.Timer.timeout.connect(self.check_power)
self.Timer.start(1000)
self.LongTimer = QTimer()
self.LongTimer.timeout.connect(self.long_timed_task)
def start(self):
"""启动定时器"""
logger.info("启动主定时器", module="主业务定时器")
self.Timer.start(1000)
self.LongTimer.start(3600000)
def stop(self):
"""停止定时器"""
logger.info("停止主定时器", module="主业务定时器")
self.Timer.stop()
self.Timer.deleteLater()
self.LongTimer.stop()
self.LongTimer.deleteLater()
def long_timed_task(self):
"""长时间定期检定任务"""
logger.info("执行长时间定期检定任务", module="主业务定时器")
Config.get_stage()
Config.main_window.setting.show_notice()
if Config.get(Config.update_IfAutoUpdate):
@@ -82,7 +98,7 @@ class _MainTimer(QObject):
and name not in Config.running_list
):
logger.info(f"定时任务:{name}")
logger.info(f"定时唤起任务:{name}", module="主业务定时器")
TaskManager.add_task("自动代理_新调度台", name, data)
def set_silence(self):
@@ -109,13 +125,20 @@ class _MainTimer(QObject):
for _ in Config.get(Config.function_BossKey).split("+")
)
)
logger.info(
f"模拟按键:{Config.get(Config.function_BossKey)}",
module="主业务定时器",
)
except Exception as e:
logger.error(f"模拟按键时出错:{e}")
logger.exception(f"模拟按键时出错:{e}", module="主业务定时器")
def check_power(self):
"""检查电源操作"""
if Config.power_sign != "NoAction" and not Config.running_list:
logger.info(f"触发电源操作:{Config.power_sign}", module="主业务定时器")
from app.ui import ProgressRingMessageBox
mode_book = {
@@ -129,9 +152,13 @@ class _MainTimer(QObject):
Config.main_window, f"{mode_book[Config.power_sign]}倒计时"
)
if choice.exec():
logger.info(
f"确认执行电源操作:{Config.power_sign}", module="主业务定时器"
)
System.set_power(Config.power_sign)
Config.set_power_sign("NoAction")
else:
logger.info(f"取消电源操作:{Config.power_sign}", module="主业务定时器")
Config.set_power_sign("NoAction")