From 76f330e4d3d6b7675a2b0111eb78c48dbf1245bd Mon Sep 17 00:00:00 2001 From: DLmaster361 Date: Tue, 23 Sep 2025 16:00:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=90=8E=E7=AB=AF=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=94=B5=E6=BA=90=E6=93=8D=E4=BD=9C=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/dispatch.py | 24 ++++++++-- app/core/config.py | 5 ++- app/core/task_manager.py | 32 +++++++++----- app/services/system.py | 49 ++++++++++++++++++++- app/services/update.py | 2 +- app/task/MAA.py | 2 +- app/utils/constants.py | 18 ++++++-- frontend/electron/services/gitService.ts | 2 +- frontend/electron/services/pythonService.ts | 2 +- main.py | 2 +- 10 files changed, 112 insertions(+), 26 deletions(-) diff --git a/app/api/dispatch.py b/app/api/dispatch.py index 3370771..d41b261 100644 --- a/app/api/dispatch.py +++ b/app/api/dispatch.py @@ -22,7 +22,7 @@ from fastapi import APIRouter, Body -from app.core import TaskManager +from app.core import Config, TaskManager from app.services import System from app.models.schema import * @@ -58,11 +58,27 @@ async def stop_task(task: DispatchIn = Body(...)) -> OutBase: return OutBase() -@router.post("/power", summary="电源操作", response_model=OutBase, status_code=200) -async def power_task(task: PowerIn = Body(...)) -> OutBase: +@router.post( + "/set/power", summary="设置电源标志", response_model=OutBase, status_code=200 +) +async def set_power(task: PowerIn = Body(...)) -> OutBase: try: - await System.set_power(task.signal) + Config.power_sign = task.signal + except Exception as e: + return OutBase( + code=500, status="error", message=f"{type(e).__name__}: {str(e)}" + ) + return OutBase() + + +@router.post( + "/cancel/power", summary="取消电源任务", response_model=OutBase, status_code=200 +) +async def cancel_power_task() -> OutBase: + + try: + await System.cancel_power_task() except Exception as e: return OutBase( code=500, status="error", message=f"{type(e).__name__}: {str(e)}" diff --git a/app/core/config.py b/app/core/config.py index 4c48848..8e64e7b 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -659,6 +659,9 @@ class AppConfig(GlobalConfig): self.server: Optional[uvicorn.Server] = None self.websocket: Optional[WebSocket] = None + self.power_sign: Literal[ + "NoAction", "Shutdown", "ShutdownForce", "Hibernate", "Sleep", "KillSelf" + ] = "NoAction" self.silence_dict: Dict[Path, datetime] = {} self.if_ignore_silence: List[uuid.UUID] = [] self.temp_task: List[asyncio.Task] = [] @@ -1924,7 +1927,7 @@ class AppConfig(GlobalConfig): try: response = requests.get( - "https://download.auto-mas.top/d/AUTO_MAA/Server/notice.json", + "https://download.auto-mas.top/d/AUTO_MAS/Server/notice.json", timeout=10, proxies=self.get_proxies(), ) diff --git a/app/core/task_manager.py b/app/core/task_manager.py index 4f8d7b1..0000c75 100644 --- a/app/core/task_manager.py +++ b/app/core/task_manager.py @@ -25,9 +25,11 @@ from functools import partial from typing import Dict, Optional, Literal from .config import Config, MaaConfig, GeneralConfig, QueueConfig +from app.services import System from app.models.schema import WebSocketMessage from app.utils import get_logger from app.task import * +from app.utils.constants import POWER_SIGN_MAP logger = get_logger("业务调度") @@ -321,17 +323,25 @@ class _TaskManager: if mode == "自动代理" and task_id in Config.QueueConfig: - await Config.send_json( - WebSocketMessage( - id=str(task_id), - type="Signal", - data={ - "power": Config.QueueConfig[task_id].get( - "Info", "AfterAccomplish" - ) - }, - ).model_dump() - ) + if Config.power_sign != "NoAction": + Config.power_sign = Config.QueueConfig[task_id].get( + "Info", "AfterAccomplish" + ) + + if len(self.task_dict) == 0 and Config.power_sign != "NoAction": + logger.info(f"所有任务已结束,准备执行电源操作: {Config.power_sign}") + await Config.send_json( + WebSocketMessage( + id="Main", + type="Message", + data={ + "type": "Countdown", + "title": f"{POWER_SIGN_MAP[Config.power_sign]}倒计时", + "message": f"程序将在倒计时结束后执行 {POWER_SIGN_MAP[Config.power_sign]} 操作", + }, + ).model_dump() + ) + await System.start_power_task() async def start_startup_queue(self): """开始运行启动时运行的调度队列""" diff --git a/app/services/system.py b/app/services/system.py index 0826486..1b97eaa 100644 --- a/app/services/system.py +++ b/app/services/system.py @@ -21,6 +21,7 @@ import sys import ctypes +import asyncio import win32gui import win32process import psutil @@ -29,9 +30,10 @@ import tempfile import getpass from datetime import datetime from pathlib import Path -from typing import Literal +from typing import Literal, Optional from app.core import Config +from app.models.schema import WebSocketMessage from app.utils.logger import get_logger logger = get_logger("系统服务") @@ -41,6 +43,10 @@ class _SystemHandler: ES_CONTINUOUS = 0x80000000 ES_SYSTEM_REQUIRED = 0x00000001 + countdown = 60 + + def __init__(self) -> None: + self.power_task: Optional[asyncio.Task] = None async def set_Sleep(self) -> None: """同步系统休眠状态""" @@ -245,6 +251,47 @@ class _SystemHandler: logger.info("执行退出主程序操作") Config.server.should_exit = True + async def _power_task( + self, + power_sign: Literal[ + "NoAction", "Shutdown", "ShutdownForce", "Hibernate", "Sleep", "KillSelf" + ], + ) -> None: + """电源任务""" + + await asyncio.sleep(self.countdown) + if power_sign == "KillSelf": + await Config.send_json( + WebSocketMessage( + id="Main", type="Signal", data={"RequestClose": "请求前端关闭"} + ).model_dump() + ) + await self.set_power(power_sign) + + async def start_power_task(self): + """开始电源任务""" + + if self.power_task is None or self.power_task.done(): + self.power_task = asyncio.create_task(self._power_task(Config.power_sign)) + logger.info( + f"电源任务已启动, {self.countdown}秒后执行: {Config.power_sign}" + ) + else: + logger.warning("已有电源任务在运行, 请勿重复启动") + + async def cancel_power_task(self): + """取消电源任务""" + + if self.power_task is not None and not self.power_task.done(): + self.power_task.cancel() + try: + await self.power_task + except asyncio.CancelledError: + logger.info("电源任务已取消") + else: + logger.warning("当前无电源任务在运行") + raise RuntimeError("当前无电源任务在运行") + async def kill_emulator_processes(self): """这里暂时仅支持 MuMu 模拟器""" diff --git a/app/services/update.py b/app/services/update.py index cba43f0..eb58cd0 100644 --- a/app/services/update.py +++ b/app/services/update.py @@ -359,7 +359,7 @@ class _UpdateHandler: await Config.send_json( WebSocketMessage( id="Update", - type="Message", + type="Info", data={"Error": f"解压失败, {type(e).__name__}: {e}"}, ).model_dump() ) diff --git a/app/task/MAA.py b/app/task/MAA.py index 3e03d35..b6b57a9 100644 --- a/app/task/MAA.py +++ b/app/task/MAA.py @@ -1093,7 +1093,7 @@ class MaaManager: return result_text async def get_message(self, message_id: str): - """获取当前任务的属性值""" + """获取客户端回应消息""" logger.info(f"等待客户端回应消息: {message_id}") diff --git a/app/utils/constants.py b/app/utils/constants.py index dac3cbf..f06a990 100644 --- a/app/utils/constants.py +++ b/app/utils/constants.py @@ -102,28 +102,28 @@ RESOURCE_STAGE_DROP_INFO = { "Display": "PR-A", "Value": "PR-A", "Drop": "PR-A", - "DropName": "医疗/重装芯片", + "DropName": "奶/盾芯片", "Activity": {"Tip": "一四五日", "StageName": "资源关卡"}, }, "PR-B-1": { "Display": "PR-B", "Value": "PR-B", "Drop": "PR-B", - "DropName": "术师/狙击芯片", + "DropName": "术/狙芯片", "Activity": {"Tip": "一二五六", "StageName": "资源关卡"}, }, "PR-C-1": { "Display": "PR-C", "Value": "PR-C", "Drop": "PR-C", - "DropName": "先锋/辅助芯片", + "DropName": "先/辅芯片", "Activity": {"Tip": "三四六日", "StageName": "资源关卡"}, }, "PR-D-1": { "Display": "PR-D", "Value": "PR-D", "Drop": "PR-D", - "DropName": "近卫/特种芯片", + "DropName": "近/特芯片", "Activity": {"Tip": "二三六日", "StageName": "资源关卡"}, }, } @@ -229,6 +229,16 @@ MATERIALS_MAP = { } """掉落物索引表""" +POWER_SIGN_MAP = { + "NoAction": "无动作", + "Shutdown": "关机", + "ShutdownForce": "强制关机", + "Hibernate": "休眠", + "Sleep": "睡眠", + "KillSelf": "退出程序", +} +"""电源操作类型索引表""" + RESERVED_NAMES = { "CON", "PRN", diff --git a/frontend/electron/services/gitService.ts b/frontend/electron/services/gitService.ts index 92d84fb..4b659df 100644 --- a/frontend/electron/services/gitService.ts +++ b/frontend/electron/services/gitService.ts @@ -11,7 +11,7 @@ export function setMainWindow(window: BrowserWindow) { mainWindow = window } -const gitDownloadUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/git.zip' +const gitDownloadUrl = 'https://download.auto-mas.top/d/AUTO_MAS/git.zip' // 递归复制目录,包括文件和隐藏文件 function copyDirSync(src: string, dest: string) { diff --git a/frontend/electron/services/pythonService.ts b/frontend/electron/services/pythonService.ts index 24da491..a70c61e 100644 --- a/frontend/electron/services/pythonService.ts +++ b/frontend/electron/services/pythonService.ts @@ -68,7 +68,7 @@ async function installPip(pythonPath: string, appRoot: string): Promise { console.log('pip未安装,开始安装...') const getPipPath = path.join(pythonPath, 'get-pip.py') - const getPipUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/get-pip.py' + const getPipUrl = 'https://download.auto-mas.top/d/AUTO_MAS/get-pip.py' console.log(`Python可执行文件路径: ${pythonExe}`) console.log(`get-pip.py下载URL: ${getPipUrl}`) diff --git a/main.py b/main.py index c630b0e..8036c8f 100644 --- a/main.py +++ b/main.py @@ -104,7 +104,7 @@ def main(): await Config.send_json( WebSocketMessage( - id="Main", type="Signal", data={"Close": "后端已安全关闭"} + id="Main", type="Signal", data={"Closed": "后端已安全关闭"} ).model_dump() )