Skip to content

Commit

Permalink
feat: add boolean conditions (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
wangl-cc authored Dec 30, 2023
1 parent aeedcca commit 47dc1f0
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 33 deletions.
6 changes: 4 additions & 2 deletions README-EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ and the `params` field of matched variant will be merged into the parameters of

**Note**: If the `filename` field is a relative path, it will be relative to `$MAA_CONFIG_DIR/infrast`. Besides, the custom infrastructure plan file will not be read by `maa-cli` but `MaaCore`. So the format of the file must be `JSON` and time period defined in the file will not be used to select the corresponding sub-plan. So you must specify the `plan_index` field in the parameters of the task to use the correct infrastructure plan in the corresponding time period. This will ensure that the correct infrastructure plan is used in the appropriate time period.

Besides of `Time` condition, there are also `DateTime`, `Weakday`, and `Combined` conditions. `DateTime` condition is used to specify a specific datetime period, `Weekday` condition is used to specify some days in a week, `Combined` condition is used to specify a combination of multiple conditions.
Besides of `Time` condition, there are also `DateTime`, `Weakday`, conditions. `DateTime` condition is used to specify a specific datetime period, `Weekday` condition is used to specify some days in a week.

```toml
[[tasks]]
Expand All @@ -249,6 +249,8 @@ params = { stage = "1-7" }

Beside of above conditions, there is a condition `OnSideStory` which depends on hot update resource to check if there is any opening side story. Thus, the condition of fight `SL-8`can be simplified as `{ type = "OnSideStory", client = "Official" }`, where `client` is the client type of game.

Beside of above basic condition, `{ type = "And", conditions = [...] }` `{ type = "Or", conditions = [...] }`, and `{ type = "Not", condition = ... }` can be used for logical combination of conditions.

With default strategy, if multiple variants are matched, only the first one will be used. And if the condition is not given, the variant will always be matched. So you can put a variant without condition at the end of variants.

The strategy of matching variants can be changed by `strategy` field:
Expand All @@ -262,7 +264,7 @@ strategy = "merge" # or "first" (default)
[[tasks.variants]]
params = { expiring_medicine = 1000 }
[tasks.variants.condition]
type = "Combined"
type = "And"
conditions = [
{ type = "Time", start = "18:00:00" },
{ type = "Weekday", weekdays = ["Sun"] },
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ params = { plan_index = 0 }

**注意**:如果你的自定义基建计划文件使用相对路径,应该相对于 `$MAA_CONFIG_DIR/infrast`。此外,由于基建文件是由 `MaaCore` 而不是 `maa-cli` 读取的,因此这些文件的格式必须是 `JSON`。同时,`maa-cli` 不会读取基建文件,也不会根据其中定义的时间段来选择相应的子计划。因此,必须通过 `condition` 字段来指定在相应时间段使用正确的基建计划的参数中的 `plan_index` 字段。这样可以确保在适当的时间段使用正确的基建计划。

除了 `Time` 条件,还有 `DateTime``Weakday` 以及 `Combined` 条件。`DateTime` 条件用于指定一个时间段,`Weekday` 条件用于指定一周中的某些天`Combined` 条件用于指定多个条件的组合
除了 `Time` 条件,还有 `DateTime``Weakday` 条件。`DateTime` 条件用于指定一个时间段,`Weekday` 条件用于指定一周中的某些天。

```toml
[[tasks]]
Expand All @@ -264,6 +264,8 @@ params = { stage = "1-7" }

除了上述确定的条件之外,还有一个依赖于热更新资源的条件 `OnSideStory`,当你启动该条件后,`maa-cli` 会尝试读取相应的资源来判断当前是否有正在开启的活动,如果有那么对应的变体会被匹配。 比如上述夏活期间刷 `SL-8` 的条件就可以简化为 `{ type = "OnSideStory", client = "Official" }`,这里的 `client` 参数用于确定你使用的客户端,因为不同的客户端的活动时间不同,对于使用官服或者 b 服的用户,这可以省略。通过这个条件,每次活动更新之后你可以只需要更新需要刷的关卡而不需要手动编辑对应活动的开放时间。

除了以上基础条件之外,你可以使用 `{ type = "And", conditions = [...] }``{ type = "Or", conditions = [...] }`, `{ type = "Not", condition = ... }` 来对条件进行逻辑运算。

在默认的策略下,如果有多个变体被匹配,第一个将会被使用。如果没有给出条件,那么变体将会总是被匹配,所以你可以把没有条件的变体放在最后,作为默认的情况。

你可以使用 `strategy` 字段来改变匹配策略:
Expand All @@ -278,7 +280,7 @@ strategy = "merge" # 或者 "first" (默认)
params = { expiring_medicine = 1000 }

[tasks.variants.condition]
type = "Combined"
type = "And"
conditions = [
{ type = "Time", start = "18:00:00" },
{ type = "Weekday", weekdays = ["Sun"] },
Expand Down
23 changes: 22 additions & 1 deletion maa-cli/schemas/task.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,35 @@
{
"type": "object",
"properties": {
"type": { "const": "Combined" },
"type": { "const": "And" },
"conditions": {
"type": "array",
"items": { "$ref": "#/definitions/condition" }
}
},
"required": ["type", "conditions"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": { "const": "Or" },
"conditions": {
"type": "array",
"items": { "$ref": "#/definitions/condition" }
}
},
"required": ["type", "conditions"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": { "const": "Not" },
"condition": { "$ref": "#/definitions/condition" }
},
"required": ["type", "condition"],
"additionalProperties": false
}
]
},
Expand Down
162 changes: 136 additions & 26 deletions maa-cli/src/config/task/condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ pub enum Condition {
/// The task is always active
#[default]
Always,
/// The task is never active, only used for testing
#[cfg(test)]
Never,
/// The task is active on the specified weekdays
Weekday { weekdays: Vec<Weekday> },
/// The task is active on the specified time range
Expand Down Expand Up @@ -43,7 +40,12 @@ pub enum Condition {
client: ClientType,
},
/// The task is active if all the sub-conditions are met
Combined { conditions: Vec<Condition> },
#[serde(alias = "Combined")]
And { conditions: Vec<Condition> },
/// The task is active if any of the sub-conditions is met
Or { conditions: Vec<Condition> },
/// The task is active if the inner condition is not met
Not { condition: Box<Condition> },
}

