/
matcher.go
155 lines (146 loc) · 5.9 KB
/
matcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package core
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/hexbotio/hex-plugin"
"github.com/hexbotio/hex/models"
"github.com/hexbotio/hex/parse"
"github.com/mohae/deepcopy"
)
// Matcher function
func Matcher(inputMsgs <-chan models.Message, outputMsgs chan<- models.Message, plugins *map[string]models.Plugin, rules *map[string]models.Rule, config models.Config) {
state := make(map[string]bool)
for _, rule := range *rules {
state[rule.Id] = true
}
for {
message := <-inputMsgs
match := false
config.Logger.Debug("Matcher - Eval of Message ID:" + message.Attributes["hex.id"])
config.Logger.Trace(fmt.Sprintf("Message: %+v", message))
if parse.EitherMember(config.ACL, message.Attributes["hex.user"], message.Attributes["hex.channel"]) {
Commands(message, outputMsgs, rules, config)
}
for _, rule := range *rules {
// match for input
if rule.Active && rule.Match != "" && parse.Match(rule.Match, message.Attributes["hex.input"]) {
if parse.EitherMember(config.ACL, message.Attributes["hex.user"], message.Attributes["hex.channel"]) {
if parse.EitherMember(rule.ACL, message.Attributes["hex.user"], message.Attributes["hex.channel"]) {
match = true
config.Logger.Debug("Matcher - Matched Rule '" + rule.Name + "' with input '" + message.Attributes["hex.input"] + "' on ID:" + message.Attributes["hex.id"])
config.Logger.Trace(fmt.Sprintf("Message: %+v", message))
msg := deepcopy.Copy(message).(models.Message)
go runRule(rule, msg, outputMsgs, state, *plugins, config)
}
}
}
// match for schedule
if rule.Active && rule.Schedule != "" && rule.Schedule == message.Attributes["hex.schedule"] {
match = true
config.Logger.Debug("Matcher - Matched Rule '" + rule.Name + "' with schedule '" + message.Attributes["hex.schedule"] + "' on ID:" + message.Attributes["hex.id"])
config.Logger.Trace(fmt.Sprintf("Message: %+v", message))
msg := deepcopy.Copy(message).(models.Message)
go runRule(rule, msg, outputMsgs, state, *plugins, config)
}
// match for webhook
if rule.Active && rule.URL != "" && parse.Match(rule.URL, message.Attributes["hex.url"]) {
match = true
config.Logger.Debug("Matcher - Matched Rule '" + rule.Name + "' with url '" + message.Attributes["hex.url"] + "' on ID:" + message.Attributes["hex.id"])
config.Logger.Trace(fmt.Sprintf("Message: %+v", message))
msg := deepcopy.Copy(message).(models.Message)
go runRule(rule, msg, outputMsgs, state, *plugins, config)
}
}
if !match && message.Attributes["hex.service"] == "command" {
StopPlugins(*plugins, config)
os.Exit(0)
}
}
}
func runRule(rule models.Rule, message models.Message, outputMsgs chan<- models.Message, state map[string]bool, plugins map[string]models.Plugin, config models.Config) {
config.Logger.Debug("Matcher - Running Rule " + rule.Name + " for ID:" + message.Attributes["hex.id"])
config.Logger.Trace(fmt.Sprintf("Message: %+v", message))
message.Attributes["hex.rule.runid"] = models.MessageID()
message.Attributes["hex.rule.name"] = rule.Name
message.Attributes["hex.rule.format"] = strconv.FormatBool(rule.Format)
message.Attributes["hex.rule.channel"] = rule.Channel
message.Attributes["hex.rule.threaded"] = strconv.FormatBool(rule.Threaded)
for key, value := range config.Vars {
message.Attributes["hex.var."+key] = value
}
ruleResult := true
lastAction := true
lastConfig := rule.Actions[0].Config
for actionCounter, action := range rule.Actions {
config.Logger.Debug("Matcher - Evaluating Action " + rule.Name + "." +
action.Type + " [" + strconv.Itoa(actionCounter) + "] for ID:" + message.Attributes["hex.id"])
config.Logger.Trace(fmt.Sprintf("Message: %+v", message))
if lastAction || action.RunOnFail {
if _, exists := plugins[action.Type]; exists {
startTime := models.MessageTimestamp()
attrName := "hex.output." + strconv.Itoa(actionCounter)
if action.LastConfig {
action.Config = lastConfig
}
for key, _ := range action.Config {
action.Config[key] = parse.Substitute(action.Config[key], message.Attributes)
}
cmd := parse.Substitute(action.Command, message.Attributes)
args := hexplugin.Arguments{
Debug: rule.Debug || config.Debug,
Command: cmd,
Config: action.Config,
}
resp := plugins[action.Type].Action.Perform(args)
if !resp.Success {
ruleResult = false
}
lastAction = resp.Success
lastConfig = action.Config
message.Attributes[attrName+".duration"] = strconv.FormatInt(models.MessageTimestamp()-startTime, 10)
if action.OutputToVar {
message.Attributes[attrName+".response"] = strings.TrimSpace(resp.Output)
} else if !action.HideOutput {
if !action.OutputFailOnly || (action.OutputFailOnly && !resp.Success) {
if !rule.GroupOutput {
message.Outputs = []models.Output{models.Output{
Rule: rule.Name,
Response: resp.Output,
Success: resp.Success,
Command: cmd,
}}
message.EndTime = models.MessageTimestamp()
if !rule.OutputOnChange && (!rule.OutputFailOnly || !ruleResult) {
outputMsgs <- message
} else if rule.OutputOnChange && ruleResult != state[rule.Id] {
outputMsgs <- message
}
} else {
message.Outputs = append(message.Outputs, models.Output{
Rule: rule.Name,
Response: resp.Output,
Success: resp.Success,
Command: cmd,
})
}
}
}
} else {
config.Logger.Error("Matcher - Missing Plugin " + action.Type)
}
}
}
if rule.GroupOutput {
message.EndTime = models.MessageTimestamp()
if !rule.OutputOnChange && (!rule.OutputFailOnly || !ruleResult) {
outputMsgs <- message
} else if rule.OutputOnChange && ruleResult != state[rule.Id] {
outputMsgs <- message
}
}
config.Logger.Debug("Matcher - Output ID:" + message.Attributes["hex.id"])
config.Logger.Trace(fmt.Sprintf("Message: %+v", message))
state[rule.Id] = ruleResult
}