Allow interactions configured #1

merged
opened by willdot.net targeting main from feed-interactions
Changed files
+61 -8
cmd
register-feed
+1
.env-example
··· 5 5 FEED_DISPLAY_NAME="My demo feed" 6 6 FEED_DESCRIPTION="This is a demo feed" 7 7 FEED_DID="did:web:demo-feed.com" 8 + ACCEPTS_INTERACTIONS="true"
+14 -8
cmd/register-feed/main.go
··· 33 33 } 34 34 35 35 type registerRecord struct { 36 - Did string `json:"did"` 37 - DisplayName string `json:"displayName"` 38 - Description string `json:"description"` 39 - CreatedAt time.Time `json:"createdAt"` 36 + Did string `json:"did"` 37 + DisplayName string `json:"displayName"` 38 + Description string `json:"description"` 39 + CreatedAt time.Time `json:"createdAt"` 40 + AcceptsInteractions bool `json:"acceptsInteractions"` 40 41 } 41 42 42 43 func main() { ··· 136 137 if feedDID == "" { 137 138 return fmt.Errorf("FEED_DID environment not set") 138 139 } 140 + acceptsInteractions := false 141 + if os.Getenv("ACCEPTS_INTERACTIONS") == "true" { 142 + acceptsInteractions = true 143 + } 139 144 140 145 reqData := registerFeedGen{ 141 146 Repo: auth.Did, 142 147 Collection: "app.bsky.feed.generator", 143 148 Rkey: feedName, 144 149 Record: registerRecord{ 145 - Did: feedDID, 146 - DisplayName: feedDisplayName, 147 - Description: feedDescription, 148 - CreatedAt: time.Now(), 150 + Did: feedDID, 151 + DisplayName: feedDisplayName, 152 + Description: feedDescription, 153 + CreatedAt: time.Now(), 154 + AcceptsInteractions: acceptsInteractions, 149 155 }, 150 156 } 151 157
+44
handlers.go
··· 4 4 "context" 5 5 "encoding/json" 6 6 "fmt" 7 + "io" 7 8 "log/slog" 8 9 "net/http" 9 10 "net/url" ··· 112 113 _, _ = w.Write(b) 113 114 } 114 115 116 + // FeedInteractions details the interactions that a user had with a feed when they viewed it 117 + type FeedInteractions struct { 118 + Interactions []Interaction `json:"interactions"` 119 + } 120 + 121 + type Interaction struct { 122 + Item string `json:"item"` 123 + Event string `json:"event"` 124 + } 125 + 126 + // HandleFeedInteractions will handle when the client sends back a feed interaction so you can improve 127 + // the feed quality for the user 128 + func (s *Server) HandleFeedInteractions(w http.ResponseWriter, r *http.Request) { 129 + slog.Debug("handle feed interactions") 130 + userDID, err := getRequestUserDID(r) 131 + if err != nil { 132 + slog.Error("validate user auth", "error", err) 133 + http.Error(w, "validate auth", http.StatusUnauthorized) 134 + return 135 + } 136 + 137 + body, err := io.ReadAll(r.Body) 138 + if err != nil { 139 + slog.Error("read feed interactions request body", "error", err) 140 + http.Error(w, "read body", http.StatusBadRequest) 141 + return 142 + } 143 + 144 + var feedInteractions FeedInteractions 145 + err = json.Unmarshal(body, &feedInteractions) 146 + if err != nil { 147 + slog.Error("decode feed interactions request body", "error", err) 148 + http.Error(w, "decode body", http.StatusBadRequest) 149 + return 150 + } 151 + 152 + // here is where you would likely do something with the data that is sent to you such as improving the 153 + // data you store for a users feed 154 + for _, interaction := range feedInteractions.Interactions { 155 + slog.Info("interaction for user", "user", userDID, "item", interaction.Item, "interaction", interaction.Event) 156 + } 157 + } 158 + 115 159 // WellKnownResponse is what's returned on a well-known endpoint 116 160 type WellKnownResponse struct { 117 161 Context []string `json:"@context"`
+1
readme.md
··· 25 25 * FEED_DISPLAY_NAME - This is the name you will give your feed that users will be able to see 26 26 * FEED_DESCRIPTION - This is a description of your feed that users will be able to see 27 27 * FEED_DID - This is the DID that will be used to register the record. Unless you know what you are doing it's best to use `did:web:` + FEED_HOST_NAME (eg "did:web:demo-feed.com") 28 + * ACCEPTS_INTERACTIONS - Set this to be true if you wish your feed to accepts interactions such as "show more" or "show less" 28 29 29 30 First you need to run the feed generator by building the application `go build -o demo-feed-generator ./cmd/feed-generator/main.go` and then running it `./demo-feed-generator` 30 31
+1
server.go
··· 42 42 mux := http.NewServeMux() 43 43 mux.HandleFunc("/xrpc/app.bsky.feed.getFeedSkeleton", srv.HandleGetFeedSkeleton) 44 44 mux.HandleFunc("/xrpc/app.bsky.feed.describeFeedGenerator", srv.HandleDescribeFeedGenerator) 45 + mux.HandleFunc("POST /xrpc/app.bsky.feed.sendInteractions", srv.HandleFeedInteractions) 45 46 mux.HandleFunc("/.well-known/did.json", srv.HandleWellKnown) 46 47 addr := fmt.Sprintf("0.0.0.0:%d", port) 47 48