Skip to content

代码编写指南

interface.json 编写

interface.json 是 MaaFramework 的标准化项目结构声明,旨在提供GUI界面菜单配置。 interface_cli.json 则是为命令行版本提供配置。

MaaLYSK主要采用的协议版本为interface V2,具体编写规则参照MaaFramework官方的协议文档:Project Interface V2 协议

task字段配置

字段类型默认值说明
namestring-任务显示名称
entrystring-任务入口接口
default_checkbooleanfalse是否默认选中
descriptionstringnull任务说明文档(支持富文本)
optionstring-任务可选选项

option字段配置

  • input字段配置
json
{
    "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字段配置
json
{
    "option": {
        "选择探测星系": {
            "type": "select",
            "cases": [
                {
                    "name": "银砂星系",
                    "pipeline_override": {
                        "星间探测准备": {
                            "next": [
                                "是否探测中",
                                "收取探测信号",
                                "银砂星系_开始探测",
                                "[JumpBack]前往银砂星系"
                            ]
                        },
                        "收取探测信号": {
                            "next": [
                                "银砂星系_开始探测",
                                "[JumpBack]前往银砂星系",
                                "[JumpBack]星间探测点击"
                            ]
                        }
                    }
                },
                {
                    "name": "辉曜星系",
                    "pipeline_override": {
                        "星间探测准备": {
                            "next": [
                                "是否探测中",
                                "收取探测信号",
                                "辉曜星系_开始探测",
                                "[JumpBack]前往辉曜星系"
                            ]
                        },
                        "收取探测信号": {
                            "next": [
                                "辉曜星系_开始探测",
                                "[JumpBack]前往辉曜星系",
                                "[JumpBack]星间探测点击"
                            ]
                        }
                    }
                }
            ]
        }
    }
}
  • switch字段配置
json
{
    "option": {
        "是否使用加速魔方": {
            "type": "switch",
            "cases": [
                {
                    "name": "No",
                    "pipeline_override": {
                        "是否探测中": {
                            "next": ["stop"]
                        }
                    }
                },
                {
                    "name": "Yes",
                    "pipeline_override": {
                        "是否探测中": {
                            "next": ["结束探测"]
                        }
                    }
                }
            ]
        }
    }
}
  • checkbox字段配置
json
{
    "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 协议:

json
{
    "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 | 暂无翻译

    一个按标准资源结构存储的文件夹,包含 pipelinemodelimage 等文件夹。

  • Resource | 资源

    多个 Bundle 按一定次序加载的资源结构。

  • PI | 项目接口

    ProjectInterface, 符合 ProjectInterface 定义 的标准化项目结构声明。

  • OCR | 文字识别

    对包含文本内容的图像进行处理和识别,并提取其中所包含的文字及排版信息。

  • ROI | 感兴趣区域

    定义图像识别边界,仅在该区域内进行相关图像处理。

  • BOX | 命中匹配区域

    识别命中后返回的匹配区域,表示“识别到了哪里”。

  • TARGET | 动作执行目标区域

    动作执行目标区域,用于决定“点哪里 / 滑哪里 / 在哪里滚动”。

    target 默认值为 true,即使用当前识别得到的 box 作为 target;也可以由用户显式指定固定点、固定区域或其他节点。

举个例子
  • Interface.jsonresource\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。

  • 在其中,经常要进行识图文字识别识色来判断在什么页面,执行什么操作,这便涉及到ROIBOXTARGET

    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

    其中:

    • 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列表为空)后任务结束

正则用语小结

匹配任何汉字

json
"expected": "[\u4e00-\u9fa5]"

匹配HH:DD:SS的时间字符串,可为任何数字

json
"expected": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$"

匹配2位数

json
"expected": "^\d{2}$

匹配1-99

json
"expected": ["^[1-9]$", "^[1-9][0-9]$"],

通用节点模块编写

入口节点模块

主要是主界面、手机等模块的入口导航,存放在base中,命名规则参照开发规范。

点击xxxx页面确认往往为配套出现,点击A节点的next列表为A界面确认A页面确认next列表为空。

1、点击xx:较为通用的入口,一般是主界面、手机等界面的入口。如:日程、思念、商城、约会、手机信息等。

2、xx页面确认:确认在xx页面的节点,点击和确认操作的名称需配套。如点击日程,确认不要取成任务页面确认,而是日程页面确认

json
   "点击日程": {
      "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:一般是左下角点点就可以出去的节点,如”点击空白处继续“、”牵绊度提升“、”获得奖励“等。

json
   "退出获得奖励popop": {
      "recognition": {
         "type": "OCR",
         "param": {
            "expected": ["获得奖励", "奖励", "获得"],
            "roi": [273, 376, 190, 79]
         }
      },
      "action": {
         "type": "Click",
         "param": {
            "target": [19, 936, 12, 13]
         }
      },
      "next": []
   }

2、确定返回xxpopop:屏幕中央,有确定、取消键,且选项选为确定的节点。

例:确定返回快乐出游popop:喵喵牌、娃娃机直接退出,选择确定按钮

json
   "确定返回快乐出游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:屏幕中央,有确定、取消键,且选项选为取消的节点。

例:取消返回陪我一起:不返回陪伴,选择取消按钮

json
   "取消返回陪我一起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、其他弹窗模块,如网络重连等。

json
    "断线重连popop": {
        "recognition": {
            "type": "TemplateMatch",
            "param": {
                "template": ["popop/再次重连.png"],
                "roi": [379, 628, 193, 68]
            }
        },
        "action": {
            "type": "Click",
            "param": {}
        },
        "focus": "断线重连失败,尝试再次重连"
    }

左上角返回模块

主要是各页面左上角退出的截图,存放在mainmap中。

json
    "导航回主页面(思念)": {
        "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中,因为深空选人页面是固定的,可以反复复用。

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": ["进入喵喵牌页面"]
    }

interfacetask中,不同task后续节点不同,利用pipeline_override,将上述模块next节点导航到不同模块。

json
{
    "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的效果。

json
{
    "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+通用入口节点的模式,可以节省很多节点!

json
    "进入日常收尾": {
        "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中:

json
    "返回上一级页面": {
        "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,不进行任何识别直接盲点的保底,也导致在某些地方可能会卡住。预计后期酱所有弹窗都做成识别,取消盲点的节点。