fn deserialize_from_str<'de, S, D>(deserializer: D) -> Result<Option<S>, D::Error>
Expand All @@ -66,8 +68,6 @@ impl Condition {
pub fn is_active(&self) -> bool {
match self {
Condition::Always => true,
#[cfg(test)]
Condition::Never => false,
Condition::Weekday { weekdays } => {
let now = Local::now();
let weekday = now.date_naive().weekday();
Expand All @@ -93,14 +93,23 @@ impl Condition {
}
}
Condition::OnSideStory { client } => has_side_story_open(*client),
Condition::Combined { conditions } => {
Condition::And { conditions } => {
for condition in conditions {
if !condition.is_active() {
return false;
}
}
true
}
Condition::Or { conditions } => {
for condition in conditions {
if condition.is_active() {
return true;
}
}
false
}
Condition::Not { condition } => !condition.is_active(),
}
}
}
Expand Down Expand Up @@ -230,35 +239,47 @@ mod tests {
// fn on_side_story() {}

#[test]
fn combined() {
let now = chrono::Local::now();
let now_time = now.time();
let weekday = now.date_naive().weekday();

assert!(Condition::Combined {
fn boolean() {
assert!(Condition::And {
conditions: vec![Condition::Always, Condition::Always]
}
.is_active());
assert!(!Condition::And {
conditions: vec![
Condition::Time {
start: Some(now_time + Duration::minutes(-10)),
end: Some(now_time + Duration::minutes(10)),
},
Condition::Weekday {
weekdays: vec![weekday]
Condition::Always,
Condition::Not {
condition: Box::new(Condition::Always)
},
]
}
.is_active());
assert!(!Condition::Combined {

assert!(Condition::Or {
conditions: vec![
Condition::Time {
start: Some(now_time + Duration::minutes(10)),
end: Some(now_time + Duration::minutes(20)),
},
Condition::Weekday {
weekdays: vec![weekday]
Condition::Always,
Condition::Not {
condition: Box::new(Condition::Always)
}
]
}
.is_active());

assert!(!Condition::Or {
conditions: vec![
Condition::Not {
condition: Box::new(Condition::Always)
},
Condition::Not {
condition: Box::new(Condition::Always)
}
]
}
.is_active());

assert!(!Condition::Not {
condition: Box::new(Condition::Always)
}
.is_active());
}
}

Expand Down Expand Up @@ -375,5 +396,94 @@ mod tests {
],
);
}

#[test]
fn boolean() {
assert_de_tokens(
&Condition::And {
conditions: vec![Condition::Always, Condition::Always],
},
&[
Token::Map { len: Some(2) },
Token::Str("type"),
Token::Str("Combined"),
Token::Str("conditions"),
Token::Seq { len: Some(2) },
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("Always"),
Token::MapEnd,
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("Always"),
Token::MapEnd,
Token::SeqEnd,
Token::MapEnd,
],
);

assert_de_tokens(
&Condition::And {
conditions: vec![Condition::Always, Condition::Always],
},
&[
Token::Map { len: Some(2) },
Token::Str("type"),
Token::Str("And"),
Token::Str("conditions"),
Token::Seq { len: Some(2) },
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("Always"),
Token::MapEnd,
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("Always"),
Token::MapEnd,
Token::SeqEnd,
Token::MapEnd,
],
);

assert_de_tokens(
&Condition::Or {
conditions: vec![Condition::Always, Condition::Always],
},
&[
Token::Map { len: Some(2) },
Token::Str("type"),
Token::Str("Or"),
Token::Str("conditions"),
Token::Seq { len: Some(2) },
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("Always"),
Token::MapEnd,
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("Always"),
Token::MapEnd,
Token::SeqEnd,
Token::MapEnd,
],
);

assert_de_tokens(
&Condition::Not {
condition: Box::new(Condition::Always),
},
&[
Token::Map { len: Some(2) },
Token::Str("type"),
Token::Str("Not"),
Token::Str("condition"),
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("Always"),
Token::MapEnd,
Token::MapEnd,
],
);
}
}
}
8 changes: 6 additions & 2 deletions maa-cli/src/config/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,9 @@ mod tests {

fn never_active() -> TaskVariant {
TaskVariant {
condition: Condition::Never,
condition: Condition::Not {
condition: Box::new(Condition::Always),
},
params: MAAValue::default(),
}
}
Expand Down Expand Up @@ -470,7 +472,9 @@ mod tests {
Strategy::First,
vec![
TaskVariant {
condition: Condition::Never,
condition: Condition::Not {
condition: Box::new(Condition::Always),
},
params: object!("a" => 2),
},
TaskVariant {
Expand Down

2 comments on commit 47dc1f0

@moomiji
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:Weakday

@wangl-cc
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks.

Please sign in to comment.