fork of indigo with slightly nicer lexgen
at main 5.9 kB view raw
1package rules 2 3import ( 4 "fmt" 5 "time" 6 "unicode/utf8" 7 8 appbsky "github.com/bluesky-social/indigo/api/bsky" 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 "github.com/bluesky-social/indigo/automod" 11 "github.com/bluesky-social/indigo/automod/countstore" 12 "github.com/bluesky-social/indigo/automod/helpers" 13) 14 15var _ automod.PostRuleFunc = ReplyCountPostRule 16 17// does not count "self-replies" (direct to self, or in own post thread) 18func ReplyCountPostRule(c *automod.RecordContext, post *appbsky.FeedPost) error { 19 if post.Reply == nil || helpers.IsSelfThread(c, post) { 20 return nil 21 } 22 23 did := c.Account.Identity.DID.String() 24 if c.GetCount("reply", did, countstore.PeriodDay) > 3 { 25 // TODO: disabled, too noisy for prod 26 //c.AddAccountFlag("frequent-replier") 27 } 28 c.Increment("reply", did) 29 30 parentURI, err := syntax.ParseATURI(post.Reply.Parent.Uri) 31 if err != nil { 32 c.Logger.Warn("failed to parse reply AT-URI", "uri", post.Reply.Parent.Uri) 33 return nil 34 } 35 c.IncrementDistinct("reply-to", did, parentURI.Authority().String()) 36 return nil 37} 38 39// triggers on the N+1 post 40// var identicalReplyLimit = 6 41// TODO: bumping temporarily 42var identicalReplyLimit = 20 43var identicalReplyActionLimit = 75 44 45var _ automod.PostRuleFunc = IdenticalReplyPostRule 46 47// Looks for accounts posting the exact same text multiple times. Does not currently count the number of distinct accounts replied to, just counts replies at all. 48// 49// There can be legitimate situations that trigger this rule, so in most situations should be a "report" not "label" action. 50func IdenticalReplyPostRule(c *automod.RecordContext, post *appbsky.FeedPost) error { 51 if post.Reply == nil || helpers.IsSelfThread(c, post) { 52 return nil 53 } 54 55 // don't action short replies, or accounts more than two weeks old 56 if utf8.RuneCountInString(post.Text) <= 10 { 57 return nil 58 } 59 if helpers.AccountIsOlderThan(&c.AccountContext, 14*24*time.Hour) { 60 return nil 61 } 62 63 // don't count if there is a follow-back relationship 64 if helpers.ParentOrRootIsFollower(c, post) { 65 return nil 66 } 67 68 // increment before read. use a specific period (IncrementPeriod()) to reduce the number of counters (one per unique post text) 69 period := countstore.PeriodDay 70 bucket := c.Account.Identity.DID.String() + "/" + helpers.HashOfString(post.Text) 71 c.IncrementPeriod("reply-text", bucket, period) 72 73 count := c.GetCount("reply-text", bucket, period) 74 if count >= identicalReplyLimit { 75 c.AddAccountFlag("multi-identical-reply") 76 c.ReportAccount(automod.ReportReasonSpam, fmt.Sprintf("possible spam (new account, %d identical reply-posts today)", count)) 77 c.Notify("slack") 78 } 79 if count >= identicalReplyActionLimit && utf8.RuneCountInString(post.Text) > 100 { 80 c.ReportAccount(automod.ReportReasonRude, fmt.Sprintf("likely spam/harassment (new account, %d identical reply-posts today), actioned (remove label urgently if account is ok)", count)) 81 c.AddAccountLabel("!warn") 82 c.Notify("slack") 83 } 84 85 return nil 86} 87 88// Similar to above rule but only counts replies to the same post. More aggressively applies a spam label to new accounts that are less than a day old. 89var identicalReplySameParentLimit = 3 90var identicalReplySameParentMaxAge = 24 * time.Hour 91var identicalReplySameParentMaxPosts int64 = 50 92var _ automod.PostRuleFunc = IdenticalReplyPostSameParentRule 93 94func IdenticalReplyPostSameParentRule(c *automod.RecordContext, post *appbsky.FeedPost) error { 95 if post.Reply == nil || helpers.IsSelfThread(c, post) { 96 return nil 97 } 98 99 if helpers.ParentOrRootIsFollower(c, post) { 100 return nil 101 } 102 103 postCount := c.Account.PostsCount 104 if helpers.AccountIsOlderThan(&c.AccountContext, identicalReplySameParentMaxAge) || postCount >= identicalReplySameParentMaxPosts { 105 return nil 106 } 107 108 period := countstore.PeriodHour 109 bucket := c.Account.Identity.DID.String() + "/" + post.Reply.Parent.Uri + "/" + helpers.HashOfString(post.Text) 110 c.IncrementPeriod("reply-text-same-post", bucket, period) 111 112 count := c.GetCount("reply-text-same-post", bucket, period) 113 if count >= identicalReplySameParentLimit { 114 c.AddAccountFlag("multi-identical-reply-same-post") 115 c.ReportAccount(automod.ReportReasonSpam, fmt.Sprintf("possible spam (%d identical reply-posts to same post today)", count)) 116 c.AddAccountLabel("spam") 117 c.Notify("slack") 118 } 119 120 return nil 121} 122 123// TODO: bumping temporarily 124// var youngReplyAccountLimit = 12 125var youngReplyAccountLimit = 200 126var _ automod.PostRuleFunc = YoungAccountDistinctRepliesRule 127 128func YoungAccountDistinctRepliesRule(c *automod.RecordContext, post *appbsky.FeedPost) error { 129 // only replies, and skip self-replies (eg, threads) 130 if post.Reply == nil || helpers.IsSelfThread(c, post) { 131 return nil 132 } 133 134 // don't action short replies, or accounts more than two weeks old 135 if utf8.RuneCountInString(post.Text) <= 10 { 136 return nil 137 } 138 if helpers.AccountIsOlderThan(&c.AccountContext, 14*24*time.Hour) { 139 return nil 140 } 141 142 // don't count if there is a follow-back relationship 143 if helpers.ParentOrRootIsFollower(c, post) { 144 return nil 145 } 146 147 parentURI, err := syntax.ParseATURI(post.Reply.Parent.Uri) 148 if err != nil { 149 c.Logger.Warn("failed to parse reply AT-URI", "uri", post.Reply.Parent.Uri) 150 return nil 151 } 152 parentDID, err := parentURI.Authority().AsDID() 153 if err != nil { 154 c.Logger.Warn("reply AT-URI authority not a DID", "uri", post.Reply.Parent.Uri) 155 return nil 156 } 157 158 did := c.Account.Identity.DID.String() 159 160 c.IncrementDistinct("young-reply-to", did, parentDID.String()) 161 // NOTE: won't include the increment from this event 162 count := c.GetCountDistinct("young-reply-to", did, countstore.PeriodHour) 163 if count >= youngReplyAccountLimit { 164 c.AddAccountFlag("new-account-distinct-account-reply") 165 c.ReportAccount(automod.ReportReasonRude, fmt.Sprintf("possible spam (new account, reply-posts to %d distinct accounts in past hour)", count)) 166 c.Notify("slack") 167 } 168 169 return nil 170}