-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathactions.go
129 lines (101 loc) · 3.39 KB
/
actions.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
package main
import (
"path"
"sync"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"gopkg.in/irc.v4"
"github.com/Luzifer/go_helpers/v2/fieldcollection"
"github.com/Luzifer/twitch-bot/v3/internal/locker"
"github.com/Luzifer/twitch-bot/v3/plugins"
)
var (
availableActions = map[string]plugins.ActorCreationFunc{}
availableActionsLock = new(sync.RWMutex)
)
// Compile-time assertion
var _ plugins.ActorRegistrationFunc = registerAction
func getActorByName(name string) (plugins.Actor, error) {
availableActionsLock.RLock()
defer availableActionsLock.RUnlock()
acf, ok := availableActions[name]
if !ok {
return nil, errors.Errorf("undefined actor %q called", name)
}
return acf(), nil
}
func registerAction(name string, acf plugins.ActorCreationFunc) {
availableActionsLock.Lock()
defer availableActionsLock.Unlock()
if _, ok := availableActions[name]; ok {
log.WithField("name", name).Fatal("Duplicate registration of actor")
}
availableActions[name] = acf
}
func triggerAction(c *irc.Client, m *irc.Message, rule *plugins.Rule, ra *plugins.RuleAction, eventData *fieldcollection.FieldCollection) (preventCooldown bool, err error) {
availableActionsLock.RLock()
defer availableActionsLock.RUnlock()
a, err := getActorByName(ra.Type)
if err != nil {
return false, errors.Wrap(err, "getting actor")
}
logger := log.WithField("actor", a.Name())
if a.IsAsync() {
go func() {
if _, err := a.Execute(c, m, rule, eventData, ra.Attributes); err != nil {
logger.WithError(err).Error("Error in async actor")
}
}()
return preventCooldown, nil
}
apc, err := a.Execute(c, m, rule, eventData, ra.Attributes)
return apc, errors.Wrap(err, "execute action")
}
func handleMessage(c *irc.Client, m *irc.Message, event *string, eventData *fieldcollection.FieldCollection) {
// Send events to registered handlers
if event != nil {
go notifyEventHandlers(*event, eventData)
}
matchingRules := config.GetMatchingRules(m, event, eventData)
for i := range matchingRules {
go handleMessageRuleExecution(c, m, matchingRules[i], eventData)
}
}
func handleMessageRuleExecution(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *fieldcollection.FieldCollection) {
locker.LockByKey(path.Join("rule-execution", r.MatcherID()))
defer locker.UnlockByKey(path.Join("rule-execution", r.MatcherID()))
var (
ruleEventData = fieldcollection.NewFieldCollection()
preventCooldown bool
)
if eventData != nil {
ruleEventData.SetFromData(eventData.Data())
}
ActionsLoop:
for _, a := range r.Actions {
apc, err := triggerAction(c, m, r, a, ruleEventData)
switch {
case err == nil:
// Rule execution did not cause an error, we store the
// cooldown modifier and continue
preventCooldown = preventCooldown || apc
continue ActionsLoop
case errors.Is(err, plugins.ErrStopRuleExecution):
// Action has asked to stop executing this rule so we store
// the cooldown modifier and stop executing the actions stack
// Action experienced an error: We don't store the cooldown
// state of this action and stop executing the actions stack
// for this rule
preventCooldown = preventCooldown || apc
break ActionsLoop
default:
// Break execution for this rule when one action fails
// Lock command
log.WithError(err).Error("Unable to trigger action")
break ActionsLoop
}
}
if !preventCooldown {
r.SetCooldown(timerService, m, eventData)
}
}