1package xrpcclient
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "fmt"
8 "io"
9 "net/http"
10
11 "github.com/bluesky-social/indigo/api/atproto"
12 "github.com/bluesky-social/indigo/xrpc"
13 indigoxrpc "github.com/bluesky-social/indigo/xrpc"
14 oauth "tangled.sh/icyphox.sh/atproto-oauth"
15)
16
17type Client struct {
18 *oauth.XrpcClient
19 authArgs *oauth.XrpcAuthedRequestArgs
20}
21
22func NewClient(client *oauth.XrpcClient, authArgs *oauth.XrpcAuthedRequestArgs) *Client {
23 return &Client{
24 XrpcClient: client,
25 authArgs: authArgs,
26 }
27}
28
29func (c *Client) RepoPutRecord(ctx context.Context, input *atproto.RepoPutRecord_Input) (*atproto.RepoPutRecord_Output, error) {
30 var out atproto.RepoPutRecord_Output
31 if err := c.Do(ctx, c.authArgs, xrpc.Procedure, "application/json", "com.atproto.repo.putRecord", nil, input, &out); err != nil {
32 return nil, err
33 }
34
35 return &out, nil
36}
37
38func (c *Client) RepoApplyWrites(ctx context.Context, input *atproto.RepoApplyWrites_Input) (*atproto.RepoApplyWrites_Output, error) {
39 var out atproto.RepoApplyWrites_Output
40 if err := c.Do(ctx, c.authArgs, xrpc.Procedure, "application/json", "com.atproto.repo.applyWrites", nil, input, &out); err != nil {
41 return nil, err
42 }
43
44 return &out, nil
45}
46
47func (c *Client) RepoGetRecord(ctx context.Context, cid string, collection string, repo string, rkey string) (*atproto.RepoGetRecord_Output, error) {
48 var out atproto.RepoGetRecord_Output
49
50 params := map[string]interface{}{
51 "cid": cid,
52 "collection": collection,
53 "repo": repo,
54 "rkey": rkey,
55 }
56 if err := c.Do(ctx, c.authArgs, xrpc.Query, "", "com.atproto.repo.getRecord", params, nil, &out); err != nil {
57 return nil, err
58 }
59
60 return &out, nil
61}
62
63func (c *Client) RepoUploadBlob(ctx context.Context, input io.Reader) (*atproto.RepoUploadBlob_Output, error) {
64 var out atproto.RepoUploadBlob_Output
65 if err := c.Do(ctx, c.authArgs, xrpc.Procedure, "*/*", "com.atproto.repo.uploadBlob", nil, input, &out); err != nil {
66 return nil, err
67 }
68
69 return &out, nil
70}
71
72func (c *Client) SyncGetBlob(ctx context.Context, cid string, did string) ([]byte, error) {
73 buf := new(bytes.Buffer)
74
75 params := map[string]interface{}{
76 "cid": cid,
77 "did": did,
78 }
79 if err := c.Do(ctx, c.authArgs, xrpc.Query, "", "com.atproto.sync.getBlob", params, nil, buf); err != nil {
80 return nil, err
81 }
82
83 return buf.Bytes(), nil
84}
85
86func (c *Client) RepoDeleteRecord(ctx context.Context, input *atproto.RepoDeleteRecord_Input) (*atproto.RepoDeleteRecord_Output, error) {
87 var out atproto.RepoDeleteRecord_Output
88 if err := c.Do(ctx, c.authArgs, xrpc.Procedure, "application/json", "com.atproto.repo.deleteRecord", nil, input, &out); err != nil {
89 return nil, err
90 }
91
92 return &out, nil
93}
94
95func (c *Client) ServerGetServiceAuth(ctx context.Context, aud string, exp int64, lxm string) (*atproto.ServerGetServiceAuth_Output, error) {
96 var out atproto.ServerGetServiceAuth_Output
97
98 params := map[string]interface{}{
99 "aud": aud,
100 "exp": exp,
101 "lxm": lxm,
102 }
103 if err := c.Do(ctx, c.authArgs, xrpc.Query, "", "com.atproto.server.getServiceAuth", params, nil, &out); err != nil {
104 return nil, err
105 }
106
107 return &out, nil
108}
109
110// produces a more manageable error
111func HandleXrpcErr(err error) error {
112 if err == nil {
113 return nil
114 }
115
116 var xrpcerr *indigoxrpc.Error
117 if ok := errors.As(err, &xrpcerr); !ok {
118 return fmt.Errorf("Recieved invalid XRPC error response.")
119 }
120
121 switch xrpcerr.StatusCode {
122 case http.StatusNotFound:
123 return fmt.Errorf("XRPC is unsupported on this knot, consider upgrading your knot.")
124 case http.StatusUnauthorized:
125 return fmt.Errorf("Unauthorized XRPC request.")
126 default:
127 return fmt.Errorf("Failed to perform operation. Try again later.")
128 }
129}