backend for xcvr appview
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

added follow unfollow endpoints

+97
+2
server/internal/handler/handler.go
··· 35 35 // xcvr handlers 36 36 mux.HandleFunc("POST /xcvr/profile", h.oauthMiddleware(h.postProfile)) 37 37 mux.HandleFunc("POST /xcvr/beep", h.oauthMiddleware(h.beep)) 38 + mux.HandleFunc("DELETE /xcvr/follow", h.oauthMiddleware(h.unfollow)) 39 + mux.HandleFunc("POST /xcvr/follow", h.oauthMiddleware(h.follow)) 38 40 // lexicon handlers 39 41 mux.HandleFunc("GET /xrpc/org.xcvr.feed.getChannels", h.WithCORS(h.getChannels)) 40 42 mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getMessages", h.WithCORS(h.getMessages))
+36
server/internal/handler/xcvrHandlers.go
··· 45 45 } 46 46 w.Write(nil) 47 47 } 48 + 49 + func (h *Handler) unfollow(cs *atoauth.ClientSession, w http.ResponseWriter, r *http.Request) { 50 + if cs == nil { 51 + h.badRequest(w, errors.New("must be logged in!")) 52 + return 53 + } 54 + qp := r.URL.Query() 55 + furi := qp.Get("followUri") 56 + if furi == "" { 57 + h.badRequest(w, errors.New("must unfollow a user")) 58 + return 59 + } 60 + err := h.rm.Unfollow(cs, furi, r.Context()) 61 + if err != nil { 62 + h.serverError(w, errors.New("failed to unfollow: "+err.Error())) 63 + } 64 + w.Write(nil) 65 + } 66 + 67 + func (h *Handler) follow(cs *atoauth.ClientSession, w http.ResponseWriter, r *http.Request) { 68 + if cs == nil { 69 + h.badRequest(w, errors.New("must be logged in!")) 70 + return 71 + } 72 + qp := r.URL.Query() 73 + did := qp.Get("did") 74 + if did == "" { 75 + h.badRequest(w, errors.New("must unfollow a user")) 76 + return 77 + } 78 + err := h.rm.Follow(cs, did, r.Context()) 79 + if err != nil { 80 + h.serverError(w, errors.New("failed to unfollow: "+err.Error())) 81 + } 82 + w.Write(nil) 83 + }
+52
server/internal/oauth/oauthclient.go
··· 4 4 "context" 5 5 "encoding/json" 6 6 "errors" 7 + 7 8 "github.com/bluesky-social/indigo/api/atproto" 8 9 "github.com/bluesky-social/indigo/atproto/auth/oauth" 9 10 "github.com/bluesky-social/indigo/atproto/syntax" 10 11 12 + "rvcx/internal/atputils" 11 13 "rvcx/internal/lex" 12 14 "rvcx/internal/log" 13 15 "rvcx/internal/types" ··· 40 42 return nil 41 43 } 42 44 45 + func Follow(cs *oauth.ClientSession, did string, ctx context.Context) error { 46 + c := cs.APIClient() 47 + body := map[string]any{ 48 + "repo": *c.AccountDID, 49 + "collection": "app.bsky.graph.follow", 50 + "record": map[string]any{ 51 + "$type": "app.bsky.graph.follow", 52 + "did": did, 53 + "createdAt": syntax.DatetimeNow(), 54 + }, 55 + } 56 + err := c.Post(ctx, "com.atproto.repo.createRecord", body, nil) 57 + if err != nil { 58 + return errors.New("failed to follow: " + err.Error()) 59 + } 60 + return nil 61 + } 62 + 63 + func Unfollow(cs *oauth.ClientSession, followuri string, ctx context.Context) error { 64 + c := cs.APIClient() 65 + rkey, err := atputils.RkeyFromUri(followuri) 66 + if err != nil { 67 + return errors.New("bad" + err.Error()) 68 + } 69 + 70 + params := map[string]any{ 71 + "repo": *c.AccountDID, 72 + "collection": "app.bsky.graph.follow", 73 + "rkey": rkey, 74 + } 75 + var getOut atproto.RepoGetRecord_Output 76 + err = c.Get(ctx, "com.atproto.repo.getRecord", params, &getOut) 77 + if err != nil { 78 + return errors.New("error getting: " + err.Error()) 79 + } 80 + if getOut.Cid == nil { 81 + return errors.New("not sure what to do in this case, no cid from good req") 82 + } 83 + body := map[string]any{ 84 + "repo": *c.AccountDID, 85 + "collection": "app.bsky.graph.follow", 86 + "rkey": rkey, 87 + "swapRecord": "getOut.cid", 88 + } 89 + err = c.Post(ctx, "com.atproto.repo.deleteRecord", body, nil) 90 + if err != nil { 91 + return errors.New("failed to tweet: " + err.Error()) 92 + } 93 + return nil 94 + } 43 95 func CreateXCVRProfile(cs *oauth.ClientSession, profile *lex.ProfileRecord, ctx context.Context) (p *lex.ProfileRecord, err error) { 44 96 c := cs.APIClient() 45 97 nsid, err := syntax.ParseNSID("com.atproto.repo.getRecord")
+7
server/internal/recordmanager/beep.go
··· 9 9 func (rm *RecordManager) Beep(cs *atoauth.ClientSession, ctx context.Context) error { 10 10 return oauth.MakeBskyPost(cs, "beep_", ctx) 11 11 } 12 + 13 + func (rm *RecordManager) Unfollow(cs *atoauth.ClientSession, followuri string, ctx context.Context) error { 14 + return oauth.Unfollow(cs, followuri, ctx) 15 + } 16 + func (rm *RecordManager) Follow(cs *atoauth.ClientSession, did string, ctx context.Context) error { 17 + return oauth.Follow(cs, did, ctx) 18 + }