+1
-1
automod/effects/effects.go
automod/engine/effects.go
+1
-1
automod/effects/effects.go
automod/engine/effects.go
+1
-1
automod/effects/report.go
automod/engine/report.go
+1
-1
automod/effects/report.go
automod/engine/report.go
+1
-2
automod/engine/capture.go
+1
-2
automod/engine/capture.go
···
5
5
6
6
comatproto "github.com/bluesky-social/indigo/api/atproto"
7
7
"github.com/bluesky-social/indigo/atproto/syntax"
8
-
"github.com/bluesky-social/indigo/automod/event"
9
8
)
10
9
11
10
// REVIEW: if this "capture" code can leave the engine package. It seems likely.
12
11
13
12
type AccountCapture struct {
14
13
CapturedAt syntax.Datetime `json:"capturedAt"`
15
-
AccountMeta event.AccountMeta `json:"accountMeta"`
14
+
AccountMeta AccountMeta `json:"accountMeta"`
16
15
PostRecords []comatproto.RepoListRecords_Record `json:"postRecords"`
17
16
}
18
17
+12
-14
automod/engine/engine.go
+12
-14
automod/engine/engine.go
···
10
10
"github.com/bluesky-social/indigo/atproto/syntax"
11
11
"github.com/bluesky-social/indigo/automod/cachestore"
12
12
"github.com/bluesky-social/indigo/automod/countstore"
13
-
"github.com/bluesky-social/indigo/automod/effects"
14
-
"github.com/bluesky-social/indigo/automod/event"
15
13
"github.com/bluesky-social/indigo/automod/flagstore"
16
14
"github.com/bluesky-social/indigo/automod/setstore"
17
15
"github.com/bluesky-social/indigo/xrpc"
···
55
53
if err != nil {
56
54
return err
57
55
}
58
-
evt := &event.IdentityEvent{
59
-
RepoEvent: event.RepoEvent{
56
+
evt := &IdentityEvent{
57
+
RepoEvent: RepoEvent{
60
58
Account: *am,
61
59
},
62
60
}
63
-
eff := &effects.Effects{
61
+
eff := &Effects{
64
62
// XXX: Logger: eng.Logger.With("did", am.Identity.DID),
65
63
}
66
64
if err := eng.Rules.CallIdentityRules(evt, eff); err != nil {
···
160
158
return nil
161
159
}
162
160
163
-
func (e *Engine) NewRecordProcessingContext(am event.AccountMeta, path, recCID string, rec any) (*event.RecordEvent, *effects.Effects) {
161
+
func (e *Engine) NewRecordProcessingContext(am AccountMeta, path, recCID string, rec any) (*RecordEvent, *Effects) {
164
162
// 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?
165
163
parts := strings.SplitN(path, "/", 2)
166
-
return &event.RecordEvent{
167
-
RepoEvent: event.RepoEvent{
164
+
return &RecordEvent{
165
+
RepoEvent: RepoEvent{
168
166
Account: am,
169
167
},
170
168
Record: rec,
171
169
Collection: parts[0],
172
170
RecordKey: parts[1],
173
171
CID: recCID,
174
-
}, &effects.Effects{
172
+
}, &Effects{
175
173
// XXX: Logger: e.Logger.With("did", am.Identity.DID, "collection", parts[0], "rkey", parts[1]),
176
174
RecordLabels: []string{},
177
175
RecordFlags: []string{},
178
-
RecordReports: []effects.ModReport{},
176
+
RecordReports: []ModReport{},
179
177
RecordTakedown: false,
180
178
}
181
179
}
182
180
183
-
func (e *Engine) NewRecordDeleteProcessingContext(am event.AccountMeta, path string) (*event.RecordDeleteEvent, *effects.Effects) {
181
+
func (e *Engine) NewRecordDeleteProcessingContext(am AccountMeta, path string) (*RecordDeleteEvent, *Effects) {
184
182
parts := strings.SplitN(path, "/", 2)
185
-
return &event.RecordDeleteEvent{
186
-
RepoEvent: event.RepoEvent{
183
+
return &RecordDeleteEvent{
184
+
RepoEvent: RepoEvent{
187
185
Account: am,
188
186
},
189
187
Collection: parts[0],
190
188
RecordKey: parts[1],
191
-
}, &effects.Effects{
189
+
}, &Effects{
192
190
// XXX: Logger: e.Logger.With("did", am.Identity.DID, "collection", parts[0], "rkey", parts[1]),
193
191
}
194
192
}
+7
-8
automod/engine/fetchaccountmeta.go
+7
-8
automod/engine/fetchaccountmeta.go
···
9
9
appbsky "github.com/bluesky-social/indigo/api/bsky"
10
10
"github.com/bluesky-social/indigo/atproto/identity"
11
11
"github.com/bluesky-social/indigo/atproto/syntax"
12
-
"github.com/bluesky-social/indigo/automod/event"
13
12
"github.com/bluesky-social/indigo/automod/util"
14
13
)
15
14
16
-
func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) (*event.AccountMeta, error) {
15
+
func (e *Engine) GetAccountMeta(ctx context.Context, ident *identity.Identity) (*AccountMeta, error) {
17
16
18
17
// wipe parsed public key; it's a waste of space and can't serialize
19
18
ident.ParsedPublicKey = nil
···
21
20
// fallback in case client wasn't configured (eg, testing)
22
21
if e.BskyClient == nil {
23
22
e.Logger.Warn("skipping account meta hydration")
24
-
am := event.AccountMeta{
23
+
am := AccountMeta{
25
24
Identity: ident,
26
-
Profile: event.ProfileSummary{},
25
+
Profile: ProfileSummary{},
27
26
}
28
27
return &am, nil
29
28
}
···
33
32
return nil, err
34
33
}
35
34
if existing != "" {
36
-
var am event.AccountMeta
35
+
var am AccountMeta
37
36
err := json.Unmarshal([]byte(existing), &am)
38
37
if err != nil {
39
38
return nil, fmt.Errorf("parsing AccountMeta from cache: %v", err)
···
63
62
return nil, err
64
63
}
65
64
66
-
am := event.AccountMeta{
65
+
am := AccountMeta{
67
66
Identity: ident,
68
-
Profile: event.ProfileSummary{
67
+
Profile: ProfileSummary{
69
68
HasAvatar: pv.Avatar != nil,
70
69
Description: pv.Description,
71
70
DisplayName: pv.DisplayName,
···
89
88
if err != nil {
90
89
return nil, err
91
90
}
92
-
ap := event.AccountPrivate{}
91
+
ap := AccountPrivate{}
93
92
if pv.Email != nil && *pv.Email != "" {
94
93
ap.Email = *pv.Email
95
94
}
+3
-5
automod/engine/persist.go
+3
-5
automod/engine/persist.go
···
5
5
"fmt"
6
6
7
7
comatproto "github.com/bluesky-social/indigo/api/atproto"
8
-
"github.com/bluesky-social/indigo/automod/effects"
9
-
"github.com/bluesky-social/indigo/automod/event"
10
8
"github.com/bluesky-social/indigo/automod/util"
11
9
)
12
10
13
-
func (eng *Engine) persistCounters(ctx context.Context, eff *effects.Effects) error {
11
+
func (eng *Engine) persistCounters(ctx context.Context, eff *Effects) error {
14
12
// TODO: dedupe this array
15
13
for _, ref := range eff.CounterIncrements {
16
14
if ref.Period != nil {
···
39
37
// If necessary, will "purge" identity and account caches, so that state updates will be picked up for subsequent events.
40
38
//
41
39
// Note that this method expects to run *before* counts are persisted (it accesses and updates some counts)
42
-
func (eng *Engine) persistAccountEffects(ctx context.Context, evt *event.RepoEvent, eff *effects.Effects) error {
40
+
func (eng *Engine) persistAccountEffects(ctx context.Context, evt *RepoEvent, eff *Effects) error {
43
41
44
42
// de-dupe actions
45
43
newLabels := dedupeLabelActions(eff.AccountLabels, evt.Account.AccountLabels, evt.Account.AccountNegatedLabels)
···
136
134
// Persists some record-level state: labels, takedowns, reports.
137
135
//
138
136
// 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.
139
-
func (eng *Engine) persistEffectss(ctx context.Context, evt *event.RecordEvent, eff *effects.Effects) error {
137
+
func (eng *Engine) persistEffectss(ctx context.Context, evt *RecordEvent, eff *Effects) error {
140
138
if err := eng.persistAccountEffects(ctx, &evt.RepoEvent, eff); err != nil {
141
139
return err
142
140
}
+13
-15
automod/engine/persisthelpers.go
+13
-15
automod/engine/persisthelpers.go
···
9
9
comatproto "github.com/bluesky-social/indigo/api/atproto"
10
10
"github.com/bluesky-social/indigo/atproto/syntax"
11
11
"github.com/bluesky-social/indigo/automod/countstore"
12
-
"github.com/bluesky-social/indigo/automod/effects"
13
-
"github.com/bluesky-social/indigo/automod/event"
14
12
"github.com/bluesky-social/indigo/automod/util"
15
13
"github.com/bluesky-social/indigo/xrpc"
16
14
)
···
56
54
}
57
55
58
56
// 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.
59
-
func (eng *Engine) dedupeReportActions(evt *event.RepoEvent, eff *effects.Effects, reports []effects.ModReport) []effects.ModReport {
60
-
newReports := []effects.ModReport{}
57
+
func (eng *Engine) dedupeReportActions(evt *RepoEvent, eff *Effects, reports []ModReport) []ModReport {
58
+
newReports := []ModReport{}
61
59
for _, r := range reports {
62
-
counterName := "automod-account-report-" + effects.ReasonShortName(r.ReasonType)
60
+
counterName := "automod-account-report-" + ReasonShortName(r.ReasonType)
63
61
existing, err := eng.GetCount(counterName, evt.Account.Identity.DID.String(), countstore.PeriodDay)
64
62
if err != nil {
65
63
panic(err) // XXX
66
64
}
67
65
if existing > 0 {
68
-
eng.Logger.Debug("skipping account report due to counter", "existing", existing, "reason", effects.ReasonShortName(r.ReasonType))
66
+
eng.Logger.Debug("skipping account report due to counter", "existing", existing, "reason", ReasonShortName(r.ReasonType))
69
67
} else {
70
68
eff.Increment(counterName, evt.Account.Identity.DID.String())
71
69
newReports = append(newReports, r)
···
74
72
return newReports
75
73
}
76
74
77
-
func (eng *Engine) circuitBreakReports(eff *effects.Effects, reports []effects.ModReport) []effects.ModReport {
75
+
func (eng *Engine) circuitBreakReports(eff *Effects, reports []ModReport) []ModReport {
78
76
if len(reports) == 0 {
79
-
return []effects.ModReport{}
77
+
return []ModReport{}
80
78
}
81
79
c, err := eng.GetCount("automod-quota", "report", countstore.PeriodDay)
82
80
if err != nil {
83
81
panic(err) // XXX
84
82
}
85
-
if c >= effects.QuotaModReportDay {
83
+
if c >= QuotaModReportDay {
86
84
eng.Logger.Warn("CIRCUIT BREAKER: automod reports")
87
-
return []effects.ModReport{}
85
+
return []ModReport{}
88
86
}
89
87
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?
90
88
return reports
91
89
}
92
90
93
-
func (eng *Engine) circuitBreakTakedown(eff *effects.Effects, takedown bool) bool {
91
+
func (eng *Engine) circuitBreakTakedown(eff *Effects, takedown bool) bool {
94
92
if !takedown {
95
93
return takedown
96
94
}
···
98
96
if err != nil {
99
97
panic(err) // XXX
100
98
}
101
-
if c >= effects.QuotaModTakedownDay {
99
+
if c >= QuotaModTakedownDay {
102
100
eng.Logger.Warn("CIRCUIT BREAKER: automod takedowns")
103
101
return false
104
102
}
···
109
107
// Creates a moderation report, but checks first if there was a similar recent one, and skips if so.
110
108
//
111
109
// Returns a bool indicating if a new report was created.
112
-
func (eng *Engine) createReportIfFresh(ctx context.Context, xrpcc *xrpc.Client, evt *event.RepoEvent, mr effects.ModReport) (bool, error) {
110
+
func (eng *Engine) createReportIfFresh(ctx context.Context, xrpcc *xrpc.Client, evt *RepoEvent, mr ModReport) (bool, error) {
113
111
// before creating a report, query to see if automod has already reported this account in the past week for the same reason
114
112
// NOTE: this is running in an inner loop (if there are multiple reports), which is a bit inefficient, but seems acceptable
115
113
···
128
126
if err != nil {
129
127
return false, err
130
128
}
131
-
if time.Since(created.Time()) > effects.ReportDupePeriod {
129
+
if time.Since(created.Time()) > ReportDupePeriod {
132
130
continue
133
131
}
134
132
···
153
151
return true, nil
154
152
}
155
153
156
-
func slackBody(header string, acct event.AccountMeta, newLabels, newFlags []string, newReports []effects.ModReport, newTakedown bool) string {
154
+
func slackBody(header string, acct AccountMeta, newLabels, newFlags []string, newReports []ModReport, newTakedown bool) string {
157
155
msg := header
158
156
msg += fmt.Sprintf("`%s` / `%s` / <https://bsky.app/profile/%s|bsky> / <https://admin.prod.bsky.dev/repositories/%s|ozone>\n",
159
157
acct.Identity.DID,
+3
-5
automod/engine/ruleset.go
+3
-5
automod/engine/ruleset.go
···
4
4
"fmt"
5
5
6
6
appbsky "github.com/bluesky-social/indigo/api/bsky"
7
-
"github.com/bluesky-social/indigo/automod/effects"
8
-
"github.com/bluesky-social/indigo/automod/event"
9
7
)
10
8
11
9
type RuleSet struct {
···
16
14
IdentityRules []IdentityRuleFunc
17
15
}
18
16
19
-
func (r *RuleSet) CallRecordRules(evt *event.RecordEvent, eff *effects.Effects) error {
17
+
func (r *RuleSet) CallRecordRules(evt *RecordEvent, eff *Effects) error {
20
18
// first the generic rules
21
19
for _, f := range r.RecordRules {
22
20
err := f(evt, eff)
···
52
50
return nil
53
51
}
54
52
55
-
func (r *RuleSet) CallRecordDeleteRules(evt *event.RecordDeleteEvent, eff *effects.Effects) error {
53
+
func (r *RuleSet) CallRecordDeleteRules(evt *RecordDeleteEvent, eff *Effects) error {
56
54
for _, f := range r.RecordDeleteRules {
57
55
err := f(evt, eff)
58
56
if err != nil {
···
62
60
return nil
63
61
}
64
62
65
-
func (r *RuleSet) CallIdentityRules(evt *event.IdentityEvent, eff *effects.Effects) error {
63
+
func (r *RuleSet) CallIdentityRules(evt *IdentityEvent, eff *Effects) error {
66
64
for _, f := range r.IdentityRules {
67
65
err := f(evt, eff)
68
66
if err != nil {
+5
-7
automod/engine/ruletypes.go
+5
-7
automod/engine/ruletypes.go
···
2
2
3
3
import (
4
4
appbsky "github.com/bluesky-social/indigo/api/bsky"
5
-
"github.com/bluesky-social/indigo/automod/effects"
6
-
"github.com/bluesky-social/indigo/automod/event"
7
5
)
8
6
9
-
type IdentityRuleFunc = func(evt *event.IdentityEvent, eff *effects.Effects) error
10
-
type RecordRuleFunc = func(evt *event.RecordEvent, eff *effects.Effects) error
11
-
type PostRuleFunc = func(evt *event.RecordEvent, eff *effects.Effects, post *appbsky.FeedPost) error
12
-
type ProfileRuleFunc = func(evt *event.RecordEvent, eff *effects.Effects, profile *appbsky.ActorProfile) error
13
-
type RecordDeleteRuleFunc = func(evt *event.RecordDeleteEvent, eff *effects.Effects) error
7
+
type IdentityRuleFunc = func(evt *IdentityEvent, eff *Effects) error
8
+
type RecordRuleFunc = func(evt *RecordEvent, eff *Effects) error
9
+
type PostRuleFunc = func(evt *RecordEvent, eff *Effects, post *appbsky.FeedPost) error
10
+
type ProfileRuleFunc = func(evt *RecordEvent, eff *Effects, profile *appbsky.ActorProfile) error
11
+
type RecordDeleteRuleFunc = func(evt *RecordDeleteEvent, eff *Effects) error
+4
-6
automod/engine/testing.go
+4
-6
automod/engine/testing.go
···
13
13
"github.com/bluesky-social/indigo/atproto/syntax"
14
14
"github.com/bluesky-social/indigo/automod/cachestore"
15
15
"github.com/bluesky-social/indigo/automod/countstore"
16
-
"github.com/bluesky-social/indigo/automod/effects"
17
-
"github.com/bluesky-social/indigo/automod/event"
18
16
"github.com/bluesky-social/indigo/automod/flagstore"
19
17
"github.com/bluesky-social/indigo/automod/setstore"
20
18
)
21
19
22
20
var _ PostRuleFunc = simpleRule
23
21
24
-
func simpleRule(evt *event.RecordEvent, eff *effects.Effects, post *appbsky.FeedPost) error {
22
+
func simpleRule(evt *RecordEvent, eff *Effects, post *appbsky.FeedPost) error {
25
23
for _, tag := range post.Tags {
26
24
if evt.InSet("bad-hashtags", tag) {
27
25
eff.AddRecordLabel("bad-hashtag")
···
102
100
103
101
// initial identity rules
104
102
// 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.
105
-
idevt := &event.IdentityEvent{
106
-
RepoEvent: event.RepoEvent{
103
+
idevt := &IdentityEvent{
104
+
RepoEvent: RepoEvent{
107
105
Account: capture.AccountMeta,
108
106
},
109
107
}
110
-
ideff := &effects.Effects{
108
+
ideff := &Effects{
111
109
// XXX: Logger: eng.Logger.With("did", capture.AccountMeta.Identity.DID),
112
110
}
113
111
if err := eng.Rules.CallIdentityRules(idevt, ideff); err != nil {
+1
-1
automod/event/account_meta.go
automod/engine/account_meta.go
+1
-1
automod/event/account_meta.go
automod/engine/account_meta.go