feat: convert MAA GUI profiles to maa-cli task config file - #559
Conversation
Introduce gui::convert for TaskQueue profiles with StartUp and Fight mappings, plus a default GUI profile fixture for regression tests. Co-authored-by: Cursor <cursoragent@cursor.com>
Wire gui::convert behind convert -g, map Infrast/Recruit/Mall/Award/ Roguelike/Reclamation from GUI TaskQueue, and align StartUp output with daily.json input templates. Co-authored-by: Cursor <cursoragent@cursor.com>
The --gui flag targets MAA GUI profile JSON files; warn users when the input uses another format. Co-authored-by: Cursor <cursoragent@cursor.com>
Prompt users to pick a configuration when converting GUI profiles with multiple entries; use Current as the default in batch mode. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Hey - 我发现了 1 个问题,并给出了一些总体反馈:
- GUI 转换辅助函数在每个子模块中都重新实现了大量几乎相同的测试工具(临时文件读写、JSON 加载/存储);建议将这些工具集中到一个共享的测试辅助模块中,以减少重复并让测试更易维护。
- 在
convert_task中,未知的 GUI$type条目会被静默丢弃;可以考虑记录日志或以其他方式暴露被跳过的任务类型,这样用户就能理解为什么某些 GUI 任务没有出现在生成的配置中。 - 通过
SelectD的交互式select_configuration提示在非交互环境(例如自动化场景)中会阻塞;建议增加一个标志或基于环境变量的覆盖机制,当存在多个配置项时可以以非交互方式选择配置。
给 AI Agent 的提示
请根据以下代码审查评论进行修改:
## 总体评论
- GUI 转换辅助函数在每个子模块中都重新实现了大量几乎相同的测试工具(临时文件读写、JSON 加载/存储);建议将这些工具集中到一个共享的测试辅助模块中,以减少重复并让测试更易维护。
- 在 `convert_task` 中,未知的 GUI `$type` 条目会被静默丢弃;可以考虑记录日志或以其他方式暴露被跳过的任务类型,这样用户就能理解为什么某些 GUI 任务没有出现在生成的配置中。
- 通过 `SelectD` 的交互式 `select_configuration` 提示在非交互环境(例如自动化场景)中会阻塞;建议增加一个标志或基于环境变量的覆盖机制,当存在多个配置项时可以以非交互方式选择配置。
## 具体评论
### 评论 1
<location path="crates/maa-cli/src/config/gui.rs" line_range="71-80" />
<code_context>
+ Ok(object!("tasks" => tasks??))
+}
+
+fn convert_task(task: &MAAValue) -> Result<Option<MAAValue>> {
+ // GUI task discriminator, e.g. "FightTask"
+ let type_tag = task
+ .get("$type")
+ .and_then(|v| v.as_str())
+ .context("GUI task missing $type")?;
+ match type_tag {
+ "StartUpTask" => start_up::convert_start_up_task(task),
+ "FightTask" => fight::convert_fight_task(task),
+ "InfrastTask" => infrast::convert_infrast_task(task),
+ "RecruitTask" => recruit::convert_recruit_task(task),
+ "MallTask" => mall::convert_mall_task(task),
+ "AwardTask" => award::convert_award_task(task),
+ "RoguelikeTask" => roguelike::convert_roguelike_task(task),
+ "ReclamationTask" => reclamation::convert_reclamation_task(task),
+ _ => Ok(None),
+ }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 静默丢弃未知的 GUI 任务类型可能会隐藏配置问题;建议至少记录一条日志。
在 `convert_task` 中,未匹配到的 `$type` 会返回 `Ok(None)`,因此相关任务会被静默丢弃:
```rust
match type_tag {
// ...
"ReclamationTask" => reclamation::convert_reclamation_task(task),
_ => Ok(None),
}
```
为避免隐藏配置错误或新增加的任务类型,请至少增加一条 `log::warn!`,包含 `$type`(以及可能的标识符如 `Name`)。你可以暂时保留当前宽松的行为,之后再通过一个标志在“警告”与“未知类型视为硬错误”之间切换。
建议实现:
```rust
fn convert_task(task: &MAAValue) -> Result<Option<MAAValue>> {
// GUI task discriminator, e.g. "FightTask"
let type_tag = task
.get("$type")
.and_then(|v| v.as_str())
.context("GUI task missing $type")?;
match type_tag {
"StartUpTask" => start_up::convert_start_up_task(task),
"FightTask" => fight::convert_fight_task(task),
"InfrastTask" => infrast::convert_infrast_task(task),
"RecruitTask" => recruit::convert_recruit_task(task),
"MallTask" => mall::convert_mall_task(task),
"AwardTask" => award::convert_award_task(task),
"RoguelikeTask" => roguelike::convert_roguelike_task(task),
"ReclamationTask" => reclamation::convert_reclamation_task(task),
_ => {
let name = task
.get("Name")
.and_then(|v| v.as_str())
.unwrap_or("<unknown>");
log::warn!(
"Unknown GUI task type `{}` (Name: `{}`); skipping task",
type_tag,
name
);
Ok(None)
}
}
}
```
如果本模块尚未使用 `log` crate,请确保:
1. 在 `Cargo.toml` 中已添加 `log` 依赖(很可能已经存在)。
2. 在可执行程序中初始化了某个日志实现(例如 `env_logger`、`tracing` 的桥接等),以确保 `log::warn!` 实际能输出日志。
由于函数签名与返回行为保持不变(未知类型仍返回 `Ok(None)`),因此不需要对调用方做进一步修改。
</issue_to_address>帮我变得更有用!请对每条评论点击 👍 或 👎,我会根据你的反馈改进后续审查。
Original comment in English
Hey - I've found 1 issue, and left some high level feedback:
- The GUI conversion helpers reimplement a lot of near-identical test utilities (temp file write/read, JSON load/store) in each submodule; consider centralizing these into a shared test helper to reduce repetition and keep the tests easier to maintain.
- Unknown GUI
$typeentries are silently dropped inconvert_task; it might be helpful to log or otherwise surface that a task type was skipped so users understand why some GUI tasks did not appear in the generated config. - The interactive
select_configurationprompt viaSelectDwill block in non-interactive contexts (e.g. automation); consider adding a flag or environment-based override to select a configuration non-interactively when multiple entries exist.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The GUI conversion helpers reimplement a lot of near-identical test utilities (temp file write/read, JSON load/store) in each submodule; consider centralizing these into a shared test helper to reduce repetition and keep the tests easier to maintain.
- Unknown GUI `$type` entries are silently dropped in `convert_task`; it might be helpful to log or otherwise surface that a task type was skipped so users understand why some GUI tasks did not appear in the generated config.
- The interactive `select_configuration` prompt via `SelectD` will block in non-interactive contexts (e.g. automation); consider adding a flag or environment-based override to select a configuration non-interactively when multiple entries exist.
## Individual Comments
### Comment 1
<location path="crates/maa-cli/src/config/gui.rs" line_range="71-80" />
<code_context>
+ Ok(object!("tasks" => tasks??))
+}
+
+fn convert_task(task: &MAAValue) -> Result<Option<MAAValue>> {
+ // GUI task discriminator, e.g. "FightTask"
+ let type_tag = task
+ .get("$type")
+ .and_then(|v| v.as_str())
+ .context("GUI task missing $type")?;
+ match type_tag {
+ "StartUpTask" => start_up::convert_start_up_task(task),
+ "FightTask" => fight::convert_fight_task(task),
+ "InfrastTask" => infrast::convert_infrast_task(task),
+ "RecruitTask" => recruit::convert_recruit_task(task),
+ "MallTask" => mall::convert_mall_task(task),
+ "AwardTask" => award::convert_award_task(task),
+ "RoguelikeTask" => roguelike::convert_roguelike_task(task),
+ "ReclamationTask" => reclamation::convert_reclamation_task(task),
+ _ => Ok(None),
+ }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Silently dropping unknown GUI task types may hide configuration issues; consider at least logging them.
In `convert_task`, unmatched `$type` values return `Ok(None)`, so those tasks are dropped silently:
```rust
match type_tag {
// ...
"ReclamationTask" => reclamation::convert_reclamation_task(task),
_ => Ok(None),
}
```
To avoid hiding misconfigured or new task types, please add at least a `log::warn!` including the `$type` (and possibly an identifier like `Name`). You can keep the tolerant behavior now and later introduce a flag if you want to switch between warning and hard error for unknown types.
Suggested implementation:
```rust
fn convert_task(task: &MAAValue) -> Result<Option<MAAValue>> {
// GUI task discriminator, e.g. "FightTask"
let type_tag = task
.get("$type")
.and_then(|v| v.as_str())
.context("GUI task missing $type")?;
match type_tag {
"StartUpTask" => start_up::convert_start_up_task(task),
"FightTask" => fight::convert_fight_task(task),
"InfrastTask" => infrast::convert_infrast_task(task),
"RecruitTask" => recruit::convert_recruit_task(task),
"MallTask" => mall::convert_mall_task(task),
"AwardTask" => award::convert_award_task(task),
"RoguelikeTask" => roguelike::convert_roguelike_task(task),
"ReclamationTask" => reclamation::convert_reclamation_task(task),
_ => {
let name = task
.get("Name")
.and_then(|v| v.as_str())
.unwrap_or("<unknown>");
log::warn!(
"Unknown GUI task type `{}` (Name: `{}`); skipping task",
type_tag,
name
);
Ok(None)
}
}
}
```
If the `log` crate is not yet used in this module, ensure that:
1. The `log` dependency is present in `Cargo.toml` (likely already is).
2. A logger implementation (e.g. `env_logger`, `tracing` bridge, etc.) is initialized in your binary so that `log::warn!` actually emits output.
No further changes to callers are required since the function signature and return behavior remain the same (`Ok(None)` for unknown types).
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- warn when skipping unsupported GUI task $type values - add --gui-config flag and MAA_GUI_CONFIG env for non-interactive multi-profile selection - merge pick_configuration into select_configuration - consolidate per-task tests into gui::tests with gui_task_test! macro
|
本次修改采纳了 @sourcery-ai 的部分优化建议: English versionThis PR incorporates several optimization suggestions from @sourcery-ai: Added a gui_config flag that accepts a string to specify particular configuration files, preventing potential blocking issues. |
|
Tested on my local environment, everything works as expected. 在我的设备上进行了测试,运行正常。 Test Logs / 测试日志Converted config / 转换的配置[[tasks]]
type = "StartUp"
[tasks.params.client_type]
alternatives = [
"Official",
"YoStarEN",
"YoStarJP",
]
description = "a client type"
[tasks.params.client_type.deps]
start_game_enabled = true
[tasks.params.start_game_enabled]
default = true
description = "start the game"
[[tasks]]
type = "Fight"
name = "日常经验本"
strategy = "merge"
[[tasks.variants]]
[tasks.variants.condition]
type = "Weekday"
weekdays = [
"Mon",
"Wed",
"Fri",
]
[tasks.variants.params]
stage = "LS-6"
medicine_expire_days = 2
[[tasks]]
type = "Fight"
name = "日常龙门币"
strategy = "merge"
[[tasks.variants]]
[tasks.variants.condition]
type = "Weekday"
weekdays = [
"Tue",
"Thu",
"Sat",
]
[tasks.variants.params]
stage = "CE-6"
medicine_expire_days = 2
[[tasks]]
type = "Infrast"
name = ""
[tasks.params]
mode = 0
facility = [
"Mfg",
"Trade",
"Control",
"Power",
"Reception",
"Office",
"Dorm",
"Processing",
"Training",
]
drones = "Money"
threshold = 0.3
replenish = true
dorm_notstationed_enabled = true
dorm_trust_enabled = true
reception_message_board = true
reception_clue_exchange = true
reception_send_clue = true
[[tasks]]
type = "Recruit"
name = ""
[tasks.params]
times = 4
extra_tags_mode = 0
refresh = true
expedite = true
select = [
5,
4,
3,
]
confirm = [
5,
4,
3,
]
[tasks.params.recruitment_time]
4 = 540
3 = 540
[[tasks]]
type = "Mall"
name = ""
[tasks.params]
shopping = true
credit_fight = false
formation_index = 0
visit_friends = true
buy_first = [
"加急许可",
"招聘许可",
]
blacklist = [
"碳",
"家具",
]
force_shopping_if_credit_full = true
only_buy_discount = false
reserve_max_credit = false
[[tasks]]
type = "Award"
name = ""
[tasks.params]
award = true
mail = true
recruit = false
orundum = true
mining = true
specialaccess = true |
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## main #559 +/- ##
===========================================
- Coverage 72.07% 14.51% -57.57%
===========================================
Files 72 11 -61
Lines 6679 434 -6245
Branches 6679 434 -6245
===========================================
- Hits 4814 63 -4751
+ Misses 1523 370 -1153
+ Partials 342 1 -341 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
wangl-cc
left a comment
There was a problem hiding this comment.
感谢贡献,我大概看了一下这个 PR。这个功能的方向是有价值的,不过还有几个需要修正的问题。
-
目前
maa convert的职责是保持数据结构不变,只在 TOML、YAML 和 JSON 之间转换序列化格式;这里实际上是在解析 MAA WPF GUI 的配置结构,将TaskQueue中的任务和字段映射成 maa-cli v1 task config。转换过程中还会选择 GUI configuration、重组字段,并可能跳过不支持的任务,因此更接近一次有损的语义迁移。我倾向于将命令调整为:
maa migrate wpf <input> [output]这样可以清楚地区分:
convert:同一数据结构之间的 TOML/YAML/JSON 格式转换;migrate:从外部配置模型迁移到 maa-cli 配置模型;import:将已有配置安装到 maa-cli 的配置目录。
-
当前迁移是有损的,部分影响执行的参数没有处理,但用户无法知道具体丢失了什么。第一版不一定支持所有字段,但是应该在汇总被跳过的任务和字段。否则生成的文件可以解析,执行行为可能与原 GUI 配置不同。
-
禁用任务会在迁移后变成可执行任务。当前
IsEnable = false只会产生 warning,任务仍然会写入,而且没有保留禁用语义。这可能导致用户迁移后意外执行原本关闭的肉鸽、生息演算等任务。
|
此外,你可以需要 rebase 或者 merge 一下 main,CI 里面有很多在 main 里面修复的问题。 |
Summary
This PR adds GUI profile conversion to
maa-cliby mapping MAA GUITaskQueueentries into maa-cli task config shape, exposed throughmaa convert -g.It lets users take an existing GUI profile node (specifically
gui.new.json, located inMAA_ROOT_DIR\configfor Windows MAA) and produce editable TOML/YAML/JSON task configs without hand-rewriting every field. Since this file stores the full configuration of the GUI program, converting it into a format supported bymaa-clisaves significant manual migration effort.What changed
maa convert -g/--guiflag to theConvertsubtask incrates/maa-cli/src/command.rsto run GUI conversion only when explicitly requestedcrates/maa-cli/src/config/gui.rswhich contains the logic to transform specified GUI profile nodes into maa-cli v1 configs, including 9 unit tests targeting this new logicWhy
MAA GUI stores task settings in its own profile JSON shape, while
maa-cliexpects task configs with typed params, variants, and input templates.Users who start from a GUI profile currently have no supported path to move those settings into
maa-cliconfigs. This closes that gap by providing a deterministic conversion layer instead of manual migration.User impact
This PR introduces the new
-g/--guiflag. Users can now run:If the input file is not a JSON file but the flag is enabled, a notice will be printed:
The --gui flag is for converting MAA GUI profiles, which are typically JSON files; input <input file path> is not JSONIf a task within the GUI profile is disabled (
IsEnableis false), the conversion layer will still process and include it in the final output, but it will emit a warning notification during the process.Furthermore, if the GUI profile contains multiple configurations under
ROOT.Configurations, the command-line interface will prompt the user to interactively select which node they want to convert:The output is a normal
maa-clitask config file that can be edited, versioned, and used with existingmaa-cliworkflows.Validation
cargo test -p maa-cli gui::9 passed, 0 failed
cargo test -p maa-cli convert4 passed, 0 failed
Manual validation
cargo run -- convert -g crates/maa-cli/fixtures/gui/default_profile.json /tmp/t.tomlcargo run -- convert -g crates/maa-cli/fixtures/gui/default_profile.json /tmp/y.yamlNotes
gui.rsis renaming these fields, converting specific fields intocondition, and placing others intoparams. I have roughly checked all fields, and theoretically none should cause MaaCore errors, though I will conduct actual integration testing later today or tomorrow. If you think it is necessary, I can write a document detailing the handling of each key in the GUI profile.config-v2. I am willing to continue working on this feature onceconfig-v2stabilizes, enabling the GUI profile to convert directly into theconfig-v2format.Summary by Sourcery
摘要 (Summary)
本 PR 为
maa-cli增加了 GUI 配置转换功能。该功能通过将 MAA GUI 的TaskQueue项映射到maa-cli的任务配置结构中,并通过maa convert -g命令对外提供。它允许用户直接使用现有的 GUI 配置文件(具体为
gui.new.json,对于 Windows MAA,该文件位于MAA_ROOT_DIR\config)并生成可编辑的 TOML/YAML/JSON 任务配置,无需手动重写每个字段。由于该文件存储了 GUI 程序的全部配置内容,能够将其转换为maa-cli支持的格式可以大幅节省手动配置的时间成本。变更内容 (What changed)
crates/maa-cli/src/command.rs处的Convert子任务中添加一个名为gui的 flag,引入maa convert -g/--gui参数,仅在明确请求时才执行 GUI 转换。crates/maa-cli/src/config/gui.rs,文件包含将所有 GUI profile 指定节点变换为maa-cliv1 config 的逻辑,并且包含 9 个关于新逻辑的测试。变更原因 (Why)
MAA GUI 将任务设置存储在自有的配置文件 JSON 结构中,而
maa-cli则期望任务配置具备类型化的参数、变体和输入模板。目前,从 GUI 配置文件开始使用的用户没有一条受支持的途径将这些设置迁移到
maa-cli配置中。本 PR 通过提供一个确定性的转换层来填补这一空白,从而免去了手动迁移的麻烦。用户影响 (User impact)
本 PR 引入了新的
-g/--gui标志。用户现在可以运行:如果输入文件不是 JSON 文件但开启了该标志,程序将会输出一条提示信息:
The --gui flag is for converting MAA GUI profiles, which are typically JSON files; input <input file path> is not JSON如果 GUI profile 中的某个任务被禁用(
IsEnable为 false),转换层在转换中仍会接受并包含它,但在过程中会输出一条警告提示。此外,如果 GUI profile 中包含多个配置(指
ROOT.Configurations有多个子项),命令行界面将会提示用户交互式地选择需要被转换的节点:输出是一个正常的
maa-cli任务配置文件,可以进行编辑、版本控制,并用于现有的maa-cli工作流中。验证 (Validation)
cargo test -p maa-cli gui::9 通过, 0 失败
cargo test -p maa-cli convert4 通过, 0 失败
手动验证 (Manual validation)
cargo run -- convert -g crates/maa-cli/fixtures/gui/default_profile.json /tmp/t.tomlcargo run -- convert -g crates/maa-cli/fixtures/gui/default_profile.json /tmp/y.yaml注意事项 (Notes)
gui.rs的主要任务就是将 these 字段重命名。并且特定字段转换为condition,其他放到params。我粗略的检查过所有字段,这些理论上都不会引起 MaaCore 报错,但今天稍晚或是明天我才会实际进行测试。如果你们认为有必要,我可以去写一个文档讲述对 GUI profile 每个键的处理细节。config-v2。我愿意在config-v2稳定后为这个功能继续编写代码,让 GUI profile 直接转为config-v2格式的配置文件。Summary by Sourcery
在 maa-cli 中新增支持,通过新的 CLI 标志将 MAA GUI 配置文件转换为 maa-cli 任务配置文件。
新功能:
maa convert命令引入-g/--gui标志,用于触发 GUI 配置转换。TaskQueue条目转换为 maa-cli v1 任务配置结构,包括对多配置文件(multi-configuration profiles)的交互式选择处理。增强:
--gui标志以及在转换被禁用的 GUI 任务时发出警告,以改进用户反馈。测试:
Original summary in English
Summary by Sourcery
Add support in maa-cli to convert MAA GUI profiles into maa-cli task configuration files via a new CLI flag.
New Features:
Enhancements:
Tests: