this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

rules: refactor with some helpers

+131 -68
+1 -12
automod/rules/hashtags.go
··· 5 5 ) 6 6 7 7 func BanHashtagsPostRule(evt *automod.PostEvent) error { 8 - for _, tag := range evt.Post.Tags { 8 + for _, tag := range ExtractHashtags(evt.Post) { 9 9 if evt.InSet("banned-hashtags", tag) { 10 10 evt.AddRecordLabel("bad-hashtag") 11 11 break 12 - } 13 - } 14 - for _, facet := range evt.Post.Facets { 15 - for _, feat := range facet.Features { 16 - if feat.RichtextFacet_Tag != nil { 17 - tag := feat.RichtextFacet_Tag.Tag 18 - if evt.InSet("banned-hashtags", tag) { 19 - evt.AddRecordLabel("bad-hashtag") 20 - break 21 - } 22 - } 23 12 } 24 13 } 25 14 return nil
+78
automod/rules/helpers.go
··· 1 + package rules 2 + 3 + import ( 4 + "fmt" 5 + 6 + appbsky "github.com/bluesky-social/indigo/api/bsky" 7 + ) 8 + 9 + func dedupeStrings(in []string) []string { 10 + var out []string 11 + seen := make(map[string]bool) 12 + for _, v := range in { 13 + if !seen[v] { 14 + out = append(out, v) 15 + seen[v] = true 16 + } 17 + } 18 + return out 19 + } 20 + 21 + func ExtractHashtags(post *appbsky.FeedPost) []string { 22 + var tags []string 23 + for _, tag := range post.Tags { 24 + tags = append(tags, tag) 25 + } 26 + for _, facet := range post.Facets { 27 + for _, feat := range facet.Features { 28 + if feat.RichtextFacet_Tag != nil { 29 + tags = append(tags, feat.RichtextFacet_Tag.Tag) 30 + } 31 + } 32 + } 33 + return dedupeStrings(tags) 34 + } 35 + 36 + type PostFacet struct { 37 + Text string 38 + URL *string 39 + DID *string 40 + Tag *string 41 + } 42 + 43 + func ExtractFacets(post *appbsky.FeedPost) ([]PostFacet, error) { 44 + var out []PostFacet 45 + 46 + for _, facet := range post.Facets { 47 + for _, feat := range facet.Features { 48 + if int(facet.Index.ByteEnd) > len([]byte(post.Text)) || facet.Index.ByteStart > facet.Index.ByteEnd { 49 + return nil, fmt.Errorf("invalid facet byte range") 50 + } 51 + 52 + txt := string([]byte(post.Text)[facet.Index.ByteStart:facet.Index.ByteEnd]) 53 + if txt == "" { 54 + return nil, fmt.Errorf("empty facet text") 55 + } 56 + 57 + if feat.RichtextFacet_Link != nil { 58 + out = append(out, PostFacet{ 59 + Text: txt, 60 + URL: &feat.RichtextFacet_Link.Uri, 61 + }) 62 + } 63 + if feat.RichtextFacet_Tag != nil { 64 + out = append(out, PostFacet{ 65 + Text: txt, 66 + Tag: &feat.RichtextFacet_Tag.Tag, 67 + }) 68 + } 69 + if feat.RichtextFacet_Mention != nil { 70 + out = append(out, PostFacet{ 71 + Text: txt, 72 + DID: &feat.RichtextFacet_Mention.Did, 73 + }) 74 + } 75 + } 76 + } 77 + return out, nil 78 + }
+52 -56
automod/rules/misleading.go
··· 9 9 ) 10 10 11 11 func MisleadingURLPostRule(evt *automod.PostEvent) error { 12 - for _, facet := range evt.Post.Facets { 13 - for _, feat := range facet.Features { 14 - if feat.RichtextFacet_Link != nil { 15 - if int(facet.Index.ByteEnd) > len([]byte(evt.Post.Text)) || facet.Index.ByteStart > facet.Index.ByteEnd { 16 - evt.Logger.Warn("invalid facet range") 17 - evt.AddRecordLabel("invalid") // TODO: or some other "this record is corrupt" indicator? 18 - continue 19 - } 20 - txt := string([]byte(evt.Post.Text)[facet.Index.ByteStart:facet.Index.ByteEnd]) 21 - 22 - linkURL, err := url.Parse(feat.RichtextFacet_Link.Uri) 23 - if err != nil { 24 - evt.Logger.Warn("invalid link metadata URL", "uri", feat.RichtextFacet_Link.Uri) 25 - continue 26 - } 12 + facets, err := ExtractFacets(evt.Post) 13 + if err != nil { 14 + evt.Logger.Warn("invalid facets", "err", err) 15 + evt.AddRecordLabel("invalid") // TODO: or some other "this record is corrupt" indicator? 16 + return nil 17 + } 18 + for _, facet := range facets { 19 + if facet.URL != nil { 20 + linkURL, err := url.Parse(*facet.URL) 21 + if err != nil { 22 + evt.Logger.Warn("invalid link metadata URL", "uri", facet.URL) 23 + continue 24 + } 27 25 28 - // try parsing as a full URL 29 - textURL, err := url.Parse(txt) 30 - if err != nil { 31 - evt.Logger.Warn("invalid link text URL", "uri", txt) 32 - continue 33 - } 26 + // try parsing as a full URL 27 + textURL, err := url.Parse(facet.Text) 28 + if err != nil { 29 + evt.Logger.Warn("invalid link text URL", "uri", facet.Text) 30 + continue 31 + } 34 32 35 - // for now just compare domains to handle the most obvious cases 36 - // this public code will obviously get discovered and bypassed. this doesn't earn you any security cred! 37 - if linkURL.Host != textURL.Host { 38 - evt.Logger.Warn("misleading mismatched domains", "link", linkURL.Host, "text", textURL.Host) 39 - evt.AddRecordLabel("misleading") 40 - } 33 + // for now just compare domains to handle the most obvious cases 34 + // this public code will obviously get discovered and bypassed. this doesn't earn you any security cred! 35 + if linkURL.Host != textURL.Host { 36 + evt.Logger.Warn("misleading mismatched domains", "link", linkURL.Host, "text", textURL.Host) 37 + evt.AddRecordLabel("misleading") 41 38 } 42 39 } 43 40 } ··· 47 44 func MisleadingMentionPostRule(evt *automod.PostEvent) error { 48 45 // TODO: do we really need to route context around? probably 49 46 ctx := context.TODO() 50 - for _, facet := range evt.Post.Facets { 51 - for _, feat := range facet.Features { 52 - if feat.RichtextFacet_Mention != nil { 53 - if int(facet.Index.ByteEnd) > len([]byte(evt.Post.Text)) || facet.Index.ByteStart > facet.Index.ByteEnd { 54 - evt.Logger.Warn("invalid facet range") 55 - evt.AddRecordLabel("invalid") // TODO: or some other "this record is corrupt" indicator? 56 - continue 57 - } 58 - txt := string([]byte(evt.Post.Text)[facet.Index.ByteStart:facet.Index.ByteEnd]) 59 - if txt[0] == '@' { 60 - txt = txt[1:] 61 - } 62 - handle, err := syntax.ParseHandle(txt) 63 - if err != nil { 64 - evt.Logger.Warn("mention was not a valid handle", "text", txt) 65 - continue 66 - } 47 + facets, err := ExtractFacets(evt.Post) 48 + if err != nil { 49 + evt.Logger.Warn("invalid facets", "err", err) 50 + evt.AddRecordLabel("invalid") // TODO: or some other "this record is corrupt" indicator? 51 + return nil 52 + } 53 + for _, facet := range facets { 54 + if facet.DID != nil { 55 + txt := facet.Text 56 + if txt[0] == '@' { 57 + txt = txt[1:] 58 + } 59 + handle, err := syntax.ParseHandle(txt) 60 + if err != nil { 61 + evt.Logger.Warn("mention was not a valid handle", "text", txt) 62 + continue 63 + } 67 64 68 - mentioned, err := evt.Engine.Directory.LookupHandle(ctx, handle) 69 - if err != nil { 70 - evt.Logger.Warn("could not resolve handle", "handle", handle) 71 - evt.AddRecordLabel("misleading") 72 - break 73 - } 65 + mentioned, err := evt.Engine.Directory.LookupHandle(ctx, handle) 66 + if err != nil { 67 + evt.Logger.Warn("could not resolve handle", "handle", handle) 68 + evt.AddRecordLabel("misleading") 69 + break 70 + } 74 71 75 - // TODO: check if mentioned DID was recently updated? might be a caching issue 76 - if mentioned.DID.String() != feat.RichtextFacet_Mention.Did { 77 - evt.Logger.Warn("misleading mention", "text", txt, "did", mentioned.DID) 78 - evt.AddRecordLabel("misleading") 79 - continue 80 - } 72 + // TODO: check if mentioned DID was recently updated? might be a caching issue 73 + if mentioned.DID.String() != *facet.DID { 74 + evt.Logger.Warn("misleading mention", "text", txt, "did", facet.DID) 75 + evt.AddRecordLabel("misleading") 76 + continue 81 77 } 82 78 } 83 79 }