fork of indigo with slightly nicer lexgen
at main 7.4 kB view raw
1package rules 2 3import ( 4 "bytes" 5 "fmt" 6 "strings" 7 8 appbsky "github.com/bluesky-social/indigo/api/bsky" 9 "github.com/bluesky-social/indigo/automod" 10 "github.com/bluesky-social/indigo/automod/helpers" 11 "github.com/bluesky-social/indigo/automod/keyword" 12) 13 14func BadWordPostRule(c *automod.RecordContext, post *appbsky.FeedPost) error { 15 isJapanese := false 16 for _, lang := range post.Langs { 17 if lang == "ja" || strings.HasPrefix(lang, "ja-") { 18 isJapanese = true 19 } 20 } 21 for _, tok := range helpers.ExtractTextTokensPost(post) { 22 word := keyword.SlugIsExplicitSlur(tok) 23 // used very frequently in a reclaimed context 24 if word != "" && word != "faggot" && word != "tranny" && word != "coon" && !(word == "kike" && isJapanese) { 25 c.AddRecordFlag("bad-word-text") 26 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in post text or alttext: %s", word)) 27 //c.Notify("slack") 28 break 29 } 30 // de-pluralize 31 tok = strings.TrimSuffix(tok, "s") 32 if c.InSet("worst-words", tok) { 33 // skip this specific term, if used in a Japanese language post 34 if isJapanese && tok == "kike" { 35 continue 36 } 37 38 c.AddRecordFlag("bad-word-text") 39 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in post text or alttext: %s", tok)) 40 //c.Notify("slack") 41 break 42 } 43 } 44 return nil 45} 46 47var _ automod.PostRuleFunc = BadWordPostRule 48 49func BadWordProfileRule(c *automod.RecordContext, profile *appbsky.ActorProfile) error { 50 if profile.DisplayName != nil { 51 word := keyword.SlugContainsExplicitSlur(keyword.Slugify(*profile.DisplayName)) 52 if word != "" { 53 c.AddRecordFlag("bad-word-name") 54 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in display name: %s", word)) 55 //c.Notify("slack") 56 } 57 } 58 for _, tok := range helpers.ExtractTextTokensProfile(profile) { 59 // de-pluralize 60 tok = strings.TrimSuffix(tok, "s") 61 if c.InSet("worst-words", tok) { 62 c.AddRecordFlag("bad-word-text") 63 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in profile description: %s", tok)) 64 //c.Notify("slack") 65 break 66 } 67 } 68 return nil 69} 70 71var _ automod.ProfileRuleFunc = BadWordProfileRule 72 73// looks for the specific harassment situation of a replay to another user with only a single word 74func ReplySingleBadWordPostRule(c *automod.RecordContext, post *appbsky.FeedPost) error { 75 if post.Reply != nil && !helpers.IsSelfThread(c, post) { 76 tokens := helpers.ExtractTextTokensPost(post) 77 if len(tokens) != 1 { 78 return nil 79 } 80 tok := tokens[0] 81 if c.InSet("bad-words", tok) || keyword.SlugIsExplicitSlur(tok) != "" { 82 c.AddRecordFlag("reply-single-bad-word") 83 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("bad single-word reply: %s", tok)) 84 //c.Notify("slack") 85 } 86 } 87 return nil 88} 89 90var _ automod.PostRuleFunc = ReplySingleBadWordPostRule 91 92// scans for bad keywords in records other than posts and profiles 93func BadWordOtherRecordRule(c *automod.RecordContext) error { 94 name := "" 95 text := "" 96 switch c.RecordOp.Collection.String() { 97 case "app.bsky.graph.list": 98 var list appbsky.GraphList 99 if err := list.UnmarshalCBOR(bytes.NewReader(c.RecordOp.RecordCBOR)); err != nil { 100 return fmt.Errorf("failed to parse app.bsky.graph.list record: %v", err) 101 } 102 name += " " + list.Name 103 if list.Description != nil { 104 text += " " + *list.Description 105 } 106 if list.Purpose != nil { 107 text += " " + *list.Purpose 108 } 109 case "app.bsky.feed.generator": 110 var generator appbsky.FeedGenerator 111 if err := generator.UnmarshalCBOR(bytes.NewReader(c.RecordOp.RecordCBOR)); err != nil { 112 return fmt.Errorf("failed to parse app.bsky.feed.generator record: %v", err) 113 } 114 name += " " + generator.DisplayName 115 if generator.Description != nil { 116 text += " " + *generator.Description 117 } 118 } 119 if name != "" { 120 // check for explicit slurs or bad word tokens 121 word := keyword.SlugContainsExplicitSlur(keyword.Slugify(name)) 122 if word != "" { 123 c.AddRecordFlag("bad-word-name") 124 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in name: %s", word)) 125 c.Notify("slack") 126 } 127 tokens := keyword.TokenizeText(name) 128 for _, tok := range tokens { 129 if c.InSet("bad-words", tok) { 130 c.AddRecordFlag("bad-word-name") 131 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in name: %s", tok)) 132 c.Notify("slack") 133 break 134 } 135 } 136 } 137 if text != "" { 138 // check for explicit slurs or worst word tokens 139 word := keyword.SlugContainsExplicitSlur(keyword.Slugify(text)) 140 if word != "" { 141 c.AddRecordFlag("bad-word-text") 142 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in description: %s", word)) 143 c.Notify("slack") 144 } 145 tokens := keyword.TokenizeText(text) 146 for _, tok := range tokens { 147 // de-pluralize 148 tok = strings.TrimSuffix(tok, "s") 149 if c.InSet("worst-words", tok) { 150 c.AddRecordFlag("bad-word-text") 151 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in description: %s", tok)) 152 c.Notify("slack") 153 break 154 } 155 } 156 } 157 return nil 158} 159 160var _ automod.RecordRuleFunc = BadWordOtherRecordRule 161 162// scans the record-key for all records 163func BadWordRecordKeyRule(c *automod.RecordContext) error { 164 // check record key 165 word := keyword.SlugIsExplicitSlur(keyword.Slugify(c.RecordOp.RecordKey.String())) 166 if word != "" { 167 c.AddRecordFlag("bad-word-recordkey") 168 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in record-key (URL): %s", word)) 169 c.Notify("slack") 170 } 171 tokens := keyword.TokenizeIdentifier(c.RecordOp.RecordKey.String()) 172 for _, tok := range tokens { 173 if c.InSet("bad-words", tok) { 174 c.AddRecordFlag("bad-word-recordkey") 175 c.ReportRecord(automod.ReportReasonRude, fmt.Sprintf("possible bad word in record-key (URL): %s", tok)) 176 c.Notify("slack") 177 break 178 } 179 } 180 181 return nil 182} 183 184var _ automod.RecordRuleFunc = BadWordRecordKeyRule 185 186func BadWordHandleRule(c *automod.AccountContext) error { 187 word := keyword.SlugContainsExplicitSlur(keyword.Slugify(c.Account.Identity.Handle.String())) 188 if word != "" { 189 c.AddAccountFlag("bad-word-handle") 190 c.ReportAccount(automod.ReportReasonRude, fmt.Sprintf("possible bad word in handle (username): %s", word)) 191 //c.Notify("slack") 192 return nil 193 } 194 195 tokens := keyword.TokenizeIdentifier(c.Account.Identity.Handle.String()) 196 for _, tok := range tokens { 197 if c.InSet("bad-words", tok) { 198 c.AddAccountFlag("bad-word-handle") 199 c.ReportAccount(automod.ReportReasonRude, fmt.Sprintf("possible bad word in handle (username): %s", tok)) 200 //c.Notify("slack") 201 break 202 } 203 } 204 205 return nil 206} 207 208var _ automod.IdentityRuleFunc = BadWordHandleRule 209 210func BadWordDIDRule(c *automod.AccountContext) error { 211 if c.Account.Identity.DID.Method() == "plc" { 212 return nil 213 } 214 word := keyword.SlugContainsExplicitSlur(keyword.Slugify(c.Account.Identity.DID.String())) 215 if word != "" { 216 c.AddAccountFlag("bad-word-did") 217 c.ReportAccount(automod.ReportReasonRude, fmt.Sprintf("possible bad word in DID (account identifier): %s", word)) 218 c.Notify("slack") 219 return nil 220 } 221 222 tokens := keyword.TokenizeIdentifier(c.Account.Identity.DID.String()) 223 for _, tok := range tokens { 224 if c.InSet("bad-words", tok) { 225 c.AddAccountFlag("bad-word-did") 226 c.ReportAccount(automod.ReportReasonRude, fmt.Sprintf("possible bad word in DID (account identifier): %s", tok)) 227 c.Notify("slack") 228 break 229 } 230 } 231 232 return nil 233} 234 235var _ automod.IdentityRuleFunc = BadWordDIDRule