Allow interactions configured #1

merged
opened by willdot.net targeting main from feed-interactions
Changed files
+61 -8
cmd
register-feed
+1
.env-example
···
FEED_DISPLAY_NAME="My demo feed"
FEED_DESCRIPTION="This is a demo feed"
FEED_DID="did:web:demo-feed.com"
+
ACCEPTS_INTERACTIONS="true"
+14 -8
cmd/register-feed/main.go
···
}
type registerRecord struct {
-
Did string `json:"did"`
-
DisplayName string `json:"displayName"`
-
Description string `json:"description"`
-
CreatedAt time.Time `json:"createdAt"`
+
Did string `json:"did"`
+
DisplayName string `json:"displayName"`
+
Description string `json:"description"`
+
CreatedAt time.Time `json:"createdAt"`
+
AcceptsInteractions bool `json:"acceptsInteractions"`
}
func main() {
···
if feedDID == "" {
return fmt.Errorf("FEED_DID environment not set")
}
+
acceptsInteractions := false
+
if os.Getenv("ACCEPTS_INTERACTIONS") == "true" {
+
acceptsInteractions = true
+
}
reqData := registerFeedGen{
Repo: auth.Did,
Collection: "app.bsky.feed.generator",
Rkey: feedName,
Record: registerRecord{
-
Did: feedDID,
-
DisplayName: feedDisplayName,
-
Description: feedDescription,
-
CreatedAt: time.Now(),
+
Did: feedDID,
+
DisplayName: feedDisplayName,
+
Description: feedDescription,
+
CreatedAt: time.Now(),
+
AcceptsInteractions: acceptsInteractions,
},
}
+44
handlers.go
···
"context"
"encoding/json"
"fmt"
+
"io"
"log/slog"
"net/http"
"net/url"
···
_, _ = w.Write(b)
}
+
// FeedInteractions details the interactions that a user had with a feed when they viewed it
+
type FeedInteractions struct {
+
Interactions []Interaction `json:"interactions"`
+
}
+
+
type Interaction struct {
+
Item string `json:"item"`
+
Event string `json:"event"`
+
}
+
+
// HandleFeedInteractions will handle when the client sends back a feed interaction so you can improve
+
// the feed quality for the user
+
func (s *Server) HandleFeedInteractions(w http.ResponseWriter, r *http.Request) {
+
slog.Debug("handle feed interactions")
+
userDID, err := getRequestUserDID(r)
+
if err != nil {
+
slog.Error("validate user auth", "error", err)
+
http.Error(w, "validate auth", http.StatusUnauthorized)
+
return
+
}
+
+
body, err := io.ReadAll(r.Body)
+
if err != nil {
+
slog.Error("read feed interactions request body", "error", err)
+
http.Error(w, "read body", http.StatusBadRequest)
+
return
+
}
+
+
var feedInteractions FeedInteractions
+
err = json.Unmarshal(body, &feedInteractions)
+
if err != nil {
+
slog.Error("decode feed interactions request body", "error", err)
+
http.Error(w, "decode body", http.StatusBadRequest)
+
return
+
}
+
+
// here is where you would likely do something with the data that is sent to you such as improving the
+
// data you store for a users feed
+
for _, interaction := range feedInteractions.Interactions {
+
slog.Info("interaction for user", "user", userDID, "item", interaction.Item, "interaction", interaction.Event)
+
}
+
}
+
// WellKnownResponse is what's returned on a well-known endpoint
type WellKnownResponse struct {
Context []string `json:"@context"`
+1
readme.md
···
* FEED_DISPLAY_NAME - This is the name you will give your feed that users will be able to see
* FEED_DESCRIPTION - This is a description of your feed that users will be able to see
* 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")
+
* ACCEPTS_INTERACTIONS - Set this to be true if you wish your feed to accepts interactions such as "show more" or "show less"
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`
+1
server.go
···
mux := http.NewServeMux()
mux.HandleFunc("/xrpc/app.bsky.feed.getFeedSkeleton", srv.HandleGetFeedSkeleton)
mux.HandleFunc("/xrpc/app.bsky.feed.describeFeedGenerator", srv.HandleDescribeFeedGenerator)
+
mux.HandleFunc("POST /xrpc/app.bsky.feed.sendInteractions", srv.HandleFeedInteractions)
mux.HandleFunc("/.well-known/did.json", srv.HandleWellKnown)
addr := fmt.Sprintf("0.0.0.0:%d", port)