// Package automation implements the `ssm:` automation block that can be // added to core-config.yaml, letting users declaratively: // // 1. build a new proxy-group from proxies matching a subscription/keyword // filter ("proxy-groups"), // 2. strip unwanted proxies — matched by subscription/name-pattern and, // optionally, a rate/multiplier encoded in the name — out of every // proxy-group's proxies list, subscription-provided or built by a // proxy-groups rule ("filters"), and // 3. append/prepend/replace an arbitrary path in the generated config // ("patches"). // // The `ssm:` key itself is metadata for this tool, not a real mihomo/clash // config key, so it's always stripped out of the generated config before // writing (see internal/corecfg.Apply). package automation // ProxyRef describes a single proxy or proxy-group in the pool merged from // every active subscription — both are matchable the same way, so a // `match.name-pattern` can target either a raw proxy or one of a // subscription's own groups (e.g. its default selector). type ProxyRef struct { // OriginalName is the proxy/group's name as it appeared in its // subscription, before subscription-name prefixing. Name-pattern // matching operates on this. OriginalName string // DisplayName is OriginalName prefixed with its source subscription's // name (e.g. "home-sub | HK 01"), which is what actually appears in the // generated config's proxies/proxy-groups and must be used when // referencing this proxy/group from a built proxy-group. DisplayName string // Subscription is the name of the subscription this proxy/group came // from. Subscription string } // MatchRule narrows which proxies a proxy-groups/filters rule considers. // All fields are optional; an empty/omitted field imposes no // restriction on that dimension. Within Subscriptions, entries are OR'd // together; the dimensions themselves (subscriptions, name-pattern) are // AND'd together. type MatchRule struct { Subscriptions []string `yaml:"subscriptions"` // NamePattern is a regexp matched against the proxy's original // (pre-prefix) name. Use "|" for OR-ing alternatives and the "(?i)" // flag for case-insensitive matching, e.g. "(?i)HK|Hong Kong". NamePattern string `yaml:"name-pattern"` } // ProxyGroupRule defines a new proxy-group built from every proxy matching // Match. Any YAML fields beyond name/type/match (e.g. url, interval, // tolerance) are passed through verbatim onto the generated proxy-group. type ProxyGroupRule struct { Name string `yaml:"name"` Type string `yaml:"type"` Match MatchRule `yaml:"match"` Extra map[string]interface{} `yaml:",inline"` } // CompareRule extracts a float from a proxy's (pre-prefix) name via // Pattern's first capture group and matches proxies whose extracted value // satisfies "value Operator Value". Proxies whose name doesn't match // Pattern at all don't match this rule. type CompareRule struct { Pattern string `yaml:"pattern"` Operator string `yaml:"operator"` Value float64 `yaml:"value"` } // FilterRule removes unwanted proxies from every proxy-group's `proxies` // list in the generated config. Match scopes which proxies this rule // considers; Compares is optional and adds a second narrowing step. A // scoped proxy is unwanted — and gets stripped out wherever it's // referenced — if Compares is empty (Match alone acts as a denylist), or // if it satisfies every entry in Compares (AND'd) when Compares is // non-empty. type FilterRule struct { Name string `yaml:"name"` Compares []CompareRule `yaml:"compares"` Match MatchRule `yaml:"match"` } // PatchRule appends/prepends/replaces Value at Path (a dot/bracket path // like "dns.nameserver" or "proxy-groups[0].proxies") in the generated // config. type PatchRule struct { Path string `yaml:"path"` Op string `yaml:"op"` Value interface{} `yaml:"value"` } // Config is the full `ssm:` automation block parsed from core-config.yaml. type Config struct { ProxyGroups []ProxyGroupRule `yaml:"proxy-groups"` Filters []FilterRule `yaml:"filters"` Patches []PatchRule `yaml:"patches"` }