From 6dd52e53207f5dc045056d14c5066967e1c889ed Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sun, 17 Apr 2022 16:54:12 +0200 Subject: [PATCH] [customevent] Add actor to create events within rules Signed-off-by: Knut Ahlers --- internal/apimodules/customevent/actor.go | 41 ++++++++++++ .../apimodules/customevent/customevent.go | 62 ++++++++++++++----- 2 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 internal/apimodules/customevent/actor.go diff --git a/internal/apimodules/customevent/actor.go b/internal/apimodules/customevent/actor.go new file mode 100644 index 0000000..21cef76 --- /dev/null +++ b/internal/apimodules/customevent/actor.go @@ -0,0 +1,41 @@ +package customevent + +import ( + "strings" + + "github.com/go-irc/irc" + "github.com/pkg/errors" + + "github.com/Luzifer/twitch-bot/plugins" +) + +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 }("") + + fd, err := formatMessage(attrs.MustString("fields", ptrStringEmpty), m, r, eventData) + if err != nil { + return false, errors.Wrap(err, "executing fields template") + } + + if fd == "" { + return false, errors.New("fields template evaluated to empty string") + } + + return false, errors.Wrap( + triggerEvent(plugins.DeriveChannel(m, eventData), strings.NewReader(fd)), + "triggering event", + ) +} + +func (a actor) IsAsync() bool { return false } +func (a actor) Name() string { return actorName } + +func (a actor) Validate(attrs *plugins.FieldCollection) (err error) { + if v, err := attrs.String("fields"); err != nil || v == "" { + return errors.New("fields is expected to be non-empty string") + } + + return nil +} diff --git a/internal/apimodules/customevent/customevent.go b/internal/apimodules/customevent/customevent.go index a5cdd16..1c55d72 100644 --- a/internal/apimodules/customevent/customevent.go +++ b/internal/apimodules/customevent/customevent.go @@ -2,6 +2,7 @@ package customevent import ( "encoding/json" + "io" "net/http" "strings" @@ -11,10 +12,36 @@ import ( "github.com/Luzifer/twitch-bot/plugins" ) -var eventCreatorFunc plugins.EventHandlerFunc +const actorName = "customevent" + +var ( + eventCreatorFunc plugins.EventHandlerFunc + formatMessage plugins.MsgFormatter +) func Register(args plugins.RegistrationArguments) error { eventCreatorFunc = args.CreateEvent + formatMessage = args.FormatMessage + + args.RegisterActor(actorName, func() plugins.Actor { return &actor{} }) + + args.RegisterActorDocumentation(plugins.ActionDocumentation{ + Description: "Create a custom event", + Name: "Custom Event", + Type: actorName, + + Fields: []plugins.ActionDocumentationField{ + { + Default: "{}", + Description: "JSON representation of fields in the event (`map[string]any`)", + Key: "fields", + Name: "Fields", + Optional: false, + SupportTemplate: true, + Type: plugins.ActionDocumentationFieldTypeString, + }, + }, + }) args.RegisterAPIRoute(plugins.HTTPRouteRegistrationArgs{ Description: "Creates an `custom` event containing the fields provided in the request body", @@ -37,28 +64,35 @@ func Register(args plugins.RegistrationArguments) error { } func handleCreateEvent(w http.ResponseWriter, r *http.Request) { - var ( - channel = mux.Vars(r)["channel"] - payload = make(map[string]any) - ) + channel := mux.Vars(r)["channel"] if channel == "" { http.Error(w, errors.New("missing channel").Error(), http.StatusBadRequest) return } + channel = "#" + strings.TrimLeft(channel, "#") // Sanitize - if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { - http.Error(w, errors.Wrap(err, "parsing event payload").Error(), http.StatusBadRequest) - return - } - - fields := plugins.FieldCollectionFromData(payload) - fields.Set("channel", "#"+strings.TrimLeft(channel, "#")) - - if err := eventCreatorFunc("custom", fields); err != nil { + if err := triggerEvent(channel, r.Body); err != nil { http.Error(w, errors.Wrap(err, "creating event").Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } + +func triggerEvent(channel string, fieldData io.Reader) error { + payload := make(map[string]any) + + if err := json.NewDecoder(fieldData).Decode(&payload); err != nil { + return errors.Wrap(err, "parsing event payload") + } + + fields := plugins.FieldCollectionFromData(payload) + fields.Set("channel", "#"+strings.TrimLeft(channel, "#")) + + if err := eventCreatorFunc("custom", fields); err != nil { + return errors.Wrap(err, "creating event") + } + + return nil +}