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" ) func findUserPDS(ctx context.Context, did string) (string, error) { id, err := identity.DefaultDirectory().LookupDID(ctx, syntax.DID(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 fetchRecords[v any](ctx context.Context, xrpcClient *xrpc.Client, collection, did string, extractFn func(v) string) (Set[string], error) { all := make(Set[string]) cursor := "" for { // todo: ratelimits?? idk what this does for those out, err := atproto.RepoListRecords(ctx, xrpcClient, collection, cursor, 100, did, false) if err != nil { return nil, err } for _, record := range out.Records { raw, _ := record.Value.MarshalJSON() var val v if err := json.Unmarshal(raw, &val); err != nil { return nil, err } s := extractFn(val) if len(s) > 0 { all[s] = struct{}{} } } if out.Cursor == nil || *out.Cursor == "" { break } cursor = *out.Cursor } return all, nil } func fetchReposts(ctx context.Context, xrpcClient *xrpc.Client, did string) (Set[string], error) { return fetchRecords(ctx, xrpcClient, "app.bsky.feed.repost", did, func(v bsky.FeedRepost) string { return v.Subject.Uri }) } func fetchFollows(ctx context.Context, xrpcClient *xrpc.Client, did string) (Set[string], error) { return fetchRecords(ctx, xrpcClient, "app.bsky.graph.follow", did, func(v bsky.GraphFollow) string { return v.Subject }) }