From 2e944adb0495ca53eb02183be4caa5c2453c1e8c Mon Sep 17 00:00:00 2001 From: Steven Arnott Date: Thu, 24 Oct 2024 15:57:40 -0400 Subject: [PATCH] Split add and remove action to seeperate places Update example Some refactoring in matcher.go --- Makefile | 3 +- config-example/rules/reaction.yaml | 10 +++--- core/matcher.go | 53 +++++++++++++++++++++++++----- core/matcher_test.go | 39 +++++++++++++++------- models/message.go | 4 +-- models/rule.go | 3 +- remote/slack/helper.go | 17 +++++----- 7 files changed, 92 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index 6687d3dd..aec14cda 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,8 @@ test-coverage: clean: validate @echo "Running $@ tasks" -rm -v ./flottbot* - -rm -v ./debug + +## -rm -v ./debug #Not created # ┌┐ ┬ ┬┬┬ ┌┬┐ # ├┴┐│ │││ ││ diff --git a/config-example/rules/reaction.yaml b/config-example/rules/reaction.yaml index 4289c97a..76f910fe 100644 --- a/config-example/rules/reaction.yaml +++ b/config-example/rules/reaction.yaml @@ -1,11 +1,11 @@ -name: plus-one-reaction +name: question-reaction active: true -reaction_match: ":+1:" +reactions_added: "question" args: # actions -actions: # this rule takes no actions, just collects and responds with whats laid out in the format_response section - +actions: # no actions # response -format_output: "Oh a reaction?" # message to send to your user when they say hello +format_output: ${_user.firstname} could you elaborate more on your request? + start_message_thread: true # start a thread with the response direct_message_only: false # allow messaging inside channels diff --git a/core/matcher.go b/core/matcher.go index 3f44a411..c96a9f81 100644 --- a/core/matcher.go +++ b/core/matcher.go @@ -38,7 +38,7 @@ RuleSearch: // Only check active rules. if rule.Active { // Init some variables for use below - processedInput, hit := getProccessedInputAndHitValue(message.Input, rule.Respond, rule.Hear, message.Reaction, rule.ReactionMatch) + processedInput, hit := getProccessedInputAndHitValue(message, rule) // Determine what service we are processing the rule for switch message.Service { case models.MsgServiceChat, models.MsgServiceCLI: @@ -63,27 +63,62 @@ RuleSearch: } // getProccessedInputAndHitValue gets the processed input from the message input and the true/false if it was a successfully hit rule. -func getProccessedInputAndHitValue(messageInput, ruleRespondValue, ruleHearValue, messageReaction, ruleReactionMatch string) (string, bool) { +func getProccessedInputAndHitValue(message models.Message, rule models.Rule) (string, bool) { processedInput, hit := "", false - if ruleRespondValue != "" { + + ruleRespondValue := rule.Respond + ruleHearValue := rule.Hear + + if rule.Respond != "" { + log.Debug().Msgf("Respond Rule %s", rule.Name) + + messageInput := message.Input processedInput, hit = utils.Match(ruleRespondValue, messageInput, true) - } else if ruleHearValue != "" { // Are we listening to everything? + } else if rule.Hear != "" { // Are we listening to everything? + log.Debug().Msgf("HearRule %s", rule.Name) + + messageInput := message.Input _, hit = utils.Match(ruleHearValue, messageInput, false) - } else if ruleReactionMatch != "" { - processedInput, hit = utils.Match(ruleReactionMatch, messageReaction, false) + } else if rule.ReactionsAdded != "" { + log.Debug().Msgf("ReactionAdded Rule %s", rule.Name) + + messageReaction := message.ReactionAdded + processedInput, hit = utils.Match(rule.ReactionsAdded, messageReaction, false) + } else if rule.ReactionsRemoved != "" { + log.Debug().Msgf("ReactionRemoved Rule %s", rule.Name) + + messageReaction := message.ReactionRemoved + processedInput, hit = utils.Match(rule.ReactionsRemoved, messageReaction, false) } return processedInput, hit } +// isChatRule checks that the rule applies to chat. +func isValidChatRule(rule models.Rule) bool { + if rule.Respond != "" { + return true + } + + if rule.Hear != "" { + return true + } + + if rule.ReactionsAdded != "" || rule.ReactionsRemoved != "" { + return true + } + + return false +} + // handleChatServiceRule handles the processing logic for a rule that came from either the chat application or CLI remote. // //nolint:gocyclo // refactor candidate func handleChatServiceRule(outputMsgs chan<- models.Message, message models.Message, hitRule chan<- models.Rule, rule models.Rule, processedInput string, hit bool, bot *models.Bot) (bool, bool) { match, stopSearch := false, false - if rule.Respond != "" || rule.Hear != "" || rule.ReactionMatch != "" { - // You can only use 'respond', 'hear', or 'reaction match' + if isValidChatRule(rule) { + // You can only use 'respond', 'hear', or 'reactions' if rule.Respond != "" && rule.Hear != "" { log.Debug().Msgf("rule %#q has both 'hear' and 'match' or 'respond' defined. please choose one or the other", rule.Name) } @@ -235,7 +270,7 @@ func isValidHitChatRule(message *models.Message, rule models.Rule, processedInpu } // If this wasn't a 'hear' rule, handle the args - if rule.Hear == "" && rule.ReactionMatch == "" { + if rule.Hear == "" && rule.ReactionsAdded == "" { // Get all the args that the message sender supplied args := utils.RuleArgTokenizer(processedInput) diff --git a/core/matcher_test.go b/core/matcher_test.go index 40649811..062c518c 100644 --- a/core/matcher_test.go +++ b/core/matcher_test.go @@ -584,11 +584,13 @@ func TestUpdateReaction(t *testing.T) { func Test_getProccessedInputAndHitValue(t *testing.T) { type args struct { - messageInput string - ruleRespondValue string - ruleHearValue string - messageReaction string - ruleReactionMatch string + messageInput string + ruleRespondValue string + ruleHearValue string + messageReactionAdded string + messageReactionRemoved string + ruleReactionAdded string + ruleReactionRemoved string } tests := []struct { @@ -597,16 +599,31 @@ func Test_getProccessedInputAndHitValue(t *testing.T) { want string want1 bool }{ - {"hit", args{"hello foo", "hello", "hello", "", ""}, "foo", true}, - {"hit no hear value", args{"hello foo", "hello", "", "", ""}, "foo", true}, - {"hit no respond value - drops args", args{"hello foo", "", "hello", "", ""}, "", true}, - {"no match", args{"hello foo", "", "", "", ""}, "", false}, - {"hit reaction", args{"", "", "", ":hello:", ":hello:"}, ":hello:", true}, + {"hit", args{"hello foo", "hello", "hello", "", "", "", ""}, "foo", true}, + {"hit no hear value", args{"hello foo", "hello", "", "", "", "", ""}, "foo", true}, + {"hit no respond value - drops args", args{"hello foo", "", "hello", "", "", "", ""}, "", true}, + {"no match", args{"hello foo", "", "", "", "", "", ""}, "", false}, + {"hit reaction added", args{"", "", "", "hello", "", "hello", ""}, "hello", true}, + {"hit reaction removed", args{"", "", "", "", "hello", "", "hello"}, "hello", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, got1 := getProccessedInputAndHitValue(tt.args.messageInput, tt.args.ruleRespondValue, tt.args.ruleHearValue, tt.args.messageReaction, tt.args.ruleReactionMatch) + rule := models.Rule{ + Hear: tt.args.ruleHearValue, + Respond: tt.args.ruleRespondValue, + ReactionsAdded: tt.args.ruleReactionAdded, + ReactionsRemoved: tt.args.ruleReactionRemoved, + } + + message := models.Message{ + Input: tt.args.messageInput, + ReactionAdded: tt.args.messageReactionAdded, + ReactionRemoved: tt.args.messageReactionRemoved, + } + + got, got1 := getProccessedInputAndHitValue(message, rule) + if got != tt.want { t.Errorf("getProccessedInputAndHitValue() got = %v, want %v", got, tt.want) } diff --git a/models/message.go b/models/message.go index 3065e0ee..254df4d0 100644 --- a/models/message.go +++ b/models/message.go @@ -17,8 +17,8 @@ type Message struct { ChannelName string Input string Output string - ReactionAction string - Reaction string + ReactionAdded string + ReactionRemoved string Error string Timestamp string ThreadID string diff --git a/models/rule.go b/models/rule.go index 45f39e61..2dfbcc25 100644 --- a/models/rule.go +++ b/models/rule.go @@ -7,7 +7,8 @@ type Rule struct { Name string `mapstructure:"name" binding:"required"` Respond string `mapstructure:"respond" binding:"omitempty"` Hear string `mapstructure:"hear" binding:"omitempty"` - ReactionMatch string `mapstructure:"reaction_match" binding:"omitempty"` + ReactionsAdded string `mapstructure:"reactions_added" binding:"omitempty"` + ReactionsRemoved string `mapstructure:"reactions_removed" binding:"omitempty"` Schedule string `mapstructure:"schedule"` Args []string `mapstructure:"args" binding:"required"` DirectMessageOnly bool `mapstructure:"direct_message_only" binding:"required"` diff --git a/remote/slack/helper.go b/remote/slack/helper.go index b70916a7..36198e6a 100644 --- a/remote/slack/helper.go +++ b/remote/slack/helper.go @@ -449,14 +449,15 @@ func populateMessage(message models.Message, msgType models.MessageType, channel func populateReaction(message models.Message, msgType models.MessageType, channel, action, reaction, timeStamp, link string, user *slack.User, bot *models.Bot) models.Message { switch msgType { case models.MsgTypeDirect, models.MsgTypeChannel, models.MsgTypePrivateChannel: - message.ReactionAction = action - message.Reaction = reaction - message.Input = "" - message.Output = "" - message.Timestamp = timeStamp - message.SourceLink = link - message.Vars["_reaction.action"] = action - message.Vars["_reaction"] = reaction + switch action { + case "added": + message.ReactionAdded = reaction + case "removed": + message.ReactionRemoved = reaction + } + + message.Vars["_reaction.added"] = message.ReactionAdded + message.Vars["_reaction.removed"] = message.ReactionRemoved message = populateMessage(message, msgType, channel, "", timeStamp, "", link, false, user, bot) default: