MaaAssistantArknights 远程控制 API 协议详解:getTask 轮询、reportStatus 上报与任务编排实战
【免费下载链接】MaaAssistantArknights《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients.项目地址: https://gitcode.com/GitHub_Trending/ma/MaaAssistantArknights
MAA(MaaAssistantArknights,《明日方舟》全日常一键小助手)提供了一套完整的远程控制协议,允许第三方服务通过两个匿名可访问的 HTTP(S) 端点对 MAA 实例进行任务下发、状态查询与结果回收。本文以仓库中的 remote-control-schema.md 协议文档为骨架,结合 RemoteControlService.cs 等核心源码,完整讲解任务获取端点与任务上报端点的数据契约、全部任务类型、MAA 侧轮询与双队列执行机制,并给出 QQBot 与网站两种可落地的服务端实现思路,帮助开发者独立搭建自己的 MAA 控制中台。
一、协议总览:两个端点、一条轮询链路
远程控制的核心思想非常简单:MAA 客户端作为被控方,主动向服务端轮询任务,执行完毕后把结果回报给服务端。整个协议只依赖两个端点,且服务端无需任何鉴权逻辑(匿名访问),端点在服务端侧对 MAA 完全透明:
| 端点 | 作用 | 调用方向 | 调用频率 |
|---|---|---|---|
| 任务获取端点(getTask) | 获取待执行任务列表 | MAA → 服务端 | 固定间隔轮询(默认 1 秒,可配置) |
| 任务上报端点(reportStatus) | 汇报任务执行结果 | MAA → 服务端 | 每个任务执行完毕后 |
两个端点的路径完全自由,只要符合 HTTP(S) 协议即可,例如https://your-control-host.net/maa/getTask与https://your-control-host.net/maa/reportStatus。
安全警告(协议原文明确强调):如果端点使用http://明文协议,MAA 每次连接都会发出安全警告;将明文传输服务部署到公网是极其危险且不推荐的行为,仅可用于本地测试。从源码看,这一检查实现在IsEndpointValid方法中:端点必须以https://或http://开头,前者直接放行,后者会弹出RemoteControlConnectionTestWarningHttpUnsafe警告(见 RemoteControlService.cs)。
关于 JSON 注释的提醒:JSON 本身不支持注释,协议文档中代码块里的//注释仅用于教学演示,实际传输时请务必删除。
二、任务获取端点(getTask)
2.1 请求格式
MAA 以固定间隔(默认 1000ms,配置项RemoteControlPollIntervalMs)持续向该端点发起POST请求,Content-Type=application/json,请求体固定携带两个字段:
{ "user": "ea6c39eb-a45f-4d82-9ecc-33a7bf2ae4dc", "device": "f7cd9682-3de9-4eef-9137-ec124ea9e9ec" }user:用户标识符,由用户手动填写在 MAA 设置界面,通常是平台侧分配的用户编号或密钥;device:设备标识符,由 MAA 自动生成的 UUID 字符串,标识当前这台 MAA 实例。
如果需要复用该端点实现其他业务,可以在请求中附加自定义参数,但MAA 只会传递user与device这两个字段。这一点在源码中得到印证——轮询循环构造请求时只序列化了new { user = uid, device = did }(见 RemoteControlService.cs)。
2.2 响应格式
端点必须以 JSON 格式返回响应,核心字段是tasks——一个任务对象数组。如果响应中没有tasks字段,该连接将被视为无效。
{ "tasks": [ { "id": "b353c469-b902-4357-bd8f-d133199eea31", "type": "CaptureImage" }, { "id": "15be4725-5bd3-443d-8ae3-0a5ae789254c", "type": "LinkStart" }, { "id": "15be4725-5bd3-443d-8ae3-0a5ae789254c", "type": "LinkStart-Recruiting" }, { "id": "b353c469-b902-4357-bd8f-d133199eea31", "type": "Toolbox-GachaOnce" }, { "id": "b353c469-b902-4357-bd8f-d133199eea31", "type": "Settings-ConnectAddress", "params": "value" }, { "id": "b353c469-b902-4357-bd8f-d133199eea31", "type": "CaptureImageNow" }, { "id": "b353c469-b902-4357-bd8f-d133199eea31", "type": "StopTask" }, { "id": "b353c469-b902-4357-bd8f-d133199eea31", "type": "HeartBeat" } ] }每个任务对象包含:
| 字段 | 类型 | 说明 |
|---|---|---|
id | 字符串 | 任务唯一 ID,任务执行结果上报(reportStatus)时原样带回 |
type | 字符串 | 任务类型,决定 MAA 执行的动作,见下文任务类型清单 |
params | 字符串(可选) | 任务参数,目前仅Settings-*系列任务使用 |
同理,tasks之外也可以附加自定义返回值,但MAA 只读取tasks字段。
2.3 任务类型完整清单
MAA 把任务分为两类:顺序执行任务(Sequential Tasks)与即时执行任务(Instant Tasks)。从源码看,两类任务分别进入_sequentialTaskQueue与_instantTaskQueue两个独立队列,由两个独立的执行循环消费(见 RemoteControlService.cs)。
顺序执行任务:按下发顺序排队执行,前一个任务结束才开始下一个。例如先下发公开招募任务、再下发截图任务,则截图会在招募任务结束后才执行。支持的类型:
| type 值 | 功能说明 |
|---|---|
LinkStart | 一键长草(完整流程) |
LinkStart-Base | 一键长草——基建 |
LinkStart-WakeUp | 一键长草——唤醒 |
LinkStart-Combat | 一键长草——战斗 |
LinkStart-Recruiting | 一键长草——公开招募 |
LinkStart-Mall | 一键长草——信用商店 |
LinkStart-Mission | 一键长草——领取奖励 |
LinkStart-AutoRoguelike | 一键长草——自动肉鸽(集成战略) |
LinkStart-Reclamation | 一键长草——生息演算 |
Toolbox-GachaOnce | 工具箱——单抽 |
Toolbox-GachaTenTimes | 工具箱——十连 |
CaptureImage | 截图当前模拟器画面,执行完毕后将 Base64 字符串放入上报 payload |
Settings-ConnectAddress | 修改连接设置中的ConnectAddress属性(连接地址),params传新值 |
Settings-Stage1 | 修改作战任务的关卡选择(Stage),params传关卡名 |
其中LinkStart-*系列的特点是:按当前配置单独执行对应子功能,忽略主界面上的功能勾选状态。从源码LinkStart(IEnumerable<string> originalNames)方法可见,MAA 会按任务名查找对应类型的已保存任务配置(如InfrastTask、FightTask、RoguelikeTask等)并序列化后执行(见 RemoteControlService.cs)。各任务类型的实际动作逻辑如下表(源码ExecuteSequentialJobLoop中的switch分支):
| type 值 | 源码动作 |
|---|---|
LinkStart | 等待空闲后调用TaskQueueViewModel.LinkStart()启动完整一键长草流程 |
LinkStart-*(子功能) | 按Base/WakeUp/Combat/Recruiting/Mall/Mission/AutoRoguelike/Reclamation映射到对应任务配置并启动 |
Toolbox-GachaOnce/Toolbox-GachaTenTimes | 调用ToolboxViewModel.GachaOnce()/GachaTenTimes()执行抽卡 |
CaptureImage | 通过AsstProxy连接模拟器并抓取最新画面,用 PNG 编码后转 Base64 存入 payload |
Settings-ConnectAddress | 在 UI 线程上把ConnectAddress设置为params值 |
Settings-Stage1 | 在 UI 线程上把作战任务的Stage设置为params值 |
即时执行任务:可以在顺序任务执行期间随时插入,MAA 保证这类任务尽可能快地返回结果,通常用于控制远程控制功能本身。多个即时任务同样按下发顺序执行,但由于执行速度很快,一般无需关注其先后次序:
| type 值 | 功能说明 |
|---|---|
CaptureImageNow | 立即截图,与CaptureImage基本相同,但不等待其他任务直接执行 |
StopTask | 尝试结束当前正在执行的任务;若任务列表还有其他任务则继续执行下一个。注意:该任务不等待当前任务确认停止后才返回,远端应使用心跳任务(HeartBeat)确认停止命令是否生效 |
HeartBeat | 心跳任务,立即返回,payload 为当前顺序任务队列中正在执行的任务 ID;若当前无任务执行则返回空字符串 |
去重与可重入:getTask 端点应当是可重入的,可以反复返回相同的任务列表;MAA 会自动记录已接收的任务 ID,对相同 ID 的任务不会重复执行。源码中_enqueueTaskIds列表承担这一职责——轮询时若任务 ID 已存在则直接跳过(见 RemoteControlService.cs)。
补充说明(协议原文 note):
Settings系列任务不是收到后立即执行,而是排在前面任务之后按顺序执行;- 若服务端下发了未知类型的任务,MAA 会将其忽略(源码中同时会解锁一个
NotFound404成就彩蛋,见 RemoteControlService.cs)。
三、任务上报端点(reportStatus)
每当 MAA 完成一个任务(无论顺序任务还是即时任务),都会向该端点发起一次POST上报。请求头Content-Type=application/json,请求体:
{ "user": "ea6c39eb-a45f-4d82-9ecc-33a7bf2ae4dc", "device": "f7cd9682-3de9-4eef-9137-ec124ea9e9ec", "task": "15be4725-5bd3-443d-8ae3-0a5ae789254c", "status": "SUCCESS", "payload": "" }| 字段 | 类型 | 说明 |
|---|---|---|
user | 字符串 | 用户标识符,与 getTask 请求一致 |
device | 字符串 | 设备标识符,与 getTask 请求一致 |
task | 字符串 | 本次上报的任务 ID,与 getTask 下发时的id一一对应 |
status | 字符串 | 执行结果,SUCCESS或FAILED。注意:即使任务本身执行失败,绝大多数情况下仍返回SUCCESS;FAILED只在上文任务说明中明确指出的特殊场景(如截图失败)返回 |
payload | 字符串 | 随上报携带的数据,内容因任务类型而异。例如截图任务上报时,这里携带截图的 Base64 字符串 |
上报端点的响应完全随意:MAA 既不读取响应内容,也不校验 HTTP 状态码;若上报请求失败(如网络异常),MAA 只会在日志中记录一条错误(RemoteControlService report task failed.),不会重试也不会阻塞主流程。这意味着服务端可以放心地异步处理上报结果,不必追求即时响应。
四、MAA 侧实现原理:源码级剖析
远程控制功能在仓库中的实现集中于 RemoteControlService.cs,其运行模型可概括为"一拉三循环":
- 轮询循环(PollJobTaskLoop):以
RemoteControlPollIntervalMs(默认 1000ms)为周期调用 getTask 端点,解析返回的tasks数组,按类型把任务分别投入顺序队列与即时队列,并记录任务 ID 去重(见 RemoteControlService.cs)。 - 顺序任务执行循环(ExecuteSequentialJobLoop):不断从顺序队列取出任务执行,每个任务完成后立即调用 reportStatus 上报,再取出下一个(见 RemoteControlService.cs)。
- 即时任务执行循环(ExecuteInstantJobLoop):独立消费即时队列,保证
HeartBeat、StopTask、CaptureImageNow能随时插入执行(见 RemoteControlService.cs)。
几个值得注意的实现细节:
- 单例注入:
RemoteControlService以单例方式注册在依赖注入容器中(见 Bootstrapper.cs),轮询与执行循环随 MAA 启动常驻运行。 - 心跳的实现:
HeartBeat任务读取的是_currentSequentialTaskId字段——即当前正在执行的顺序任务的 ID,为空则表示当前空闲(见 RemoteControlService.cs)。因此心跳不仅是"保活",更是查询 MAA 当前运行状态的唯一手段。 - 停止的实现:
StopTask调用AsstProxy.AsstStop()尝试终止当前任务,且不等待结果立即上报返回(见 RemoteControlService.cs),所以远端要用心跳确认停止生效。 - 连接测试:设置界面提供"测试连接"按钮,其实现
ConnectionTest()会向 getTask 端点发送一次 POST,依据 HTTP 状态码判断连通性;非 2xx 状态码会以 Toast 提示失败原因(见 RemoteControlService.cs)。这正是下文网站示例中"401 即测试失败"的机制来源。 - 设备标识符:可手动"重新生成",对应源码
RegenerateDeviceIdentity(),生成新的 GUID(见 RemoteControlService.cs)。
4.1 客户端配置项与界面
被控端 MAA 需要在"设置 → 远程控制"界面填写以下配置(界面定义见 RemoteControlUserControl.xaml,配置模型见 RemoteControl.cs):
| 界面字段 | 配置项 | 说明 |
|---|---|---|
| 获取任务端点 | RemoteControlGetTaskEndpointUri | getTask 端点完整 URL |
| 汇报任务端点 | RemoteControlReportStatusUri | reportStatus 端点完整 URL |
| 轮询间隔 (ms) | RemoteControlPollIntervalMs | 轮询周期,默认 1000(毫秒) |
| 用户标识符 | RemoteControlUserIdentity | 平台侧分配给用户的标识 |
| 设备标识符(只读) | RemoteControlDeviceIdentity | MAA 自动生成的设备 UUID,可一键重新生成 |
界面还提供"测试连接"按钮与指向开发者文档的链接。一个安全细节值得注意:user、device以及两个端点地址在保存到本地配置时均经过加密处理(SimpleEncryptionHelper.Encrypt,见 RemoteControlUserControlModel.cs),同时界面上方明确提示"随意填入未知来源的地址可能会导致您的账户受到损失"(见 zh-cn.xaml)。
五、工作流示例一:通过 QQBot 控制 MAA
协议文档给出了一个完整可参考的服务端设计范式。开发者 A 希望用 QQBot 控制 MAA,于是开发了一个部署在公网的后端,提供两个端点:
https://myqqbot.com/maa/getTask https://myqqbot.com/maa/reportStatus完整的流程设计如下:
- getTask 兼任注册接口:getTask 接口对收到的任何参数都默认返回
200 OK与空任务列表{"tasks":[]};同时每次收到请求都去数据库查重——若设备未注册,则把device与user记录入库。这样 getTask 顺带完成了用户注册功能,用户无需单独走注册流程。 - 引导用户配置:QQBot 提供一条指令让用户提交
deviceId。使用说明要求用户把 QQ 号填入 MAA 的"用户标识符",并把 MAA 的"设备标识符"通过 QQ 聊天发给 Bot。 - 基于轮询的自动绑定:Bot 收到标识符后,按消息的 QQ 号查库;查不到就提示用户先配置 MAA。由于 MAA 配置完成后就会持续轮询 getTask,用户只要配置过,提交时库里必然已有对应记录——MAA 的持续轮询天然构成了设备验证手段。
- 标记验证并放行任务:Bot 找到记录后将其标记为已验证,此后该
device+user组合的 getTask 请求才会返回真实任务列表。 - 任务下发与结果回传:用户在 QQ 中下发指令,Bot 把任务写入数据库,getTask 轮询时即可取走;该 Bot 还贴心地在每次用户指令后默认附带一条截图任务。任务执行完毕后,MAA 调用 reportStatus 上报结果,Bot 收到后在 QQ 侧给用户发消息并展示截图。
这个流程把"注册、验证、下发、回传"四个环节全部建立在两个匿名端点上,是远程控制协议最典型的落地形态。
六、工作流示例二:通过网站批量管理 MAA
开发者 B 面向多实例批量管理场景建设了一个网站,自有用户体系,后端同样只暴露两个匿名端点:
https://mywebsite.com/maa/getTask https://mywebsite.com/maa/reportStatus与 QQBot 方案的关键差异在于显式授权与状态码语义:
- 网站在"连接 MAA 实例"界面给每个用户分配一个随机字符串(开发者称之为"用户密钥"),并提供输入框让用户填写设备 ID。
- 使用说明要求用户把用户密钥填入 MAA 的"用户标识符",再把 MAA 生成的"设备标识符"填到网站上。
- 只有用户成功创建 MAA 连接后,getTask 才返回
200 OK;否则返回401 Unauthorized。这一状态码语义与 MAA 的"连接测试"机制天然衔接:用户在 MAA 设置里点"测试连接"时,若信息填错(设备未在网站绑定),MAA 会收到 401 并提示测试失败。 - 用户在网站上即可下发任务、查看任务队列、浏览截图,其底层实现与 QQBot 示例一致,全部由 getTask 与 reportStatus 两个端点组合完成。
两种工作流对比,QQBot 方案更轻量、以轮询驱动注册验证;网站方案更严谨,用 HTTP 状态码表达授权状态,适合需要批量纳管大量 MAA 实例的运营场景。
七、开发者落地清单与注意事项
基于协议文档与源码实现,服务端开发者在实现时应注意以下几点:
- 端点必须为 HTTP(S),路径任意;生产环境务必使用 HTTPS,避免明文传输用户标识与设备标识被窃取。
- getTask 必须返回
tasks数组,否则连接判为无效;没有任务时返回{"tasks":[]}即可。 - 任务 ID 必须唯一且稳定——MAA 按 ID 去重,重复下发相同 ID 不会再次执行;需要重跑同一任务时应生成新 ID。
- 截图任务的体量:模拟器全屏截图的 Base64 可能达到数十 MB,很容易超过常见网关(Nginx、网关代理等)的默认请求体大小限制。若需要下发
CaptureImage/CaptureImageNow,务必提前调大上报端点的最大请求尺寸,否则截图上报会被网关拦截。 - 状态判定策略:绝大多数任务无论成败都上报
SUCCESS,只有明确失败场景才上报FAILED;同时StopTask不等确认即返回,因此"停止是否生效""当前是否空闲"应依靠HeartBeat的 payload 来判断,而不是依赖 StopTask 的上报结果。 - 顺序任务与即时任务分开设计:需要"先 A 后 B"的编排全部用顺序任务;
HeartBeat、StopTask、CaptureImageNow是即时任务,可随时穿插。 - 本地配置加密:用户标识符、设备标识符与端点地址在 MAA 本地以加密形式存储,服务端应同样以安全方式保管用户标识与设备绑定关系。
八、总结
MAA 远程控制协议的设计极简而实用:MAA 主动轮询的模型免去了服务端到客户端的反向连接与内网穿透需求;匿名端点配合用户/设备双标识,让注册、验证、下发、上报四个环节可以在任意后端技术栈上轻松实现。理解 RemoteControlService.cs 中的"轮询 + 双队列执行 + 结果上报"闭环,以及 RemoteControl.cs 中五个配置项的语义,即可在此基础上构建 QQBot、网站控制台、消息推送机器人等任意形态的 MAA 远程管理平台。更多协议细节可查阅仓库中的 英文版协议文档,多语言文档位于 docs 目录下的各语言protocol/子目录。
【免费下载链接】MaaAssistantArknights《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients.项目地址: https://gitcode.com/GitHub_Trending/ma/MaaAssistantArknights
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考