this repo has no description
0
fork

Configure Feed

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

automod: add removing of labels from accounts and records (#950)

authored by

Hailey and committed by
GitHub
464ab8d6 dd179c33

+72 -14
+8
automod/engine/context.go
··· 242 242 c.effects.AddAccountLabel(val) 243 243 } 244 244 245 + func (c *AccountContext) RemoveAccountLabel(val string) { 246 + c.effects.RemoveAccountLabel(val) 247 + } 248 + 245 249 func (c *AccountContext) AddAccountTag(val string) { 246 250 c.effects.AddAccountTag(val) 247 251 } ··· 268 272 269 273 func (c *RecordContext) AddRecordLabel(val string) { 270 274 c.effects.AddRecordLabel(val) 275 + } 276 + 277 + func (c *RecordContext) RemoveRecordLabel(val string) { 278 + c.effects.RemoveRecordLabel(val) 271 279 } 272 280 273 281 func (c *RecordContext) AddRecordTag(val string) {
+28
automod/engine/effects.go
··· 28 28 CounterDistinctIncrements []CounterDistinctRef // TODO: better variable names 29 29 // Label values which should be applied to the overall account, as a result of rule execution. 30 30 AccountLabels []string 31 + // Label values which should be removed from the overall account, as a result of rule execution. 32 + RemovedAccountLabels []string 31 33 // Moderation tags (similar to labels, but private) which should be applied to the overall account, as a result of rule execution. 32 34 AccountTags []string 33 35 // automod flags (metadata) which should be applied to the account as a result of rule execution. ··· 42 44 AccountAcknowledge bool 43 45 // Same as "AccountLabels", but at record-level 44 46 RecordLabels []string 47 + // Same as "RemovedRecordLabels", but at record-level 48 + RemovedRecordLabels []string 45 49 // Same as "AccountTags", but at record-level 46 50 RecordTags []string 47 51 // Same as "AccountFlags", but at record-level ··· 98 102 e.AccountLabels = append(e.AccountLabels, val) 99 103 } 100 104 105 + // Enqueues the provided label (string value) to be removed from the account at the end of rule processing. 106 + func (e *Effects) RemoveAccountLabel(val string) { 107 + e.mu.Lock() 108 + defer e.mu.Unlock() 109 + for _, v := range e.RemovedAccountLabels { 110 + if v == val { 111 + return 112 + } 113 + } 114 + e.RemovedAccountLabels = append(e.RemovedAccountLabels, val) 115 + } 116 + 101 117 // Enqueues the provided label (string value) to be added to the account at the end of rule processing. 102 118 func (e *Effects) AddAccountTag(val string) { 103 119 e.mu.Lock() ··· 162 178 } 163 179 } 164 180 e.RecordLabels = append(e.RecordLabels, val) 181 + } 182 + 183 + // Enqueues the provided label (string value) to be removed from the record at the end of rule processing. 184 + func (e *Effects) RemoveRecordLabel(val string) { 185 + e.mu.Lock() 186 + defer e.mu.Unlock() 187 + for _, v := range e.RemovedRecordLabels { 188 + if v == val { 189 + return 190 + } 191 + } 192 + e.RemovedRecordLabels = append(e.RemovedRecordLabels, val) 165 193 } 166 194 167 195 // Enqueues the provided tag (string value) to be added to the record at the end of rule processing.
+29 -10
automod/engine/persist.go
··· 6 6 7 7 comatproto "github.com/bluesky-social/indigo/api/atproto" 8 8 toolsozone "github.com/bluesky-social/indigo/api/ozone" 9 + "github.com/bluesky-social/indigo/automod/keyword" 9 10 ) 10 11 11 12 func (eng *Engine) persistCounters(ctx context.Context, eff *Effects) error { ··· 42 43 43 44 // de-dupe actions 44 45 newLabels := dedupeLabelActions(c.effects.AccountLabels, c.Account.AccountLabels, c.Account.AccountNegatedLabels) 46 + rmdLabels := []string{} 47 + for _, lbl := range dedupeStrings(c.effects.RemovedAccountLabels) { 48 + // we don't need to try and remove labels whenever they are either _not_ already in the account labels, _or_ if they are 49 + // being applied by some other rule before persisting 50 + if !keyword.TokenInSet(lbl, c.Account.AccountLabels) || keyword.TokenInSet(lbl, c.effects.AccountLabels) { 51 + continue 52 + } 53 + rmdLabels = append(rmdLabels, lbl) 54 + } 45 55 existingTags := []string{} 46 56 if c.Account.Private != nil { 47 57 existingTags = c.Account.Private.AccountTags ··· 83 93 } 84 94 } 85 95 86 - anyModActions := newTakedown || newEscalation || newAcknowledge || len(newLabels) > 0 || len(newTags) > 0 || len(newFlags) > 0 || len(newReports) > 0 96 + anyModActions := newTakedown || newEscalation || newAcknowledge || len(newLabels) > 0 || len(rmdLabels) > 0 || len(newTags) > 0 || len(newFlags) > 0 || len(newReports) > 0 87 97 if anyModActions && eng.Notifier != nil { 88 98 for _, srv := range dedupeStrings(c.effects.NotifyServices) { 89 99 if err := eng.Notifier.SendAccount(ctx, srv, c); err != nil { ··· 111 121 112 122 xrpcc := eng.OzoneClient 113 123 114 - if len(newLabels) > 0 { 115 - c.Logger.Info("labeling account", "newLabels", newLabels) 124 + if len(newLabels) > 0 || len(rmdLabels) > 0 { 125 + c.Logger.Info("updating account labels", "newLabels", newLabels, "rmdLabels", rmdLabels) 116 126 for _, val := range newLabels { 117 127 // note: WithLabelValues is a prometheus label, not an atproto label 118 128 actionNewLabelCount.WithLabelValues("account", val).Inc() ··· 123 133 Event: &toolsozone.ModerationEmitEvent_Input_Event{ 124 134 ModerationDefs_ModEventLabel: &toolsozone.ModerationDefs_ModEventLabel{ 125 135 CreateLabelVals: newLabels, 126 - NegateLabelVals: []string{}, 136 + NegateLabelVals: rmdLabels, 127 137 Comment: &comment, 128 138 }, 129 139 }, ··· 265 275 266 276 atURI := c.RecordOp.ATURI().String() 267 277 newLabels := dedupeStrings(c.effects.RecordLabels) 278 + rmdLabels := []string{} 268 279 newTags := dedupeStrings(c.effects.RecordTags) 269 280 newEscalation := c.effects.RecordEscalate 270 281 newAcknowledge := c.effects.RecordAcknowledge 271 282 272 - if (newEscalation || newAcknowledge || len(newLabels) > 0 || len(newTags) > 0) && eng.OzoneClient != nil { 283 + if (newEscalation || newAcknowledge || len(newLabels) > 0 || len(rmdLabels) > 0 || len(newTags) > 0) && eng.OzoneClient != nil { 273 284 // fetch existing record labels, tags, etc 274 285 rv, err := toolsozone.ModerationGetRecord(ctx, eng.OzoneClient, c.RecordOp.CID.String(), c.RecordOp.ATURI().String()) 275 286 if err != nil { ··· 288 299 existingLabels = dedupeStrings(existingLabels) 289 300 negLabels = dedupeStrings(negLabels) 290 301 newLabels = dedupeLabelActions(newLabels, existingLabels, negLabels) 302 + for _, lbl := range dedupeStrings(c.effects.RemovedRecordLabels) { 303 + // we don't need to try and remove labels whenever they are either _not_ already in the record labels, _or_ if they are 304 + // being applied by some other rule before persisting 305 + if !keyword.TokenInSet(lbl, existingLabels) || keyword.TokenInSet(lbl, newLabels) { 306 + continue 307 + } 308 + rmdLabels = append(rmdLabels, lbl) 309 + } 291 310 existingTags := []string{} 292 311 hasSubjectStatus := rv.Moderation != nil && rv.Moderation.SubjectStatus != nil 293 312 if hasSubjectStatus && rv.Moderation.SubjectStatus.Tags != nil { ··· 331 350 return fmt.Errorf("circuit-breaking acknowledge: %w", err) 332 351 } 333 352 334 - if newEscalation || newAcknowledge || newTakedown || len(newLabels) > 0 || len(newTags) > 0 || len(newFlags) > 0 || len(newReports) > 0 { 353 + if newEscalation || newAcknowledge || newTakedown || len(newLabels) > 0 || len(rmdLabels) > 0 || len(newTags) > 0 || len(newFlags) > 0 || len(newReports) > 0 { 335 354 if eng.Notifier != nil { 336 355 for _, srv := range dedupeStrings(c.effects.NotifyServices) { 337 356 if err := eng.Notifier.SendRecord(ctx, srv, c); err != nil { ··· 351 370 } 352 371 353 372 // exit early 354 - if !newAcknowledge && !newEscalation && !newTakedown && len(newLabels) == 0 && len(newTags) == 0 && len(newReports) == 0 { 373 + if !newAcknowledge && !newEscalation && !newTakedown && len(newLabels) == 0 && len(rmdLabels) == 0 && len(newTags) == 0 && len(newReports) == 0 { 355 374 return nil 356 375 } 357 376 ··· 371 390 } 372 391 373 392 xrpcc := eng.OzoneClient 374 - if len(newLabels) > 0 { 375 - c.Logger.Info("labeling record", "newLabels", newLabels) 393 + if len(newLabels) > 0 || len(rmdLabels) > 0 { 394 + c.Logger.Info("updating record labels", "newLabels", newLabels, "rmdLabels", rmdLabels) 376 395 for _, val := range newLabels { 377 396 // note: WithLabelValues is a prometheus label, not an atproto label 378 397 actionNewLabelCount.WithLabelValues("record", val).Inc() ··· 383 402 Event: &toolsozone.ModerationEmitEvent_Input_Event{ 384 403 ModerationDefs_ModEventLabel: &toolsozone.ModerationDefs_ModEventLabel{ 385 404 CreateLabelVals: newLabels, 386 - NegateLabelVals: []string{}, 405 + NegateLabelVals: rmdLabels, 387 406 Comment: &comment, 388 407 }, 389 408 },
+7 -4
automod/engine/slack.go
··· 17 17 if service != "slack" { 18 18 return nil 19 19 } 20 - msg := slackBody("⚠️ Automod Account Action ⚠️\n", c.Account, c.effects.AccountLabels, c.effects.AccountFlags, c.effects.AccountReports, c.effects.AccountTakedown) 20 + msg := slackBody("⚠️ Automod Account Action ⚠️\n", c.Account, c.effects.AccountLabels, c.effects.RemovedAccountLabels, c.effects.AccountFlags, c.effects.AccountReports, c.effects.AccountTakedown) 21 21 c.Logger.Debug("sending slack notification") 22 22 return n.sendSlackMsg(ctx, msg) 23 23 } ··· 27 27 return nil 28 28 } 29 29 atURI := fmt.Sprintf("at://%s/%s/%s", c.Account.Identity.DID, c.RecordOp.Collection, c.RecordOp.RecordKey) 30 - msg := slackBody("⚠️ Automod Record Action ⚠️\n", c.Account, c.effects.RecordLabels, c.effects.RecordFlags, c.effects.RecordReports, c.effects.RecordTakedown) 30 + msg := slackBody("⚠️ Automod Record Action ⚠️\n", c.Account, c.effects.RecordLabels, c.effects.RemovedRecordLabels, c.effects.RecordFlags, c.effects.RecordReports, c.effects.RecordTakedown) 31 31 msg += fmt.Sprintf("`%s`\n", atURI) 32 32 c.Logger.Debug("sending slack notification") 33 33 return n.sendSlackMsg(ctx, msg) ··· 68 68 return nil 69 69 } 70 70 71 - func slackBody(header string, acct AccountMeta, newLabels, newFlags []string, newReports []ModReport, newTakedown bool) string { 71 + func slackBody(header string, acct AccountMeta, newLabels, rmdLabels, newFlags []string, newReports []ModReport, newTakedown bool) string { 72 72 msg := header 73 73 msg += fmt.Sprintf("`%s` / `%s` / <https://bsky.app/profile/%s|bsky> / <https://admin.prod.bsky.dev/repositories/%s|ozone>\n", 74 74 acct.Identity.DID, ··· 77 77 acct.Identity.DID, 78 78 ) 79 79 if len(newLabels) > 0 { 80 - msg += fmt.Sprintf("Labels: `%s`\n", strings.Join(newLabels, ", ")) 80 + msg += fmt.Sprintf("Added Labels: `%s`\n", strings.Join(newLabels, ", ")) 81 + } 82 + if len(rmdLabels) > 0 { 83 + msg += fmt.Sprintf("Removed Labels: `%s`\n", strings.Join(rmdLabels, ", ")) 81 84 } 82 85 if len(newFlags) > 0 { 83 86 msg += fmt.Sprintf("Flags: `%s`\n", strings.Join(newFlags, ", "))