From 7b20e4d3feacb89bc14602c85da2012be547fccc Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sun, 5 Feb 2023 16:08:22 +0100 Subject: [PATCH] [stopexec] Add `stopexec` actor Signed-off-by: Knut Ahlers --- actions.go | 22 ++++++++-- internal/actors/stopexec/actor.go | 71 +++++++++++++++++++++++++++++++ plugins/rule.go | 5 +++ plugins_core.go | 2 + wiki/Actors.md | 13 ++++++ 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 internal/actors/stopexec/actor.go diff --git a/actions.go b/actions.go index ec10175..cad6342 100644 --- a/actions.go +++ b/actions.go @@ -74,13 +74,29 @@ func handleMessage(c *irc.Client, m *irc.Message, event *string, eventData *plug for _, r := range config.GetMatchingRules(m, event, eventData) { var preventCooldown bool + ActionsLoop: for _, a := range r.Actions { apc, err := triggerAction(c, m, r, a, eventData) - if err != nil { + 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 + preventCooldown = preventCooldown || apc + break ActionsLoop + + default: + // Action experienced an error: We don't store the cooldown + // state of this action and stop executing the actions stack + // for this rule log.WithError(err).Error("Unable to trigger action") - break // Break execution when one action fails + break ActionsLoop // Break execution for this rule when one action fails } - preventCooldown = preventCooldown || apc } // Lock command diff --git a/internal/actors/stopexec/actor.go b/internal/actors/stopexec/actor.go new file mode 100644 index 0000000..6dd7e57 --- /dev/null +++ b/internal/actors/stopexec/actor.go @@ -0,0 +1,71 @@ +package stopexec + +import ( + "github.com/go-irc/irc" + "github.com/pkg/errors" + + "github.com/Luzifer/twitch-bot/v3/plugins" +) + +const actorName = "stopexec" + +var formatMessage plugins.MsgFormatter + +func Register(args plugins.RegistrationArguments) error { + formatMessage = args.FormatMessage + + args.RegisterActor(actorName, func() plugins.Actor { return &actor{} }) + + args.RegisterActorDocumentation(plugins.ActionDocumentation{ + Description: "Stop Rule Execution on Condition", + Name: "Stop Execution", + Type: actorName, + + Fields: []plugins.ActionDocumentationField{ + { + Default: "", + Description: "Condition when to stop execution (must evaluate to \"true\" to stop execution)", + Key: "when", + Name: "When", + Optional: false, + SupportTemplate: true, + Type: plugins.ActionDocumentationFieldTypeString, + }, + }, + }) + + return nil +} + +type actor struct{} + +func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) { + ptrStringEmpty := func(v string) *string { return &v }("") + + when, err := formatMessage(attrs.MustString("when", ptrStringEmpty), m, r, eventData) + if err != nil { + return false, errors.Wrap(err, "executing when template") + } + + if when == "true" { + return false, plugins.ErrStopRuleExecution + } + + return false, nil +} + +func (a actor) IsAsync() bool { return false } +func (a actor) Name() string { return actorName } + +func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) { + whenTemplate, err := attrs.String("when") + if err != nil || whenTemplate == "" { + return errors.New("when must be non-empty string") + } + + if err = tplValidator(whenTemplate); err != nil { + return errors.Wrap(err, "validating when template") + } + + return nil +} diff --git a/plugins/rule.go b/plugins/rule.go index 4968e81..33bca0c 100644 --- a/plugins/rule.go +++ b/plugins/rule.go @@ -28,6 +28,11 @@ const ( remoteRuleFetchTimeout = 5 * time.Second ) +// ErrStopRuleExecution is a way for actions to terminate execution +// of the current rule gracefully. No actions after this has been +// returned will be executed and no error state will be set +var ErrStopRuleExecution = errors.New("stop rule execution now") + type ( Rule struct { UUID string `hash:"-" json:"uuid,omitempty" yaml:"uuid,omitempty"` diff --git a/plugins_core.go b/plugins_core.go index 52c2c5a..6268f10 100644 --- a/plugins_core.go +++ b/plugins_core.go @@ -25,6 +25,7 @@ import ( "github.com/Luzifer/twitch-bot/v3/internal/actors/respond" "github.com/Luzifer/twitch-bot/v3/internal/actors/shield" "github.com/Luzifer/twitch-bot/v3/internal/actors/shoutout" + "github.com/Luzifer/twitch-bot/v3/internal/actors/stopexec" "github.com/Luzifer/twitch-bot/v3/internal/actors/timeout" "github.com/Luzifer/twitch-bot/v3/internal/actors/variables" "github.com/Luzifer/twitch-bot/v3/internal/actors/vip" @@ -62,6 +63,7 @@ var ( respond.Register, shield.Register, shoutout.Register, + stopexec.Register, timeout.Register, variables.Register, vip.Register, diff --git a/wiki/Actors.md b/wiki/Actors.md index eb08a9a..1b0862b 100644 --- a/wiki/Actors.md +++ b/wiki/Actors.md @@ -357,6 +357,19 @@ Perform a Twitch-native shoutout user: "" ``` +## Stop Execution + +Stop Rule Execution on Condition + +```yaml +- type: stopexec + attributes: + # Condition when to stop execution (must evaluate to "true" to stop execution) + # Optional: false + # Type: string (Supports Templating) + when: "" +``` + ## Timeout User Timeout user from chat