-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
automod: mad science, attempting to factor engine/event/effect apart.
Non-compiling checkpoint. Separating events and effects feels good. Read-only event structures and append-only effects structures clarifies many semantics. All of the persistence logic itself stayed close to the engine, and takes the effects struct as a parameter. This felt super good. It means all the methods on the effects struct aire simply for mutating it. The struct itself remains legible and has no side-effects on its own (except logging), so the ability to test this without entanglements should be quite high. Distinct packages for events and effects is up for debate. It made it easier to have the compiler help me enforce detanglement during the experiment, but is not necessarily a good idea in the long run. The biggest issue that needs further pondering is what we want to see the ruletypes file look like. We aimed to take one more step from here to introduce a "business context" object and reduce the number of parameters of rule funcs to approximately one (maybe two, for unpacked specific values like "post")... but the trouble with that idea is that we still see that e.g. IdentityEvent and RecordEvent are still already distinct types. We note that RecordEvent is a strict superset of IdentityEvent, so maybe we just decide to take the superset one in order to have a single simple "business context" object... but we're not yet entirely sure if that flies the way we like.
- Loading branch information
Showing
18 changed files
with
966 additions
and
948 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package effects | ||
|
||
import ( | ||
"log/slog" | ||
"time" | ||
) | ||
|
||
var ( | ||
// time period within which automod will not re-report an account for the same reasonType | ||
ReportDupePeriod = 7 * 24 * time.Hour | ||
// number of reports automod can file per day, for all subjects and types combined (circuit breaker) | ||
QuotaModReportDay = 50 | ||
// number of takedowns automod can action per day, for all subjects combined (circuit breaker) | ||
QuotaModTakedownDay = 10 | ||
) | ||
|
||
type CounterRef struct { | ||
Name string | ||
Val string | ||
Period *string | ||
} | ||
|
||
type CounterDistinctRef struct { | ||
Name string | ||
Bucket string | ||
Val string | ||
} | ||
|
||
type RepoEffect struct { | ||
// Any error encountered while processing the event can be stashed in this field and handled at the end of all processing. | ||
//Err error // REVIEW: nothing seems to have actually used this. If we keep it we should decide the methods that pile mutations into this struct don't return errors; two paths is bad. | ||
|
||
// slog logger handle, with event-specific structured fields pre-populated. Pointer, but expected to not be nil. | ||
Logger *slog.Logger | ||
|
||
// List of counters which should be incremented as part of processing this event. These are collected during rule execution and persisted in bulk at the end. | ||
CounterIncrements []CounterRef | ||
// Similar to "CounterIncrements", but for "distinct" style counters | ||
CounterDistinctIncrements []CounterDistinctRef // TODO: better variable names | ||
// Label values which should be applied to the overall account, as a result of rule execution. | ||
AccountLabels []string | ||
// Moderation flags (similar to labels, but private) which should be applied to the overall account, as a result of rule execution. | ||
AccountFlags []string | ||
// Reports which should be filed against this account, as a result of rule execution. | ||
AccountReports []ModReport | ||
// If "true", indicates that a rule indicates that the entire account should have a takedown. | ||
AccountTakedown bool | ||
} | ||
|
||
// Enqueues the named counter to be incremented at the end of all rule processing. Will automatically increment for all time periods. | ||
// | ||
// "name" is the counter namespace. | ||
// "val" is the specific counter with that namespace. | ||
func (e *RepoEffect) Increment(name, val string) { | ||
e.CounterIncrements = append(e.CounterIncrements, CounterRef{Name: name, Val: val}) | ||
} | ||
|
||
// Enqueues the named counter to be incremented at the end of all rule processing. Will only increment the indicated time period bucket. | ||
func (e *RepoEffect) IncrementPeriod(name, val string, period string) { | ||
e.CounterIncrements = append(e.CounterIncrements, CounterRef{Name: name, Val: val, Period: &period}) | ||
} | ||
|
||
// Enqueues the named "distinct value" counter based on the supplied string value ("val") to be incremented at the end of all rule processing. Will automatically increment for all time periods. | ||
func (e *RepoEffect) IncrementDistinct(name, bucket, val string) { | ||
e.CounterDistinctIncrements = append(e.CounterDistinctIncrements, CounterDistinctRef{Name: name, Bucket: bucket, Val: val}) | ||
} | ||
|
||
// Enqueues the provided label (string value) to be added to the account at the end of rule processing. | ||
func (e *RepoEffect) AddAccountLabel(val string) { | ||
e.AccountLabels = append(e.AccountLabels, val) | ||
} | ||
|
||
// Enqueues the provided flag (string value) to be recorded (in the Engine's flagstore) at the end of rule processing. | ||
func (e *RepoEffect) AddAccountFlag(val string) { | ||
e.AccountFlags = append(e.AccountFlags, val) | ||
} | ||
|
||
// Enqueues a moderation report to be filed against the account at the end of rule processing. | ||
func (e *RepoEffect) ReportAccount(reason, comment string) { | ||
if comment == "" { | ||
comment = "(no comment)" | ||
} | ||
comment = "automod: " + comment | ||
e.AccountReports = append(e.AccountReports, ModReport{ReasonType: reason, Comment: comment}) | ||
} | ||
|
||
// Enqueues the entire account to be taken down at the end of rule processing. | ||
func (e *RepoEffect) TakedownAccount() { | ||
e.AccountTakedown = true | ||
} | ||
|
||
func (e *RepoEffect) CanonicalLogLine() { | ||
e.Logger.Info("canonical-event-line", | ||
"accountLabels", e.AccountLabels, | ||
"accountFlags", e.AccountFlags, | ||
"accountTakedown", e.AccountTakedown, | ||
"accountReports", len(e.AccountReports), | ||
) | ||
} | ||
|
||
type IdentityEffect struct { | ||
RepoEffect | ||
} | ||
|
||
type RecordEffect struct { | ||
RepoEffect | ||
|
||
// Same as "AccountLabels", but at record-level | ||
RecordLabels []string | ||
// Same as "AccountFlags", but at record-level | ||
RecordFlags []string | ||
// Same as "AccountReports", but at record-level | ||
RecordReports []ModReport | ||
// Same as "AccountTakedown", but at record-level | ||
RecordTakedown bool | ||
// TODO: commit metadata | ||
} | ||
|
||
// Enqueues the provided label (string value) to be added to the record at the end of rule processing. | ||
func (e *RecordEffect) AddRecordLabel(val string) { | ||
e.RecordLabels = append(e.RecordLabels, val) | ||
} | ||
|
||
// Enqueues the provided flag (string value) to be recorded (in the Engine's flagstore) at the end of rule processing. | ||
func (e *RecordEffect) AddRecordFlag(val string) { | ||
e.RecordFlags = append(e.RecordFlags, val) | ||
} | ||
|
||
// Enqueues a moderation report to be filed against the record at the end of rule processing. | ||
func (e *RecordEffect) ReportRecord(reason, comment string) { | ||
if comment == "" { | ||
comment = "(automod)" | ||
} else { | ||
comment = "automod: " + comment | ||
} | ||
e.RecordReports = append(e.RecordReports, ModReport{ReasonType: reason, Comment: comment}) | ||
} | ||
|
||
// Enqueues the record to be taken down at the end of rule processing. | ||
func (e *RecordEffect) TakedownRecord() { | ||
e.RecordTakedown = true | ||
} | ||
|
||
func (e *RecordEffect) CanonicalLogLine() { | ||
e.Logger.Info("canonical-event-line", | ||
"accountLabels", e.AccountLabels, | ||
"accountFlags", e.AccountFlags, | ||
"accountTakedown", e.AccountTakedown, | ||
"accountReports", len(e.AccountReports), | ||
"recordLabels", e.RecordLabels, | ||
"recordFlags", e.RecordFlags, | ||
"recordTakedown", e.RecordTakedown, | ||
"recordReports", len(e.RecordReports), | ||
) | ||
} | ||
|
||
type RecordDeleteEffect struct { | ||
RepoEffect | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.