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("POST /xrpc/app.bsky.feed.sendInteractions", srv.HandleFeedInteractions)
46 mux.HandleFunc("/.well-known/did.json", srv.HandleWellKnown)
47 addr := fmt.Sprintf("0.0.0.0:%d", port)
48
49 srv.httpsrv = &http.Server{
50 Addr: addr,
51 Handler: mux,
52 }
53 return srv, nil
54}
55
56// Run will start the server - it is a blocking function
57func (s *Server) Run() {
58 err := s.httpsrv.ListenAndServe()
59 if err != nil {
60 slog.Error("listen and serve", "error", err)
61 }
62}
63
64// Stop will shutdown the server
65func (s *Server) Stop(ctx context.Context) error {
66 return s.httpsrv.Shutdown(ctx)
67}