package decision import ( "strings" ) // Rule maps a condition to a set of actions. type Rule struct { Name string Match MatchFunc Actions []Action } // MatchFunc is a pure predicate over a MessageContext. type MatchFunc func(ctx MessageContext) bool // Evaluate runs all rules against the context and returns the matching actions. Pure. func Evaluate(ctx MessageContext, rules []Rule) []Action { var actions []Action for _, rule := range rules { if rule.Match(ctx) { actions = append(actions, rule.Actions...) } } return actions } // MatchCommand returns a MatchFunc that matches when the command equals cmd. func MatchCommand(cmd string) MatchFunc { return func(ctx MessageContext) bool { return strings.EqualFold(ctx.Command, cmd) } } // MatchPrefix returns a MatchFunc that matches when content starts with prefix. func MatchPrefix(prefix string) MatchFunc { return func(ctx MessageContext) bool { return strings.HasPrefix(ctx.Content, prefix) } } // MatchAny returns a MatchFunc that matches any message. func MatchAny() MatchFunc { return func(_ MessageContext) bool { return true } } // MatchMinPowerLevel returns a MatchFunc that requires a minimum Matrix power level. func MatchMinPowerLevel(level int) MatchFunc { return func(ctx MessageContext) bool { return ctx.PowerLevel >= level } } // And composes multiple MatchFuncs with logical AND. func And(fns ...MatchFunc) MatchFunc { return func(ctx MessageContext) bool { for _, fn := range fns { if !fn(ctx) { return false } } return true } } // Or composes multiple MatchFuncs with logical OR. func Or(fns ...MatchFunc) MatchFunc { return func(ctx MessageContext) bool { for _, fn := range fns { if fn(ctx) { return true } } return false } }