collection of golang services under the Red Dwarf umbrella server.reddwarf.app
bluesky reddwarf microcosm appview
15
fork

Configure Feed

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

profileviews

+226 -10
+61 -10
main.go
··· 2 2 3 3 import ( 4 4 "context" 5 + "encoding/json" 6 + "flag" 5 7 "fmt" 6 8 "log" 7 9 "net/http" 8 - "os" 9 10 "time" 10 11 11 12 "tangled.org/whey.party/red-dwarf-server/microcosm/constellation" 12 13 "tangled.org/whey.party/red-dwarf-server/microcosm/slingshot" 14 + appbskyactordefs "tangled.org/whey.party/red-dwarf-server/shims/lex/app/bsky/actor" 15 + "tangled.org/whey.party/red-dwarf-server/shims/utils" 13 16 "tangled.org/whey.party/red-dwarf-server/sticket" 14 17 15 18 // "github.com/bluesky-social/indigo/atproto/atclient" ··· 22 25 // "github.com/bluesky-social/jetstream/pkg/models" 23 26 ) 24 27 28 + var ( 29 + JETSTREAM_URL string 30 + SPACEDUST_URL string 31 + SLINGSHOT_URL string 32 + CONSTELLATION_URL string 33 + ) 34 + 35 + func initURLs(prod bool) { 36 + if !prod { 37 + JETSTREAM_URL = "wss://jetstream.whey.party/subscribe" 38 + SPACEDUST_URL = "wss://spacedust.whey.party/subscribe" 39 + SLINGSHOT_URL = "https://slingshot.whey.party" 40 + CONSTELLATION_URL = "https://constellation.whey.party" 41 + } else { 42 + JETSTREAM_URL = "ws://localhost:6008/subscribe" 43 + SPACEDUST_URL = "ws://localhost:9998/subscribe" 44 + SLINGSHOT_URL = "http://localhost:7729" 45 + CONSTELLATION_URL = "http://localhost:7728" 46 + } 47 + } 48 + 25 49 const ( 26 - JETSTREAM_URL = "ws://localhost:6008/subscribe" 27 - SPACEDUST_URL = "ws://localhost:9998/subscribe" 28 - SLINGSHOT_URL = "http://localhost:7729" 29 - CONSTELLATION_URL = "http://localhost:7728" 50 + BSKYIMAGECDN_URL = "https://cdn.bsky.app" 51 + BSKYVIDEOCDN_URL = "https://video.bsky.app" 30 52 ) 31 53 32 54 func main() { 33 - fmt.Fprintf(os.Stdout, "red-dwarf-server started") 55 + log.Println("red-dwarf-server started") 56 + prod := flag.Bool("prod", false, "use production URLs instead of localhost") 57 + flag.Parse() 58 + 59 + initURLs(*prod) 34 60 35 61 ctx := context.Background() 36 62 mailbox := sticket.New() ··· 39 65 // spacedust is type definitions only 40 66 // jetstream types is probably available from jetstream/pkg/models 41 67 42 - responsewow, _ := agnostic.RepoGetRecord(ctx, sl, "", "app.bsky.feed.profile", "did:plc:44ybard66vv44zksje25o7dz", "self") 68 + responsewow, err := agnostic.RepoGetRecord(ctx, sl, "", "app.bsky.actor.profile", "did:web:did12.whey.party", "self") 69 + if err != nil { 70 + log.Println(err) 71 + } 72 + 73 + log.Println(responsewow.Uri) 74 + 75 + var didtest *utils.DID 76 + didval, errdid := utils.NewDID("did:web:did12.whey.party") 77 + if errdid != nil { 78 + didtest = nil 79 + } else { 80 + didtest = &didval 81 + } 82 + profiletest, _, _ := appbskyactordefs.ProfileViewBasic(ctx, *didtest, sl, BSKYIMAGECDN_URL) 43 83 44 - fmt.Fprintf(os.Stdout, responsewow.Uri) 84 + log.Println(*profiletest.DisplayName) 85 + log.Println(*profiletest.Avatar) 45 86 46 87 http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { 47 88 mailbox.HandleWS(&w, r) 48 89 }) 49 90 91 + bskyappdid, _ := utils.NewDID("did:plc:z72i7hdynmk6r22z27h6tvur") 92 + 93 + profiletest2, _, _ := appbskyactordefs.ProfileViewDetailed(ctx, bskyappdid, sl, cs, BSKYIMAGECDN_URL) 94 + 95 + data, err := json.MarshalIndent(profiletest2, "", " ") 96 + if err != nil { 97 + panic(err) 98 + } 99 + fmt.Println(string(data)) 100 + 50 101 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 51 - fmt.Fprintf(w, "hello worldio !") 102 + log.Println("hello worldio !") 52 103 clientUUID := sticket.GetUUIDFromRequest(r) 53 104 hasSticket := clientUUID != "" 54 105 if hasSticket { ··· 77 128 } 78 129 79 130 func getPostThreadV2(w http.ResponseWriter, r *http.Request) { 80 - fmt.Fprintf(w, "hello worldio !") 131 + log.Println("hello worldio !") 81 132 }
+124
shims/lex/app/bsky/actor/defs.go
··· 1 + package appbskyactordefs 2 + 3 + import ( 4 + "context" 5 + "encoding/json" 6 + 7 + "github.com/bluesky-social/indigo/api/agnostic" 8 + appbsky "github.com/bluesky-social/indigo/api/bsky" 9 + "tangled.org/whey.party/red-dwarf-server/microcosm" 10 + "tangled.org/whey.party/red-dwarf-server/microcosm/constellation" 11 + "tangled.org/whey.party/red-dwarf-server/microcosm/slingshot" 12 + "tangled.org/whey.party/red-dwarf-server/shims/utils" 13 + ) 14 + 15 + func ProfileViewBasic(ctx context.Context, did utils.DID, sl *microcosm.MicrocosmClient, imgcdn string) (*appbsky.ActorDefs_ProfileViewBasic, *appbsky.ActorProfile, error) { 16 + profileview, profile, err := ProfileView(ctx, did, sl, imgcdn) 17 + 18 + return &appbsky.ActorDefs_ProfileViewBasic{ 19 + Associated: profileview.Associated, 20 + Avatar: profileview.Avatar, 21 + CreatedAt: profileview.CreatedAt, 22 + Debug: profileview.Debug, 23 + Did: profileview.Did, 24 + DisplayName: profileview.DisplayName, 25 + Handle: profileview.Handle, 26 + Labels: profileview.Labels, 27 + Pronouns: profileview.Pronouns, 28 + Status: profileview.Status, 29 + Verification: profileview.Verification, 30 + Viewer: profileview.Viewer, 31 + }, profile, err 32 + } 33 + 34 + func ProfileView(ctx context.Context, did utils.DID, sl *microcosm.MicrocosmClient, imgcdn string) (*appbsky.ActorDefs_ProfileView, *appbsky.ActorProfile, error) { 35 + identity, err_i := slingshot.ResolveMiniDoc(ctx, sl, string(did)) 36 + if err_i != nil { 37 + identity = nil 38 + } 39 + profilerecord, err_r := agnostic.RepoGetRecord(ctx, sl, "", "app.bsky.actor.profile", string(did), "self") 40 + if err_r != nil { 41 + return nil, nil, err_r 42 + } 43 + 44 + var profile appbsky.ActorProfile 45 + if err := json.Unmarshal(*profilerecord.Value, &profile); err != nil { 46 + return nil, nil, err 47 + } 48 + 49 + var handle string 50 + if identity != nil { 51 + handle = identity.Handle 52 + } else { 53 + handle = string(did) 54 + } 55 + 56 + var displayName string 57 + if profile.DisplayName != nil { 58 + displayName = *profile.DisplayName 59 + } else { 60 + if handle != "" { 61 + displayName = handle 62 + } else { 63 + displayName = string(did) 64 + } 65 + } 66 + avatar := utils.MakeImageCDN(did, imgcdn, "avatar", profile.Avatar.Ref.String()) 67 + 68 + return &appbsky.ActorDefs_ProfileView{ 69 + Associated: nil, 70 + Avatar: &avatar, 71 + CreatedAt: profile.CreatedAt, 72 + Debug: nil, 73 + Description: profile.Description, 74 + Did: string(did), 75 + DisplayName: &displayName, 76 + Handle: handle, 77 + IndexedAt: profile.CreatedAt, 78 + Labels: nil, 79 + Pronouns: nil, 80 + Status: nil, 81 + Verification: nil, 82 + Viewer: nil, 83 + }, &profile, nil 84 + } 85 + 86 + func ProfileViewDetailed(ctx context.Context, did utils.DID, sl *microcosm.MicrocosmClient, cs *microcosm.MicrocosmClient, imgcdn string) (*appbsky.ActorDefs_ProfileViewDetailed, *appbsky.ActorProfile, error) { 87 + profileview, profile, err := ProfileView(ctx, did, sl, imgcdn) 88 + if err != nil { 89 + return nil, nil, err 90 + } 91 + followerCount_Out, err_i := constellation.LegacyLinksCountDistinctDids(ctx, cs, string(did), "app.bsky.graph.follow", ".subject", nil) 92 + if err_i != nil { 93 + followerCount_Out = nil 94 + } 95 + followerCount := int64(followerCount_Out.Total) 96 + 97 + banner := utils.MakeImageCDN(did, imgcdn, "banner", profile.Avatar.Ref.String()) 98 + 99 + nilCount := int64(-1) 100 + 101 + return &appbsky.ActorDefs_ProfileViewDetailed{ 102 + Associated: profileview.Associated, 103 + Avatar: profileview.Avatar, 104 + Banner: &banner, 105 + CreatedAt: profileview.CreatedAt, 106 + Debug: profileview.Debug, 107 + Description: profileview.Description, 108 + Did: profileview.Did, 109 + DisplayName: profileview.DisplayName, 110 + FollowersCount: &followerCount, 111 + FollowsCount: &nilCount, // hardcoded placeholder 112 + Handle: profileview.Handle, 113 + IndexedAt: profileview.IndexedAt, 114 + JoinedViaStarterPack: nil, // hardcoded placeholder 115 + Labels: profileview.Labels, 116 + PinnedPost: profile.PinnedPost, 117 + PostsCount: &nilCount, // hardcoded placeholder 118 + Pronouns: profileview.Pronouns, 119 + Status: profileview.Status, 120 + Verification: profileview.Verification, 121 + Viewer: profileview.Viewer, 122 + Website: profile.Website, 123 + }, profile, nil 124 + }
+41
shims/utils/utils.go
··· 1 + package utils 2 + 3 + import ( 4 + "fmt" 5 + "regexp" 6 + ) 7 + 8 + type DID string 9 + 10 + var didPattern = regexp.MustCompile(`^did:(plc|web):.+$`) 11 + 12 + func NewDID(s string) (DID, error) { 13 + if !didPattern.MatchString(s) { 14 + return "", fmt.Errorf("invalid DID: %s", s) 15 + } 16 + return DID(s), nil 17 + } 18 + 19 + type AtURI string 20 + 21 + var atUriPattern = regexp.MustCompile(`^at://did:(plc|web):.+/.+/.+$`) 22 + 23 + func NewAtURI(s string) (AtURI, error) { 24 + if !atUriPattern.MatchString(s) { 25 + return "", fmt.Errorf("invalid AtURI: %s", s) 26 + } 27 + return AtURI(s), nil 28 + } 29 + 30 + func SafeStringPtr(s *string) *string { 31 + if s != nil { 32 + return s 33 + } 34 + return nil 35 + } 36 + 37 + func PtrString(s string) *string { return &s } 38 + 39 + func MakeImageCDN(did DID, imgcdn string, kind string, cid string) string { 40 + return imgcdn + "/img/" + kind + "/plain/" + string(did) + "/" + cid + "@jpeg" 41 + }