feat: support merging multiple subscriptions

This commit is contained in:
2026-07-19 23:58:02 +08:00
parent fb4d927220
commit 087869d84f
17 changed files with 1341 additions and 60 deletions

View File

@ -0,0 +1,82 @@
package automation
import (
"fmt"
"regexp"
"strconv"
)
// buildRateFilterGroups implements the "rate-filters" ssm feature: for each
// rule, extract a rate from each candidate proxy's original name via
// rule.Pattern's first capture group, keep those satisfying
// "rate rule.Operator rule.Value", and append a new proxy-group containing
// them. Proxies whose name doesn't match Pattern at all are excluded.
// Returns one error per rule with an invalid pattern/operator; other rules
// still run.
func buildRateFilterGroups(finalConfig map[string]interface{}, rules []RateFilterRule, proxies []ProxyRef) []error {
var errs []error
for _, rule := range rules {
re, err := regexp.Compile(rule.Pattern)
if err != nil {
errs = append(errs, fmt.Errorf("rate-filter '%s': invalid pattern %q: %w", rule.Name, rule.Pattern, err))
continue
}
cmp, err := comparator(rule.Operator)
if err != nil {
errs = append(errs, fmt.Errorf("rate-filter '%s': %w", rule.Name, err))
continue
}
candidates := matchProxies(proxies, rule.Match)
names := make([]interface{}, 0, len(candidates))
for _, p := range candidates {
m := re.FindStringSubmatch(p.OriginalName)
if len(m) < 2 {
continue
}
rate, err := strconv.ParseFloat(m[1], 64)
if err != nil {
continue
}
if cmp(rate, rule.Value) {
names = append(names, p.DisplayName)
}
}
if len(names) == 0 {
fmt.Printf(" ssm: rate-filter '%s' matched 0 proxies\n", rule.Name)
}
group := map[string]interface{}{
"name": rule.Name,
"type": defaultString(rule.Type, "select"),
"proxies": names,
}
for k, v := range rule.Extra {
group[k] = v
}
appendProxyGroup(finalConfig, group)
}
return errs
}
func comparator(op string) (func(a, b float64) bool, error) {
switch op {
case "<":
return func(a, b float64) bool { return a < b }, nil
case "<=":
return func(a, b float64) bool { return a <= b }, nil
case ">":
return func(a, b float64) bool { return a > b }, nil
case ">=":
return func(a, b float64) bool { return a >= b }, nil
case "==", "=":
return func(a, b float64) bool { return a == b }, nil
case "!=":
return func(a, b float64) bool { return a != b }, nil
default:
return nil, fmt.Errorf("unknown operator %q (expected <, <=, >, >=, ==, !=)", op)
}
}