···169169 return out
170170}
171171172172+// Returns a pointer to the underlying automod engine. This usually should NOT be used in rules.
173173+//
174174+// This is an escape hatch for hacking on the system before features get fully integerated in to the content API surface. The Engine API is not stable.
175175+func (c *BaseContext) InternalEngine() *Engine {
176176+ return c.engine
177177+}
178178+172179func NewAccountContext(ctx context.Context, eng *Engine, meta AccountMeta) AccountContext {
173180 return AccountContext{
174181 BaseContext: BaseContext{
+32-8
automod/engine/engine.go
···4343 AdminClient *xrpc.Client
4444 // used to fetch blobs from upstream PDS instances
4545 BlobClient *http.Client
4646+4747+ // internal configuration
4848+ Config EngineConfig
4949+}
5050+5151+type EngineConfig struct {
5252+ // if enabled, account metadata is not hydrated for every event by default
5353+ SkipAccountMeta bool
4654}
47554856// Entrypoint for external code pushing arbitrary identity events in to the engine.
···8088 return fmt.Errorf("identity not found for DID: %s", did.String())
8189 }
82908383- am, err := eng.GetAccountMeta(ctx, ident)
8484- if err != nil {
8585- eventErrorCount.WithLabelValues("identity").Inc()
8686- return fmt.Errorf("failed to fetch account metadata: %w", err)
9191+ var am *AccountMeta
9292+ if !eng.Config.SkipAccountMeta {
9393+ am, err = eng.GetAccountMeta(ctx, ident)
9494+ if err != nil {
9595+ eventErrorCount.WithLabelValues("identity").Inc()
9696+ return fmt.Errorf("failed to fetch account metadata: %w", err)
9797+ }
9898+ } else {
9999+ am = &AccountMeta{
100100+ Identity: ident,
101101+ Profile: ProfileSummary{},
102102+ }
87103 }
88104 ac := NewAccountContext(ctx, eng, *am)
89105 if err := eng.Rules.CallIdentityRules(&ac); err != nil {
···136152 return fmt.Errorf("identity not found for DID: %s", op.DID)
137153 }
138154139139- am, err := eng.GetAccountMeta(ctx, ident)
140140- if err != nil {
141141- eventErrorCount.WithLabelValues("record").Inc()
142142- return fmt.Errorf("failed to fetch account metadata: %w", err)
155155+ var am *AccountMeta
156156+ if !eng.Config.SkipAccountMeta {
157157+ am, err = eng.GetAccountMeta(ctx, ident)
158158+ if err != nil {
159159+ eventErrorCount.WithLabelValues("identity").Inc()
160160+ return fmt.Errorf("failed to fetch account metadata: %w", err)
161161+ }
162162+ } else {
163163+ am = &AccountMeta{
164164+ Identity: ident,
165165+ Profile: ProfileSummary{},
166166+ }
143167 }
144168 rc := NewRecordContext(ctx, eng, *am, op)
145169 rc.Logger.Debug("processing record")
+2-2
automod/engine/fetch_account_meta.go
···24242525 // fallback in case client wasn't configured (eg, testing)
2626 if e.BskyClient == nil {
2727- logger.Warn("skipping account meta hydration")
2727+ logger.Debug("skipping account meta hydration")
2828 am := AccountMeta{
2929 Identity: ident,
3030 Profile: ProfileSummary{},
···6464 // most common cause of this is a race between automod and ozone/appview for new accounts. just sleep a couple seconds and retry!
6565 var xrpcError *xrpc.Error
6666 if err != nil && errors.As(err, &xrpcError) && (xrpcError.StatusCode == 400 || xrpcError.StatusCode == 404) {
6767- logger.Info("account profile lookup initially failed (from bsky appview), will retry", "err", err, "sleepDuration", newAccountRetryDuration)
6767+ logger.Debug("account profile lookup initially failed (from bsky appview), will retry", "err", err, "sleepDuration", newAccountRetryDuration)
6868 time.Sleep(newAccountRetryDuration)
6969 pv, err = appbsky.ActorGetProfile(ctx, e.BskyClient, ident.DID.String())
7070 }
···11-package main
22-33-import (
44- "context"
55- "fmt"
66- "time"
77-88- toolsozone "github.com/bluesky-social/indigo/api/ozone"
99- "github.com/bluesky-social/indigo/atproto/syntax"
1010-)
1111-1212-func (s *Server) RunOzoneConsumer(ctx context.Context) error {
1313-1414- cur, err := s.ReadLastOzoneCursor(ctx)
1515- if err != nil {
1616- return err
1717- }
1818-1919- if cur == "" {
2020- cur = syntax.DatetimeNow().String()
2121- }
2222- since, err := syntax.ParseDatetime(cur)
2323- if err != nil {
2424- return err
2525- }
2626-2727- s.logger.Info("subscribing to ozone event log", "upstream", s.engine.OzoneClient.Host, "cursor", cur, "since", since)
2828- var limit int64 = 50
2929- period := time.Second * 5
3030-3131- for {
3232- //func ModerationQueryEvents(ctx context.Context, c *xrpc.Client, addedLabels []string, addedTags []string, comment string, createdAfter string, createdBefore string, createdBy string, cursor string, hasComment bool, includeAllUserRecords bool, limit int64, removedLabels []string, removedTags []string, reportTypes []string, sortDirection string, subject string, types []string) (*ModerationQueryEvents_Output, error) {
3333- me, err := toolsozone.ModerationQueryEvents(
3434- ctx,
3535- s.engine.OzoneClient,
3636- nil, // addedLabels: If specified, only events where all of these labels were added are returned
3737- nil, // addedTags: If specified, only events where all of these tags were added are returned
3838- "", // comment: If specified, only events with comments containing the keyword are returned
3939- since.String(), // createdAfter: Retrieve events created after a given timestamp
4040- "", // createdBefore: Retrieve events created before a given timestamp
4141- "", // createdBy
4242- "", // cursor
4343- false, // hasComment: If true, only events with comments are returned
4444- true, // includeAllUserRecords: If true, events on all record types (posts, lists, profile etc.) owned by the did are returned
4545- limit,
4646- nil, // removedLabels: If specified, only events where all of these labels were removed are returned
4747- nil, // removedTags
4848- nil, // reportTypes
4949- "asc", // sortDirection: Sort direction for the events. Defaults to descending order of created at timestamp.
5050- "", // subject
5151- nil, // types: The types of events (fully qualified string in the format of tools.ozone.moderation.defs#modEvent<name>) to filter by. If not specified, all events are returned.
5252- )
5353- if err != nil {
5454- s.logger.Warn("ozone query events failed; sleeping then will retrying", "err", err, "period", period.String())
5555- time.Sleep(period)
5656- continue
5757- }
5858-5959- // track if the response contained anything new
6060- anyNewEvents := false
6161- for _, evt := range me.Events {
6262- createdAt, err := syntax.ParseDatetime(evt.CreatedAt)
6363- if err != nil {
6464- return fmt.Errorf("invalid time format for ozone 'createdAt': %w", err)
6565- }
6666- // skip if the timestamp is the exact same
6767- if createdAt == since {
6868- continue
6969- }
7070- anyNewEvents = true
7171- // TODO: is there a race condition here?
7272- if !createdAt.Time().After(since.Time()) {
7373- s.logger.Error("out of order ozone event", "createdAt", createdAt, "since", since)
7474- return fmt.Errorf("out of order ozone event")
7575- }
7676- if err = s.HandleOzoneEvent(ctx, evt); err != nil {
7777- s.logger.Error("failed to process ozone event", "event", evt)
7878- }
7979- since = createdAt
8080- s.lastOzoneCursor.Store(since.String())
8181- }
8282- if !anyNewEvents {
8383- s.logger.Debug("... ozone poller sleeping", "period", period.String())
8484- time.Sleep(period)
8585- }
8686- }
8787-}
8888-8989-func (s *Server) HandleOzoneEvent(ctx context.Context, eventView *toolsozone.ModerationDefs_ModEventView) error {
9090-9191- s.logger.Debug("received ozone event", "eventID", eventView.Id, "createdAt", eventView.CreatedAt)
9292-9393- if err := s.engine.ProcessOzoneEvent(ctx, eventView); err != nil {
9494- s.logger.Error("engine failed to process ozone event", "err", err)
9595- }
9696- return nil
9797-}