MAA 远程控制协议开发指南:构建基于 HTTP 的 MaaAssistantArknights 任务调度服务
【免费下载链接】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 实例下发任务、接收执行结果,从而实现 QQ 机器人、网页管理后台等形态的远程调度。本文基于仓库中的 远程控制协议规范,结合 RemoteControlService.cs 等源码实现,完整讲解协议的数据格式、任务类型、配置方式与两类端到端接入示例。读完后,你将能够独立实现一个合规的 MAA 远程控制服务端。
协议概览:两个端点,一种轮询模型
远程控制的核心是一个简单的请求-响应轮询模型。被控制的 MAA 客户端扮演"轮询方",而你的服务端只需要提供两个匿名可访问的 HTTP(S) Web 端点:
| 端点 | 方向 | 作用 |
|---|---|---|
| 任务获取端点(Task Retrieval Endpoint) | MAA → 服务端 | MAA 以固定间隔持续轮询,获取待执行任务并顺序执行 |
| 任务汇报端点(Task Reporting Endpoint) | MAA → 服务端 | MAA 完成任务后,向服务端汇报执行结果与附加数据 |
两个端点均由服务端提供,路径完全自由,例如https://your-control-host.net/maa/getTask与https://your-control-host.net/maa/reportStatus。MAA 在配置界面中分别填入这两个地址即可建立连接。
在 RemoteControlService.cs 中可以看到轮询的实现:PollJobTaskLoop每经过RemoteControlPollIntervalMs(默认 1000ms)就向任务获取端点发起一次 POST 请求,解析返回的tasks数组后,根据任务类型分别放入顺序任务队列(_sequentialTaskQueue)或即时任务队列(_instantTaskQueue),由两个独立的执行循环ExecuteSequentialJobLoop与ExecuteInstantJobLoop消费。
安全警告:请务必使用 HTTPS
协议规范明确要求:
如果端点使用 HTTP 协议,MAA 每次连接都会发出安全警告。在公网上部署明文传输服务极不推荐且危险,仅供测试使用。
这一警告在源码中同样落地——IsEndpointValid 会检查端点前缀:https://直接放行,http://放行但弹出"端点未启用 https,可能不安全"的提示(对应本地化字符串RemoteControlConnectionTestWarningHttpUnsafe),其他格式则判定为非法。此外 MAA 的设置界面还会展示一条醒目的安全提示(RemoteControlTooltips:"注意:随意填入未知来源的地址可能会导致您的账户受到损失")。由于该功能会执行一键长草、截图等敏感操作,服务端身份与链路加密缺一不可。
MAA 侧配置:五个配置项
远程控制功能位于 MAA 设置界面的"远程控制"分区(视图见 RemoteControlUserControl.xaml,配置模型见 RemoteControl.cs):
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
RemoteControlGetTaskEndpointUri | string | 空 | 任务获取端点地址 |
RemoteControlReportStatusUri | string | 空 | 任务汇报端点地址 |
RemoteControlUserIdentity | string | 空 | 用户标识符,由用户在设置中手动填写 |
RemoteControlDeviceIdentity | string | 空 | 设备标识符,由 MAA 自动生成(GUID),只读展示 |
RemoteControlPollIntervalMs | int | 1000 | 轮询间隔,单位毫秒 |
交互细节(对应 RemoteControlUserControlModel.cs):
- 设备标识符只读但可重新生成:界面上"设备标识符"输入框为
IsReadOnly="True",旁边的"重新生成"按钮调用 RegenerateDeviceIdentity,用Guid.NewGuid().ToString("N")生成新的设备标识。若你为用户管理设备,请务必提示用户把重新生成后的标识更新到你的服务端。 - 测试连接:设置界面的"测试连接"按钮调用 ConnectionTest,向任务获取端点发送一次
POST { user, device },根据 HTTP 状态码弹窗提示"连接测试成功"或"连接测试失败,原因: {0}"。这正是协议示例中"用户按下测试连接按钮"行为的来源。 - 配置持久化与加密存储:端点、用户标识、设备标识在写入配置时经过
SimpleEncryptionHelper.Encrypt加密后落盘,轮询间隔则以明文存储。从安全角度看,这些凭据至少不是以明文形式保存在配置文件中。 - 动态启停:一旦填入了合法的任务获取端点,
RemoteControlService.InitializePollJobTask()会立即启动三个后台循环(轮询、顺序任务执行、即时任务执行);若端点被清空,循环会自动退出并把_inited复位(见 InitializePollJobTask)。
注意:JSON 文件不支持注释。规范中的注释(如
// User identifier...)仅用于演示,切勿直接复制到生产配置中。
任务获取端点协议
请求格式
端点必须接受POST请求,Content-Type=application/json,请求体为:
{ "user": "ea6c39eb-a45f-4d82-9ecc-33a7bf2ae4dc", "device": "f7cd9682-3de9-4eef-9137-ec124ea9e9ec" }user:用户在 MAA 设置中填写的用户标识符。device:MAA 自动生成的设备标识符。- 除这两个字段外,你可以按需增加自定义字段,但 MAA 只发送
user和device两者。
在源码中,该请求由 PollJobTaskLoop 通过Instances.HttpService.PostAsJsonAsync(endpoint, new { user = uid, device = did })发送,字段与规范完全一致。
响应格式与任务类型
端点必须返回至少包含tasks字段的 JSON,且当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,用于在任务汇报时标识该任务。MAA 内部维护_enqueueTaskIds列表,对相同 ID 的任务不会重复执行(见 PollJobTaskLoop),因此你的端点应当"可重入"——即便重复返回同一个任务列表,也不会导致任务被执行两次。这也意味着每个任务 ID 应当全局唯一(如使用 UUID)。type:任务类型,决定 MAA 执行何种操作,见下表。params:可选参数,仅配置修改类任务使用,值为字符串。
顺序任务(Sequential Tasks)
顺序任务进入_sequentialTaskQueue,严格按下发顺序排队执行。例如先下发招募任务再下发截图任务,截图必然在招募完成后才执行。任务执行期间 MAA 会通过_currentSequentialTaskId记录当前正在执行的任务 ID(见 ExecuteSequentialJobLoop),该状态会被即时任务读取。
| 任务类型 | 行为 | 备注 |
|---|---|---|
LinkStart | 执行"一键长草" | 等价于主界面的一键长草按钮,执行全部已勾选任务 |
LinkStart-Base | 只执行基建换班 | 忽略主界面勾选,立即执行对应子功能 |
LinkStart-WakeUp | 只执行唤醒 | 同上 |
LinkStart-Combat | 只执行刷理智 | 同上 |
LinkStart-Recruiting | 只执行公招 | 同上 |
LinkStart-Mall | 只执行领取信用及购物 | 同上 |
LinkStart-Mission | 只执行领取奖励 | 同上 |
LinkStart-AutoRoguelike | 只执行自动肉鸽 | 同上 |
LinkStart-Reclamation | 只执行生息演算 | 同上 |
Toolbox-GachaOnce | 工具箱单抽 | 对应GachaOnce |
Toolbox-GachaTenTimes | 工具箱十连 | 对应GachaTenTimes |
CaptureImage | 截图当前模拟器画面 | 截图以 Base64 字符串放入汇报的payload;截图可达数十 MB,注意网关请求体大小限制 |
Settings-ConnectAddress | 修改连接地址 | params为新地址,等价于修改连接设置中的ConnectAddress属性 |
Settings-Stage1 | 修改关卡名 | params为新关卡名,写入作战任务的第一个关卡计划 |
LinkStart-[TaskName]与Settings-[SettingsName]系列的具体取值即上表所列,与规范::: note提示块内容一致,且与源码 PollJobTaskLoop 中的case分支一一对应。其中LinkStart-*系列在 LinkStart 方法中通过反射式SerializeTask将当前配置序列化后启动,源码注释明确其"不调用 StartScript、不使用模型里的列表、在结尾等待 RunningStatus"等设计特点。
安全边界:并非所有设置都可被远程修改。规范强调"For security, not all settings can be modified"。当前白名单仅有ConnectAddress与Stage1两个,其他任何设置都不接受远程修改。
即时任务(Instant Tasks)
即时任务进入_instantTaskQueue,由独立的执行循环处理,因此可在顺序任务执行期间插入运行。MAA 保证这类任务会快速返回结果,通常用于控制远程控制功能本身:
| 任务类型 | 行为 | 备注 |
|---|---|---|
CaptureImageNow | 立即截图 | 与CaptureImage类似,但不等待其他任务,立即执行并返回截图 |
StopTask | 停止当前任务 | 尝试结束正在运行的任务;若任务列表中还有其他任务则继续执行下一个。该任务不等待当前任务确认停止即返回,规范建议用心跳任务来确认停止是否生效 |
HeartBeat | 心跳 | 立即返回,payload为当前正在执行的顺序任务 ID;若无任务在执行则为空字符串 |
即时任务同样按下发顺序执行,但由于它们本身执行极快,顺序通常无关紧要。源码 ExecuteInstantJobLoop 展示了实现:HeartBeat直接返回_currentSequentialTaskId,StopTask调用AsstStop()后立即返回(源码注释:"无需等待,甩出任务即可返回,远端应该用心跳来确认界面卡死和取消是否成功"),CaptureImageNow通过AsstConnect连接后调用AsstGetFreshImage()获取截图并编码为 PNG Base64。
一个关键细节:截图任务的体积问题
CaptureImage/CaptureImageNow会将模拟器当前画面编码为 PNG 并以 Base64 字符串放进汇报请求。源码 ExecuteSequentialJobLoop 中的实现路径是AsstConnect → AsstGetFreshImageAsync → PngBitmapEncoder → Convert.ToBase64String。规范特别提醒:单张截图可能达到几十 MB,超出常见网关的默认请求体大小限制。如果你的服务部署在 Nginx 等反向代理之后,务必为汇报端点调大client_max_body_size(或其他等价配置)。
任务汇报端点协议
当 MAA 完成任务后,会向汇报端点发送执行结果:
{ "user": "ea6c39eb-a45f-4d82-9ecc-33a7bf2ae4dc", "device": "f7cd9682-3de9-4eef-9137-ec124ea9e9ec", "task": "15be4725-5bd3-443d-8ae3-0a5ae789254c", "status": "SUCCESS", "payload": "" }字段说明:
user/device:同任务获取请求,用于服务端识别是哪个 MAA 实例在汇报。task:被汇报的任务 ID,对应任务获取响应中的id。status:任务执行结果,取值为SUCCESS或FAILED。一般情况下即使任务实际执行失败也会返回SUCCESS,仅当任务描述中特别说明的特殊情况(如CaptureImage截图失败、连接失败)才返回FAILED。从源码看,status的默认值即SUCCESS,仅当AsstConnect失败或截图为空时被置为FAILED。payload:附加数据,字符串类型,取决于任务类型——截图任务携带截图 Base64,心跳任务携带当前顺序任务 ID。
该端点的响应内容任意,MAA 不读取响应体、不检查状态码;汇报请求失败时,MAA 仅在日志中记录错误(源码中对应"RemoteControlService report task failed."的Log.Logger.Error调用)。这意味着汇报端点可以设计为"异步落库即返回"以降低对 MAA 侧的影响。
端到端示例一:用 QQ 机器人控制 MAA
规范给出了完整的参考实现思路。开发者 A 希望用 QQ 机器人远程控制用户的 MAA,于是在公网部署了两个端点:
https://myqqbot.com/maa/getTask https://myqqbot.com/maa/reportStatus完整流程:
- 注册与识别:
getTask接口对所有请求返回200 OK和空的tasks列表。每次收到请求时检查数据库是否存在该设备记录,若不存在则把device与user记录入库——该接口同时承担了用户注册职能。 - 绑定:机器人在 QQ 频道提供提交
deviceId的命令。用户按要求在 MAA 的"用户标识符"栏填写自己的 QQ 号,并通过 QQ 聊天把 MAA 生成的"设备标识符"发送给机器人。机器人收到后,根据消息中的 QQ 号在数据库查对应记录;查不到则提示用户先配置 MAA。 - 验证:由于 MAA 配置完成后会持续发送轮询请求,用户通过 QQ 提交设备标识时,数据库理应已存在对应记录。机器人将这条记录标记为"已验证",此后该
device+user组合的getTask请求才会返回真实任务列表。 - 下发任务:用户在 QQ 发送命令后,机器人把任务写入数据库,
getTask在下一次轮询时将其返回。示例中机器人还会在每个用户命令后自动附带一个截图任务,以便回传执行画面。 - 结果回传:MAA 执行完毕后调用
reportStatus汇报,机器人解析结果并向用户发送 QQ 消息、展示截图。
这个示例的巧妙之处在于:轮询请求本身被复用为在线注册与心跳机制——只要 MAA 配置正确,服务端就能持续感知实例的在线状态。
端到端示例二:用网页后台批量管理 MAA
开发者 B 为批量管理 MAA 实例搭建了带用户体系的网站,同样只提供两个匿名端点:
https://mywebsite.com/maa/getTask https://mywebsite.com/maa/reportStatus与示例一的差异点:
- 用户键:网站将"用户键"(
User Key,随机字符串)展示给用户,用户需将其填入 MAA 的"用户标识符",同时把"设备标识符"填回网站上的输入框。 - 鉴权通过"行为"实现:网站只有在 MAA 连接创建成功后才让
getTask返回200 OK,否则返回401 Unauthorized。用户若在 MAA 中填错信息并点击"测试连接"按钮,就会收到"连接测试失败"通知——这正是 MAA 侧 ConnectionTest 根据非 2xx 状态码弹出失败提示的行为。 - 任务管理:用户可在网站上发布任务、排队、查看截图,实现方式与 QQ 机器人示例同构——都是通过
getTask下发、reportStatus回收结果。
服务端实现要点总结
综合规范与源码,一个合规服务端需要满足以下硬性要求:
- 两个匿名端点:均接受
POST application/json,无需任何鉴权头(鉴权通过user/device与 HTTP 状态码隐式完成)。 tasks字段不可缺失:getTask响应缺少tasks数组会被视为连接无效。- 任务 ID 全局唯一且可重入:MAA 按 ID 去重,同一 ID 不会重复执行;端点应能安全地反复返回相同任务列表。
- 立即执行的快捷任务:
StopTask需要配合HeartBeat使用才能可靠确认停止状态。 - 注意负载:单实例默认每 1 秒轮询一次(可调
RemoteControlPollIntervalMs),大规模部署时应统计请求量并合理设计数据库读写;截图任务会带来数十 MB 的汇报请求,需要放大代理与网关的请求体上限。
作为被控制端,MAA 侧的全部逻辑集中在 RemoteControlService.cs,配置项定义在 RemoteControl.cs,设置界面见 RemoteControlUserControl.xaml。需要查阅更多协议细节时,可对照多语言版本规范文档(中文版、日文版)以及 MAA 总协议索引 protocol/README.md。
【免费下载链接】MaaAssistantArknights《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients.项目地址: https://gitcode.com/GitHub_Trending/ma/MaaAssistantArknights
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考