feat: 载入各种服务
This commit is contained in:
@@ -39,6 +39,9 @@ from utils import get_logger
|
||||
from models.ConfigBase import *
|
||||
|
||||
|
||||
logger = get_logger("配置管理")
|
||||
|
||||
|
||||
class GlobalConfig(ConfigBase):
|
||||
"""全局配置"""
|
||||
|
||||
@@ -99,8 +102,6 @@ class GlobalConfig(ConfigBase):
|
||||
Notify_ToAddress = ConfigItem("Notify", "ToAddress", "")
|
||||
Notify_IfServerChan = ConfigItem("Notify", "IfServerChan", False, BoolValidator())
|
||||
Notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
||||
Notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
||||
Notify_ServerChanTag = ConfigItem("Notify", "ServerChanTag", "")
|
||||
Notify_IfCompanyWebHookBot = ConfigItem(
|
||||
"Notify", "IfCompanyWebHookBot", False, BoolValidator()
|
||||
)
|
||||
@@ -284,8 +285,6 @@ class MaaUserConfig(ConfigBase):
|
||||
"Notify", "IfServerChan", False, BoolValidator()
|
||||
)
|
||||
self.Notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
||||
self.Notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
||||
self.Notify_ServerChanTag = ConfigItem("Notify", "ServerChanTag", "")
|
||||
self.Notify_IfCompanyWebHookBot = ConfigItem(
|
||||
"Notify", "IfCompanyWebHookBot", False, BoolValidator()
|
||||
)
|
||||
@@ -471,8 +470,6 @@ class GeneralUserConfig(ConfigBase):
|
||||
"Notify", "IfServerChan", False, BoolValidator()
|
||||
)
|
||||
self.Notify_ServerChanKey = ConfigItem("Notify", "ServerChanKey", "")
|
||||
self.Notify_ServerChanChannel = ConfigItem("Notify", "ServerChanChannel", "")
|
||||
self.Notify_ServerChanTag = ConfigItem("Notify", "ServerChanTag", "")
|
||||
self.Notify_IfCompanyWebHookBot = ConfigItem(
|
||||
"Notify", "IfCompanyWebHookBot", False, BoolValidator()
|
||||
)
|
||||
@@ -596,13 +593,12 @@ class AppConfig(GlobalConfig):
|
||||
self.power_sign = "NoAction"
|
||||
self.if_ignore_silence = False
|
||||
|
||||
self.logger = get_logger("配置管理")
|
||||
self.logger.info("")
|
||||
self.logger.info("===================================")
|
||||
self.logger.info("AUTO_MAA 后端应用程序")
|
||||
self.logger.info(f"版本号: v{self.VERSION}")
|
||||
self.logger.info(f"根目录: {self.root_path}")
|
||||
self.logger.info("===================================")
|
||||
logger.info("")
|
||||
logger.info("===================================")
|
||||
logger.info("AUTO_MAA 后端应用程序")
|
||||
logger.info(f"版本号: v{self.VERSION}")
|
||||
logger.info(f"根目录: {self.root_path}")
|
||||
logger.info("===================================")
|
||||
|
||||
# 检查目录
|
||||
self.log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
@@ -623,21 +619,21 @@ class AppConfig(GlobalConfig):
|
||||
await self.QueueConfig.connect(self.config_path / "QueueConfig.json")
|
||||
|
||||
# self.check_data()
|
||||
self.logger.info("程序初始化完成")
|
||||
logger.info("程序初始化完成")
|
||||
|
||||
async def add_script(
|
||||
self, script: Literal["MAA", "General"]
|
||||
) -> tuple[uuid.UUID, ConfigBase]:
|
||||
"""添加脚本配置"""
|
||||
|
||||
self.logger.info(f"添加脚本配置:{script}")
|
||||
logger.info(f"添加脚本配置:{script}")
|
||||
|
||||
return await self.ScriptConfig.add(self.CLASS_BOOK[script])
|
||||
|
||||
async def get_script(self, script_id: Optional[str]) -> tuple[list, dict]:
|
||||
"""获取脚本配置"""
|
||||
|
||||
self.logger.info(f"获取脚本配置:{script_id}")
|
||||
logger.info(f"获取脚本配置:{script_id}")
|
||||
|
||||
if script_id is None:
|
||||
data = await self.ScriptConfig.toDict()
|
||||
@@ -653,15 +649,13 @@ class AppConfig(GlobalConfig):
|
||||
) -> None:
|
||||
"""更新脚本配置"""
|
||||
|
||||
self.logger.info(f"更新脚本配置:{script_id}")
|
||||
logger.info(f"更新脚本配置:{script_id}")
|
||||
|
||||
uid = uuid.UUID(script_id)
|
||||
|
||||
for group, items in data.items():
|
||||
for name, value in items.items():
|
||||
self.logger.debug(
|
||||
f"更新脚本配置:{script_id} - {group}.{name} = {value}"
|
||||
)
|
||||
logger.debug(f"更新脚本配置:{script_id} - {group}.{name} = {value}")
|
||||
await self.ScriptConfig[uid].set(group, name, value)
|
||||
|
||||
await self.ScriptConfig.save()
|
||||
@@ -669,21 +663,21 @@ class AppConfig(GlobalConfig):
|
||||
async def del_script(self, script_id: str) -> None:
|
||||
"""删除脚本配置"""
|
||||
|
||||
self.logger.info(f"删除脚本配置:{script_id}")
|
||||
logger.info(f"删除脚本配置:{script_id}")
|
||||
|
||||
await self.ScriptConfig.remove(uuid.UUID(script_id))
|
||||
|
||||
async def reorder_script(self, index_list: list[str]) -> None:
|
||||
"""重新排序脚本"""
|
||||
|
||||
self.logger.info(f"重新排序脚本:{index_list}")
|
||||
logger.info(f"重新排序脚本:{index_list}")
|
||||
|
||||
await self.ScriptConfig.setOrder([uuid.UUID(_) for _ in index_list])
|
||||
|
||||
async def add_user(self, script_id: str) -> tuple[uuid.UUID, ConfigBase]:
|
||||
"""添加用户配置"""
|
||||
|
||||
self.logger.info(f"{script_id} 添加用户配置")
|
||||
logger.info(f"{script_id} 添加用户配置")
|
||||
|
||||
script_config = self.ScriptConfig[uuid.UUID(script_id)]
|
||||
|
||||
@@ -702,16 +696,14 @@ class AppConfig(GlobalConfig):
|
||||
) -> None:
|
||||
"""更新用户配置"""
|
||||
|
||||
self.logger.info(f"{script_id} 更新用户配置:{user_id}")
|
||||
logger.info(f"{script_id} 更新用户配置:{user_id}")
|
||||
|
||||
script_config = self.ScriptConfig[uuid.UUID(script_id)]
|
||||
uid = uuid.UUID(user_id)
|
||||
|
||||
for group, items in data.items():
|
||||
for name, value in items.items():
|
||||
self.logger.debug(
|
||||
f"更新脚本配置:{script_id} - {group}.{name} = {value}"
|
||||
)
|
||||
logger.debug(f"更新脚本配置:{script_id} - {group}.{name} = {value}")
|
||||
if isinstance(script_config, (MaaConfig | GeneralConfig)):
|
||||
await script_config.UserData[uid].set(group, name, value)
|
||||
|
||||
@@ -720,7 +712,7 @@ class AppConfig(GlobalConfig):
|
||||
async def del_user(self, script_id: str, user_id: str) -> None:
|
||||
"""删除用户配置"""
|
||||
|
||||
self.logger.info(f"{script_id} 删除用户配置:{user_id}")
|
||||
logger.info(f"{script_id} 删除用户配置:{user_id}")
|
||||
|
||||
script_config = self.ScriptConfig[uuid.UUID(script_id)]
|
||||
uid = uuid.UUID(user_id)
|
||||
@@ -732,7 +724,7 @@ class AppConfig(GlobalConfig):
|
||||
async def reorder_user(self, script_id: str, index_list: list[str]) -> None:
|
||||
"""重新排序用户"""
|
||||
|
||||
self.logger.info(f"{script_id} 重新排序用户:{index_list}")
|
||||
logger.info(f"{script_id} 重新排序用户:{index_list}")
|
||||
|
||||
script_config = self.ScriptConfig[uuid.UUID(script_id)]
|
||||
|
||||
@@ -743,14 +735,14 @@ class AppConfig(GlobalConfig):
|
||||
async def add_queue(self) -> tuple[uuid.UUID, ConfigBase]:
|
||||
"""添加调度队列"""
|
||||
|
||||
self.logger.info("添加调度队列")
|
||||
logger.info("添加调度队列")
|
||||
|
||||
return await self.QueueConfig.add(QueueConfig)
|
||||
|
||||
async def get_queue(self, queue_id: Optional[str]) -> tuple[list, dict]:
|
||||
"""获取调度队列配置"""
|
||||
|
||||
self.logger.info(f"获取调度队列配置:{queue_id}")
|
||||
logger.info(f"获取调度队列配置:{queue_id}")
|
||||
|
||||
if queue_id is None:
|
||||
data = await self.QueueConfig.toDict()
|
||||
@@ -766,15 +758,13 @@ class AppConfig(GlobalConfig):
|
||||
) -> None:
|
||||
"""更新调度队列配置"""
|
||||
|
||||
self.logger.info(f"更新调度队列配置:{queue_id}")
|
||||
logger.info(f"更新调度队列配置:{queue_id}")
|
||||
|
||||
uid = uuid.UUID(queue_id)
|
||||
|
||||
for group, items in data.items():
|
||||
for name, value in items.items():
|
||||
self.logger.debug(
|
||||
f"更新调度队列配置:{queue_id} - {group}.{name} = {value}"
|
||||
)
|
||||
logger.debug(f"更新调度队列配置:{queue_id} - {group}.{name} = {value}")
|
||||
await self.QueueConfig[uid].set(group, name, value)
|
||||
|
||||
await self.QueueConfig.save()
|
||||
@@ -782,21 +772,21 @@ class AppConfig(GlobalConfig):
|
||||
async def del_queue(self, queue_id: str) -> None:
|
||||
"""删除调度队列配置"""
|
||||
|
||||
self.logger.info(f"删除调度队列配置:{queue_id}")
|
||||
logger.info(f"删除调度队列配置:{queue_id}")
|
||||
|
||||
await self.QueueConfig.remove(uuid.UUID(queue_id))
|
||||
|
||||
async def reorder_queue(self, index_list: list[str]) -> None:
|
||||
"""重新排序调度队列"""
|
||||
|
||||
self.logger.info(f"重新排序调度队列:{index_list}")
|
||||
logger.info(f"重新排序调度队列:{index_list}")
|
||||
|
||||
await self.QueueConfig.setOrder([uuid.UUID(_) for _ in index_list])
|
||||
|
||||
async def add_time_set(self, queue_id: str) -> tuple[uuid.UUID, ConfigBase]:
|
||||
"""添加时间设置配置"""
|
||||
|
||||
self.logger.info(f"{queue_id} 添加时间设置配置")
|
||||
logger.info(f"{queue_id} 添加时间设置配置")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
|
||||
@@ -813,16 +803,14 @@ class AppConfig(GlobalConfig):
|
||||
) -> None:
|
||||
"""更新时间设置配置"""
|
||||
|
||||
self.logger.info(f"{queue_id} 更新时间设置配置:{time_set_id}")
|
||||
logger.info(f"{queue_id} 更新时间设置配置:{time_set_id}")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
uid = uuid.UUID(time_set_id)
|
||||
|
||||
for group, items in data.items():
|
||||
for name, value in items.items():
|
||||
self.logger.debug(
|
||||
f"更新时间设置配置:{queue_id} - {group}.{name} = {value}"
|
||||
)
|
||||
logger.debug(f"更新时间设置配置:{queue_id} - {group}.{name} = {value}")
|
||||
if isinstance(queue_config, QueueConfig):
|
||||
await queue_config.TimeSet[uid].set(group, name, value)
|
||||
|
||||
@@ -831,7 +819,7 @@ class AppConfig(GlobalConfig):
|
||||
async def del_time_set(self, queue_id: str, time_set_id: str) -> None:
|
||||
"""删除时间设置配置"""
|
||||
|
||||
self.logger.info(f"{queue_id} 删除时间设置配置:{time_set_id}")
|
||||
logger.info(f"{queue_id} 删除时间设置配置:{time_set_id}")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
uid = uuid.UUID(time_set_id)
|
||||
@@ -843,7 +831,7 @@ class AppConfig(GlobalConfig):
|
||||
async def reorder_time_set(self, queue_id: str, index_list: list[str]) -> None:
|
||||
"""重新排序时间设置"""
|
||||
|
||||
self.logger.info(f"{queue_id} 重新排序时间设置:{index_list}")
|
||||
logger.info(f"{queue_id} 重新排序时间设置:{index_list}")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
|
||||
@@ -856,14 +844,14 @@ class AppConfig(GlobalConfig):
|
||||
) -> tuple[uuid.UUID, ConfigBase]:
|
||||
"""添加计划表"""
|
||||
|
||||
self.logger.info(f"添加计划表:{script}")
|
||||
logger.info(f"添加计划表:{script}")
|
||||
|
||||
return await self.PlanConfig.add(self.CLASS_BOOK[script])
|
||||
|
||||
async def get_plan(self, plan_id: Optional[str]) -> tuple[list, dict]:
|
||||
"""获取计划表配置"""
|
||||
|
||||
self.logger.info(f"获取计划表配置:{plan_id}")
|
||||
logger.info(f"获取计划表配置:{plan_id}")
|
||||
|
||||
if plan_id is None:
|
||||
data = await self.PlanConfig.toDict()
|
||||
@@ -877,15 +865,13 @@ class AppConfig(GlobalConfig):
|
||||
async def update_plan(self, plan_id: str, data: Dict[str, Dict[str, Any]]) -> None:
|
||||
"""更新计划表配置"""
|
||||
|
||||
self.logger.info(f"更新计划表配置:{plan_id}")
|
||||
logger.info(f"更新计划表配置:{plan_id}")
|
||||
|
||||
uid = uuid.UUID(plan_id)
|
||||
|
||||
for group, items in data.items():
|
||||
for name, value in items.items():
|
||||
self.logger.debug(
|
||||
f"更新计划表配置:{plan_id} - {group}.{name} = {value}"
|
||||
)
|
||||
logger.debug(f"更新计划表配置:{plan_id} - {group}.{name} = {value}")
|
||||
await self.PlanConfig[uid].set(group, name, value)
|
||||
|
||||
await self.PlanConfig.save()
|
||||
@@ -893,21 +879,21 @@ class AppConfig(GlobalConfig):
|
||||
async def del_plan(self, plan_id: str) -> None:
|
||||
"""删除计划表配置"""
|
||||
|
||||
self.logger.info(f"删除计划表配置:{plan_id}")
|
||||
logger.info(f"删除计划表配置:{plan_id}")
|
||||
|
||||
await self.PlanConfig.remove(uuid.UUID(plan_id))
|
||||
|
||||
async def reorder_plan(self, index_list: list[str]) -> None:
|
||||
"""重新排序计划表"""
|
||||
|
||||
self.logger.info(f"重新排序计划表:{index_list}")
|
||||
logger.info(f"重新排序计划表:{index_list}")
|
||||
|
||||
await self.PlanConfig.setOrder([uuid.UUID(_) for _ in index_list])
|
||||
|
||||
async def add_queue_item(self, queue_id: str) -> tuple[uuid.UUID, ConfigBase]:
|
||||
"""添加队列项配置"""
|
||||
|
||||
self.logger.info(f"{queue_id} 添加队列项配置")
|
||||
logger.info(f"{queue_id} 添加队列项配置")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
|
||||
@@ -924,7 +910,7 @@ class AppConfig(GlobalConfig):
|
||||
) -> None:
|
||||
"""更新队列项配置"""
|
||||
|
||||
self.logger.info(f"{queue_id} 更新队列项配置:{queue_item_id}")
|
||||
logger.info(f"{queue_id} 更新队列项配置:{queue_item_id}")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
uid = uuid.UUID(queue_item_id)
|
||||
@@ -933,9 +919,7 @@ class AppConfig(GlobalConfig):
|
||||
for name, value in items.items():
|
||||
if uuid.UUID(value) not in self.ScriptConfig:
|
||||
raise ValueError(f"Script with uid {value} does not exist.")
|
||||
self.logger.debug(
|
||||
f"更新队列项配置:{queue_id} - {group}.{name} = {value}"
|
||||
)
|
||||
logger.debug(f"更新队列项配置:{queue_id} - {group}.{name} = {value}")
|
||||
if isinstance(queue_config, QueueConfig):
|
||||
await queue_config.QueueItem[uid].set(group, name, value)
|
||||
|
||||
@@ -944,7 +928,7 @@ class AppConfig(GlobalConfig):
|
||||
async def del_queue_item(self, queue_id: str, queue_item_id: str) -> None:
|
||||
"""删除队列项配置"""
|
||||
|
||||
self.logger.info(f"{queue_id} 删除队列项配置:{queue_item_id}")
|
||||
logger.info(f"{queue_id} 删除队列项配置:{queue_item_id}")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
uid = uuid.UUID(queue_item_id)
|
||||
@@ -956,7 +940,7 @@ class AppConfig(GlobalConfig):
|
||||
async def reorder_queue_item(self, queue_id: str, index_list: list[str]) -> None:
|
||||
"""重新排序队列项"""
|
||||
|
||||
self.logger.info(f"{queue_id} 重新排序队列项:{index_list}")
|
||||
logger.info(f"{queue_id} 重新排序队列项:{index_list}")
|
||||
|
||||
queue_config = self.QueueConfig[uuid.UUID(queue_id)]
|
||||
|
||||
@@ -967,18 +951,18 @@ class AppConfig(GlobalConfig):
|
||||
async def get_setting(self) -> Dict[str, Any]:
|
||||
"""获取全局设置"""
|
||||
|
||||
self.logger.info("获取全局设置")
|
||||
logger.info("获取全局设置")
|
||||
|
||||
return await self.toDict(ignore_multi_config=True)
|
||||
|
||||
async def update_setting(self, data: Dict[str, Dict[str, Any]]) -> None:
|
||||
"""更新全局设置"""
|
||||
|
||||
self.logger.info(f"更新全局设置")
|
||||
logger.info(f"更新全局设置")
|
||||
|
||||
for group, items in data.items():
|
||||
for name, value in items.items():
|
||||
self.logger.debug(f"更新全局设置 - {group}.{name} = {value}")
|
||||
logger.debug(f"更新全局设置 - {group}.{name} = {value}")
|
||||
await self.set(group, name, value)
|
||||
|
||||
def server_date(self) -> date:
|
||||
@@ -994,25 +978,29 @@ class AppConfig(GlobalConfig):
|
||||
dt = dt - timedelta(days=1)
|
||||
return dt.date()
|
||||
|
||||
def get_proxies(self) -> Dict[str, str]:
|
||||
"""获取代理设置"""
|
||||
return {
|
||||
"http": self.get("Update", "ProxyAddress"),
|
||||
"https": self.get("Update", "ProxyAddress"),
|
||||
}
|
||||
|
||||
async def get_stage(self) -> tuple[bool, Dict[str, Dict[str, list]]]:
|
||||
"""从MAA服务器更新活动关卡信息"""
|
||||
|
||||
self.logger.info("开始获取活动关卡信息")
|
||||
logger.info("开始获取活动关卡信息")
|
||||
|
||||
response = requests.get(
|
||||
"https://api.maa.plus/MaaAssistantArknights/api/gui/StageActivity.json",
|
||||
timeout=10,
|
||||
proxies={
|
||||
"http": self.get("Update", "ProxyAddress"),
|
||||
"https": self.get("Update", "ProxyAddress"),
|
||||
},
|
||||
proxies=self.get_proxies(),
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
stage_infos = response.json()["Official"]["sideStoryStage"]
|
||||
if_get_maa_stage = True
|
||||
else:
|
||||
self.logger.warning(f"无法从MAA服务器获取活动关卡信息:{response.text}")
|
||||
logger.warning(f"无法从MAA服务器获取活动关卡信息:{response.text}")
|
||||
if_get_maa_stage = False
|
||||
stage_infos = []
|
||||
|
||||
@@ -1054,23 +1042,18 @@ class AppConfig(GlobalConfig):
|
||||
async def get_server_info(self, type: str) -> Dict[str, Any]:
|
||||
"""获取公告信息"""
|
||||
|
||||
self.logger.info(f"开始从 AUTO_MAA 服务器获取 {type} 信息")
|
||||
logger.info(f"开始从 AUTO_MAA 服务器获取 {type} 信息")
|
||||
|
||||
response = requests.get(
|
||||
url=f"http://221.236.27.82:10197/d/AUTO_MAA/Server/{type}.json",
|
||||
timeout=10,
|
||||
proxies={
|
||||
"http": self.get("Update", "ProxyAddress"),
|
||||
"https": self.get("Update", "ProxyAddress"),
|
||||
},
|
||||
proxies=self.get_proxies(),
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
self.logger.warning(
|
||||
f"无法从 AUTO_MAA 服务器获取 {type} 信息:{response.text}"
|
||||
)
|
||||
logger.warning(f"无法从 AUTO_MAA 服务器获取 {type} 信息:{response.text}")
|
||||
raise ConnectionError(
|
||||
"Cannot connect to the notice server. Please check your network connection or try again later."
|
||||
)
|
||||
|
||||
@@ -26,16 +26,17 @@ from utils import get_logger
|
||||
from .config import Config
|
||||
|
||||
|
||||
logger = get_logger("主业务定时器")
|
||||
|
||||
|
||||
class _MainTimer:
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.logger = get_logger("主业务定时器")
|
||||
|
||||
async def second_task(self):
|
||||
"""每秒定期任务"""
|
||||
self.logger.info("每秒定期任务启动")
|
||||
logger.info("每秒定期任务启动")
|
||||
|
||||
while True:
|
||||
|
||||
|
||||
@@ -18,39 +18,28 @@
|
||||
|
||||
# Contact: DLmaster_361@163.com
|
||||
|
||||
"""
|
||||
AUTO_MAA
|
||||
AUTO_MAA通知服务
|
||||
v4.4
|
||||
作者:DLmaster_361
|
||||
"""
|
||||
|
||||
import re
|
||||
import smtplib
|
||||
import time
|
||||
import requests
|
||||
from email.header import Header
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import formataddr
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
import requests
|
||||
from PySide6.QtCore import QObject, Signal
|
||||
|
||||
from plyer import notification
|
||||
|
||||
from app.core import Config, logger
|
||||
from app.utils.security import Crypto
|
||||
from app.utils.ImageUtils import ImageUtils
|
||||
from core import Config
|
||||
from utils import get_logger, ImageUtils
|
||||
|
||||
logger = get_logger("通知服务")
|
||||
|
||||
|
||||
class Notification(QObject):
|
||||
class Notification:
|
||||
|
||||
push_info_bar = Signal(str, str, str, int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def push_plyer(self, title, message, ticker, t) -> bool:
|
||||
"""
|
||||
@@ -63,19 +52,22 @@ class Notification(QObject):
|
||||
:return: bool
|
||||
"""
|
||||
|
||||
if Config.get(Config.notify_IfPushPlyer):
|
||||
if Config.get("Notify", "IfPushPlyer"):
|
||||
|
||||
logger.info(f"推送系统通知:{title}", module="通知服务")
|
||||
logger.info(f"推送系统通知:{title}")
|
||||
|
||||
notification.notify(
|
||||
title=title,
|
||||
message=message,
|
||||
app_name="AUTO_MAA",
|
||||
app_icon=str(Config.app_path / "resources/icons/AUTO_MAA.ico"),
|
||||
timeout=t,
|
||||
ticker=ticker,
|
||||
toast=True,
|
||||
)
|
||||
if notification.notify is not None:
|
||||
notification.notify(
|
||||
title=title,
|
||||
message=message,
|
||||
app_name="AUTO_MAA",
|
||||
app_icon=(Path.cwd() / "resources/icons/AUTO_MAA.ico").as_posix(),
|
||||
timeout=t,
|
||||
ticker=ticker,
|
||||
toast=True,
|
||||
)
|
||||
else:
|
||||
logger.error("plyer.notification 未正确导入,无法推送系统通知")
|
||||
|
||||
return True
|
||||
|
||||
@@ -90,12 +82,12 @@ class Notification(QObject):
|
||||
"""
|
||||
|
||||
if (
|
||||
Config.get(Config.notify_SMTPServerAddress) == ""
|
||||
or Config.get(Config.notify_AuthorizationCode) == ""
|
||||
Config.get("Notify", "SMTPServerAddress") == ""
|
||||
or Config.get("Notify", "AuthorizationCode") == ""
|
||||
or not bool(
|
||||
re.match(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||
Config.get(Config.notify_FromAddress),
|
||||
Config.get("Notify", "FromAddress"),
|
||||
)
|
||||
)
|
||||
or not bool(
|
||||
@@ -106,336 +98,146 @@ class Notification(QObject):
|
||||
)
|
||||
):
|
||||
logger.error(
|
||||
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址",
|
||||
module="通知服务",
|
||||
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"邮件通知推送异常",
|
||||
"请正确设置邮件通知的SMTP服务器地址、授权码、发件人地址和收件人地址",
|
||||
-1,
|
||||
raise ValueError(
|
||||
"The SMTP server address, authorization code, sender address, or recipient address is not set correctly."
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
# 定义邮件正文
|
||||
if mode == "文本":
|
||||
message = MIMEText(content, "plain", "utf-8")
|
||||
elif mode == "网页":
|
||||
message = MIMEMultipart("alternative")
|
||||
message["From"] = formataddr(
|
||||
(
|
||||
Header("AUTO_MAA通知服务", "utf-8").encode(),
|
||||
Config.get(Config.notify_FromAddress),
|
||||
)
|
||||
) # 发件人显示的名字
|
||||
message["To"] = formataddr(
|
||||
(Header("AUTO_MAA用户", "utf-8").encode(), to_address)
|
||||
) # 收件人显示的名字
|
||||
message["Subject"] = Header(title, "utf-8")
|
||||
|
||||
if mode == "网页":
|
||||
message.attach(MIMEText(content, "html", "utf-8"))
|
||||
|
||||
smtpObj = smtplib.SMTP_SSL(Config.get(Config.notify_SMTPServerAddress), 465)
|
||||
smtpObj.login(
|
||||
Config.get(Config.notify_FromAddress),
|
||||
Crypto.win_decryptor(Config.get(Config.notify_AuthorizationCode)),
|
||||
# 定义邮件正文
|
||||
if mode == "文本":
|
||||
message = MIMEText(content, "plain", "utf-8")
|
||||
elif mode == "网页":
|
||||
message = MIMEMultipart("alternative")
|
||||
message["From"] = formataddr(
|
||||
(
|
||||
Header("AUTO_MAA通知服务", "utf-8").encode(),
|
||||
Config.get("Notify", "FromAddress"),
|
||||
)
|
||||
smtpObj.sendmail(
|
||||
Config.get(Config.notify_FromAddress), to_address, message.as_string()
|
||||
)
|
||||
smtpObj.quit()
|
||||
logger.success(f"邮件发送成功:{title}", module="通知服务")
|
||||
except Exception as e:
|
||||
logger.exception(f"发送邮件时出错:{e}", module="通知服务")
|
||||
self.push_info_bar.emit("error", "发送邮件时出错", f"{e}", -1)
|
||||
) # 发件人显示的名字
|
||||
message["To"] = formataddr(
|
||||
(Header("AUTO_MAA用户", "utf-8").encode(), to_address)
|
||||
) # 收件人显示的名字
|
||||
message["Subject"] = str(Header(title, "utf-8"))
|
||||
|
||||
def ServerChanPush(
|
||||
self, title, content, send_key, tag, channel
|
||||
) -> Union[bool, str]:
|
||||
if mode == "网页":
|
||||
message.attach(MIMEText(content, "html", "utf-8"))
|
||||
|
||||
smtpObj = smtplib.SMTP_SSL(Config.get("Notify", "SMTPServerAddress"), 465)
|
||||
smtpObj.login(
|
||||
Config.get("Notify", "FromAddress"),
|
||||
Config.get("Notify", "AuthorizationCode"),
|
||||
)
|
||||
smtpObj.sendmail(
|
||||
Config.get("Notify", "FromAddress"), to_address, message.as_string()
|
||||
)
|
||||
smtpObj.quit()
|
||||
logger.success(f"邮件发送成功:{title}")
|
||||
|
||||
def ServerChanPush(self, title, content, send_key) -> None:
|
||||
"""
|
||||
使用Server酱推送通知
|
||||
|
||||
:param title: 通知标题
|
||||
:param content: 通知内容
|
||||
:param send_key: Server酱的SendKey
|
||||
:param tag: 通知标签
|
||||
:param channel: 通知频道
|
||||
:return: bool or str
|
||||
"""
|
||||
|
||||
if not send_key:
|
||||
logger.error("请正确设置Server酱的SendKey", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error", "Server酱通知推送异常", "请正确设置Server酱的SendKey", -1
|
||||
)
|
||||
return None
|
||||
raise ValueError("The ServerChan SendKey can not be empty.")
|
||||
|
||||
try:
|
||||
# 构造 URL
|
||||
if send_key.startswith("sctp"):
|
||||
match = re.match(r"^sctp(\d+)t", send_key)
|
||||
if match:
|
||||
url = f"https://{match.group(1)}.push.ft07.com/send/{send_key}.send"
|
||||
else:
|
||||
raise ValueError("SendKey 格式错误(sctp)")
|
||||
# 构造 URL
|
||||
if send_key.startswith("sctp"):
|
||||
match = re.match(r"^sctp(\d+)t", send_key)
|
||||
if match:
|
||||
url = f"https://{match.group(1)}.push.ft07.com/send/{send_key}.send"
|
||||
else:
|
||||
url = f"https://sctapi.ftqq.com/{send_key}.send"
|
||||
raise ValueError("SendKey format is incorrect (sctp<int>).")
|
||||
else:
|
||||
url = f"https://sctapi.ftqq.com/{send_key}.send"
|
||||
|
||||
# 构建 tags 和 channel
|
||||
def is_valid(s):
|
||||
return s == "" or (
|
||||
s == "|".join(s.split("|"))
|
||||
and (s.count("|") == 0 or all(s.split("|")))
|
||||
)
|
||||
# 请求发送
|
||||
params = {"title": title, "desp": content}
|
||||
headers = {"Content-Type": "application/json;charset=utf-8"}
|
||||
|
||||
tags = "|".join(_.strip() for _ in tag.split("|"))
|
||||
channels = "|".join(_.strip() for _ in channel.split("|"))
|
||||
response = requests.post(
|
||||
url, json=params, headers=headers, timeout=10, proxies=Config.get_proxies()
|
||||
)
|
||||
result = response.json()
|
||||
|
||||
options = {}
|
||||
if is_valid(tags):
|
||||
options["tags"] = tags
|
||||
else:
|
||||
logger.warning("Server酱 Tag 配置不正确,将被忽略", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"warning",
|
||||
"Server酱通知推送异常",
|
||||
"请正确设置 ServerChan 的 Tag",
|
||||
-1,
|
||||
)
|
||||
if result.get("code") == 0:
|
||||
logger.success(f"Server酱推送通知成功:{title}")
|
||||
else:
|
||||
raise Exception(f"ServerChan failed to send notification: {response.text}")
|
||||
|
||||
if is_valid(channels):
|
||||
options["channel"] = channels
|
||||
else:
|
||||
logger.warning(
|
||||
"Server酱 Channel 配置不正确,将被忽略", module="通知服务"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"warning",
|
||||
"Server酱通知推送异常",
|
||||
"请正确设置 ServerChan 的 Channel",
|
||||
-1,
|
||||
)
|
||||
|
||||
# 请求发送
|
||||
params = {"title": title, "desp": content, **options}
|
||||
headers = {"Content-Type": "application/json;charset=utf-8"}
|
||||
|
||||
response = requests.post(
|
||||
url,
|
||||
json=params,
|
||||
headers=headers,
|
||||
timeout=10,
|
||||
proxies={
|
||||
"http": Config.get(Config.update_ProxyAddress),
|
||||
"https": Config.get(Config.update_ProxyAddress),
|
||||
},
|
||||
)
|
||||
result = response.json()
|
||||
|
||||
if result.get("code") == 0:
|
||||
logger.success(f"Server酱推送通知成功:{title}", module="通知服务")
|
||||
return True
|
||||
else:
|
||||
error_code = result.get("code", "-1")
|
||||
logger.exception(
|
||||
f"Server酱通知推送失败:响应码:{error_code}", module="通知服务"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"error", "Server酱通知推送失败", f"响应码:{error_code}", -1
|
||||
)
|
||||
return f"Server酱通知推送失败:{error_code}"
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Server酱通知推送异常:{e}", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"Server酱通知推送异常",
|
||||
"请检查相关设置和网络连接。如全部配置正确,请稍后再试。",
|
||||
-1,
|
||||
)
|
||||
return f"Server酱通知推送异常:{str(e)}"
|
||||
|
||||
def CompanyWebHookBotPush(self, title, content, webhook_url) -> Union[bool, str]:
|
||||
def WebHookPush(self, title, content, webhook_url) -> None:
|
||||
"""
|
||||
使用企业微信群机器人推送通知
|
||||
WebHook 推送通知
|
||||
|
||||
:param title: 通知标题
|
||||
:param content: 通知内容
|
||||
:param webhook_url: 企业微信群机器人的WebHook地址
|
||||
:return: bool or str
|
||||
:param webhook_url: WebHook地址
|
||||
"""
|
||||
|
||||
if webhook_url == "":
|
||||
logger.error("请正确设置企业微信群机器人的WebHook地址", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送异常",
|
||||
"请正确设置企业微信群机器人的WebHook地址",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
if not webhook_url:
|
||||
raise ValueError("The webhook URL can not be empty.")
|
||||
|
||||
content = f"{title}\n{content}"
|
||||
data = {"msgtype": "text", "text": {"content": content}}
|
||||
|
||||
for _ in range(3):
|
||||
try:
|
||||
response = requests.post(
|
||||
url=webhook_url,
|
||||
json=data,
|
||||
timeout=10,
|
||||
proxies={
|
||||
"http": Config.get(Config.update_ProxyAddress),
|
||||
"https": Config.get(Config.update_ProxyAddress),
|
||||
},
|
||||
)
|
||||
info = response.json()
|
||||
break
|
||||
except Exception as e:
|
||||
err = e
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
logger.error(f"推送企业微信群机器人时出错:{err}", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送失败",
|
||||
f"使用企业微信群机器人推送通知时出错:{err}",
|
||||
-1,
|
||||
)
|
||||
return None
|
||||
response = requests.post(
|
||||
url=webhook_url, json=data, timeout=10, proxies=Config.get_proxies()
|
||||
)
|
||||
info = response.json()
|
||||
|
||||
if info["errcode"] == 0:
|
||||
logger.success(f"企业微信群机器人推送通知成功:{title}", module="通知服务")
|
||||
return True
|
||||
logger.success(f"WebHook 推送通知成功:{title}")
|
||||
else:
|
||||
logger.error(f"企业微信群机器人推送通知失败:{info}", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送失败",
|
||||
f"使用企业微信群机器人推送通知时出错:{err}",
|
||||
-1,
|
||||
)
|
||||
return f"使用企业微信群机器人推送通知时出错:{err}"
|
||||
raise Exception(f"WebHook failed to send notification: {response.text}")
|
||||
|
||||
def CompanyWebHookBotPushImage(self, image_path: Path, webhook_url: str) -> bool:
|
||||
def CompanyWebHookBotPushImage(self, image_path: Path, webhook_url: str) -> None:
|
||||
"""
|
||||
使用企业微信群机器人推送图片通知
|
||||
|
||||
:param image_path: 图片文件路径
|
||||
:param webhook_url: 企业微信群机器人的WebHook地址
|
||||
:return: bool
|
||||
"""
|
||||
|
||||
try:
|
||||
# 压缩图片
|
||||
ImageUtils.compress_image_if_needed(image_path)
|
||||
if not webhook_url:
|
||||
raise ValueError("The webhook URL can not be empty.")
|
||||
|
||||
# 检查图片是否存在
|
||||
if not image_path.exists():
|
||||
logger.error(
|
||||
"图片推送异常 | 图片不存在或者压缩失败,请检查图片路径是否正确",
|
||||
module="通知服务",
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送异常",
|
||||
"图片不存在或者压缩失败,请检查图片路径是否正确",
|
||||
-1,
|
||||
)
|
||||
return False
|
||||
# 压缩图片
|
||||
ImageUtils.compress_image_if_needed(image_path)
|
||||
|
||||
if not webhook_url:
|
||||
logger.error(
|
||||
"请正确设置企业微信群机器人的WebHook地址", module="通知服务"
|
||||
)
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送异常",
|
||||
"请正确设置企业微信群机器人的WebHook地址",
|
||||
-1,
|
||||
)
|
||||
return False
|
||||
# 检查图片是否存在
|
||||
if not image_path.exists():
|
||||
raise FileNotFoundError(f"File not found: {image_path}")
|
||||
|
||||
# 获取图片base64和md5
|
||||
try:
|
||||
image_base64 = ImageUtils.get_base64_from_file(str(image_path))
|
||||
image_md5 = ImageUtils.calculate_md5_from_file(str(image_path))
|
||||
except Exception as e:
|
||||
logger.exception(f"图片编码或MD5计算失败:{e}", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人通知推送异常",
|
||||
f"图片编码或MD5计算失败:{e}",
|
||||
-1,
|
||||
)
|
||||
return False
|
||||
# 获取图片base64和md5
|
||||
image_base64 = ImageUtils.get_base64_from_file(str(image_path))
|
||||
image_md5 = ImageUtils.calculate_md5_from_file(str(image_path))
|
||||
|
||||
data = {
|
||||
"msgtype": "image",
|
||||
"image": {"base64": image_base64, "md5": image_md5},
|
||||
}
|
||||
data = {
|
||||
"msgtype": "image",
|
||||
"image": {"base64": image_base64, "md5": image_md5},
|
||||
}
|
||||
|
||||
for _ in range(3):
|
||||
try:
|
||||
response = requests.post(
|
||||
url=webhook_url,
|
||||
json=data,
|
||||
timeout=10,
|
||||
proxies={
|
||||
"http": Config.get(Config.update_ProxyAddress),
|
||||
"https": Config.get(Config.update_ProxyAddress),
|
||||
},
|
||||
)
|
||||
info = response.json()
|
||||
break
|
||||
except requests.RequestException as e:
|
||||
err = e
|
||||
logger.exception(
|
||||
f"推送企业微信群机器人图片第{_+1}次失败:{e}", module="通知服务"
|
||||
)
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
logger.error("推送企业微信群机器人图片时出错", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人图片推送失败",
|
||||
f"使用企业微信群机器人推送图片时出错:{err}",
|
||||
-1,
|
||||
)
|
||||
return False
|
||||
response = requests.post(
|
||||
url=webhook_url, json=data, timeout=10, proxies=Config.get_proxies()
|
||||
)
|
||||
info = response.json()
|
||||
|
||||
if info.get("errcode") == 0:
|
||||
logger.success(
|
||||
f"企业微信群机器人推送图片成功:{image_path.name}",
|
||||
module="通知服务",
|
||||
)
|
||||
return True
|
||||
else:
|
||||
logger.error(f"企业微信群机器人推送图片失败:{info}", module="通知服务")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人图片推送失败",
|
||||
f"使用企业微信群机器人推送图片时出错:{info}",
|
||||
-1,
|
||||
)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"推送企业微信群机器人图片时发生未知异常:{e}")
|
||||
self.push_info_bar.emit(
|
||||
"error",
|
||||
"企业微信群机器人图片推送失败",
|
||||
f"发生未知异常:{e}",
|
||||
-1,
|
||||
if info.get("errcode") == 0:
|
||||
logger.success(f"企业微信群机器人推送图片成功:{image_path.name}")
|
||||
else:
|
||||
raise Exception(
|
||||
f"Company WebHook Bot failed to send image: {response.text}"
|
||||
)
|
||||
return False
|
||||
|
||||
def send_test_notification(self):
|
||||
def send_test_notification(self) -> None:
|
||||
"""发送测试通知到所有已启用的通知渠道"""
|
||||
|
||||
logger.info("发送测试通知到所有已启用的通知渠道", module="通知服务")
|
||||
logger.info("发送测试通知到所有已启用的通知渠道")
|
||||
|
||||
# 发送系统通知
|
||||
self.push_plyer(
|
||||
@@ -446,39 +248,35 @@ class Notification(QObject):
|
||||
)
|
||||
|
||||
# 发送邮件通知
|
||||
if Config.get(Config.notify_IfSendMail):
|
||||
if Config.get("Notify", "IfSendMail"):
|
||||
self.send_mail(
|
||||
"文本",
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_ToAddress),
|
||||
Config.get("Notify", "ToAddress"),
|
||||
)
|
||||
|
||||
# 发送Server酱通知
|
||||
if Config.get(Config.notify_IfServerChan):
|
||||
if Config.get("Notify", "IfServerChan"):
|
||||
self.ServerChanPush(
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_ServerChanKey),
|
||||
Config.get(Config.notify_ServerChanTag),
|
||||
Config.get(Config.notify_ServerChanChannel),
|
||||
Config.get("Notify", "ServerChanKey"),
|
||||
)
|
||||
|
||||
# 发送企业微信机器人通知
|
||||
if Config.get(Config.notify_IfCompanyWebHookBot):
|
||||
self.CompanyWebHookBotPush(
|
||||
# 发送WebHook通知
|
||||
if Config.get("Notify", "IfCompanyWebHookBot"):
|
||||
self.WebHookPush(
|
||||
"AUTO_MAA测试通知",
|
||||
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
|
||||
Config.get(Config.notify_CompanyWebHookBotUrl),
|
||||
Config.get("Notify", "CompanyWebHookBotUrl"),
|
||||
)
|
||||
Notify.CompanyWebHookBotPushImage(
|
||||
Config.app_path / "resources/images/notification/test_notify.png",
|
||||
Config.get(Config.notify_CompanyWebHookBotUrl),
|
||||
Path.cwd() / "resources/images/notification/test_notify.png",
|
||||
Config.get("Notify", "CompanyWebHookBotUrl"),
|
||||
)
|
||||
|
||||
logger.info("测试通知发送完成", module="通知服务")
|
||||
|
||||
return True
|
||||
logger.success("测试通知发送完成")
|
||||
|
||||
|
||||
Notify = Notification()
|
||||
|
||||
@@ -18,14 +18,7 @@
|
||||
|
||||
# Contact: DLmaster_361@163.com
|
||||
|
||||
"""
|
||||
AUTO_MAA
|
||||
AUTO_MAA系统服务
|
||||
v4.4
|
||||
作者:DLmaster_361
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
import sys
|
||||
import ctypes
|
||||
import win32gui
|
||||
@@ -37,7 +30,10 @@ import getpass
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from app.core import Config
|
||||
from core import Config
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger("系统服务")
|
||||
|
||||
|
||||
class _SystemHandler:
|
||||
@@ -53,7 +49,7 @@ class _SystemHandler:
|
||||
def set_Sleep(self) -> None:
|
||||
"""同步系统休眠状态"""
|
||||
|
||||
if Config.get(Config.function_IfAllowSleep):
|
||||
if Config.get("Function", "IfAllowSleep"):
|
||||
# 设置系统电源状态
|
||||
ctypes.windll.kernel32.SetThreadExecutionState(
|
||||
self.ES_CONTINUOUS | self.ES_SYSTEM_REQUIRED
|
||||
@@ -65,7 +61,9 @@ class _SystemHandler:
|
||||
def set_SelfStart(self) -> None:
|
||||
"""同步开机自启"""
|
||||
|
||||
if Config.get(Config.start_IfSelfStart) and not self.is_startup():
|
||||
return None # 目前不支持开机自启
|
||||
|
||||
if Config.get("Function", "IfSelfStart") and not self.is_startup():
|
||||
|
||||
# 创建任务计划
|
||||
try:
|
||||
@@ -164,7 +162,7 @@ class _SystemHandler:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"程序自启动任务计划创建失败: {e}", module="系统服务")
|
||||
logger.exception(f"程序自启动任务计划创建失败: {e}")
|
||||
|
||||
elif not Config.get(Config.start_IfSelfStart) and self.is_startup():
|
||||
|
||||
@@ -179,7 +177,7 @@ class _SystemHandler:
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.success("程序自启动任务计划已删除", module="系统服务")
|
||||
logger.success("程序自启动任务计划已删除")
|
||||
else:
|
||||
logger.error(
|
||||
f"程序自启动任务计划删除失败: {result.stderr}",
|
||||
@@ -187,7 +185,7 @@ class _SystemHandler:
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"程序自启动任务计划删除失败: {e}", module="系统服务")
|
||||
logger.exception(f"程序自启动任务计划删除失败: {e}")
|
||||
|
||||
def set_power(self, mode) -> None:
|
||||
"""
|
||||
@@ -200,69 +198,65 @@ class _SystemHandler:
|
||||
|
||||
if mode == "NoAction":
|
||||
|
||||
logger.info("不执行系统电源操作", module="系统服务")
|
||||
logger.info("不执行系统电源操作")
|
||||
|
||||
elif mode == "Shutdown":
|
||||
|
||||
self.kill_emulator_processes()
|
||||
logger.info("执行关机操作", module="系统服务")
|
||||
logger.info("执行关机操作")
|
||||
subprocess.run(["shutdown", "/s", "/t", "0"])
|
||||
|
||||
elif mode == "ShutdownForce":
|
||||
logger.info("执行强制关机操作", module="系统服务")
|
||||
logger.info("执行强制关机操作")
|
||||
subprocess.run(["shutdown", "/s", "/t", "0", "/f"])
|
||||
|
||||
elif mode == "Hibernate":
|
||||
|
||||
logger.info("执行休眠操作", module="系统服务")
|
||||
logger.info("执行休眠操作")
|
||||
subprocess.run(["shutdown", "/h"])
|
||||
|
||||
elif mode == "Sleep":
|
||||
|
||||
logger.info("执行睡眠操作", module="系统服务")
|
||||
logger.info("执行睡眠操作")
|
||||
subprocess.run(
|
||||
["rundll32.exe", "powrprof.dll,SetSuspendState", "0,1,0"]
|
||||
)
|
||||
|
||||
elif mode == "KillSelf":
|
||||
|
||||
logger.info("执行退出主程序操作", module="系统服务")
|
||||
Config.main_window.close()
|
||||
QApplication.quit()
|
||||
logger.info("执行退出主程序操作")
|
||||
sys.exit(0)
|
||||
|
||||
elif sys.platform.startswith("linux"):
|
||||
|
||||
if mode == "NoAction":
|
||||
|
||||
logger.info("不执行系统电源操作", module="系统服务")
|
||||
logger.info("不执行系统电源操作")
|
||||
|
||||
elif mode == "Shutdown":
|
||||
|
||||
logger.info("执行关机操作", module="系统服务")
|
||||
logger.info("执行关机操作")
|
||||
subprocess.run(["shutdown", "-h", "now"])
|
||||
|
||||
elif mode == "Hibernate":
|
||||
|
||||
logger.info("执行休眠操作", module="系统服务")
|
||||
logger.info("执行休眠操作")
|
||||
subprocess.run(["systemctl", "hibernate"])
|
||||
|
||||
elif mode == "Sleep":
|
||||
|
||||
logger.info("执行睡眠操作", module="系统服务")
|
||||
logger.info("执行睡眠操作")
|
||||
subprocess.run(["systemctl", "suspend"])
|
||||
|
||||
elif mode == "KillSelf":
|
||||
|
||||
logger.info("执行退出主程序操作", module="系统服务")
|
||||
Config.main_window.close()
|
||||
QApplication.quit()
|
||||
logger.info("执行退出主程序操作")
|
||||
sys.exit(0)
|
||||
|
||||
def kill_emulator_processes(self):
|
||||
"""这里暂时仅支持 MuMu 模拟器"""
|
||||
|
||||
logger.info("正在清除模拟器进程", module="系统服务")
|
||||
logger.info("正在清除模拟器进程")
|
||||
|
||||
keywords = ["Nemu", "nemu", "emulator", "MuMu"]
|
||||
for proc in psutil.process_iter(["pid", "name"]):
|
||||
@@ -277,7 +271,7 @@ class _SystemHandler:
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
|
||||
logger.success("模拟器进程清除完成", module="系统服务")
|
||||
logger.success("模拟器进程清除完成")
|
||||
|
||||
def is_startup(self) -> bool:
|
||||
"""判断程序是否已经开机自启"""
|
||||
@@ -292,7 +286,7 @@ class _SystemHandler:
|
||||
)
|
||||
return result.returncode == 0
|
||||
except Exception as e:
|
||||
logger.exception(f"检查任务计划程序失败: {e}", module="系统服务")
|
||||
logger.exception(f"检查任务计划程序失败: {e}")
|
||||
return False
|
||||
|
||||
def get_window_info(self) -> list:
|
||||
@@ -316,7 +310,7 @@ class _SystemHandler:
|
||||
:param path: 进程路径
|
||||
"""
|
||||
|
||||
logger.info(f"开始中止进程: {path}", module="系统服务")
|
||||
logger.info(f"开始中止进程: {path}")
|
||||
|
||||
for pid in self.search_pids(path):
|
||||
killprocess = subprocess.Popen(
|
||||
@@ -326,7 +320,7 @@ class _SystemHandler:
|
||||
)
|
||||
killprocess.wait()
|
||||
|
||||
logger.success(f"进程已中止: {path}", module="系统服务")
|
||||
logger.success(f"进程已中止: {path}")
|
||||
|
||||
def search_pids(self, path: Path) -> list:
|
||||
"""
|
||||
@@ -336,7 +330,7 @@ class _SystemHandler:
|
||||
:return: 匹配的进程PID列表
|
||||
"""
|
||||
|
||||
logger.info(f"开始查找进程 PID: {path}", module="系统服务")
|
||||
logger.info(f"开始查找进程 PID: {path}")
|
||||
|
||||
pids = []
|
||||
for proc in psutil.process_iter(["pid", "exe"]):
|
||||
|
||||
@@ -35,14 +35,18 @@ v4.4
|
||||
import time
|
||||
import json
|
||||
import hmac
|
||||
import asyncio
|
||||
import hashlib
|
||||
import requests
|
||||
from urllib import parse
|
||||
|
||||
from app.core import Config, logger
|
||||
from core import Config
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger("森空岛签到任务")
|
||||
|
||||
|
||||
def skland_sign_in(token) -> dict:
|
||||
async def skland_sign_in(token) -> dict:
|
||||
"""森空岛签到"""
|
||||
|
||||
app_code = "4ca99fa6b56cc2ba"
|
||||
@@ -127,7 +131,7 @@ def skland_sign_in(token) -> dict:
|
||||
v["cred"] = cred
|
||||
return v
|
||||
|
||||
def login_by_token(token_code):
|
||||
async def login_by_token(token_code):
|
||||
"""
|
||||
使用token一步步拿到cred和sign_token
|
||||
|
||||
@@ -140,10 +144,10 @@ def skland_sign_in(token) -> dict:
|
||||
token_code = t["data"]["content"]
|
||||
except:
|
||||
pass
|
||||
grant_code = get_grant_code(token_code)
|
||||
return get_cred(grant_code)
|
||||
grant_code = await get_grant_code(token_code)
|
||||
return await get_cred(grant_code)
|
||||
|
||||
def get_cred(grant):
|
||||
async def get_cred(grant):
|
||||
"""
|
||||
通过grant code获取cred和sign_token
|
||||
|
||||
@@ -155,10 +159,7 @@ def skland_sign_in(token) -> dict:
|
||||
cred_code_url,
|
||||
json={"code": grant, "kind": 1},
|
||||
headers=header_login,
|
||||
proxies={
|
||||
"http": Config.get(Config.update_ProxyAddress),
|
||||
"https": Config.get(Config.update_ProxyAddress),
|
||||
},
|
||||
proxies=Config.get_proxies(),
|
||||
).json()
|
||||
if rsp["code"] != 0:
|
||||
raise Exception(f'获得cred失败:{rsp.get("messgae")}')
|
||||
@@ -166,7 +167,7 @@ def skland_sign_in(token) -> dict:
|
||||
cred = rsp["data"]["cred"]
|
||||
return cred, sign_token
|
||||
|
||||
def get_grant_code(token):
|
||||
async def get_grant_code(token):
|
||||
"""
|
||||
通过token获取grant code
|
||||
|
||||
@@ -177,10 +178,7 @@ def skland_sign_in(token) -> dict:
|
||||
grant_code_url,
|
||||
json={"appCode": app_code, "token": token, "type": 0},
|
||||
headers=header_login,
|
||||
proxies={
|
||||
"http": Config.get(Config.update_ProxyAddress),
|
||||
"https": Config.get(Config.update_ProxyAddress),
|
||||
},
|
||||
proxies=Config.get_proxies(),
|
||||
).json()
|
||||
if rsp["status"] != 0:
|
||||
raise Exception(
|
||||
@@ -188,7 +186,7 @@ def skland_sign_in(token) -> dict:
|
||||
)
|
||||
return rsp["data"]["code"]
|
||||
|
||||
def get_binding_list(cred, sign_token):
|
||||
async def get_binding_list(cred, sign_token):
|
||||
"""
|
||||
查询已绑定的角色列表
|
||||
|
||||
@@ -202,21 +200,12 @@ def skland_sign_in(token) -> dict:
|
||||
headers=get_sign_header(
|
||||
binding_url, "get", None, copy_header(cred), sign_token
|
||||
),
|
||||
proxies={
|
||||
"http": Config.get(Config.update_ProxyAddress),
|
||||
"https": Config.get(Config.update_ProxyAddress),
|
||||
},
|
||||
proxies=Config.get_proxies(),
|
||||
).json()
|
||||
if rsp["code"] != 0:
|
||||
logger.error(
|
||||
f"森空岛服务 | 请求角色列表出现问题:{rsp['message']}",
|
||||
module="森空岛签到",
|
||||
)
|
||||
logger.error(f"请求角色列表出现问题:{rsp['message']}")
|
||||
if rsp.get("message") == "用户未登录":
|
||||
logger.error(
|
||||
f"森空岛服务 | 用户登录可能失效了,请重新登录!",
|
||||
module="森空岛签到",
|
||||
)
|
||||
logger.error(f"用户登录可能失效了,请重新登录!")
|
||||
return v
|
||||
# 只取明日方舟(arknights)的绑定账号
|
||||
for i in rsp["data"]["list"]:
|
||||
@@ -225,7 +214,7 @@ def skland_sign_in(token) -> dict:
|
||||
v.extend(i.get("bindingList"))
|
||||
return v
|
||||
|
||||
def do_sign(cred, sign_token) -> dict:
|
||||
async def do_sign(cred, sign_token) -> dict:
|
||||
"""
|
||||
对所有绑定的角色进行签到
|
||||
|
||||
@@ -234,7 +223,7 @@ def skland_sign_in(token) -> dict:
|
||||
:return: 签到结果字典
|
||||
"""
|
||||
|
||||
characters = get_binding_list(cred, sign_token)
|
||||
characters = await get_binding_list(cred, sign_token)
|
||||
result = {"成功": [], "重复": [], "失败": [], "总计": len(characters)}
|
||||
|
||||
for character in characters:
|
||||
@@ -249,10 +238,7 @@ def skland_sign_in(token) -> dict:
|
||||
sign_url, "post", body, copy_header(cred), sign_token
|
||||
),
|
||||
json=body,
|
||||
proxies={
|
||||
"http": Config.get(Config.update_ProxyAddress),
|
||||
"https": Config.get(Config.update_ProxyAddress),
|
||||
},
|
||||
proxies=Config.get_proxies(),
|
||||
).json()
|
||||
|
||||
if rsp["code"] != 0:
|
||||
@@ -276,10 +262,10 @@ def skland_sign_in(token) -> dict:
|
||||
# 主流程
|
||||
try:
|
||||
# 拿到cred和sign_token
|
||||
cred, sign_token = login_by_token(token)
|
||||
time.sleep(1)
|
||||
cred, sign_token = await login_by_token(token)
|
||||
await asyncio.sleep(1)
|
||||
# 依次签到
|
||||
return do_sign(cred, sign_token)
|
||||
return await do_sign(cred, sign_token)
|
||||
except Exception as e:
|
||||
logger.exception(f"森空岛服务 | 森空岛签到失败: {e}", module="森空岛签到")
|
||||
logger.exception(f"森空岛签到失败: {e}")
|
||||
return {"成功": [], "重复": [], "失败": [], "总计": 0}
|
||||
|
||||
@@ -18,12 +18,6 @@
|
||||
|
||||
# Contact: DLmaster_361@163.com
|
||||
|
||||
"""
|
||||
AUTO_MAA
|
||||
AUTO_MAA图像组件
|
||||
v4.4
|
||||
作者:ClozyA
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
@@ -56,10 +50,8 @@ class ImageUtils:
|
||||
"""
|
||||
如果图片大于max_size_mb,则压缩并覆盖原文件,返回原始路径(Path对象)
|
||||
"""
|
||||
if hasattr(Image, "Resampling"): # Pillow 9.1.0及以后
|
||||
RESAMPLE = Image.Resampling.LANCZOS
|
||||
else:
|
||||
RESAMPLE = Image.ANTIALIAS
|
||||
|
||||
RESAMPLE = Image.Resampling.LANCZOS # Pillow 9.1.0及以后
|
||||
|
||||
max_size = max_size_mb * 1024 * 1024
|
||||
if image_path.stat().st_size <= max_size:
|
||||
@@ -70,7 +62,7 @@ class ImageUtils:
|
||||
quality = 90 if suffix in [".jpg", ".jpeg"] else None
|
||||
step = 5
|
||||
|
||||
if suffix in [".jpg", ".jpeg"]:
|
||||
if quality is not None:
|
||||
while True:
|
||||
img.save(image_path, quality=quality, optimize=True)
|
||||
if image_path.stat().st_size <= max_size or quality <= 10:
|
||||
|
||||
@@ -19,36 +19,33 @@
|
||||
# Contact: DLmaster_361@163.com
|
||||
|
||||
|
||||
import asyncio
|
||||
import psutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PySide6.QtCore import QTimer, QObject, Signal
|
||||
|
||||
|
||||
class ProcessManager(QObject):
|
||||
class ProcessManager:
|
||||
"""进程监视器类,用于跟踪主进程及其所有子进程的状态"""
|
||||
|
||||
processClosed = Signal()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.main_pid = None
|
||||
self.tracked_pids = set()
|
||||
self.check_task = None
|
||||
|
||||
self.check_timer = QTimer()
|
||||
self.check_timer.timeout.connect(self.check_processes)
|
||||
|
||||
def open_process(self, path: Path, args: list = [], tracking_time: int = 60) -> int:
|
||||
async def open_process(
|
||||
self, path: Path, args: list = [], tracking_time: int = 60
|
||||
) -> None:
|
||||
"""
|
||||
启动一个新进程并返回其pid,并开始监视该进程
|
||||
|
||||
:param path: 可执行文件的路径
|
||||
:param args: 启动参数列表
|
||||
:param tracking_time: 子进程追踪持续时间(秒)
|
||||
:return: 新进程的PID
|
||||
Parameters
|
||||
----------
|
||||
path: 可执行文件的路径
|
||||
args: 启动参数列表
|
||||
tracking_time: 子进程追踪持续时间(秒)
|
||||
"""
|
||||
|
||||
process = subprocess.Popen(
|
||||
@@ -60,9 +57,9 @@ class ProcessManager(QObject):
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
self.start_monitoring(process.pid, tracking_time)
|
||||
await self.start_monitoring(process.pid, tracking_time)
|
||||
|
||||
def start_monitoring(self, pid: int, tracking_time: int = 60) -> None:
|
||||
async def start_monitoring(self, pid: int, tracking_time: int = 60) -> None:
|
||||
"""
|
||||
启动进程监视器,跟踪指定的主进程及其子进程
|
||||
|
||||
@@ -70,7 +67,7 @@ class ProcessManager(QObject):
|
||||
:param tracking_time: 子进程追踪持续时间(秒)
|
||||
"""
|
||||
|
||||
self.clear()
|
||||
await self.clear()
|
||||
|
||||
self.main_pid = pid
|
||||
self.tracking_time = tracking_time
|
||||
@@ -89,16 +86,16 @@ class ProcessManager(QObject):
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
|
||||
# 启动持续追踪机制
|
||||
self.start_time = datetime.now()
|
||||
self.check_timer.start(100)
|
||||
# 启动持续追踪任务
|
||||
if tracking_time > 0:
|
||||
self.check_task = asyncio.create_task(self.track_processes())
|
||||
await asyncio.sleep(tracking_time)
|
||||
await self.stop_tracking()
|
||||
|
||||
def check_processes(self) -> None:
|
||||
"""检查跟踪的进程是否仍在运行,并更新子进程列表"""
|
||||
|
||||
# 仅在时限内持续更新跟踪的进程列表,发现新的子进程
|
||||
if (datetime.now() - self.start_time).total_seconds() < self.tracking_time:
|
||||
async def track_processes(self) -> None:
|
||||
"""更新子进程列表"""
|
||||
|
||||
while True:
|
||||
current_pids = set(self.tracked_pids)
|
||||
for pid in current_pids:
|
||||
try:
|
||||
@@ -109,12 +106,19 @@ class ProcessManager(QObject):
|
||||
self.tracked_pids.add(child.pid)
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
if not self.is_running():
|
||||
self.clear()
|
||||
self.processClosed.emit()
|
||||
async def stop_tracking(self) -> None:
|
||||
"""停止更新子进程列表"""
|
||||
|
||||
def is_running(self) -> bool:
|
||||
if self.check_task and not self.check_task.done():
|
||||
self.check_task.cancel()
|
||||
try:
|
||||
await self.check_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
async def is_running(self) -> bool:
|
||||
"""检查所有跟踪的进程是否还在运行"""
|
||||
|
||||
for pid in self.tracked_pids:
|
||||
@@ -127,11 +131,9 @@ class ProcessManager(QObject):
|
||||
|
||||
return False
|
||||
|
||||
def kill(self, if_force: bool = False) -> None:
|
||||
async def kill(self, if_force: bool = False) -> None:
|
||||
"""停止监视器并中止所有跟踪的进程"""
|
||||
|
||||
self.check_timer.stop()
|
||||
|
||||
for pid in self.tracked_pids:
|
||||
try:
|
||||
proc = psutil.Process(pid)
|
||||
@@ -145,13 +147,11 @@ class ProcessManager(QObject):
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
|
||||
if self.main_pid:
|
||||
self.processClosed.emit()
|
||||
self.clear()
|
||||
await self.clear()
|
||||
|
||||
def clear(self) -> None:
|
||||
async def clear(self) -> None:
|
||||
"""清空跟踪的进程列表"""
|
||||
|
||||
await self.stop_tracking()
|
||||
self.main_pid = None
|
||||
self.check_timer.stop()
|
||||
self.tracked_pids.clear()
|
||||
|
||||
@@ -25,6 +25,14 @@ __license__ = "GPL-3.0 license"
|
||||
|
||||
|
||||
from .logger import get_logger
|
||||
from .ImageUtils import ImageUtils
|
||||
from .ProcessManager import ProcessManager
|
||||
from .security import dpapi_encrypt, dpapi_decrypt
|
||||
|
||||
__all__ = ["get_logger", "dpapi_encrypt", "dpapi_decrypt"]
|
||||
__all__ = [
|
||||
"get_logger",
|
||||
"ImageUtils",
|
||||
"ProcessManager",
|
||||
"dpapi_encrypt",
|
||||
"dpapi_decrypt",
|
||||
]
|
||||
|
||||
@@ -59,6 +59,7 @@ _logger = _logger.patch(lambda record: record["extra"].setdefault("module", "未
|
||||
def get_logger(module_name: str):
|
||||
"""
|
||||
获取一个绑定 module 名的日志器
|
||||
|
||||
:param module_name: 模块名称,如 "用户管理"
|
||||
:return: 绑定后的 logger
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user