refactor: 初步完成单一ws重构

This commit is contained in:
DLmaster361
2025-08-27 17:50:56 +08:00
parent 4c2a6407a1
commit fbcc149849
10 changed files with 454 additions and 342 deletions

View File

@@ -23,6 +23,7 @@ __version__ = "5.0.0"
__author__ = "DLmaster361 <DLmaster_361@163.com>"
__license__ = "GPL-3.0 license"
from .core import router as core_router
from .info import router as info_router
from .scripts import router as scripts_router
from .plan import router as plan_router
@@ -32,6 +33,7 @@ from .history import router as history_router
from .setting import router as setting_router
__all__ = [
"core_router",
"info_router",
"scripts_router",
"plan_router",

47
app/api/core.py Normal file
View File

@@ -0,0 +1,47 @@
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# Copyright © 2025 MoeSnowyFox
# 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
import asyncio
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from app.core import Config, Broadcast
from app.services import System
from app.models.schema import *
router = APIRouter(prefix="/api/core", tags=["核心信息"])
@router.websocket("/ws")
async def connect_websocket(websocket: WebSocket):
await websocket.accept()
Config.websocket = websocket
while True:
try:
data = await asyncio.wait_for(websocket.receive_json(), timeout=30.0)
await Broadcast.put(data)
except asyncio.TimeoutError:
await websocket.send_json(
WebSocketMessage(type="Signal", data={"Ping": "无描述"}).model_dump()
)
except WebSocketDisconnect:
break
await System.set_power("KillSelf")

View File

@@ -70,33 +70,3 @@ async def power_task(task: PowerIn = Body(...)) -> OutBase:
code=500, status="error", message=f"{type(e).__name__}: {str(e)}"
)
return OutBase()
@router.websocket("/ws/{websocketId}")
async def websocket_endpoint(
websocket: WebSocket,
websocketId: str = Path(..., description="要连接的WebSocket ID"),
):
await websocket.accept()
try:
uid = uuid.UUID(websocketId)
except ValueError:
await websocket.close(code=1008, reason="无效的WebSocket ID")
return
if uid in TaskManager.connection_events and uid not in TaskManager.websocket_dict:
TaskManager.websocket_dict[uid] = websocket
TaskManager.connection_events[uid].set()
while True:
try:
data = await asyncio.wait_for(websocket.receive_json(), timeout=30.0)
await Broadcast.put(data)
except asyncio.TimeoutError:
await websocket.send_json(
TaskMessage(type="Signal", data={"Ping": "无描述"}).model_dump()
)
except WebSocketDisconnect:
TaskManager.websocket_dict.pop(uid, None)
break
else:
await websocket.close(code=1008, reason="任务不存在或已结束")

View File

@@ -189,7 +189,7 @@ async def get_web_config() -> InfoOut:
@router.post(
"/get/overview", summary="信息总览", response_model=InfoOut, status_code=200
)
async def add_overview() -> InfoOut:
async def get_overview() -> InfoOut:
try:
stage = await Config.get_stage_info("Info")
proxy = await Config.get_proxy_overview()

View File

@@ -28,6 +28,7 @@ import calendar
import requests
import truststore
from pathlib import Path
from fastapi import WebSocket
from collections import defaultdict
from datetime import datetime, timedelta, date, timezone
from typing import Literal, Optional, Tuple
@@ -583,6 +584,7 @@ class AppConfig(GlobalConfig):
self.config_path.mkdir(parents=True, exist_ok=True)
self.history_path.mkdir(parents=True, exist_ok=True)
self.websocket: Optional[WebSocket] = None
self.silence_dict: Dict[Path, datetime] = {}
self.if_ignore_silence: List[uuid.UUID] = []
self.temp_task: List[asyncio.Task] = []
@@ -865,6 +867,13 @@ class AppConfig(GlobalConfig):
db.close()
logger.success("数据文件版本更新完成")
async def send_json(self, data: dict) -> None:
"""通过WebSocket发送JSON数据"""
if Config.websocket is None:
raise RuntimeError("WebSocket 未连接")
else:
await Config.websocket.send_json(data)
async def add_script(
self, script: Literal["MAA", "General"]
) -> tuple[uuid.UUID, ConfigBase]:

View File

