diff --git a/automod/event/account_meta.go b/automod/engine/account_meta.go similarity index 97% rename from automod/event/account_meta.go rename to automod/engine/account_meta.go index d82925b8..65228e3e 100644 --- a/automod/event/account_meta.go +++ b/automod/engine/account_meta.go @@ -1,4 +1,4 @@ -package event +package engine import ( "time" diff --git a/automod/engine/capture.go b/automod/engine/capture.go index 2c457ec2..000e55c3 100644 --- a/automod/engine/capture.go +++ b/automod/engine/capture.go @@ -5,14 +5,13 @@ import ( comatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/atproto/syntax" - "github.com/bluesky-social/indigo/automod/event" ) // REVIEW: if this "capture" code can leave the engine package. It seems likely. type AccountCapture struct { CapturedAt syntax.Datetime `json:"capturedAt"` - AccountMeta event.AccountMeta `json:"accountMeta"` + AccountMeta AccountMeta `json:"accountMeta"` PostRecords []comatproto.RepoListRecords_Record `json:"postRecords"` } diff --git a/automod/effects/effects.go b/automod/engine/effects.go similarity index 99% rename from automod/effects/effects.go rename to automod/engine/effects.go index 15403909..eaf1080d 100644 --- a/automod/effects/effects.go +++ b/automod/engine/effects.go @@ -1,4 +1,4 @@ -package effects +package engine import ( "log/slog" diff --git a/automod/engine/engine.go b/automod/engine/engine.go index dc35a177..10690a14 100644 --- a/automod/engine/engine.go +++ b/automod/engine/engine.go @@ -10,8 +10,6 @@ import ( "github.com/bluesky-social/indigo/atproto/syntax" "github.com/bluesky-social/indigo/automod/cachestore" "github.com/bluesky-social/indigo/automod/countstore" - "github.com/bluesky-social/indigo/automod/effects" - "github.com/bluesky-social/indigo/automod/event" "github.com/bluesky-social/indigo/automod/flagstore" "github.com/bluesky-social/indigo/automod/setstore" "github.com/bluesky-social/indigo/xrpc" @@ -55,12 +53,12 @@ func (eng *Engine) ProcessIdentityEvent(ctx context.Context, t string, did synta if err != nil { return err } - evt := &event.IdentityEvent{ - RepoEvent: event.RepoEvent{ + evt := &IdentityEvent{ + RepoEvent: RepoEvent{ Account: *am, }, } - eff := &effects.Effects{ + eff := &Effects{ // XXX: Logger: eng.Logger.With("did", am.Identity.DID), } if err := eng.Rules.CallIdentityRules(evt, eff); err != nil { @@ -160,35 +158,35 @@ func (eng *Engine) ProcessRecordDelete(ctx context.Context, did syntax.DID, path return nil } -func (e *Engine) NewRecordProcessingContext(am event.AccountMeta, path, recCID string, rec any) (*event.RecordEvent, *effects.Effects) { +func (e *Engine) NewRecordProcessingContext(am AccountMeta, path, recCID string, rec any) (*RecordEvent, *Effects) { // REVIEW: Only reason for this to be a method on the engine is because it's bifrucating the logger off from there. Should we pinch that off? parts := strings.SplitN(path, "/", 2) - return &event.RecordEvent{ - RepoEvent: event.RepoEvent{ + return &RecordEvent{ + RepoEvent: RepoEvent{ Account: am, }, Record: rec, Collection: parts[0], RecordKey: parts[1], CID: recCID, - }, &effects.Effects{ + }, &Effects{ // XXX: Logger: e.Logger.With("did", am.Identity.DID, "collection", parts[0], "rkey", parts[1]), RecordLabels: []string{}, RecordFlags: []string{}, - RecordReports: []effects.ModReport{}, + RecordReports: []ModReport{}, RecordTakedown: false, } } -func (e *Engine) NewRecordDeleteProcessingContext(am event.AccountMeta, path string) (*event.RecordDeleteEvent, *effects.Effects) { +func (e *Engine) NewRecordDeleteProcessingContext(am AccountMeta, path string) (*RecordDeleteEvent, *Effects) { parts := strings.SplitN(path, "/", 2) - return &event.RecordDeleteEvent{ - RepoEvent: event.RepoEvent{ + return &RecordDeleteEvent{ + RepoEvent: RepoEvent{ Account: am, }, Collection: parts[0], RecordKey: parts[1], - }, &effects.Effects{ + }, &Effects{ // XXX: Logger: e.Logger.With("did", am.Identity.DID, "collection", parts[0], "rkey", parts[1]), } } diff --git a/automod/event/event.go b/automod/engine/event.go similarity index 99% rename from automod/event/event.go rename to automod/engine/event.go index 96fd1db6..c670d78f 100644 --- a/automod/event/event.go +++ b/automod/engine/event.go @@ -1,4 +1,4 @@ -package event +package engine // Base type for events specific to an account, usually derived from a repo event stream message (one such message may result in multiple `RepoEvent`) // diff --git a/automod/engine/fetchaccountmeta.go b/automod/engine/fetchaccountmeta.go index ed785083..fe3d81f0 100644 --- a/automod/engine/fetchaccountmeta.go +++ b/automod/engine/fetchaccountmeta.go @@ -9,11 +9,10 @@ import ( appbsky "github.com/bluesky-social/indigo/api/bsky" "github.com/bluesky-social/indigo/atproto/identity" "github.com/bluesky-social/indigo/atproto/syntax" - "github.com/bluesky-social/indigo/automod/event" "github.com/bluesky-social/indigo/automod/util" ) -func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) (*event.AccountMeta, error) { +func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) (*AccountMeta, error) { // wipe parsed public key; it's a waste of space and can't serialize ident.ParsedPublicKey = nil @@ -21,9 +20,9 @@ func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) ( // fallback in case client wasn't configured (eg, testing) if e.BskyClient == nil { e.Logger.Warn("skipping account meta hydration") - am := event.AccountMeta{ + am := AccountMeta{ Identity: ident, - Profile: event.ProfileSummary{}, + Profile: ProfileSummary{}, } return &am, nil } @@ -33,7 +32,7 @@ func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) ( return nil, err } if existing != "" { - var am event.AccountMeta + var am AccountMeta err := json.Unmarshal([]byte(existing), &am) if err != nil { return nil, fmt.Errorf("parsing AccountMeta from cache: %v", err) @@ -63,9 +62,9 @@ func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) ( return nil, err } - am := event.AccountMeta{ + am := AccountMeta{ Identity: ident, - Profile: event.ProfileSummary{ + Profile: ProfileSummary{ HasAvatar: pv.Avatar != nil, Description: pv.Description, DisplayName: pv.DisplayName, @@ -89,7 +88,7 @@ func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) ( if err != nil { return nil, err } - ap := event.AccountPrivate{} + ap := AccountPrivate{} if pv.Email != nil && *pv.Email != "" { ap.Email = *pv.Email } diff --git a/automod/engine/persist.go b/automod/engine/persist.go index 5c83b442..73202a10 100644 --- a/automod/engine/persist.go +++ b/automod/engine/persist.go @@ -5,12 +5,10 @@ import ( "fmt" comatproto "github.com/bluesky-social/indigo/api/atproto" - "github.com/bluesky-social/indigo/automod/effects" - "github.com/bluesky-social/indigo/automod/event" "github.com/bluesky-social/indigo/automod/util" ) -func (eng *Engine) persistCounters(ctx context.Context, eff *effects.Effects) error { +func (eng *Engine) persistCounters(ctx context.Context, eff *Effects) error { // TODO: dedupe this array for _, ref := range eff.CounterIncrements { if ref.Period != nil { @@ -39,7 +37,7 @@ func (eng *Engine) persistCounters(ctx context.Context, eff *effects.Effects) er // If necessary, will "purge" identity and account caches, so that state updates will be picked up for subsequent events. // // Note that this method expects to run *before* counts are persisted (it accesses and updates some counts) -func (eng *Engine) persistAccountEffects(ctx context.Context, evt *event.RepoEvent, eff *effects.Effects) error { +func (eng *Engine) persistAccountEffects(ctx context.Context, evt *RepoEvent, eff *Effects) error { // de-dupe actions newLabels := dedupeLabelActions(eff.AccountLabels, evt.Account.AccountLabels, evt.Account.AccountNegatedLabels) @@ -136,7 +134,7 @@ func (eng *Engine) persistAccountEffects(ctx context.Context, evt *event.RepoEve // Persists some record-level state: labels, takedowns, reports. // // NOTE: this method currently does *not* persist record-level flags to any storage, and does not de-dupe most actions, on the assumption that the record is new (from firehose) and has no existing mod state. -func (eng *Engine) persistEffectss(ctx context.Context, evt *event.RecordEvent, eff *effects.Effects) error { +func (eng *Engine) persistEffectss(ctx context.Context, evt *RecordEvent, eff *Effects) error { if err := eng.persistAccountEffects(ctx, &evt.RepoEvent, eff); err != nil { return err } diff --git a/automod/engine/persisthelpers.go b/automod/engine/persisthelpers.go index ce468207..de090d80 100644 --- a/automod/engine/persisthelpers.go +++ b/automod/engine/persisthelpers.go @@ -9,8 +9,6 @@ import ( comatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/atproto/syntax" "github.com/bluesky-social/indigo/automod/countstore" - "github.com/bluesky-social/indigo/automod/effects" - "github.com/bluesky-social/indigo/automod/event" "github.com/bluesky-social/indigo/automod/util" "github.com/bluesky-social/indigo/xrpc" ) @@ -56,16 +54,16 @@ func dedupeFlagActions(flags, existing []string) []string { } // REVIEW: this does does both reads and then mutations of the planned effect, rather than just returning things, which neither the name nor signiture clearly suggests. -func (eng *Engine) dedupeReportActions(evt *event.RepoEvent, eff *effects.Effects, reports []effects.ModReport) []effects.ModReport { - newReports := []effects.ModReport{} +func (eng *Engine) dedupeReportActions(evt *RepoEvent, eff *Effects, reports []ModReport) []ModReport { + newReports := []ModReport{} for _, r := range reports { - counterName := "automod-account-report-" + effects.ReasonShortName(r.ReasonType) + counterName := "automod-account-report-" + ReasonShortName(r.ReasonType) existing, err := eng.GetCount(counterName, evt.Account.Identity.DID.String(), countstore.PeriodDay) if err != nil { panic(err) // XXX } if existing > 0 { - eng.Logger.Debug("skipping account report due to counter", "existing", existing, "reason", effects.ReasonShortName(r.ReasonType)) + eng.Logger.Debug("skipping account report due to counter", "existing", existing, "reason", ReasonShortName(r.ReasonType)) } else { eff.Increment(counterName, evt.Account.Identity.DID.String()) newReports = append(newReports, r) @@ -74,23 +72,23 @@ func (eng *Engine) dedupeReportActions(evt *event.RepoEvent, eff *effects.Effect return newReports } -func (eng *Engine) circuitBreakReports(eff *effects.Effects, reports []effects.ModReport) []effects.ModReport { +func (eng *Engine) circuitBreakReports(eff *Effects, reports []ModReport) []ModReport { if len(reports) == 0 { - return []effects.ModReport{} + return []ModReport{} } c, err := eng.GetCount("automod-quota", "report", countstore.PeriodDay) if err != nil { panic(err) // XXX } - if c >= effects.QuotaModReportDay { + if c >= QuotaModReportDay { eng.Logger.Warn("CIRCUIT BREAKER: automod reports") - return []effects.ModReport{} + return []ModReport{} } eff.Increment("automod-quota", "report") // REVIEW: should this increment just happen directly on the engine? it's not part of the relatively pure rule application logic, and we just had to read the engine again for it, so, maybe? return reports } -func (eng *Engine) circuitBreakTakedown(eff *effects.Effects, takedown bool) bool { +func (eng *Engine) circuitBreakTakedown(eff *Effects, takedown bool) bool { if !takedown { return takedown } @@ -98,7 +96,7 @@ func (eng *Engine) circuitBreakTakedown(eff *effects.Effects, takedown bool) boo if err != nil { panic(err) // XXX } - if c >= effects.QuotaModTakedownDay { + if c >= QuotaModTakedownDay { eng.Logger.Warn("CIRCUIT BREAKER: automod takedowns") return false } @@ -109,7 +107,7 @@ func (eng *Engine) circuitBreakTakedown(eff *effects.Effects, takedown bool) boo // Creates a moderation report, but checks first if there was a similar recent one, and skips if so. // // Returns a bool indicating if a new report was created. -func (eng *Engine) createReportIfFresh(ctx context.Context, xrpcc *xrpc.Client, evt *event.RepoEvent, mr effects.ModReport) (bool, error) { +func (eng *Engine) createReportIfFresh(ctx context.Context, xrpcc *xrpc.Client, evt *RepoEvent, mr ModReport) (bool, error) { // before creating a report, query to see if automod has already reported this account in the past week for the same reason // NOTE: this is running in an inner loop (if there are multiple reports), which is a bit inefficient, but seems acceptable @@ -128,7 +126,7 @@ func (eng *Engine) createReportIfFresh(ctx context.Context, xrpcc *xrpc.Client, if err != nil { return false, err } - if time.Since(created.Time()) > effects.ReportDupePeriod { + if time.Since(created.Time()) > ReportDupePeriod { continue } @@ -153,7 +151,7 @@ func (eng *Engine) createReportIfFresh(ctx context.Context, xrpcc *xrpc.Client, return true, nil } -func slackBody(header string, acct event.AccountMeta, newLabels, newFlags []string, newReports []effects.ModReport, newTakedown bool) string { +func slackBody(header string, acct AccountMeta, newLabels, newFlags []string, newReports []ModReport, newTakedown bool) string { msg := header msg += fmt.Sprintf("`%s` / `%s` / / \n", acct.Identity.DID, diff --git a/automod/effects/report.go b/automod/engine/report.go similarity index 98% rename from automod/effects/report.go rename to automod/engine/report.go index cfdc6cb9..52d59959 100644 --- a/automod/effects/report.go +++ b/automod/engine/report.go @@ -1,4 +1,4 @@ -package effects +package engine // Simplified variant of input parameters for com.atproto.moderation.createReport, for internal tracking type ModReport struct { diff --git a/automod/engine/ruleset.go b/automod/engine/ruleset.go index bae97784..89d202e2 100644 --- a/automod/engine/ruleset.go +++ b/automod/engine/ruleset.go @@ -4,8 +4,6 @@ import ( "fmt" appbsky "github.com/bluesky-social/indigo/api/bsky" - "github.com/bluesky-social/indigo/automod/effects" - "github.com/bluesky-social/indigo/automod/event" ) type RuleSet struct { @@ -16,7 +14,7 @@ type RuleSet struct { IdentityRules []IdentityRuleFunc } -func (r *RuleSet) CallRecordRules(evt *event.RecordEvent, eff *effects.Effects) error { +func (r *RuleSet) CallRecordRules(evt *RecordEvent, eff *Effects) error { // first the generic rules for _, f := range r.RecordRules { err := f(evt, eff) @@ -52,7 +50,7 @@ func (r *RuleSet) CallRecordRules(evt *event.RecordEvent, eff *effects.Effects) return nil } -func (r *RuleSet) CallRecordDeleteRules(evt *event.RecordDeleteEvent, eff *effects.Effects) error { +func (r *RuleSet) CallRecordDeleteRules(evt *RecordDeleteEvent, eff *Effects) error { for _, f := range r.RecordDeleteRules { err := f(evt, eff) if err != nil { @@ -62,7 +60,7 @@ func (r *RuleSet) CallRecordDeleteRules(evt *event.RecordDeleteEvent, eff *effec return nil } -func (r *RuleSet) CallIdentityRules(evt *event.IdentityEvent, eff *effects.Effects) error { +func (r *RuleSet) CallIdentityRules(evt *IdentityEvent, eff *Effects) error { for _, f := range r.IdentityRules { err := f(evt, eff) if err != nil { diff --git a/automod/engine/ruletypes.go b/automod/engine/ruletypes.go index e1aa10b6..bf052a56 100644 --- a/automod/engine/ruletypes.go +++ b/automod/engine/ruletypes.go @@ -2,12 +2,10 @@ package engine import ( appbsky "github.com/bluesky-social/indigo/api/bsky" - "github.com/bluesky-social/indigo/automod/effects" - "github.com/bluesky-social/indigo/automod/event" ) -type IdentityRuleFunc = func(evt *event.IdentityEvent, eff *effects.Effects) error -type RecordRuleFunc = func(evt *event.RecordEvent, eff *effects.Effects) error -type PostRuleFunc = func(evt *event.RecordEvent, eff *effects.Effects, post *appbsky.FeedPost) error -type ProfileRuleFunc = func(evt *event.RecordEvent, eff *effects.Effects, profile *appbsky.ActorProfile) error -type RecordDeleteRuleFunc = func(evt *event.RecordDeleteEvent, eff *effects.Effects) error +type IdentityRuleFunc = func(evt *IdentityEvent, eff *Effects) error +type RecordRuleFunc = func(evt *RecordEvent, eff *Effects) error +type PostRuleFunc = func(evt *RecordEvent, eff *Effects, post *appbsky.FeedPost) error +type ProfileRuleFunc = func(evt *RecordEvent, eff *Effects, profile *appbsky.ActorProfile) error +type RecordDeleteRuleFunc = func(evt *RecordDeleteEvent, eff *Effects) error diff --git a/automod/engine/testing.go b/automod/engine/testing.go index a3debdb8..c3d6249f 100644 --- a/automod/engine/testing.go +++ b/automod/engine/testing.go @@ -13,15 +13,13 @@ import ( "github.com/bluesky-social/indigo/atproto/syntax" "github.com/bluesky-social/indigo/automod/cachestore" "github.com/bluesky-social/indigo/automod/countstore" - "github.com/bluesky-social/indigo/automod/effects" - "github.com/bluesky-social/indigo/automod/event" "github.com/bluesky-social/indigo/automod/flagstore" "github.com/bluesky-social/indigo/automod/setstore" ) var _ PostRuleFunc = simpleRule -func simpleRule(evt *event.RecordEvent, eff *effects.Effects, post *appbsky.FeedPost) error { +func simpleRule(evt *RecordEvent, eff *Effects, post *appbsky.FeedPost) error { for _, tag := range post.Tags { if evt.InSet("bad-hashtags", tag) { eff.AddRecordLabel("bad-hashtag") @@ -102,12 +100,12 @@ func ProcessCaptureRules(eng *Engine, capture AccountCapture) error { // initial identity rules // REVIEW: this area should... use the real code path that does the same thing, if at all possible? Currently this seems like great drift danger. - idevt := &event.IdentityEvent{ - RepoEvent: event.RepoEvent{ + idevt := &IdentityEvent{ + RepoEvent: RepoEvent{ Account: capture.AccountMeta, }, } - ideff := &effects.Effects{ + ideff := &Effects{ // XXX: Logger: eng.Logger.With("did", capture.AccountMeta.Identity.DID), } if err := eng.Rules.CallIdentityRules(idevt, ideff); err != nil {