diff --git a/cmd/automod/rules.go b/cmd/automod/rules.go index cadfd6b..6878e77 100644 --- a/cmd/automod/rules.go +++ b/cmd/automod/rules.go @@ -16,16 +16,21 @@ var BLADERUNNER_THRESHOLD = 2 var JABRONI_THRESHOLD = 2 func GoodbotBadbotRule(c *automod.RecordContext, post *appbsky.FeedPost) error { - if post.Reply == nil || IsSelfThread(c, post) { - return nil - } - botType := GetBotResponseType(post.Text) if botType == -1 { return nil } + authorDID := c.Account.Identity.DID + + if post.Reply == nil || IsSelfThread(c, post) { + mentionedDids := mentionedDids(post) + for _, botDID := range mentionedDids { + handleBotSignal(c, botDID, authorDID, botType) + } + return nil + } parentURI, err := syntax.ParseATURI(post.Reply.Parent.Uri) if err != nil { return nil @@ -35,11 +40,14 @@ func GoodbotBadbotRule(c *automod.RecordContext, post *appbsky.FeedPost) error { if err != nil { return err } - authorDID := c.Account.Identity.DID + handleBotSignal(c, botDID, authorDID, botType) + return nil +} +func handleBotSignal(c *automod.RecordContext, botDID string, authorDID string, botType int) { if botType == 1 { - c.IncrementDistinct("goodbot", botDID.String(), authorDID.String()) - c.IncrementDistinct("bladerunner", authorDID.String(), botDID.String()) + c.IncrementDistinct("goodbot", botDID, authorDID) + c.IncrementDistinct("bladerunner", authorDID, botDID) c.Logger.Error("good bot reply") // XXX: bypass counts for early testing @@ -47,55 +55,83 @@ func GoodbotBadbotRule(c *automod.RecordContext, post *appbsky.FeedPost) error { return err } - if c.GetCountDistinct("goodbot", botDID.String(), countstore.PeriodTotal) > GOOD_BOT_REPLY_THRESHOLD-1 { + if c.GetCountDistinct("goodbot", botDID, countstore.PeriodTotal) > GOOD_BOT_REPLY_THRESHOLD-1 { c.Logger.Error("good bot") // c.AddAccountLabel("good-bot") // c.Notify("slack") } - if c.GetCountDistinct("bladerunner", authorDID.String(), countstore.PeriodTotal) > BLADERUNNER_THRESHOLD-1 { + if c.GetCountDistinct("bladerunner", authorDID, countstore.PeriodTotal) > BLADERUNNER_THRESHOLD-1 { c.Logger.Error("bladerunner") // c.AddAccountLabel("bladerunner") // c.Notify("slack") } - return nil + return } - c.IncrementDistinct("badbot", botDID.String(), authorDID.String()) - c.IncrementDistinct("jabroni", authorDID.String(), botDID.String()) + c.IncrementDistinct("badbot", botDID, authorDID) + c.IncrementDistinct("jabroni", authorDID, botDID) c.Logger.Error("bad bot reply") - if c.GetCountDistinct("badbot", botDID.String(), countstore.PeriodTotal) > BAD_BOT_REPLY_THRESHOLD-1 { + if c.GetCountDistinct("badbot", botDID, countstore.PeriodTotal) > BAD_BOT_REPLY_THRESHOLD-1 { // @TODO: this would add label to the reply author's account not the parent/bot's account // c.AddAccountLabel("bad-bot") c.Logger.Error("bad bot") // c.Notify("slack") } - if c.GetCountDistinct("jabroni", authorDID.String(), countstore.PeriodTotal) > JABRONI_THRESHOLD-1 { + if c.GetCountDistinct("jabroni", authorDID, countstore.PeriodTotal) > JABRONI_THRESHOLD-1 { // c.AddAccountLabel("jabroni") c.Logger.Error("jabroni") // c.Notify("slack") } +} - return nil +func mentionedDids(post *appbsky.FeedPost) []string { + mentionedDids := []string{} + for _, facet := range post.Facets { + for _, feature := range facet.Features { + mention := feature.RichtextFacet_Mention + if mention == nil { + continue + } + mentionedDids = append(mentionedDids, mention.Did) + } + } + + return mentionedDids } -// @TODO: this isn't a clean check and doing some duplicate regex checks that can be avoided +// @TODO: this is a dumb check that only matches text exactly, could be improved func GetBotResponseType(s string) int { // Normalize the string by converting to lowercase and trimming spaces - s = strings.TrimSpace(strings.ToLower(s)) + tokens := TokenizeText(strings.TrimSpace(strings.ToLower(s))) + hasGoodBot := false + hasBadBot := false - if s == "good bot" { + for i, token := range tokens { + if token != "good" && token != "bad" && token != "bot" && !strings.HasPrefix(token, "@") { + return -1 + } + + if (token == "good" || token == "bad") && i+1 < len(tokens) && tokens[i+1] == "bot" { + if token == "good" { + hasGoodBot = true + } else { + hasBadBot = true + } + } + } + + if hasGoodBot { return 1 } - if s == "bad bot" { + if hasBadBot { return 0 } - // If neither pattern matches return -1 } @@ -119,3 +155,7 @@ func IsSelfThread(c *automod.RecordContext, post *appbsky.FeedPost) bool { } return false } + +func TokenizeText(text string) []string { + return strings.Fields(text) +} diff --git a/cmd/automod/rules_test.go b/cmd/automod/rules_test.go index 0b1a6eb..1860aee 100644 --- a/cmd/automod/rules_test.go +++ b/cmd/automod/rules_test.go @@ -12,4 +12,8 @@ func TestGetBotResponseType(t *testing.T) { assert.Equal(GetBotResponseType("bad bot"), 0) assert.Equal(GetBotResponseType("good bot"), 1) assert.Equal(GetBotResponseType("good bot beahvior is punished"), -1) + assert.Equal(GetBotResponseType("testing good bot one"), -1) + assert.Equal(GetBotResponseType("testing good bot"), -1) + assert.Equal(GetBotResponseType("@test.bsky.social good bot"), 1) + assert.Equal(GetBotResponseType("bad bot @one.bsky.social"), 0) }