代码编写指南
interface.json 编写
interface.json 是 MaaFramework 的标准化项目结构声明,旨在提供GUI界面菜单配置。 interface_cli.json 则是为命令行版本提供配置。
MaaLYSK主要采用的协议版本为interface V2,具体编写规则参照MaaFramework官方的协议文档:Project Interface V2 协议
task字段配置
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | string | - | 任务显示名称 |
entry | string | - | 任务入口接口 |
default_check | boolean | false | 是否默认选中 |
description | string | null | 任务说明文档(支持富文本) |
option | string | - | 任务可选选项 |
option字段配置
- input字段配置
{
"option": {
"账号前3位": {
"type": "input",
"inputs": [
{
"name": "account",
"description": "可只填1-2位连续内容,避开易混淆的字符(如Oo、Ss大小写不分、1lI认错等)",
"label": "帐号前3位中的任意内容",
"pipeline_type": "string",
"default": "xxx"
}
],
"pipeline_override": {
"选择登录账号": {
"expected": ["{account}"],
"focus": {
"Node.Action.Succeeded": "登录账号:{account}"
}
}
}
}
}
}- select字段配置
{
"option": {
"选择探测星系": {
"type": "select",
"cases": [
{
"name": "银砂星系",
"pipeline_override": {
"星间探测准备": {
"next": [
"是否探测中",
"收取探测信号",
"银砂星系_开始探测",
"[JumpBack]前往银砂星系"
]
},
"收取探测信号": {
"next": [
"银砂星系_开始探测",
"[JumpBack]前往银砂星系",
"[JumpBack]星间探测点击"
]
}
}
},
{
"name": "辉曜星系",
"pipeline_override": {
"星间探测准备": {
"next": [
"是否探测中",
"收取探测信号",
"辉曜星系_开始探测",
"[JumpBack]前往辉曜星系"
]
},
"收取探测信号": {
"next": [
"辉曜星系_开始探测",
"[JumpBack]前往辉曜星系",
"[JumpBack]星间探测点击"
]
}
}
}
]
}
}
}- switch字段配置
{
"option": {
"是否使用加速魔方": {
"type": "switch",
"cases": [
{
"name": "No",
"pipeline_override": {
"是否探测中": {
"next": ["stop"]
}
}
},
{
"name": "Yes",
"pipeline_override": {
"是否探测中": {
"next": ["结束探测"]
}
}
}
]
}
}
}- checkbox字段配置
{
"option": {
"娃娃机抓取男主": {
"type": "checkbox",
"default_case": ["沈星回", "黎深", "祁煜", "秦彻", "夏以昼"],
"cases": [
{
"name": "沈星回",
"pipeline_override": {
"娃娃机_选择沈星回": {
"enabled": true
}
}
},
{
"name": "黎深",
"pipeline_override": {
"娃娃机_选择黎深": {
"enabled": true
}
}
},
{
"name": "祁煜",
"pipeline_override": {
"娃娃机_选择祁煜": {
"enabled": true
}
}
},
{
"name": "秦彻",
"pipeline_override": {
"娃娃机_选择秦彻": {
"enabled": true
}
}
},
{
"name": "夏以昼",
"pipeline_override": {
"娃娃机_选择夏以昼": {
"enabled": true
}
}
}
]
}
}
}description文字协议
description(任务描述)默认为md语法。
| 标记 | 效果 | 示例 |
|---|---|---|
<br> | 换行 下一行 | 文字<br>下一行文字 |
<span style=\"color:red\">...</span> | 红色文字 | <span style=\"color:red\">红色文字</span> |
*...* | 斜体 | *斜体文字* |
**...** | 粗体 | **粗体文字** |
~~...~~ | ~~删除线文字~~ |
NOTE
由于
*...*默认是斜体,想要显示*符号而不是打斜体,需使用\\。如\\*。description由于本质上是一个字符串,根据代码语法,在使用md的文字颜色语法时需要多打2个斜杠转义
"号。
mfaa特有:
| 标记 | 效果 | 示例 |
|---|---|---|
[color:颜色]...[/color] | 红色文字 | [color:red]红色文字[/color] |
[b]...[/b] | 粗体 | [b]粗体文字[/b] |
[i]...[/i] | 斜体 | [i]斜体文字[/i] |
[u]...[/u] | 下划线 | [u]下划线文字[/u] |
[s]...[/s] | 删除线 | [s]删除线文字[/s] |
Pipeline 编写指南
什么是Pipeline?
Pipeline(任务流水线)是 MaaFramework 的核心概念,通过 JSON 格式描述自动化任务的执行流程。每个 Pipeline 由多个节点(Node)组成,每个节点主要包括:
- 识别算法(recognition):如何识别屏幕上的目标
- 执行动作(action):识别成功后要执行的操作
- 后继节点(next):完成当前节点后要执行的下一步
协议版本
MaaLYSK基于 MaaFramework v5.1+,使用 Pipeline v2 协议:
{
"NodeName": {
"recognition": {
"type": "OCR",
"param": {
"roi": [100, 100, 200, 50],
"expected": ["确认"]
}
},
"action": {
"type": "Click"
},
"next": ["NextNode"]
}
}术语介绍
Node | 节点
符合 任务流水线(Pipeline)协议 的一个完整的 Json Object。
Task | 任务
若干 Node 按一定次序相连的逻辑顺序结构,表示从头到尾的整个过程。
Entry | 入口
一个 Task 中的第一个 Node。
Pipeline | 流水线
一个
pipeline文件夹中包含的 Node 全体。Bundle | 暂无翻译
一个按标准资源结构存储的文件夹,包含
pipeline、model、image等文件夹。Resource | 资源
多个 Bundle 按一定次序加载的资源结构。
PI | 项目接口
ProjectInterface, 符合 ProjectInterface 定义 的标准化项目结构声明。OCR | 文字识别
对包含文本内容的图像进行处理和识别,并提取其中所包含的文字及排版信息。
ROI | 感兴趣区域
定义图像识别边界,仅在该区域内进行相关图像处理。
BOX | 命中匹配区域
识别命中后返回的匹配区域,表示“识别到了哪里”。
TARGET | 动作执行目标区域
动作执行目标区域,用于决定“点哪里 / 滑哪里 / 在哪里滚动”。
target默认值为true,即使用当前识别得到的box作为target;也可以由用户显式指定固定点、固定区域或其他节点。
举个例子
Interface.json和resource\tasks下的json文件共同组成PI。GUI任务列表里的
✈️恋与深空启动、🛍️商城日常领取是一个个TASK。想要实现TASK,最基本要在PI里声明这个从那个入口开始。
在interface中,需要写明:
json"task": [ { "name": "💤恋与深空关闭", "entry": "关闭游戏" } ]其中,
entry后面的关闭游戏就是一个NODE。node需要pipeline下面的.json文件中有定义。在pipeline中,这个NODE是这样的:
json"关闭游戏": { "recognition": { "type": "DirectHit", "param": {} }, "action": { "type": "DoNothing", "param": {} }, "next": ["[JumpBack]sub_关闭深空官服"], "focus": "关闭恋与深空官服" }一个个Node写出完整的逻辑链,就可以执行一个task。
在其中,经常要进行识图、文字识别、识色来判断在什么页面,执行什么操作,这便涉及到ROI、BOX和TARGET。
json"追踪哈特准备": { "recognition": { "type": "TemplateMatch", "param": { "template": ["fight/bounty_hunt/哈特.png"], "roi": [123, 310, 177, 255] } }, "action": { "type": "Click", "param": { "target": [223, 420, 55, 50] } }, "next": ["选择零点追踪关卡"] },利用上述代码,实现点击进入哈特页面:

其中:
ROI是红框,是在写代码时你框选的识图范围,这个尽量框大一些,避免有些边边没识别到导致识别失败。
BOX是绿框,是你截下图片后,进行调试时识别到的图片
哈特的大小。TARGET是蓝框,你写代码时设定的点击范围大小。不写
target字段、即默认为TRUE时,target大小即box(绿框)大小。
代码技巧
主要参照MAAFW官方文档里的任务流水线(Pipeline)协议-算法类型编写代码。
特殊机制说明
Next机制
- 顺序执行机制,依次检测
next列表中的节点 - 找到第一个匹配的节点后,执行其动作,并将找到的节点作为新的节点
next为空或直到timeout时间结束仍未找到匹配节点,任务结束
On_error机制
- 若定义了
on_error字段,且直到超时仍未找到的next列表的匹配节点,进入on_error - 若前方所有节点都无
jumpback节点,on_error执行结束后,任务结束,否则返回父节点
JumpBack 机制
- 标记为
[JumpBack]的节点执行完成(next为空)后,会返回到父节点继续执行 - 适用于异常处理、弹窗关闭等场景
- v5.9.0:如果jumpback的后续节点中,有节点直到超时仍未找到的
next列表的匹配节点,且on_error列表也为空,任务结束,不返回父节点
任务结束机制小结
next列表为空有
[JumpBack]节点:返回父节点无
[JumpBack]节点:任务结束next列表识别失败,无on_error列表任务直接结束
next列表识别失败,有on_error列表,on_error无匹配节点任务直接结束
next列表识别失败,有on_error列表,on_error有匹配节点有
[JumpBack]节点:执行完on_error匹配节点(至next列表为空)后返回父节点无
[JumpBack]节点:执行完on_error匹配节点(至next列表为空)后任务结束
正则用语小结
匹配任何汉字
"expected": "[\u4e00-\u9fa5]"匹配HH:DD:SS的时间字符串,可为任何数字
"expected": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$"匹配2位数
"expected": "^\d{2}$匹配1-99
"expected": ["^[1-9]$", "^[1-9][0-9]$"],通用节点模块编写
入口节点模块
主要是主界面、手机等模块的入口导航,存放在base中,命名规则参照开发规范。
点击xx和xx页面确认往往为配套出现,点击A节点的next列表为A界面确认,A页面确认的next列表为空。
1、点击xx:较为通用的入口,一般是主界面、手机等界面的入口。如:日程、思念、商城、约会、手机信息等。
2、xx页面确认:确认在xx页面的节点,点击和确认操作的名称需配套。如点击日程,确认不要取成任务页面确认,而是日程页面确认。
"点击日程": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": ["mainmap/进入日程.png"],
"roi": [563, 559, 191, 186]
}
},
"action": {
"type": "Click",
"param": {}
},
"next": ["日程页面确认"]
},
"日程页面确认": {
"recognition": {
"type": "OCR",
"param": {
"expected": ["任务"],
"roi": [74, 1175, 125, 79]
}
},
"next": []
}弹窗退出模块
主要是各类弹窗的退出点击,存放在popop中,命名规则参照开发规范。
1、退出xxpopop:一般是左下角点点就可以出去的节点,如”点击空白处继续“、”牵绊度提升“、”获得奖励“等。
"退出获得奖励popop": {
"recognition": {
"type": "OCR",
"param": {
"expected": ["获得奖励", "奖励", "获得"],
"roi": [273, 376, 190, 79]
}
},
"action": {
"type": "Click",
"param": {
"target": [19, 936, 12, 13]
}
},
"next": []
}2、确定返回xxpopop:屏幕中央,有确定、取消键,且选项选为确定的节点。
例:确定返回快乐出游popop:喵喵牌、娃娃机直接退出,选择确定按钮
"确定返回快乐出游popop": {
"recognition": {
"type": "And",
"param": {
"all_of": [
{
"recognition": {
"type": "OCR",
"param": {
"expected": ["确定"],
"roi": [402, 635, 156, 67]
}
}
},
{
"recognition": {
"type": "OCR",
"param": {
"expected": [
"终止本次游玩",
"退出将终止本次游玩,确定要退出吗?"
],
"roi": [149, 512, 417, 125]
}
}
}
],
"box_index": 0
}
},
"action": {
"type": "Click",
"param": {}
},
"next": []
}3、取消返回xxpopop:屏幕中央,有确定、取消键,且选项选为取消的节点。
例:取消返回陪我一起:不返回陪伴,选择取消按钮
"取消返回陪我一起popop": {
"recognition": {
"type": "And",
"param": {
"all_of": [
{
"recognition": {
"type": "OCR",
"param": {
"expected": ["取消"],
"roi": [101, 580, 270, 261]
}
}
},
{
"recognition": {
"type": "OCR",
"param": {
"expected": [
"继续返回与.{2,4}的.{2,4}陪伴",
"与.{2,4}的.{2,4}陪伴"
],
"roi": [149, 512, 417, 125]
}
}
}
],
"box_index": 0
}
},
"action": {
"type": "Click",
"param": {
"target_offset": [20, 14, -41, -27]
}
},
"next": ["返回等待"]
}4、其他弹窗模块,如网络重连等。
"断线重连popop": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": ["popop/再次重连.png"],
"roi": [379, 628, 193, 68]
}
},
"action": {
"type": "Click",
"param": {}
},
"focus": "断线重连失败,尝试再次重连"
}左上角返回模块
主要是各页面左上角退出的截图,存放在mainmap中。
"导航回主页面(思念)": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": "mainmap/导航回主页面(思念).png",
"green_mask": true,
"threshold": 0.45,
"roi": [0, 0, 88, 132]
}
},
"pre_delay": 500,
"action": {
"type": "Click",
"param": {
"target_offset": [3, 5, -6, -10]
}
},
"next": ["返回等待"]
}选人模块
选人模块储存在choose_character.json中,因为深空选人页面是固定的,可以反复复用。
"选择沈星回": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": ["沈星回.png"],
"roi": [196, 193, 99, 106]
}
},
"action": {
"type": "Click",
"param": {}
},
"enabled": false,
"max_hit": 1,
"focus": {
"Node.Action.Succeeded": "进入沈星回喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择黎深": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": ["黎深.png"],
"roi": [380, 411, 122, 114]
}
},
"action": {
"type": "Click",
"param": {}
},
"enabled": false,
"max_hit": 1,
"focus": {
"Node.Action.Succeeded": "进入黎深喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择祁煜": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": ["祁煜.png"],
"roi": [186, 624, 118, 111]
}
},
"action": {
"type": "Click",
"param": {}
},
"enabled": false,
"max_hit": 1,
"focus": {
"Node.Action.Succeeded": "进入祁煜喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择秦彻": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": ["秦彻.png"],
"roi": [422, 819, 110, 111]
}
},
"action": {
"type": "Click",
"param": {}
},
"enabled": false,
"max_hit": 1,
"focus": {
"Node.Action.Succeeded": "进入秦彻喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择夏以昼": {
"recognition": {
"type": "TemplateMatch",
"param": {
"template": ["夏以昼.png"],
"roi": [202, 1013, 94, 106]
}
},
"action": {
"type": "Click",
"param": {}
},
"enabled": false,
"max_hit": 1,
"focus": {
"Node.Action.Succeeded": "进入夏以昼喵喵牌"
},
"next": ["进入喵喵牌页面"]
}在interface的task中,不同task后续节点不同,利用pipeline_override,将上述模块next节点导航到不同模块。
{
"task": [
{
"name": "🐱周常喵喵牌",
"entry": "进入喵喵牌",
"default_check": false,
"option": ["囤积达上限是否继续"],
"description": "可选囤积达上限是否继续,如果不继续的话记得检查2400巧克力有没有达到",
"pipeline_override": {
"选择沈星回": {
"focus": {
"Node.Action.Succeeded": "进入沈星回喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择黎深": {
"focus": {
"Node.Action.Succeeded": "进入黎深喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择祁煜": {
"focus": {
"Node.Action.Succeeded": "进入祁煜喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择秦彻": {
"focus": {
"Node.Action.Succeeded": "进入秦彻喵喵牌"
},
"next": ["进入喵喵牌页面"]
},
"选择夏以昼": {
"focus": {
"Node.Action.Succeeded": "进入夏以昼喵喵牌"
},
"next": ["进入喵喵牌页面"]
}
}
}
]
}由于默认enabled字段为false,不做单选/多选的话,可在上述代码中直接覆盖enabled为true,也可在option中打开开关,实现指定xx做、只做xx、xx的效果。
{
"option": {
"喵喵牌游玩男主": {
"type": "checkbox",
"default_case": ["沈星回", "黎深", "祁煜", "秦彻", "夏以昼"],
"cases": [
{
"name": "沈星回",
"pipeline_override": {
"选择沈星回": {
"enabled": true
}
}
},
{
"name": "黎深",
"pipeline_override": {
"选择黎深": {
"enabled": true
}
}
},
{
"name": "祁煜",
"pipeline_override": {
"选择祁煜": {
"enabled": true
}
}
},
{
"name": "秦彻",
"pipeline_override": {
"选择秦彻": {
"enabled": true
}
}
},
{
"name": "夏以昼",
"pipeline_override": {
"选择夏以昼": {
"enabled": true
}
}
}
]
}
}
}任务自动导航实现
实现自动导航,就是在任务开始前退出各种各样的弹窗、返回到上一级页面,然后利用入口节点进入到对应页面后,再开始任务。
例:
日常收尾任务,用点击日程点到日程中去,返回上一级页面退出各类弹窗并点左上角返回,用一个日常收尾准备检测是否已在日程的每日任务页面后,开始任务。
NOTE
这里也体现出了点击日程单独写的作用:如果只用导航回主页面开始的话,完成日常、周常、密约等,都得写一个点击日程的操作。利用jumpback+通用入口节点的模式,可以节省很多节点!
"进入日常收尾": {
"recognition": {
"type": "DirectHit",
"param": {}
},
"action": {
"type": "DoNothing",
"param": {}
},
"next": [
"日常收尾准备",
"[JumpBack]点击日程",
"[JumpBack]返回上一级页面",
"[JumpBack]导航回主页面"
]
},
"日常收尾准备": {
"recognition": {
"type": "OCR",
"param": {
"expected": ["任务"],
"roi": [74, 1175, 125, 79]
}
},
"action": {
"type": "Click",
"param": {}
},
"repeat": 2,
"repeat_delay": 200,
"next": ["日常收尾点击每日"]
}返回上一级页面节点存放在mainmap中:
"返回上一级页面": {
"next": [
"导航回上级页面(绿码白)",
"导航回上级页面(绿码黑)",
"导航回主页面(主线绿码)",
"导航回主页面(思念)",
"导航回主页面(体力)",
"导航回主页面(约会)",
"导航回主页面(战斗)",
"导航回主页面(主线)",
"导航回主页面(商城)",
"导航回主页面(芯核分解)",
"退出剧情回顾popop",
"退出兑换码页面popop",
"退出娃娃机结算popop",
"退出喵喵牌结算popop",
"退出快乐出游结算popop",
"退出新娃娃获得popop",
"退出获得奖励popop",
"退出牵绊度提升popop",
"退出点击空白继续下popop",
"退出点击空白继续popop",
"退出分享页面popop",
"退出追击完成popop",
"退出零点追踪追击popop",
"退出道具使用popop",
"退出升级成功popop",
"退出突破成功popop",
"退出品阶提升popop",
"退出手账编辑popop",
"退出手账保存popop",
"退出手账保存1popop",
"网络连接失败popop",
"断线重连popop",
"账号在别处登录popop",
"取消返回陪我一起popop",
"取消返回快乐出游popop",
"确定返回快乐出游popop",
"确定返回手账保存popop",
"确定返回剧情popop",
"退出点击popop"
]
}后续计划
现在还加了一个退出点击popop,不进行任何识别直接盲点的保底,也导致在某些地方可能会卡住。预计后期酱所有弹窗都做成识别,取消盲点的节点。