A demo of a Bluesky feed generator in Go
1package feed
2
3import (
4 "context"
5 _ "embed"
6 "fmt"
7 "log/slog"
8 "net/http"
9)
10
11// Post describes a Bluesky post
12type Post struct {
13 ID int
14 RKey string
15 PostURI string
16 UserDID string
17 CreatedAt int64
18}
19
20// PostStore defines the interactions with a store
21type PostStore interface {
22 GetFeedPosts(cursor, limit int) ([]Post, error)
23 CreatePost(post Post) error
24}
25
26// Server is the feed server that will be called when a user requests to view a feed
27type Server struct {
28 httpsrv *http.Server
29 postStore PostStore
30 feedHost string
31 feedName string
32}
33
34// NewServer builds a server - call the Run function to start the server
35func NewServer(port int, feedHost, feedName string, postStore PostStore) (*Server, error) {
36 srv := &Server{
37 feedHost: feedHost,
38 feedName: feedName,
39 postStore: postStore,
40 }
41
42 mux := http.NewServeMux()
43 mux.HandleFunc("/xrpc/app.bsky.feed.getFeedSkeleton", srv.HandleGetFeedSkeleton)
44 mux.HandleFunc("/xrpc/app.bsky.feed.describeFeedGenerator", srv.HandleDescribeFeedGenerator)
45 mux.HandleFunc("/.well-known/did.json", srv.HandleWellKnown)
46 addr := fmt.Sprintf("0.0.0.0:%d", port)
47
48 srv.httpsrv = &http.Server{
49 Addr: addr,
50 Handler: mux,
51 }
52 return srv, nil
53}
54
55// Run will start the server - it is a blocking function
56func (s *Server) Run() {
57 err := s.httpsrv.ListenAndServe()
58 if err != nil {
59 slog.Error("listen and serve", "error", err)
60 }
61}
62
63// Stop will shutdown the server
64func (s *Server) Stop(ctx context.Context) error {
65 return s.httpsrv.Shutdown(ctx)
66}