its for when you want to get like notifications for your reposts
1package main
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7
8 "github.com/bluesky-social/indigo/api/atproto"
9 "github.com/bluesky-social/indigo/api/bsky"
10 "github.com/bluesky-social/indigo/atproto/identity"
11 "github.com/bluesky-social/indigo/atproto/syntax"
12 "github.com/bluesky-social/indigo/xrpc"
13 "github.com/bluesky-social/jetstream/pkg/models"
14)
15
16func findUserPDS(ctx context.Context, did syntax.DID) (string, error) {
17 id, err := identity.DefaultDirectory().LookupDID(ctx, did)
18 if err != nil {
19 return "", err
20 }
21 pdsURI := id.PDSEndpoint()
22 if len(pdsURI) == 0 {
23 return "", fmt.Errorf("no PDS URL was found in identity document")
24 }
25
26 return pdsURI, nil
27}
28
29func fetchRecord[v any](ctx context.Context, xrpcClient *xrpc.Client, val *v, event *models.Event) error {
30 out, err := atproto.RepoGetRecord(ctx, xrpcClient, "", event.Commit.Collection, event.Did, event.Commit.RKey)
31 if err != nil {
32 return err
33 }
34 raw, _ := out.Value.MarshalJSON()
35 if err := json.Unmarshal(raw, val); err != nil {
36 return err
37 }
38 return nil
39}
40
41func fetchRecords[v any](ctx context.Context, xrpcClient *xrpc.Client, cb func(syntax.ATURI, v), cursor *string, collection string, did syntax.DID) error {
42 if xrpcClient == nil {
43 pdsURI, err := findUserPDS(ctx, did)
44 if err != nil {
45 return err
46 }
47 xrpcClient = &xrpc.Client{
48 Host: pdsURI,
49 }
50 }
51
52 var cur string = ""
53 if cursor != nil {
54 cur = *cursor
55 }
56
57 for {
58 // todo: ratelimits?? idk what this does for those
59 out, err := atproto.RepoListRecords(ctx, xrpcClient, collection, cur, 100, string(did), true)
60 if err != nil {
61 return err
62 }
63
64 for _, record := range out.Records {
65 raw, _ := record.Value.MarshalJSON()
66 var val v
67 if err := json.Unmarshal(raw, &val); err != nil {
68 return err
69 }
70 cb(syntax.ATURI(record.Uri), val)
71 }
72
73 if out.Cursor == nil || *out.Cursor == "" {
74 break
75 }
76 cur = *out.Cursor
77 }
78
79 return nil
80}
81
82type FetchFollowItem struct {
83 rkey syntax.RecordKey
84 follow bsky.GraphFollow
85}
86
87func fetchFollows(ctx context.Context, xrpcClient *xrpc.Client, cursor *string, did syntax.DID) ([]FetchFollowItem, error) {
88 out := make([]FetchFollowItem, 0)
89 fetchRecords(ctx, xrpcClient, func(uri syntax.ATURI, f bsky.GraphFollow) {
90 out = append(out, FetchFollowItem{rkey: uri.RecordKey(), follow: f})
91 }, cursor, "app.bsky.graph.follow", did)
92 return out, nil
93}
94
95func fetchRepostLikes(ctx context.Context, xrpcClient *xrpc.Client, cursor *string, did syntax.DID) (map[syntax.RecordKey]bsky.FeedLike, error) {
96 out := make(map[syntax.RecordKey]bsky.FeedLike)
97 fetchRecords(ctx, xrpcClient, func(uri syntax.ATURI, f bsky.FeedLike) {
98 if f.Via != nil && syntax.ATURI(f.Via.Uri).Collection() == "app.bsky.feed.repost" {
99 out[uri.RecordKey()] = f
100 }
101 }, cursor, "app.bsky.feed.like", did)
102 return out, nil
103}