- 🚀 现代化技术栈 - 前后端分离架构,Vue 3 + FastAPI
- 🎨 美观易用 - 基于 NaiveUI 组件库,界面简洁美观,支持深色模式自动切换
- 🔌 魔改 Agent 实现 - 通过动态解析导入,无需额外 Python 环境
- 🔧 高度可定制 - Python 代码简洁易修改,轻松实现各种定制需求
- 🔔 系统通知 - 依托 Plyer 和浏览器API实现跨平台双渠道系统通知
- 🔄 自动更新 - 支持 GitHub 自动下载更新
- 📱 跨平台兼容 - Windows / Linux / macOS 全平台支持,基于浏览器提供统一的用户体验
- ⚡ 开箱即用 - 强兼官方模板,极致简单的步骤,快速接入
- 🎯 弃繁从简 - 抛弃一切不必要的组件,提供尽可能小巧的体积
| 组件 | 要求 |
|---|---|
| 系统 | Windows 10+、Linux、macOS |
| 资源 | 基于 MaaFramework 的资源项目 |
| 浏览器 | Chrome >=111;Edge >=111;Firefox >=114;Safari >=16.4 |
请先阅读MaaFW文档,选择一种集成方案
如果您选择低代码或低代码+Agent方案,只需按照指引使用 MaaFramework 项目模板 创建项目,然后将其中的 .github/workflows/install.yml 替换为本项目的 deploy/install.yaml 即可。
如果您选择全代码开发集成,并且也想使用本项目的UI,请继续阅读 项目架构与开发
| 配置 | 默认值 | 修改方法 |
|---|---|---|
| 压缩包名 | 仓库名-版本号-平台-架构 | deploy/install.yml#L170,注意下方各处也要一并修改 |
| 可执行文件名 | MWU | 暂不可修改 |
| LOGO | 暂不可修改 |
scan_select 用于在加载 interface.json 时扫描目录文件,并把扫描结果自动写入当前选项的 cases,适用于“配置文件选择”等动态枚举场景。
字段定义如下:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
"scan_select" |
是 | 选项类型 |
label |
string |
否 | 前端显示名称 |
description |
string |
否 | 描述信息 |
scan_dir |
string |
是 | 扫描目录。相对路径基于 interface.json 所在目录解析,并做越界限制(不允许跳出该目录) |
scan_filter |
string |
是 | glob pattern,用于筛选文件,如 **/*.json |
pipeline_override |
object |
是 | 任务执行时使用的覆盖配置,必须在任意层级的 attach 中至少包含一次当前选项名键(如 attach.bbc_team_config) |
cases |
OptionCase[] |
否(配置阶段应省略或空数组) | 加载时自动生成,name/label 均为相对 scan_dir 的路径+文件名 |
default_case |
string |
否 | 默认选项名称 |
示例:
加载前:
{
"option": {
"bbc_team_config": {
"type": "scan_select",
"label": "BBC 队伍配置",
"description": "选择 BBC 队伍配置文件",
"scan_dir": "./resource/BBchannel/settings",
"scan_filter": "**/*.json",
"pipeline_override": {
"队伍配置": {
"attach": {
"bbc_team_config": ""
}
}
}
}
}
}加载完成且用户选择 1.json:
该结果仅在内存中保留,不会实际修改 interface.json
{
"option": {
"bbc_team_config": {
"type": "scan_select",
"label": "BBC 队伍配置",
"description": "选择 BBC 队伍配置文件",
"scan_dir": "./resource/BBchannel/settings",
"scan_filter": "**/*.json",
"pipeline_override": {
"队伍配置": {
"attach": {
"bbc_team_config": "1.json"
}
}
},
"cases": [
{
"name": "1.json",
"label": "1.json"
},
{
"name": "bbb/c.json",
"label": "bbb/c.json"
}
]
}
}
}scan_select 会递归遍历 pipeline_override,并对所有命中的 attach.option_name 赋值。
例如在 队伍配置1 和 队伍配置2 下都存在 attach.bbc_team_config 时,两处都会被写入同一个用户选择结果。
如果您需要更多的定制化功能或想为本项目做出贡献,请阅读以下部分
项目采用前后端分离架构:
- 后端:FastAPI (Python 3.12+),提供 RESTful API 和 Server-Sent Events (SSE) 服务,运行在
http://127.0.0.1:55666 - 前端:Vue 3 + NaiveUI + Vite + UnoCSS + Pinia,构建输出到
page/目录 - 核心交互:通过 SSE (Server-Sent Events) 实现任务状态和日志的实时推送
cd front
pnpm dev # 开发服务器,自动代理 /api 到 localhost:55666
pnpm build # 构建到 ../page 目录
pnpm lint # 使用 oxlint 进行代码检查
pnpm format # 使用 Prettier 格式化代码uv run main.py # 启动 FastAPI 服务依赖管理:使用 uv 作为 Python 包管理器
cd front && pnpm run build # 前端构建
cd updater && go build # 更新器构建
uv run python -m nuitka --standalone --assume-yes-for-downloads --user-package-configuration-file=nuitka-package.config.yml --output-dir=build --include-package=PIL --include-package=maa -o MWU main.py # 后端构建MWU/
├── main.py # FastAPI 应用入口,自动打开浏览器
├── maa_utils.py # MaaWorker 类,处理所有 MAA 框架交互
├── app_state.py # 全局应用状态管理
├── scheduler_manager.py # 定时任务调度管理器
├── interface.json # 项目接口配置(V2 协议)
│
├── agent/ # Agent 动态扩展目录
│ ├── main.py # Agent 主程序
│ ├── custom/ # 自定义 Action/Reco/Sink 实现
│ ├── libs/ # Agent 依赖库
│ └── utils/ # Agent 工具类
│
├── config/ # 配置文件目录
│ ├── settings.json # 应用设置
│ ├── task_config.json # 任务配置缓存
│ └── maa_option.json # MAA 选项配置
│
├── models/ # 数据模型目录
│ ├── api.py # API 请求/响应模型
│ ├── interface.py # interface 数据模型
│ ├── interface_loader.py # interface 加载逻辑
│ ├── scheduler.py # 定时任务相关模型
│ ├── settings.py # 设置数据模型
│ └── task_config.py # 任务配置模型
│
├── maa_worker/ # MAA 核心工作进程
│ ├── agent_loader.py # Agent 动态加载器
│ ├── agent_service.py # Agent 生命周期与 PI 环境
│ ├── device_service.py # 设备发现、连接、资源加载
│ ├── event_service.py # 日志、实时事件与通知
│ ├── pipeline_override.py # pipeline_override 合并逻辑
│ ├── runtime.py # Worker 运行时状态对象
│ └── task_service.py # 任务线程启动/停止与执行主循环
│
├── services/ # 后端业务服务
│ └── update_service.py # 更新服务实现
│
├── updater/ # Go 更新器目录
│ ├── main.go # 更新器主程序
│ ├── go.mod # Go 模块定义
│ └── go.sum # Go 依赖校验
│
├── deploy/ # 部署与 CI 相关脚本
│ ├── install.yml # GitHub Actions 部署配置
│ ├── download_deps.py # 依赖下载脚本
│ └── copy_resources.py # 资源复制脚本
│
└── front/ # 前端项目目录
└── src/ # 源代码目录
├── app/ # 应用入口、路由、主题、i18n、全局样式
│ ├── App.vue
│ ├── main.ts
│ ├── router/
│ ├── theme/
│ ├── i18n/
│ └── styles/
├── views/ # 页面视图装配层
│ ├── panel/
│ └── settings/
├── components/ # 技术层组件目录
│ ├── panel/ # 主面板相关组件
│ └── settings/ # 设置页区块、弹窗、调度组件
├── services/ # API、SSE、消息反馈等服务
│ ├── api/
│ │ ├── core/
│ │ └── modules/
│ ├── realtime/
│ └── feedback/
├── stores/ # Pinia 状态管理,按技术层内子目录划分
├── types/ # 类型定义
└── utils/ # 纯函数工具
以下仅为推荐写法,对于全代码开发,选择权都在于您
main.py:FastAPI 路由与生命周期管理(启动MaaWorker、调度器、日志流)app_state.py:运行时状态与日志广播maa_utils.py:MaaWorker组装层(依赖初始化、Service 装配、截图与关闭)maa_worker/device_service.py:设备发现、连接、资源加载maa_worker/task_service.py:任务线程启动/停止与执行主循环maa_worker/pipeline_override.py:任务选项与 pipeline_override 合并maa_worker/event_service.py:统一日志、实时事件、系统通知与外部通知
任务执行主循环位于 maa_worker/task_service.py 的 run_process。如果您需要“特殊任务分支 + 默认 pipeline 共存”,推荐按下面方式扩展:
# maa_worker/task_service.py
def run_process(self, task_list, options):
try:
state = self.worker.task_state
self.worker.events.emit_task_started(task_list)
for task in task_list:
if state.stop_flag:
self.worker.tasker.post_stop().wait()
state.last_status = "stopped"
state.last_error = "任务已终止"
self.worker.events.send_log("任务已终止")
return
# 自定义入口:按 task entry 分发到专用逻辑
if task == "MyCustomEntry":
if not _run_my_custom_entry(self, options):
return
continue
# 默认入口:继续走 interface.json 的 pipeline 任务
pipeline_override = self.worker.pipeline.build_task_pipeline_override(
task, options
)
t = (
self.worker.tasker.post_task(task, pipeline_override)
if pipeline_override
else self.worker.tasker.post_task(task)
)
self.worker.events.send_log("正在运行任务: " + task)
while not t.done:
time.sleep(0.5)
if state.stop_flag:
self.worker.tasker.post_stop().wait()
state.last_status = "stopped"
state.last_error = "任务已终止"
self.worker.events.send_log("任务已终止")
return
state.last_status = "success"
state.last_error = None
self.worker.events.emit_task_completed(task_list)
except Exception as exc:
state.last_status = "failed"
state.last_error = str(exc) or "任务执行失败"
self.worker.events.emit_task_failed(task_list, state.last_error)
self.worker.events.send_log("任务出现异常,请检查终端日志")
finally:
state.running = False
state.thread = None
state.current_task_name = None
time.sleep(0.5)
def _run_my_custom_entry(task_service, options):
state = task_service.worker.task_state
task_service.worker.events.send_log("开始执行自定义任务: MyCustomEntry")
pipeline_override = task_service.worker.pipeline.build_task_pipeline_override(
"MyCustomEntry", options
)
t = (
task_service.worker.tasker.post_task("MyCustomEntry", pipeline_override)
if pipeline_override
else task_service.worker.tasker.post_task("MyCustomEntry")
)
while not t.done:
time.sleep(0.5)
if state.stop_flag:
task_service.worker.tasker.post_stop().wait()
state.last_status = "stopped"
state.last_error = "任务已终止"
task_service.worker.events.send_log("任务已终止")
return False
task_service.worker.events.send_log("自定义任务执行完成")
return True您可以在 maa_utils.py 中添加自定义的 Action 和 Recognition:
# maa_utils.py
from maa.custom_action import CustomAction
from maa.custom_recognition import CustomRecognition
from maa.define import TaskDetail
import numpy as np
from PIL import Image
# ========== 自定义 Recognition ==========
@resource.custom_recognition("MyCustomReco")
class MyCustomRecognition(CustomRecognition):
def analyze(self, context, argv: CustomRecognition.AnalyzeArg):
"""自定义识别逻辑"""
# 获取当前屏幕图像
image = context.tasker.controller.post_screencap().wait().get()
# 您的识别逻辑...
# 返回识别结果
return CustomRecognition.AnalyzeResult(
box=(x, y, w, h), # 识别框坐标
detail="识别详情"
)
# ========== 自定义 Action ==========
@resource.custom_action("MyCustomAction")
class MyCustomAction(CustomAction):
def run(self, context, argv: CustomAction.RunArg):
"""自定义操作逻辑"""
# 获取点击坐标
box = argv.rec_box
x, y = box[0] + box[2] // 2, box[1] + box[3] // 2
# 执行点击
context.tasker.controller.post_click(x, y).wait()
return CustomAction.RunResult(success=True)- 优先按模块改动:设备相关优先改
maa_worker/device_service.py,任务流优先改maa_worker/task_service.py,避免把所有逻辑塞进maa_utils.py。 - 统一事件出口:日志与通知尽量走
worker.events.emit/send_log/send_notification,便于前端 SSE 与通知配置统一生效。 - 保持任务状态可观测:自定义流程要维护
worker.task_state.last_status/worker.task_state.last_error,这样定时调度执行记录才能正确展示。 - 谨慎新增实时事件类型:若新增事件名,请同步更新后端
RealtimeEventName与前端事件处理逻辑。 - 避免命名冲突:不要新建顶层
utils包,避免与 agent 动态加载路径冲突。
MWU 基于 AGPL-3.0 许可证 开源。
-
NaiveUI
A Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast. -
FastAPI
FastAPI framework, high performance, easy to learn, fast to code, ready for production -
Nuitka
Nuitka is a Python compiler written in Python. -
Vite
Next Generation Frontend Tooling. It's fast! -
MaaFramework
基于图像识别的自动化黑盒测试框架。 -
VueDraggablePlus
支持 Vue2 和 Vue3 的拖拽组件 -
Plyer
Plyer is a platform-independent Python wrapper for platform-dependent APIs -
marked
A markdown parser and compiler. Built for speed. -
tailwindcss
A utility-first CSS framework for rapid UI development. -
Oxlint
Oxlint is designed to catch erroneous or useless code without requiring any configurations by default.
感谢所有为 MWU 做出贡献的开发者,以及 MAA 社区各位小伙伴提供的无私帮助与建议。