@@ -21,12 +21,11 @@
import uuid
import asyncio
from fastapi import WebSocket
from functools import partial
from typing import Dict, Optional
from .config import Config, MaaConfig, GeneralConfig, QueueConfig
from app.models.schema import TaskMessage
from app.models.schema import WebSocketMessage
from app.utils import get_logger
from app.task import *
@@ -41,8 +40,6 @@ class _TaskManager:
super().__init__()
self.task_dict: Dict[uuid.UUID, asyncio.Task] = {}
self.connection_events: Dict[uuid.UUID, asyncio.Event] = {}
self.websocket_dict: Dict[uuid.UUID, WebSocket] = {}
async def add_task(self, mode: str, uid: str) -> uuid.UUID:
"""
@@ -99,34 +96,23 @@ class _TaskManager:
self, mode: str, task_id: uuid.UUID, actual_id: Optional[uuid.UUID]
):
# 等待连接信号
if task_id in self.connection_events:
self.connection_events[task_id].clear()
else:
self.connection_events[task_id] = asyncio.Event()
await self.connection_events[task_id].wait()
if task_id not in self.websocket_dict:
raise RuntimeError(f"The task {task_id} is not connected to a WebSocket.")
logger.info(f"开始运行任务:{task_id},模式:{mode}")
websocket = self.websocket_dict[task_id]
if mode == "设置脚本":
if isinstance(Config.ScriptConfig[task_id], MaaConfig):
task_item = MaaManager(mode, task_id, actual_id, websocket)
task_item = MaaManager(mode, task_id, actual_id, str(task_id))
elif isinstance(Config.ScriptConfig[task_id], GeneralConfig):
task_item = GeneralManager(mode, task_id, actual_id, websocket)
task_item = GeneralManager(mode, task_id, actual_id, str(task_id))
else:
logger.error(
f"不支持的脚本类型:{type(Config.ScriptConfig[task_id]).__name__}"
)
await websocket.send_json(
TaskMessage(
type="Info", data={"Error": "脚本类型不支持"}
await Config.send_json(
WebSocketMessage(
taskId=str(task_id),
type="Info",
data={"Error": "脚本类型不支持"},
).model_dump()
)
return
@@ -148,9 +134,11 @@ class _TaskManager:
logger.error(
f"不支持的队列类型:{type(Config.QueueConfig[task_id]).__name__}"
)
await websocket.send_json(
TaskMessage(
type="Info", data={"Error": "队列类型不支持"}
await Config.send_json(
WebSocketMessage(
taskId=str(task_id),
type="Info",
data={"Error": "队列类型不支持"},
).model_dump()
)
return
@@ -180,9 +168,11 @@ class _TaskManager:
if script_id in self.task_dict:
task["status"] = "跳过"
await websocket.send_json(
TaskMessage(
type="Update", data={"task_list": task_list}
await Config.send_json(
WebSocketMessage(
taskId=str(task_id),
type="Update",
data={"task_list": task_list},
).model_dump()
)
logger.info(f"跳过任务:{script_id},该任务已在运行列表中")
@@ -190,24 +180,28 @@ class _TaskManager:
# 标记为运行中
task["status"] = "运行"
await websocket.send_json(
TaskMessage(
type="Update", data={"task_list": task_list}
await Config.send_json(
WebSocketMessage(
taskId=str(task_id),
type="Update",
data={"task_list": task_list},
).model_dump()
)
logger.info(f"任务开始:{script_id}")
if isinstance(Config.ScriptConfig[script_id], MaaConfig):
task_item = MaaManager(mode, script_id, None, websocket)
elif isinstance(Config.ScriptConfig[task_id], GeneralConfig):
task_item = GeneralManager(mode, task_id, actual_id, websocket)
task_item = MaaManager(mode, script_id, None, str(task_id))
elif isinstance(Config.ScriptConfig[script_id], GeneralConfig):
task_item = GeneralManager(mode, script_id, actual_id, str(task_id))
else:
logger.error(
f"不支持的脚本类型:{type(Config.ScriptConfig[script_id]).__name__}"
)
await websocket.send_json(
TaskMessage(
type="Info", data={"Error": "脚本类型不支持"}
await Config.send_json(
WebSocketMessage(
taskId=str(task_id),
type="Info",
data={"Error": "脚本类型不支持"},
).model_dump()
)
continue
@@ -264,11 +258,11 @@ class _TaskManager:
logger.info(f"任务 {task_id} 已结束")
self.task_dict.pop(task_id)
websocket = self.websocket_dict.get(task_id, None)
if websocket:
await websocket.send_json(
TaskMessage(type="Signal", data={"Accomplish": "无描述"}).model_dump()
)
await Config.send_json(
WebSocketMessage(
taskId=str(task_id), type="Signal", data={"Accomplish": "无描述"}
).model_dump()
)
TaskManager = _TaskManager()

View File

@@ -71,73 +71,95 @@ class GlobalConfig_Function(BaseModel):
HistoryRetentionTime: Optional[Literal[7, 15, 30, 60, 90, 180, 365, 0]] = Field(
None, description="历史记录保留时间, 0表示永久保存"
)
IfAllowSleep: Optional[bool] = Field(None, description="允许休眠")
IfSilence: Optional[bool] = Field(None, description="静默模式")
BossKey: Optional[str] = Field(None, description="模拟器老板键")
IfAgreeBilibili: Optional[bool] = Field(None, description="同意哔哩哔哩用户协议")
IfAllowSleep: Optional[bool] = Field(default=None, description="允许休眠")
IfSilence: Optional[bool] = Field(default=None, description="静默模式")
BossKey: Optional[str] = Field(default=None, description="模拟器老板键")
IfAgreeBilibili: Optional[bool] = Field(
default=None, description="同意哔哩哔哩用户协议"
)
IfSkipMumuSplashAds: Optional[bool] = Field(
None, description="跳过Mumu模拟器启动广告"
default=None, description="跳过Mumu模拟器启动广告"
)
class GlobalConfig_Voice(BaseModel):
Enabled: Optional[bool] = Field(None, description="语音功能是否启用")
Enabled: Optional[bool] = Field(default=None, description="语音功能是否启用")
Type: Optional[Literal["simple", "noisy"]] = Field(
None, description="语音类型, simple为简洁, noisy为聒噪"
default=None, description="语音类型, simple为简洁, noisy为聒噪"
)
class GlobalConfig_Start(BaseModel):
IfSelfStart: Optional[bool] = Field(None, description="是否在系统启动时自动运行")
IfSelfStart: Optional[bool] = Field(
default=None, description="是否在系统启动时自动运行"
)
IfMinimizeDirectly: Optional[bool] = Field(
None, description="启动时是否直接最小化到托盘而不显示主窗口"
default=None, description="启动时是否直接最小化到托盘而不显示主窗口"
)
class GlobalConfig_UI(BaseModel):
IfShowTray: Optional[bool] = Field(None, description="是否常态显示托盘图标")
IfToTray: Optional[bool] = Field(None, description="是否最小化到托盘")
IfShowTray: Optional[bool] = Field(default=None, description="是否常态显示托盘图标")
IfToTray: Optional[bool] = Field(default=None, description="是否最小化到托盘")
class GlobalConfig_Notify(BaseModel):
SendTaskResultTime: Optional[Literal["不推送", "任何时刻", "仅失败时"]] = Field(
None, description="任务结果推送时机"
default=None, description="任务结果推送时机"
)
IfSendStatistic: Optional[bool] = Field(None, description="是否发送统计信息")
IfSendSixStar: Optional[bool] = Field(None, description="是否发送公招六星通知")
IfPushPlyer: Optional[bool] = Field(None, description="是否推送系统通知")
IfSendMail: Optional[bool] = Field(None, description="是否发送邮件通知")
SMTPServerAddress: Optional[str] = Field(None, description="SMTP服务器地址")
AuthorizationCode: Optional[str] = Field(None, description="SMTP授权码")
FromAddress: Optional[str] = Field(None, description="邮件发送地址")
ToAddress: Optional[str] = Field(None, description="邮件接收地址")
IfServerChan: Optional[bool] = Field(None, description="是否使用ServerChan推送")
ServerChanKey: Optional[str] = Field(None, description="ServerChan推送密钥")
IfSendStatistic: Optional[bool] = Field(
default=None, description="是否发送统计信息"
)
IfSendSixStar: Optional[bool] = Field(
default=None, description="是否发送公招六星通知"
)
IfPushPlyer: Optional[bool] = Field(default=None, description="是否推送系统通知")
IfSendMail: Optional[bool] = Field(default=None, description="是否发送邮件通知")
SMTPServerAddress: Optional[str] = Field(default=None, description="SMTP服务器地址")
AuthorizationCode: Optional[str] = Field(default=None, description="SMTP授权码")
FromAddress: Optional[str] = Field(default=None, description="邮件发送地址")
ToAddress: Optional[str] = Field(default=None, description="邮件接收地址")
IfServerChan: Optional[bool] = Field(
default=None, description="是否使用ServerChan推送"
)
ServerChanKey: Optional[str] = Field(default=None, description="ServerChan推送密钥")
IfCompanyWebHookBot: Optional[bool] = Field(
None, description="是否使用企微Webhook推送"
default=None, description="是否使用企微Webhook推送"
)
CompanyWebHookBotUrl: Optional[str] = Field(
default=None, description="企微Webhook Bot URL"
)
CompanyWebHookBotUrl: Optional[str] = Field(None, description="企微Webhook Bot URL")
class GlobalConfig_Update(BaseModel):
IfAutoUpdate: Optional[bool] = Field(None, description="是否自动更新")
IfAutoUpdate: Optional[bool] = Field(default=None, description="是否自动更新")
UpdateType: Optional[Literal["stable", "beta"]] = Field(
None, description="更新类型, stable为稳定版, beta为测试版"
default=None, description="更新类型, stable为稳定版, beta为测试版"
)
Source: Optional[Literal["GitHub", "MirrorChyan", "AutoSite"]] = Field(
None, description="更新源: GitHub源, Mirror酱源, 自建源"
default=None, description="更新源: GitHub源, Mirror酱源, 自建源"
)
ProxyAddress: Optional[str] = Field(None, description="网络代理地址")
MirrorChyanCDK: Optional[str] = Field(None, description="Mirror酱CDK")
ProxyAddress: Optional[str] = Field(default=None, description="网络代理地址")
MirrorChyanCDK: Optional[str] = Field(default=None, description="Mirror酱CDK")
class GlobalConfig(BaseModel):
Function: Optional[GlobalConfig_Function] = Field(None, description="功能相关配置")
Voice: Optional[GlobalConfig_Voice] = Field(None, description="语音相关配置")
Start: Optional[GlobalConfig_Start] = Field(None, description="启动相关配置")
UI: Optional[GlobalConfig_UI] = Field(None, description="界面相关配置")
Notify: Optional[GlobalConfig_Notify] = Field(None, description="通知相关配置")
Update: Optional[GlobalConfig_Update] = Field(None, description="更新相关配置")
Function: Optional[GlobalConfig_Function] = Field(
default=None, description="功能相关配置"
)
Voice: Optional[GlobalConfig_Voice] = Field(
default=None, description="语音相关配置"
)
Start: Optional[GlobalConfig_Start] = Field(
default=None, description="启动相关配置"
)
UI: Optional[GlobalConfig_UI] = Field(default=None, description="界面相关配置")
Notify: Optional[GlobalConfig_Notify] = Field(
default=None, description="通知相关配置"
)
Update: Optional[GlobalConfig_Update] = Field(
default=None, description="更新相关配置"
)
class QueueIndexItem(BaseModel):
@@ -157,36 +179,36 @@ class TimeSetIndexItem(BaseModel):
class QueueItem_Info(BaseModel):
ScriptId: Optional[str] = Field(
None, description="任务所对应的脚本ID, 为None时表示未选择"
default=None, description="任务所对应的脚本ID, 为None时表示未选择"
)
class QueueItem(BaseModel):
Info: Optional[QueueItem_Info] = Field(None, description="队列项")
Info: Optional[QueueItem_Info] = Field(default=None, description="队列项")
class TimeSet_Info(BaseModel):
Enabled: Optional[bool] = Field(None, description="是否启用")
Time: Optional[str] = Field(None, description="时间设置, 格式为HH:MM")
Enabled: Optional[bool] = Field(default=None, description="是否启用")
Time: Optional[str] = Field(default=None, description="时间设置, 格式为HH:MM")
class TimeSet(BaseModel):
Info: Optional[TimeSet_Info] = Field(None, description="时间项")
Info: Optional[TimeSet_Info] = Field(default=None, description="时间项")
class QueueConfig_Info(BaseModel):
Name: Optional[str] = Field(None, description="队列名称")
TimeEnabled: Optional[bool] = Field(None, description="是否启用定时")
StartUpEnabled: Optional[bool] = Field(None, description="是否启动时运行")
Name: Optional[str] = Field(default=None, description="队列名称")
TimeEnabled: Optional[bool] = Field(default=None, description="是否启用定时")
StartUpEnabled: Optional[bool] = Field(default=None, description="是否启动时运行")
AfterAccomplish: Optional[
Literal[
"NoAction", "KillSelf", "Sleep", "Hibernate", "Shutdown", "ShutdownForce"
]
] = Field(None, description="完成后操作")
] = Field(default=None, description="完成后操作")
class QueueConfig(BaseModel):
Info: Optional[QueueConfig_Info] = Field(None, description="队列信息")
Info: Optional[QueueConfig_Info] = Field(default=None, description="队列信息")
class ScriptIndexItem(BaseModel):
@@ -202,15 +224,17 @@ class UserIndexItem(BaseModel):
class MaaUserConfig_Info(BaseModel):
Name: Optional[str] = Field(None, description="用户名")
Id: Optional[str] = Field(None, description="用户ID")
Mode: Optional[Literal["简洁", "详细"]] = Field(None, description="用户配置模式")
StageMode: Optional[str] = Field(None, description="关卡配置模式")
Name: Optional[str] = Field(default=None, description="用户名")
Id: Optional[str] = Field(default=None, description="用户ID")
Mode: Optional[Literal["简洁", "详细"]] = Field(
default=None, description="用户配置模式"
)
StageMode: Optional[str] = Field(default=None, description="关卡配置模式")
Server: Optional[
Literal["Official", "Bilibili", "YoStarEN", "YoStarJP", "YoStarKR", "txwy"]
] = Field(None, description="服务器")
Status: Optional[bool] = Field(None, description="用户状态")
RemainedDay: Optional[int] = Field(None, description="剩余天数")
] = Field(default=None, description="服务器")
Status: Optional[bool] = Field(default=None, description="用户状态")
RemainedDay: Optional[int] = Field(default=None, description="剩余天数")
Annihilation: Optional[
Literal[
"Close",
@@ -219,164 +243,186 @@ class MaaUserConfig_Info(BaseModel):
"LungmenOutskirts@Annihilation",
"LungmenDowntown@Annihilation",
]
] = Field(None, description="剿灭模式")
Routine: Optional[bool] = Field(None, description="是否启用日常")
] = Field(default=None, description="剿灭模式")
Routine: Optional[bool] = Field(default=None, description="是否启用日常")
InfrastMode: Optional[Literal["Normal", "Rotation", "Custom"]] = Field(
None, description="基建模式"
default=None, description="基建模式"
)
Password: Optional[str] = Field(None, description="密码")
Notes: Optional[str] = Field(None, description="备注")
MedicineNumb: Optional[int] = Field(None, description="吃理智药数量")
Password: Optional[str] = Field(default=None, description="密码")
Notes: Optional[str] = Field(default=None, description="备注")
MedicineNumb: Optional[int] = Field(default=None, description="吃理智药数量")
SeriesNumb: Optional[Literal["0", "6", "5", "4", "3", "2", "1", "-1"]] = Field(
None, description="连战次数"
default=None, description="连战次数"
)
Stage: Optional[str] = Field(None, description="关卡选择")
Stage_1: Optional[str] = Field(None, description="备选关卡 - 1")
Stage_2: Optional[str] = Field(None, description="备选关卡 - 2")
Stage_3: Optional[str] = Field(None, description="备选关卡 - 3")
Stage_Remain: Optional[str] = Field(None, description="剩余理智关卡")
IfSkland: Optional[bool] = Field(None, description="是否启用森空岛签到")
SklandToken: Optional[str] = Field(None, description="SklandToken")
Stage: Optional[str] = Field(default=None, description="关卡选择")
Stage_1: Optional[str] = Field(default=None, description="备选关卡 - 1")
Stage_2: Optional[str] = Field(default=None, description="备选关卡 - 2")
Stage_3: Optional[str] = Field(default=None, description="备选关卡 - 3")
Stage_Remain: Optional[str] = Field(default=None, description="剩余理智关卡")
IfSkland: Optional[bool] = Field(default=None, description="是否启用森空岛签到")
SklandToken: Optional[str] = Field(default=None, description="SklandToken")
class MaaUserConfig_Data(BaseModel):
LastProxyDate: Optional[str] = Field(None, description="上次代理日期")
LastAnnihilationDate: Optional[str] = Field(None, description="上次剿灭日期")
LastSklandDate: Optional[str] = Field(None, description="上次森空岛签到日期")
ProxyTimes: Optional[int] = Field(None, description="代理次数")
IfPassCheck: Optional[bool] = Field(None, description="是否通过人工排查")
LastProxyDate: Optional[str] = Field(default=None, description="上次代理日期")
LastAnnihilationDate: Optional[str] = Field(
default=None, description="上次剿灭日期"
)
LastSklandDate: Optional[str] = Field(
default=None, description="上次森空岛签到日期"
)
ProxyTimes: Optional[int] = Field(default=None, description="代理次数")
IfPassCheck: Optional[bool] = Field(default=None, description="是否通过人工排查")
class MaaUserConfig_Task(BaseModel):
IfWakeUp: Optional[bool] = Field(None, description="开始唤醒")
IfRecruiting: Optional[bool] = Field(None, description="自动公招")
IfBase: Optional[bool] = Field(None, description="基建换班")
IfCombat: Optional[bool] = Field(None, description="刷理智")
IfMall: Optional[bool] = Field(None, description="获取信用及购物")
IfMission: Optional[bool] = Field(None, description="领取奖励")
IfAutoRoguelike: Optional[bool] = Field(None, description="自动肉鸽")
IfReclamation: Optional[bool] = Field(None, description="生息演算")
IfWakeUp: Optional[bool] = Field(default=None, description="开始唤醒")
IfRecruiting: Optional[bool] = Field(default=None, description="自动公招")
IfBase: Optional[bool] = Field(default=None, description="基建换班")
IfCombat: Optional[bool] = Field(default=None, description="刷理智")
IfMall: Optional[bool] = Field(default=None, description="获取信用及购物")
IfMission: Optional[bool] = Field(default=None, description="领取奖励")
IfAutoRoguelike: Optional[bool] = Field(default=None, description="自动肉鸽")
IfReclamation: Optional[bool] = Field(default=None, description="生息演算")
class UserConfig_Notify(BaseModel):
Enabled: Optional[bool] = Field(None, description="是否启用通知")
IfSendStatistic: Optional[bool] = Field(None, description="是否发送统计信息")
IfSendSixStar: Optional[bool] = Field(None, description="是否发送高资喜报")
IfSendMail: Optional[bool] = Field(None, description="是否发送邮件通知")
ToAddress: Optional[str] = Field(None, description="邮件接收地址")
IfServerChan: Optional[bool] = Field(None, description="是否使用Server酱推送")
ServerChanKey: Optional[str] = Field(None, description="ServerChanKey")
IfCompanyWebHookBot: Optional[bool] = Field(None, description="是否使用Webhook推送")
CompanyWebHookBotUrl: Optional[str] = Field(None, description="企微Webhook Bot URL")
Enabled: Optional[bool] = Field(default=None, description="是否启用通知")
IfSendStatistic: Optional[bool] = Field(
default=None, description="是否发送统计信息"
)
IfSendSixStar: Optional[bool] = Field(default=None, description="是否发送高资喜报")
IfSendMail: Optional[bool] = Field(default=None, description="是否发送邮件通知")
ToAddress: Optional[str] = Field(default=None, description="邮件接收地址")
IfServerChan: Optional[bool] = Field(
default=None, description="是否使用Server酱推送"
)
ServerChanKey: Optional[str] = Field(default=None, description="ServerChanKey")
IfCompanyWebHookBot: Optional[bool] = Field(
default=None, description="是否使用Webhook推送"
)
CompanyWebHookBotUrl: Optional[str] = Field(
default=None, description="企微Webhook Bot URL"
)
class MaaUserConfig(BaseModel):
Info: Optional[MaaUserConfig_Info] = Field(None, description="基础信息")
Data: Optional[MaaUserConfig_Data] = Field(None, description="用户数据")
Task: Optional[MaaUserConfig_Task] = Field(None, description="任务列表")
Notify: Optional[UserConfig_Notify] = Field(None, description="单独通知")
Info: Optional[MaaUserConfig_Info] = Field(default=None, description="基础信息")
Data: Optional[MaaUserConfig_Data] = Field(default=None, description="用户数据")
Task: Optional[MaaUserConfig_Task] = Field(default=None, description="任务列表")
Notify: Optional[UserConfig_Notify] = Field(default=None, description="单独通知")
class MaaConfig_Info(BaseModel):
Name: Optional[str] = Field(None, description="脚本名称")
Path: Optional[str] = Field(None, description="脚本路径")
Name: Optional[str] = Field(default=None, description="脚本名称")
Path: Optional[str] = Field(default=None, description="脚本路径")
class MaaConfig_Run(BaseModel):
TaskTransitionMethod: Optional[Literal["NoAction", "ExitGame", "ExitEmulator"]] = (
Field(None, description="简洁任务间切换方式")
Field(default=None, description="简洁任务间切换方式")
)
ProxyTimesLimit: Optional[int] = Field(None, description="每日代理次数限制")
ADBSearchRange: Optional[int] = Field(None, description="ADB端口搜索范围")
RunTimesLimit: Optional[int] = Field(None, description="重试次数限制")
AnnihilationTimeLimit: Optional[int] = Field(None, description="剿灭超时限制")
RoutineTimeLimit: Optional[int] = Field(None, description="日常超时限制")
ProxyTimesLimit: Optional[int] = Field(default=None, description="每日代理次数限制")
ADBSearchRange: Optional[int] = Field(default=None, description="ADB端口搜索范围")
RunTimesLimit: Optional[int] = Field(default=None, description="重试次数限制")
AnnihilationTimeLimit: Optional[int] = Field(
default=None, description="剿灭超时限制"
)
RoutineTimeLimit: Optional[int] = Field(default=None, description="日常超时限制")
AnnihilationWeeklyLimit: Optional[bool] = Field(
None, description="剿灭每周仅代理至上限"
default=None, description="剿灭每周仅代理至上限"
)
class MaaConfig(BaseModel):
Info: Optional[MaaConfig_Info] = Field(None, description="脚本基础信息")
Run: Optional[MaaConfig_Run] = Field(None, description="脚本运行配置")
Info: Optional[MaaConfig_Info] = Field(default=None, description="脚本基础信息")
Run: Optional[MaaConfig_Run] = Field(default=None, description="脚本运行配置")
class GeneralUserConfig_Info(BaseModel):
Name: Optional[str] = Field(None, description="用户名")
Status: Optional[bool] = Field(None, description="用户状态")
RemainedDay: Optional[int] = Field(None, description="剩余天数")
IfScriptBeforeTask: Optional[bool] = Field(None, description="是否在任务前执行脚本")
ScriptBeforeTask: Optional[str] = Field(None, description="任务前脚本路径")
IfScriptAfterTask: Optional[bool] = Field(None, description="是否在任务后执行脚本")
ScriptAfterTask: Optional[str] = Field(None, description="任务脚本路径")
Notes: Optional[str] = Field(None, description="备注")
Name: Optional[str] = Field(default=None, description="用户名")
Status: Optional[bool] = Field(default=None, description="用户状态")
RemainedDay: Optional[int] = Field(default=None, description="剩余天数")
IfScriptBeforeTask: Optional[bool] = Field(
default=None, description="是否在任务前执行脚本"
)
ScriptBeforeTask: Optional[str] = Field(default=None, description="任务脚本路径")
IfScriptAfterTask: Optional[bool] = Field(
default=None, description="是否在任务后执行脚本"
)
ScriptAfterTask: Optional[str] = Field(default=None, description="任务后脚本路径")
Notes: Optional[str] = Field(default=None, description="备注")
class GeneralUserConfig_Data(BaseModel):
LastProxyDate: Optional[str] = Field(None, description="上次代理日期")
ProxyTimes: Optional[int] = Field(None, description="代理次数")
LastProxyDate: Optional[str] = Field(default=None, description="上次代理日期")
ProxyTimes: Optional[int] = Field(default=None, description="代理次数")
class GeneralUserConfig(BaseModel):
Info: Optional[GeneralUserConfig_Info] = Field(None, description="用户信息")
Data: Optional[GeneralUserConfig_Data] = Field(None, description="用户数据")
Notify: Optional[UserConfig_Notify] = Field(None, description="单独通知")
Info: Optional[GeneralUserConfig_Info] = Field(default=None, description="用户信息")
Data: Optional[GeneralUserConfig_Data] = Field(default=None, description="用户数据")
Notify: Optional[UserConfig_Notify] = Field(default=None, description="单独通知")
class GeneralConfig_Info(BaseModel):
Name: Optional[str] = Field(None, description="脚本名称")
RootPath: Optional[str] = Field(None, description="脚本根目录")
Name: Optional[str] = Field(default=None, description="脚本名称")
RootPath: Optional[str] = Field(default=None, description="脚本根目录")
class GeneralConfig_Script(BaseModel):
ScriptPath: Optional[str] = Field(None, description="脚本可执行文件路径")
Arguments: Optional[str] = Field(None, description="脚本启动附加命令参数")
IfTrackProcess: Optional[bool] = Field(None, description="是否追踪脚本子进程")
ConfigPath: Optional[str] = Field(None, description="配置文件路径")
ScriptPath: Optional[str] = Field(default=None, description="脚本可执行文件路径")
Arguments: Optional[str] = Field(default=None, description="脚本启动附加命令参数")
IfTrackProcess: Optional[bool] = Field(
default=None, description="是否追踪脚本子进程"
)
ConfigPath: Optional[str] = Field(default=None, description="配置文件路径")
ConfigPathMode: Optional[Literal["File", "Folder"]] = Field(
None, description="配置文件类型: 单个文件, 文件夹"
default=None, description="配置文件类型: 单个文件, 文件夹"
)
UpdateConfigMode: Optional[Literal["Never", "Success", "Failure", "Always"]] = (
Field(
None,
default=None,
description="更新配置时机, 从不, 仅成功时, 仅失败时, 任务结束时",
)
)
LogPath: Optional[str] = Field(None, description="日志文件路径")
LogPathFormat: Optional[str] = Field(None, description="日志文件名格式")
LogTimeStart: Optional[int] = Field(None, description="日志时间戳开始位置")
LogTimeEnd: Optional[int] = Field(None, description="日志时间戳结束位置")
LogTimeFormat: Optional[str] = Field(None, description="日志时间戳格式")
SuccessLog: Optional[str] = Field(None, description="成功时日志")
ErrorLog: Optional[str] = Field(None, description="错误时日志")
LogPath: Optional[str] = Field(default=None, description="日志文件路径")
LogPathFormat: Optional[str] = Field(default=None, description="日志文件名格式")
LogTimeStart: Optional[int] = Field(default=None, description="日志时间戳开始位置")
LogTimeEnd: Optional[int] = Field(default=None, description="日志时间戳结束位置")
LogTimeFormat: Optional[str] = Field(default=None, description="日志时间戳格式")
SuccessLog: Optional[str] = Field(default=None, description="成功时日志")
ErrorLog: Optional[str] = Field(default=None, description="错误时日志")
class GeneralConfig_Game(BaseModel):
Enabled: Optional[bool] = Field(None, description="游戏/模拟器相关功能是否启用")
Type: Optional[Literal["Emulator", "Client"]] = Field(
None, description="类型: 模拟器, PC端"
Enabled: Optional[bool] = Field(
default=None, description="游戏/模拟器相关功能是否启用"
)
Path: Optional[str] = Field(None, description="游戏/模拟器程序路径")
Arguments: Optional[str] = Field(None, description="游戏/模拟器启动参数")
WaitTime: Optional[int] = Field(None, description="游戏/模拟器等待启动时间")
Type: Optional[Literal["Emulator", "Client"]] = Field(
default=None, description="类型: 模拟器, PC端"
)
Path: Optional[str] = Field(default=None, description="游戏/模拟器程序路径")
Arguments: Optional[str] = Field(default=None, description="游戏/模拟器启动参数")
WaitTime: Optional[int] = Field(default=None, description="游戏/模拟器等待启动时间")
IfForceClose: Optional[bool] = Field(
None, description="是否强制关闭游戏/模拟器进程"
default=None, description="是否强制关闭游戏/模拟器进程"
)
class GeneralConfig_Run(BaseModel):
ProxyTimesLimit: Optional[int] = Field(None, description="每日代理次数限制")
RunTimesLimit: Optional[int] = Field(None, description="重试次数限制")
RunTimeLimit: Optional[int] = Field(None, description="日志超时限制")
ProxyTimesLimit: Optional[int] = Field(default=None, description="每日代理次数限制")
RunTimesLimit: Optional[int] = Field(default=None, description="重试次数限制")
RunTimeLimit: Optional[int] = Field(default=None, description="日志超时限制")
class GeneralConfig(BaseModel):
Info: Optional[GeneralConfig_Info] = Field(None, description="脚本基础信息")
Script: Optional[GeneralConfig_Script] = Field(None, description="脚本配置")
Game: Optional[GeneralConfig_Game] = Field(None, description="游戏配置")
Run: Optional[GeneralConfig_Run] = Field(None, description="运行配置")
Info: Optional[GeneralConfig_Info] = Field(default=None, description="脚本基础信息")
Script: Optional[GeneralConfig_Script] = Field(default=None, description="脚本配置")
Game: Optional[GeneralConfig_Game] = Field(default=None, description="游戏配置")
Run: Optional[GeneralConfig_Run] = Field(default=None, description="运行配置")
class PlanIndexItem(BaseModel):
@@ -385,33 +431,35 @@ class PlanIndexItem(BaseModel):
class MaaPlanConfig_Info(BaseModel):
Name: Optional[str] = Field(None, description="计划表名称")
Mode: Optional[Literal["ALL", "Weekly"]] = Field(None, description="计划表模式")
Name: Optional[str] = Field(default=None, description="计划表名称")
Mode: Optional[Literal["ALL", "Weekly"]] = Field(
default=None, description="计划表模式"
)
class MaaPlanConfig_Item(BaseModel):
MedicineNumb: Optional[int] = Field(None, description="吃理智药")
MedicineNumb: Optional[int] = Field(default=None, description="吃理智药")
SeriesNumb: Optional[Literal["0", "6", "5", "4", "3", "2", "1", "-1"]] = Field(
None, description="连战次数"
)
Stage: Optional[str] = Field(None, description="关卡选择")
Stage_1: Optional[str] = Field(None, description="备选关卡 - 1")
Stage_2: Optional[str] = Field(None, description="备选关卡 - 2")
Stage_3: Optional[str] = Field(None, description="备选关卡 - 3")
Stage_Remain: Optional[str] = Field(None, description="剩余理智关卡")
Stage: Optional[str] = Field(default=None, description="关卡选择")
Stage_1: Optional[str] = Field(default=None, description="备选关卡 - 1")
Stage_2: Optional[str] = Field(default=None, description="备选关卡 - 2")
Stage_3: Optional[str] = Field(default=None, description="备选关卡 - 3")
Stage_Remain: Optional[str] = Field(default=None, description="剩余理智关卡")
class MaaPlanConfig(BaseModel):
Info: Optional[MaaPlanConfig_Info] = Field(None, description="基础信息")
ALL: Optional[MaaPlanConfig_Item] = Field(None, description="全局")
Monday: Optional[MaaPlanConfig_Item] = Field(None, description="周一")
Tuesday: Optional[MaaPlanConfig_Item] = Field(None, description="周二")
Wednesday: Optional[MaaPlanConfig_Item] = Field(None, description="周三")
Thursday: Optional[MaaPlanConfig_Item] = Field(None, description="周四")
Friday: Optional[MaaPlanConfig_Item] = Field(None, description="周五")
Saturday: Optional[MaaPlanConfig_Item] = Field(None, description="周六")
Sunday: Optional[MaaPlanConfig_Item] = Field(None, description="周日")
Info: Optional[MaaPlanConfig_Info] = Field(default=None, description="基础信息")
ALL: Optional[MaaPlanConfig_Item] = Field(default=None, description="全局")
Monday: Optional[MaaPlanConfig_Item] = Field(default=None, description="周一")
Tuesday: Optional[MaaPlanConfig_Item] = Field(default=None, description="周二")
Wednesday: Optional[MaaPlanConfig_Item] = Field(default=None, description="周三")
Thursday: Optional[MaaPlanConfig_Item] = Field(default=None, description="周四")
Friday: Optional[MaaPlanConfig_Item] = Field(default=None, description="周五")
Saturday: Optional[MaaPlanConfig_Item] = Field(default=None, description="周六")
Sunday: Optional[MaaPlanConfig_Item] = Field(default=None, description="周日")
class HistoryIndexItem(BaseModel):
@@ -422,19 +470,20 @@ class HistoryIndexItem(BaseModel):
class HistoryData(BaseModel):
index: Optional[List[HistoryIndexItem]] = Field(
None, description="历史记录索引列表"
default=None, description="历史记录索引列表"
)
recruit_statistics: Optional[Dict[str, int]] = Field(
None, description="公招统计数据, key为星级, value为对应的公招数量"
default=None, description="公招统计数据, key为星级, value为对应的公招数量"
)
drop_statistics: Optional[Dict[str, Dict[str, int]]] = Field(
None, description="掉落统计数据, 格式为 { '关卡号': { '掉落物': 数量 } }"
default=None,
description="掉落统计数据, 格式为 { '关卡号': { '掉落物': 数量 } }",
)
error_info: Optional[Dict[str, str]] = Field(
None, description="报错信息, key为时间戳, value为错误描述"
default=None, description="报错信息, key为时间戳, value为错误描述"
)
log_content: Optional[str] = Field(
None, description="日志内容, 仅在提取单条历史记录数据时返回"
default=None, description="日志内容, 仅在提取单条历史记录数据时返回"
)
@@ -451,7 +500,7 @@ class ScriptCreateOut(OutBase):
class ScriptGetIn(BaseModel):
scriptId: Optional[str] = Field(
None, description="脚本ID, 未携带时表示获取所有脚本数据"
default=None, description="脚本ID, 未携带时表示获取所有脚本数据"
)
@@ -498,7 +547,7 @@ class UserInBase(BaseModel):
class UserGetIn(UserInBase):
userId: Optional[str] = Field(
None, description="用户ID, 未携带时表示获取所有用户数据"
default=None, description="用户ID, 未携带时表示获取所有用户数据"
)
@@ -547,7 +596,7 @@ class PlanCreateOut(OutBase):
class PlanGetIn(BaseModel):
planId: Optional[str] = Field(
None, description="计划ID, 未携带时表示获取所有计划数据"
default=None, description="计划ID, 未携带时表示获取所有计划数据"
)
@@ -576,7 +625,7 @@ class QueueCreateOut(OutBase):
class QueueGetIn(BaseModel):
queueId: Optional[str] = Field(
None, description="队列ID, 未携带时表示获取所有队列数据"
default=None, description="队列ID, 未携带时表示获取所有队列数据"
)
@@ -606,7 +655,7 @@ class QueueSetInBase(BaseModel):
class TimeSetGetIn(QueueSetInBase):
timeSetId: Optional[str] = Field(
None, description="时间设置ID, 未携带时表示获取所有时间设置数据"
default=None, description="时间设置ID, 未携带时表示获取所有时间设置数据"
)
@@ -637,7 +686,7 @@ class TimeSetReorderIn(QueueSetInBase):
class QueueItemGetIn(QueueSetInBase):
queueItemId: Optional[str] = Field(
None, description="队列项ID, 未携带时表示获取所有队列项数据"
default=None, description="队列项ID, 未携带时表示获取所有队列项数据"
)
@@ -683,7 +732,10 @@ class TaskCreateOut(OutBase):
websocketId: str = Field(..., description="新创建的任务ID")
class TaskMessage(BaseModel):
class WebSocketMessage(BaseModel):
taskId: Optional[str] = Field(
default=None, description="任务ID, 不存在时表示消息来自主程序"
)
type: Literal["Update", "Message", "Info", "Signal"] = Field(
...,
description="消息类型 Update: 更新数据, Message: 请求弹出对话框, Info: 需要在UI显示的消息, Signal: 程序信号",

View File

@@ -26,13 +26,12 @@ import subprocess
import shutil
import win32com.client
from pathlib import Path
from fastapi import WebSocket
from datetime import datetime, timedelta
from jinja2 import Environment, FileSystemLoader
from typing import List, Dict, Optional
from app.core import Broadcast, Config, MaaConfig, MaaUserConfig
from app.models.schema import TaskMessage
from app.models.schema import WebSocketMessage
from app.models.ConfigBase import MultipleConfig
from app.services import Notify, System
from app.utils import get_logger, LogMonitor, ProcessManager
@@ -50,18 +49,14 @@ class MaaManager:
"""MAA控制器"""
def __init__(
self,
mode: str,
script_id: uuid.UUID,
user_id: Optional[uuid.UUID],
websocket: WebSocket,
self, mode: str, script_id: uuid.UUID, user_id: Optional[uuid.UUID], ws_id: str
):
super().__init__()
self.mode = mode
self.script_id = script_id
self.user_id = user_id
self.websocket = websocket
self.ws_id = ws_id
self.emulator_process_manager = ProcessManager()
self.maa_process_manager = ProcessManager()
@@ -123,8 +118,10 @@ class MaaManager:
self.check_result = self.check_config()
if self.check_result != "Success!":
logger.error(f"未通过配置检查:{self.check_result}")
await self.websocket.send_json(
TaskMessage(type="Info", data={"Error": self.check_result}).model_dump()
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id, type="Info", data={"Error": self.check_result}
).model_dump()
)
return
@@ -188,16 +185,20 @@ class MaaManager:
< self.script_config.get("Run", "ProxyTimesLimit")
):
user["status"] = "运行"
await self.websocket.send_json(
TaskMessage(
type="Update", data={"user_list": self.user_list}
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"user_list": self.user_list},
).model_dump()
)
else:
user["status"] = "跳过"
await self.websocket.send_json(
TaskMessage(
type="Update", data={"user_list": self.user_list}
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"user_list": self.user_list},
).model_dump()
)
continue
@@ -228,8 +229,9 @@ class MaaManager:
"Data", "LastSklandDate"
) != datetime.now().strftime("%Y-%m-%d"):
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"log": "正在执行森空岛签到中\n请稍候~"},
).model_dump()
@@ -246,8 +248,9 @@ class MaaManager:
logger.info(
f"用户: {user['user_id']} - 森空岛签到{type}: {''.join(user_list)}",
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={
(
@@ -258,8 +261,9 @@ class MaaManager:
)
if skland_result["总计"] == 0:
logger.info(f"用户: {user['user_id']} - 森空岛签到失败")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={
"Error": f"用户 {user['name']} 森空岛签到失败",
@@ -281,8 +285,9 @@ class MaaManager:
logger.warning(
f"用户: {user['user_id']} - 未配置森空岛签到Token跳过森空岛签到"
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={
"Warning": f"用户 {user['name']} 未配置森空岛签到Token跳过森空岛签到"
@@ -324,8 +329,9 @@ class MaaManager:
logger.error(
f"用户: {user['user_id']} - 未找到日常详细配置文件"
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={"Error": f"未找到 {user['name']} 的详细配置文件"},
).model_dump()
@@ -334,8 +340,9 @@ class MaaManager:
break
# 更新当前模式到界面
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"user_status": {
@@ -417,8 +424,9 @@ class MaaManager:
self.emulator_arguments = shortcut.Arguments.split()
except Exception as e:
logger.exception(f"解析快捷方式时出现异常:{e}")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={
"Error": f"解析快捷方式时出现异常:{e}",
@@ -429,8 +437,9 @@ class MaaManager:
break
elif not self.emulator_path.exists():
logger.error(f"模拟器快捷方式不存在:{self.emulator_path}")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={
"Error": f"模拟器快捷方式 {self.emulator_path} 不存在",
@@ -480,8 +489,9 @@ class MaaManager:
logger.warning(f"释放ADB时出现异常{e}")
except Exception as e:
logger.exception(f"释放ADB时出现异常{e}")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={"Warning": f"释放ADB时出现异常{e}"},
).model_dump()
@@ -497,8 +507,9 @@ class MaaManager:
)
except Exception as e:
logger.exception(f"启动模拟器时出现异常:{e}")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={
"Error": "启动模拟器时出现异常请检查MAA中模拟器路径设置"
@@ -544,8 +555,9 @@ class MaaManager:
logger.info(
f"用户: {user['user_id']} - MAA进程完成代理任务"
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": "检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s"
@@ -559,8 +571,9 @@ class MaaManager:
)
# 打印中止信息
# 此时log变量内存储的就是出现异常的日志信息可以保存或发送用于问题排查
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": f"{self.maa_result}\n正在中止相关程序\n请等待10s"
@@ -602,8 +615,9 @@ class MaaManager:
logger.warning(f"释放ADB时出现异常{e}")
except Exception as e:
logger.exception(f"释放ADB时出现异常{e}")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={"Error": f"释放ADB时出现异常{e}"},
).model_dump()
@@ -674,8 +688,9 @@ class MaaManager:
logger.info(f"检测到MAA更新正在执行更新动作")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": "检测到MAA存在更新\nMAA正在执行更新动作\n请等待10s"
@@ -714,9 +729,11 @@ class MaaManager:
logger.info(f"开始排查用户: {user['user_id']}")
user["status"] = "运行"
await self.websocket.send_json(
TaskMessage(
type="Update", data={"user_list": self.user_list}
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"user_list": self.user_list},
).model_dump()
)
@@ -759,8 +776,9 @@ class MaaManager:
f"用户: {user['user_id']} - MAA进程成功登录PRTS",
)
self.run_book["SignIn"] = True
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"log": "检测到MAA进程成功登录PRTS"},
).model_dump()
@@ -769,8 +787,9 @@ class MaaManager:
logger.error(
f"用户: {user['user_id']} - MAA未能正确登录到PRTS: {self.maa_result}"
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": f"{self.maa_result}\n正在中止相关程序\n请等待10s"
@@ -791,8 +810,9 @@ class MaaManager:
else:
uid = str(uuid.uuid4())
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Message",
data={
"message_id": uid,
@@ -810,8 +830,9 @@ class MaaManager:
if self.run_book["SignIn"]:
uid = str(uuid.uuid4())
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Message",
data={
"message_id": uid,
@@ -827,9 +848,11 @@ class MaaManager:
await self.result_record()
await self.websocket.send_json(
TaskMessage(
type="Update", data={"user_list": self.user_list}
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"user_list": self.user_list},
).model_dump()
)
@@ -906,9 +929,9 @@ class MaaManager:
)
self.user_list[self.index]["status"] = "异常"
await self.websocket.send_json(
TaskMessage(
type="Update", data={"user_list": self.user_list}
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id, type="Update", data={"user_list": self.user_list}
).model_dump()
)
@@ -1085,8 +1108,9 @@ class MaaManager:
async def search_ADB_address(self) -> None:
"""搜索ADB实际地址"""
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": f"即将搜索ADB实际地址\n正在等待模拟器完成启动\n请等待{self.wait_time}s"
@@ -1175,8 +1199,10 @@ class MaaManager:
# 更新MAA日志
if await self.maa_process_manager.is_running():
await self.websocket.send_json(
TaskMessage(type="Update", data={"log": log}).model_dump()
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id, type="Update", data={"log": log}
).model_dump()
)
if self.mode == "自动代理":
@@ -1600,13 +1626,14 @@ class MaaManager:
logger.warning(
f"未选择用户 {self.cur_user_data.get('Info', 'Name')} 的自定义基建配置文件"
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={
"warning": f"未选择用户 {self.cur_user_data.get('Info', 'Name')} 的自定义基建配置文件"
},
)
).model_dump()
)
data["Configurations"]["Default"][
"Infrast.CustomInfrastEnabled"

View File

@@ -33,7 +33,7 @@ from typing import Union, List, Dict, Optional
from app.core import Config, GeneralConfig, GeneralUserConfig
from app.models.schema import TaskMessage
from app.models.schema import WebSocketMessage
from app.models.ConfigBase import MultipleConfig
from app.services import Notify, System
from app.utils import get_logger, LogMonitor, ProcessManager, strptime
@@ -46,18 +46,14 @@ class GeneralManager:
"""通用脚本通用控制器"""
def __init__(
self,
mode: str,
script_id: uuid.UUID,
user_id: Optional[uuid.UUID],
websocket: WebSocket,
self, mode: str, script_id: uuid.UUID, user_id: Optional[uuid.UUID], ws_id: str
):
super(GeneralManager, self).__init__()
self.mode = mode
self.script_id = script_id
self.user_id = user_id
self.websocket = websocket
self.ws_id = ws_id
self.game_process_manager = ProcessManager()
self.general_process_manager = ProcessManager()
@@ -164,8 +160,10 @@ class GeneralManager:
self.check_result = self.check_config()
if self.check_result != "Success!":
logger.error(f"未通过配置检查:{self.check_result}")
await self.websocket.send_json(
TaskMessage(type="Info", data={"Error": self.check_result}).model_dump()
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id, type="Info", data={"Error": self.check_result}
).model_dump()
)
return
@@ -229,16 +227,20 @@ class GeneralManager:
< self.script_config.get("Run", "ProxyTimesLimit")
):
user["status"] = "运行"
await self.websocket.send_json(
TaskMessage(
type="Update", data={"user_list": self.user_list}
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"user_list": self.user_list},
).model_dump()
)
else:
user["status"] = "跳过"
await self.websocket.send_json(
TaskMessage(
type="Update", data={"user_list": self.user_list}
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={"user_list": self.user_list},
).model_dump()
)
continue
@@ -254,8 +256,9 @@ class GeneralManager:
).exists():
logger.error(f"用户: {user['user_id']} - 未找到配置文件")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={"Error": f"未找到 {user['user_id']} 的配置文件"},
).model_dump()
@@ -306,8 +309,9 @@ class GeneralManager:
)
except Exception as e:
logger.exception(f"启动游戏/模拟器时出现异常:{e}")
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Info",
data={"Error": f"启动游戏/模拟器时出现异常:{e}"},
).model_dump()
@@ -326,8 +330,9 @@ class GeneralManager:
seconds=self.script_config.get("Game", "WaitTime") + 10
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": f"正在等待游戏/模拟器完成启动\n请等待{self.script_config.get('Game', 'WaitTime')}s"
@@ -368,8 +373,9 @@ class GeneralManager:
logger.info(
f"用户: {user['user_id']} - 通用脚本进程完成代理任务"
)
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": "检测到通用脚本进程完成代理任务\n正在等待相关程序结束\n请等待10s"
@@ -425,8 +431,9 @@ class GeneralManager:
)
# 打印中止信息
# 此时log变量内存储的就是出现异常的日志信息可以保存或发送用于问题排查
await self.websocket.send_json(
TaskMessage(
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id,
type="Update",
data={
"log": f"{self.general_result}\n正在中止相关程序\n请等待10s"
@@ -768,8 +775,10 @@ class GeneralManager:
# 更新日志
if await self.general_process_manager.is_running():
await self.websocket.send_json(
TaskMessage(type="Update", data={"log": log}).model_dump()
await Config.send_json(
WebSocketMessage(
taskId=self.ws_id, type="Update", data={"log": log}
).model_dump()
)
if "自动代理" in self.mode:

View File

@@ -79,6 +79,7 @@ def main():
from fastapi.middleware.cors import CORSMiddleware
from app.api import (
core_router,
info_router,
scripts_router,
plan_router,
@@ -103,6 +104,7 @@ def main():
allow_headers=["*"], # 允许所有请求头
)
app.include_router(core_router)
app.include_router(info_router)
app.include_router(scripts_router)
app.include_router(plan_router)