A collection of Custom Bluesky Feeds, including Fresh Feeds, all under one roof
at main 101 lines 3.1 kB view raw
1// Package feedrouter describes the FeedRouter type, which is responsible for generating feeds for a given DID. 2// It also describes the Feed interface, which is implemented by the various feed types. 3package feedrouter 4 5import ( 6 "context" 7 "fmt" 8 9 appbsky "github.com/bluesky-social/indigo/api/bsky" 10 did "github.com/whyrusleeping/go-did" 11) 12 13type Feed interface { 14 GetPage(ctx context.Context, feed string, userDID string, limit int64, cursor string) (feedPosts []*appbsky.FeedDefs_SkeletonFeedPost, newCursor *string, err error) 15 Describe(ctx context.Context) ([]appbsky.FeedDescribeFeedGenerator_Feed, error) 16} 17 18type FeedRouter struct { 19 FeedActorDID did.DID // DID of the Repo the Feed is published under 20 ServiceEndpoint string // URL of the FeedRouter service 21 ServiceDID did.DID // DID of the FeedRouter service 22 DIDDocument did.Document // DID Document of the FeedRouter service 23 AcceptableURIPrefixes []string // URIs that the FeedRouter is allowed to generate feeds for 24 FeedMap map[string]Feed // map of FeedName to Feed 25 Feeds []Feed 26} 27 28type NotFoundError struct { 29 error 30} 31 32// NewFeedRouter returns a new FeedRouter 33func NewFeedRouter( 34 ctx context.Context, 35 feedActorDIDString string, 36 serviceDIDString string, 37 acceptableDIDs []string, 38 serviceEndpoint string, 39) (*FeedRouter, error) { 40 acceptableURIPrefixes := []string{} 41 for _, did := range acceptableDIDs { 42 acceptableURIPrefixes = append(acceptableURIPrefixes, "at://"+did+"/app.bsky.feed.generator/") 43 } 44 45 serviceDID, err := did.ParseDID(serviceDIDString) 46 if err != nil { 47 return nil, fmt.Errorf("error parsing serviceDID: %w", err) 48 } 49 50 feedActorDID, err := did.ParseDID(feedActorDIDString) 51 if err != nil { 52 return nil, fmt.Errorf("error parsing feedActorDID: %w", err) 53 } 54 55 serviceID, err := did.ParseDID("#bsky_fg") 56 if err != nil { 57 panic(err) 58 } 59 60 doc := did.Document{ 61 Context: []string{did.CtxDIDv1}, 62 ID: serviceDID, 63 Service: []did.Service{ 64 { 65 ID: serviceID, 66 Type: "BskyFeedGenerator", 67 ServiceEndpoint: serviceEndpoint, 68 }, 69 }, 70 } 71 72 return &FeedRouter{ 73 FeedMap: map[string]Feed{}, 74 FeedActorDID: feedActorDID, 75 ServiceDID: serviceDID, 76 DIDDocument: doc, 77 AcceptableURIPrefixes: acceptableURIPrefixes, 78 ServiceEndpoint: serviceEndpoint, 79 }, nil 80} 81 82// AddFeed adds a feed to the FeedRouter 83// Feed precedence for overlapping aliases is determined by the order in which 84// they are added (first added is highest precedence) 85func (fg *FeedRouter) AddFeed(feedAliases []string, feed Feed) { 86 if fg.FeedMap == nil { 87 fg.FeedMap = map[string]Feed{} 88 } 89 90 for _, feedAlias := range feedAliases { 91 // Skip the feed if we already have the alias registered so we don't add it twice 92 // Feed precedence is determined by the order in which they are added 93 if _, ok := fg.FeedMap[feedAlias]; ok { 94 continue 95 } 96 97 fg.FeedMap[feedAlias] = feed 98 } 99 100 fg.Feeds = append(fg.Feeds, feed) 101}