package main import ( "context" "encoding/json" "fmt" "github.com/bluesky-social/indigo/api/atproto" "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/xrpc" "github.com/bluesky-social/jetstream/pkg/models" ) func findUserPDS(ctx context.Context, did syntax.DID) (string, error) { id, err := identity.DefaultDirectory().LookupDID(ctx, did) if err != nil { return "", err } pdsURI := id.PDSEndpoint() if len(pdsURI) == 0 { return "", fmt.Errorf("no PDS URL was found in identity document") } return pdsURI, nil } func fetchRecord[v any](ctx context.Context, xrpcClient *xrpc.Client, val *v, event *models.Event) error { out, err := atproto.RepoGetRecord(ctx, xrpcClient, "", event.Commit.Collection, event.Did, event.Commit.RKey) if err != nil { return err } raw, _ := out.Value.MarshalJSON() if err := json.Unmarshal(raw, val); err != nil { return err } return nil } func fetchRecords[v any](ctx context.Context, xrpcClient *xrpc.Client, cb func(syntax.ATURI, v), cursor *string, collection string, did syntax.DID) error { if xrpcClient == nil { pdsURI, err := findUserPDS(ctx, did) if err != nil { return err } xrpcClient = &xrpc.Client{ Host: pdsURI, } } var cur string = "" if cursor != nil { cur = *cursor } for { // todo: ratelimits?? idk what this does for those out, err := atproto.RepoListRecords(ctx, xrpcClient, collection, cur, 100, string(did), true) if err != nil { return err } for _, record := range out.Records { raw, _ := record.Value.MarshalJSON() var val v if err := json.Unmarshal(raw, &val); err != nil { return err } cb(syntax.ATURI(record.Uri), val) } if out.Cursor == nil || *out.Cursor == "" { break } cur = *out.Cursor } return nil } type FetchFollowItem struct { rkey syntax.RecordKey follow bsky.GraphFollow } func fetchFollows(ctx context.Context, xrpcClient *xrpc.Client, cursor *string, did syntax.DID) ([]FetchFollowItem, error) { out := make([]FetchFollowItem, 0) fetchRecords(ctx, xrpcClient, func(uri syntax.ATURI, f bsky.GraphFollow) { out = append(out, FetchFollowItem{rkey: uri.RecordKey(), follow: f}) }, cursor, "app.bsky.graph.follow", did) return out, nil } func fetchRepostLikes(ctx context.Context, xrpcClient *xrpc.Client, cursor *string, did syntax.DID) (map[syntax.RecordKey]bsky.FeedLike, error) { out := make(map[syntax.RecordKey]bsky.FeedLike) fetchRecords(ctx, xrpcClient, func(uri syntax.ATURI, f bsky.FeedLike) { if f.Via != nil && syntax.ATURI(f.Via.Uri).Collection() == "app.bsky.feed.repost" { out[uri.RecordKey()] = f } }, cursor, "app.bsky.feed.like", did) return out, nil }