From fbcc149849dd7437beb433e63eb6039416296c84 Mon Sep 17 00:00:00 2001 From: DLmaster361 Date: Wed, 27 Aug 2025 17:50:56 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=88=9D=E6=AD=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E5=8D=95=E4=B8=80ws=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/__init__.py | 2 + app/api/core.py | 47 +++++ app/api/dispatch.py | 30 --- app/api/info.py | 2 +- app/core/config.py | 9 + app/core/task_manager.py | 78 ++++---- app/models/schema.py | 400 ++++++++++++++++++++++----------------- app/task/MAA.py | 163 +++++++++------- app/task/general.py | 63 +++--- main.py | 2 + 10 files changed, 454 insertions(+), 342 deletions(-) create mode 100644 app/api/core.py diff --git a/app/api/__init__.py b/app/api/__init__.py index cf50ebf..9a3f295 100644 --- a/app/api/__init__.py +++ b/app/api/__init__.py @@ -23,6 +23,7 @@ __version__ = "5.0.0" __author__ = "DLmaster361 " __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", diff --git a/app/api/core.py b/app/api/core.py new file mode 100644 index 0000000..62c4588 --- /dev/null +++ b/app/api/core.py @@ -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 . + +# 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") diff --git a/app/api/dispatch.py b/app/api/dispatch.py index d29e389..77160a8 100644 --- a/app/api/dispatch.py +++ b/app/api/dispatch.py @@ -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="任务不存在或已结束") diff --git a/app/api/info.py b/app/api/info.py index 8ec210d..e7194ac 100644 --- a/app/api/info.py +++ b/app/api/info.py @@ -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() diff --git a/app/core/config.py b/app/core/config.py index 8dcda75..fbeaa58 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -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]: diff --git a/app/core/task_manager.py b/app/core/task_manager.py index ae8a9dc..818f14e 100644 --- a/app/core/task_manager.py +++ b/app/core/task_manager.py @@ -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() diff --git a/app/models/schema.py b/app/models/schema.py index 2b398f8..67b978c 100644 --- a/app/models/schema.py +++ b/app/models/schema.py @@ -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: 程序信号", diff --git a/app/task/MAA.py b/app/task/MAA.py index 9fa3c65..e485fad 100644 --- a/app/task/MAA.py +++ b/app/task/MAA.py @@ -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" diff --git a/app/task/general.py b/app/task/general.py index 038dd21..ff194d6 100644 --- a/app/task/general.py +++ b/app/task/general.py @@ -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: diff --git a/main.py b/main.py index 544d5e1..017017f 100644 --- a/main.py +++ b/main.py @@ -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)