From 2d4efb4832f84d5b19dcf739d17cdff5e4be2d90 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Thu, 12 Aug 2021 00:12:10 +0200 Subject: [PATCH] Add support to disable cooldown through the action module Signed-off-by: Knut Ahlers --- action_ban.go | 6 +++--- action_counter.go | 14 +++++++------- action_delay.go | 6 +++--- action_delete.go | 8 ++++---- action_raw.go | 10 +++++----- action_respond.go | 10 +++++----- action_script.go | 22 ++++++++++++---------- action_setvar.go | 12 ++++++------ action_timeout.go | 6 +++--- action_whisper.go | 10 +++++----- actions.go | 24 ++++++++++++++++-------- 11 files changed, 69 insertions(+), 59 deletions(-) diff --git a/action_ban.go b/action_ban.go index 7fef34d..879f608 100644 --- a/action_ban.go +++ b/action_ban.go @@ -15,12 +15,12 @@ type ActorBan struct { Ban *string `json:"ban" yaml:"ban"` } -func (a ActorBan) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorBan) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.Ban == nil { - return nil + return false, nil } - return errors.Wrap( + return false, errors.Wrap( c.WriteMessage(&irc.Message{ Command: "PRIVMSG", Params: []string{ diff --git a/action_counter.go b/action_counter.go index dd16887..769663a 100644 --- a/action_counter.go +++ b/action_counter.go @@ -17,28 +17,28 @@ type ActorCounter struct { Counter *string `json:"counter" yaml:"counter"` } -func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.Counter == nil { - return nil + return false, nil } counterName, err := formatMessage(*a.Counter, m, r, nil) if err != nil { - return errors.Wrap(err, "preparing response") + return false, errors.Wrap(err, "preparing response") } if a.CounterSet != nil { parseValue, err := formatMessage(*a.CounterSet, m, r, nil) if err != nil { - return errors.Wrap(err, "execute counter value template") + return false, errors.Wrap(err, "execute counter value template") } counterValue, err := strconv.ParseInt(parseValue, 10, 64) //nolint:gomnd // Those numbers are static enough if err != nil { - return errors.Wrap(err, "parse counter value") + return false, errors.Wrap(err, "parse counter value") } - return errors.Wrap( + return false, errors.Wrap( store.UpdateCounter(counterName, counterValue, true), "set counter", ) @@ -49,7 +49,7 @@ func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *Rule) error { counterStep = *a.CounterStep } - return errors.Wrap( + return false, errors.Wrap( store.UpdateCounter(counterName, counterStep, false), "update counter", ) diff --git a/action_delay.go b/action_delay.go index d8fef39..0aab90c 100644 --- a/action_delay.go +++ b/action_delay.go @@ -16,9 +16,9 @@ type ActorDelay struct { DelayJitter time.Duration `json:"delay_jitter" yaml:"delay_jitter"` } -func (a ActorDelay) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorDelay) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.Delay == 0 && a.DelayJitter == 0 { - return nil + return false, nil } totalDelay := a.Delay @@ -27,7 +27,7 @@ func (a ActorDelay) Execute(c *irc.Client, m *irc.Message, r *Rule) error { } time.Sleep(totalDelay) - return nil + return false, nil } func (a ActorDelay) IsAsync() bool { return false } diff --git a/action_delete.go b/action_delete.go index 2e4eb58..d65110b 100644 --- a/action_delete.go +++ b/action_delete.go @@ -15,17 +15,17 @@ type ActorDelete struct { DeleteMessage *bool `json:"delete_message" yaml:"delete_message"` } -func (a ActorDelete) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorDelete) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.DeleteMessage == nil || !*a.DeleteMessage { - return nil + return false, nil } msgID, ok := m.Tags.GetTag("id") if !ok || msgID == "" { - return nil + return false, nil } - return errors.Wrap( + return false, errors.Wrap( c.WriteMessage(&irc.Message{ Command: "PRIVMSG", Params: []string{ diff --git a/action_raw.go b/action_raw.go index 1d4aff9..b7cb017 100644 --- a/action_raw.go +++ b/action_raw.go @@ -13,22 +13,22 @@ type ActorRaw struct { RawMessage *string `json:"raw_message" yaml:"raw_message"` } -func (a ActorRaw) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorRaw) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.RawMessage == nil { - return nil + return false, nil } rawMsg, err := formatMessage(*a.RawMessage, m, r, nil) if err != nil { - return errors.Wrap(err, "preparing raw message") + return false, errors.Wrap(err, "preparing raw message") } msg, err := irc.ParseMessage(rawMsg) if err != nil { - return errors.Wrap(err, "parsing raw message") + return false, errors.Wrap(err, "parsing raw message") } - return errors.Wrap( + return false, errors.Wrap( c.WriteMessage(msg), "sending raw message", ) diff --git a/action_respond.go b/action_respond.go index f15a965..05af322 100644 --- a/action_respond.go +++ b/action_respond.go @@ -15,18 +15,18 @@ type ActorRespond struct { RespondFallback *string `json:"respond_fallback" yaml:"respond_fallback"` } -func (a ActorRespond) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorRespond) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.Respond == nil { - return nil + return false, nil } msg, err := formatMessage(*a.Respond, m, r, nil) if err != nil { if a.RespondFallback == nil { - return errors.Wrap(err, "preparing response") + return false, errors.Wrap(err, "preparing response") } if msg, err = formatMessage(*a.RespondFallback, m, r, nil); err != nil { - return errors.Wrap(err, "preparing response fallback") + return false, errors.Wrap(err, "preparing response fallback") } } @@ -48,7 +48,7 @@ func (a ActorRespond) Execute(c *irc.Client, m *irc.Message, r *Rule) error { } } - return errors.Wrap( + return false, errors.Wrap( c.WriteMessage(ircMessage), "sending response", ) diff --git a/action_script.go b/action_script.go index 43d4b87..3c7386a 100644 --- a/action_script.go +++ b/action_script.go @@ -19,16 +19,16 @@ type ActorScript struct { Command []string `json:"command" yaml:"command"` } -func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if len(a.Command) == 0 { - return nil + return false, nil } var command []string for _, arg := range a.Command { tmp, err := formatMessage(arg, m, r, nil) if err != nil { - return errors.Wrap(err, "execute command argument template") + return false, errors.Wrap(err, "execute command argument template") } command = append(command, tmp) @@ -49,7 +49,7 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error { "tags": m.Tags, "username": m.User, }); err != nil { - return errors.Wrap(err, "encoding script input") + return false, errors.Wrap(err, "encoding script input") } cmd := exec.CommandContext(ctx, command[0], command[1:]...) // #nosec G204 // This is expected to call a command with parameters @@ -59,12 +59,12 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error { cmd.Stdout = stdout if err := cmd.Run(); err != nil { - return errors.Wrap(err, "running command") + return false, errors.Wrap(err, "running command") } if stdout.Len() == 0 { // Script was successful but did not yield actions - return nil + return false, nil } var ( @@ -74,16 +74,18 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error { decoder.DisallowUnknownFields() if err := decoder.Decode(&actions); err != nil { - return errors.Wrap(err, "decoding actions output") + return false, errors.Wrap(err, "decoding actions output") } for _, action := range actions { - if err := triggerActions(c, m, r, action); err != nil { - return errors.Wrap(err, "execute returned action") + apc, err := triggerActions(c, m, r, action) + if err != nil { + return preventCooldown, errors.Wrap(err, "execute returned action") } + preventCooldown = preventCooldown || apc } - return nil + return preventCooldown, nil } func (a ActorScript) IsAsync() bool { return false } diff --git a/action_setvar.go b/action_setvar.go index 7b109a6..6a7aa62 100644 --- a/action_setvar.go +++ b/action_setvar.go @@ -15,18 +15,18 @@ type ActorSetVariable struct { Set string `json:"set" yaml:"set"` } -func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.Variable == "" { - return nil + return false, nil } varName, err := formatMessage(a.Variable, m, r, nil) if err != nil { - return errors.Wrap(err, "preparing variable name") + return false, errors.Wrap(err, "preparing variable name") } if a.Clear { - return errors.Wrap( + return false, errors.Wrap( store.RemoveVariable(varName), "removing variable", ) @@ -34,10 +34,10 @@ func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *Rule) error value, err := formatMessage(a.Set, m, r, nil) if err != nil { - return errors.Wrap(err, "preparing value") + return false, errors.Wrap(err, "preparing value") } - return errors.Wrap( + return false, errors.Wrap( store.SetVariable(varName, value), "setting variable", ) diff --git a/action_timeout.go b/action_timeout.go index 05c61c5..eeb4688 100644 --- a/action_timeout.go +++ b/action_timeout.go @@ -16,12 +16,12 @@ type ActorTimeout struct { Timeout *time.Duration `json:"timeout" yaml:"timeout"` } -func (a ActorTimeout) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorTimeout) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.Timeout == nil { - return nil + return false, nil } - return errors.Wrap( + return false, errors.Wrap( c.WriteMessage(&irc.Message{ Command: "PRIVMSG", Params: []string{ diff --git a/action_whisper.go b/action_whisper.go index a1ecf77..42c5f19 100644 --- a/action_whisper.go +++ b/action_whisper.go @@ -16,19 +16,19 @@ type ActorWhisper struct { WhisperTo *string `json:"whisper_to" yaml:"whisper_to"` } -func (a ActorWhisper) Execute(c *irc.Client, m *irc.Message, r *Rule) error { +func (a ActorWhisper) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) { if a.WhisperTo == nil || a.WhisperMessage == nil { - return nil + return false, nil } to, err := formatMessage(*a.WhisperTo, m, r, nil) if err != nil { - return errors.Wrap(err, "preparing whisper receiver") + return false, errors.Wrap(err, "preparing whisper receiver") } msg, err := formatMessage(*a.WhisperMessage, m, r, nil) if err != nil { - return errors.Wrap(err, "preparing whisper message") + return false, errors.Wrap(err, "preparing whisper message") } channel := "#tmijs" // As a fallback, copied from tmi.js @@ -36,7 +36,7 @@ func (a ActorWhisper) Execute(c *irc.Client, m *irc.Message, r *Rule) error { channel = fmt.Sprintf("#%s", config.Channels[0]) } - return errors.Wrap( + return false, errors.Wrap( c.WriteMessage(&irc.Message{ Command: "PRIVMSG", Params: []string{ diff --git a/actions.go b/actions.go index bad6eea..d4c8110 100644 --- a/actions.go +++ b/actions.go @@ -11,7 +11,7 @@ import ( type ( Actor interface { // Execute will be called after the config was read into the Actor - Execute(*irc.Client, *irc.Message, *Rule) error + Execute(*irc.Client, *irc.Message, *Rule) (preventCooldown bool, err error) // IsAsync may return true if the Execute function is to be executed // in a Go routine as of long runtime. Normally it should return false // except in very specific cases @@ -35,7 +35,7 @@ func registerAction(af ActorCreationFunc) { availableActions = append(availableActions, af) } -func triggerActions(c *irc.Client, m *irc.Message, rule *Rule, ra *RuleAction) error { +func triggerActions(c *irc.Client, m *irc.Message, rule *Rule, ra *RuleAction) (preventCooldown bool, err error) { availableActionsLock.RLock() defer availableActionsLock.RUnlock() @@ -52,30 +52,38 @@ func triggerActions(c *irc.Client, m *irc.Message, rule *Rule, ra *RuleAction) e if a.IsAsync() { go func() { - if err := a.Execute(c, m, rule); err != nil { + if _, err := a.Execute(c, m, rule); err != nil { logger.WithError(err).Error("Error in async actor") } }() continue } - if err := a.Execute(c, m, rule); err != nil { - return errors.Wrap(err, "execute action") + apc, err := a.Execute(c, m, rule) + if err != nil { + return preventCooldown, errors.Wrap(err, "execute action") } + preventCooldown = preventCooldown || apc } - return nil + return preventCooldown, nil } func handleMessage(c *irc.Client, m *irc.Message, event *string) { for _, r := range config.GetMatchingRules(m, event) { + var preventCooldown bool + for _, a := range r.Actions { - if err := triggerActions(c, m, r, a); err != nil { + apc, err := triggerActions(c, m, r, a) + if err != nil { log.WithError(err).Error("Unable to trigger action") } + preventCooldown = preventCooldown || apc } // Lock command - r.setCooldown(m) + if !preventCooldown { + r.setCooldown(m) + } } }