From 59545c874d8635c39ccdbecf84f953686dd5c03c Mon Sep 17 00:00:00 2001 From: Guillermo Serrahima Date: Wed, 18 Mar 2026 22:02:13 +0100 Subject: more naming refactors --- backend/fetchers/app.bsky.actor.go | 43 ++++++--------- backend/fetchers/app.bsky.feed.go | 28 +++++----- backend/fetchers/cache.go | 19 ++++--- backend/general/profiles.go | 24 --------- backend/notes/app.bsky.actor.go | 40 ++++++++++++++ backend/notes/app.bsky.feed.go | 102 ++++++++++++++++++++++++++++++++++++ backend/notes/app.bsky.feed.post.go | 102 ------------------------------------ backend/notes/author.go | 9 ++++ 8 files changed, 189 insertions(+), 178 deletions(-) delete mode 100644 backend/general/profiles.go create mode 100644 backend/notes/app.bsky.actor.go create mode 100644 backend/notes/app.bsky.feed.go delete mode 100644 backend/notes/app.bsky.feed.post.go create mode 100644 backend/notes/author.go diff --git a/backend/fetchers/app.bsky.actor.go b/backend/fetchers/app.bsky.actor.go index 45ced77..e07f5be 100644 --- a/backend/fetchers/app.bsky.actor.go +++ b/backend/fetchers/app.bsky.actor.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "llostre/backend/general" + "llostre/backend/notes" "log/slog" "net" "net/http" @@ -129,7 +130,7 @@ func FetchHandleAndPDS(did string) (string, string, error) { return handle, pds, nil } -func FetchProfile(logger *slog.Logger, did, pdsURL, slingshotURL string, cache *Cache) (*general.Profile, error) { +func FetchAppBskyActorProfile(logger *slog.Logger, did, pdsURL, slingshotURL string, cache *Cache) (*notes.Author, error) { // construct profile record URI uri := fmt.Sprintf("at://%s/app.bsky.actor.profile/self", did) @@ -139,7 +140,7 @@ func FetchProfile(logger *slog.Logger, did, pdsURL, slingshotURL string, cache * if err != nil { logger.Warn("slingshot profile fetch failed", "did", did, "error", err) } else { - return parseProfile(did, rawRecord, cache) + return parseAppBskyActorProfile(did, rawRecord, cache) } } @@ -149,47 +150,33 @@ func FetchProfile(logger *slog.Logger, did, pdsURL, slingshotURL string, cache * return nil, err } - return parseProfile(did, rawRecord, cache) + return parseAppBskyActorProfile(did, rawRecord, cache) } -func FetchProfiles(logger *slog.Logger, dids []string, pdsURL, slingshotURL string, cache *Cache) map[string]general.Profile { - profiles := make(map[string]general.Profile) +func FetchAppBskyActorProfiles(logger *slog.Logger, dids []string, pdsURL, slingshotURL string, cache *Cache) map[string]notes.Author { + authors := make(map[string]notes.Author) for _, did := range dids { - profile, err := FetchProfile(logger, did, pdsURL, slingshotURL, cache) + author, err := FetchAppBskyActorProfile(logger, did, pdsURL, slingshotURL, cache) if err == nil { - profiles[did] = *profile + authors[did] = *author } } - return profiles + return authors } -func parseProfile(did string, rawRecord *general.RawRecord, cache *Cache) (*general.Profile, error) { - var profileData general.ProfileRecord - if err := json.Unmarshal(rawRecord.Value, &profileData); err != nil { +func parseAppBskyActorProfile(did string, rawRecord *general.RawRecord, cache *Cache) (*notes.Author, error) { + var profile notes.AppBskyActorProfileRecord + if err := json.Unmarshal(rawRecord.Value, &profile); err != nil { return nil, err } - var avatarURL, bannerURL string - if profileData.Avatar != nil { - avatarURL = fmt.Sprintf("https://cdn.bsky.app/img/avatar/plain/%s/%s@jpeg", did, profileData.Avatar.Ref.Link) - } - if profileData.Banner != nil { - bannerURL = fmt.Sprintf("https://cdn.bsky.app/img/banner/plain/%s/%s@jpeg", did, profileData.Banner.Ref.Link) - } - handle, err := FetchHandle(did) if err != nil { handle = "handle.invalid" } - profile := &general.Profile{ - Handle: handle, - DisplayName: profileData.DisplayName, - AvatarURL: avatarURL, - BannerURL: bannerURL, - Pronouns: profileData.Pronouns, - } + author, err := notes.AuthorFromAppBskyActorProfileRecord(profile, did, handle) - cache.SetProfile(did, profile) - return profile, nil + cache.SetAuthor(did, author) + return author, nil } diff --git a/backend/fetchers/app.bsky.feed.go b/backend/fetchers/app.bsky.feed.go index abc3b46..10c08e7 100644 --- a/backend/fetchers/app.bsky.feed.go +++ b/backend/fetchers/app.bsky.feed.go @@ -10,10 +10,10 @@ import ( func FetchAppBskyFeedPosts(logger *slog.Logger, did, pdsURL, slingshotURL string, cache *Cache) ([]notes.Note, error) { // get author of the note - profile, found := cache.GetProfile(did) + author, found := cache.GetProfile(did) var err error if !found { - profile, err = FetchProfile(logger, did, pdsURL, slingshotURL, cache) + author, err = FetchAppBskyActorProfile(logger, did, pdsURL, slingshotURL, cache) } if err != nil { return nil, err @@ -21,20 +21,20 @@ func FetchAppBskyFeedPosts(logger *slog.Logger, did, pdsURL, slingshotURL string // try slingshot first if given if slingshotURL != "" { - values, err := general.FetchRawRecordCollection(logger, did, "app.bsky.feed.post", slingshotURL) + records, err := general.FetchRawRecordCollection(logger, did, "app.bsky.feed.post", slingshotURL) if err != nil { logger.Error("Fetching from slingshot failed") } else { - return parseAppBskyFeedPostCollection(logger, values, profile, cache) + return parseAppBskyFeedPostCollection(logger, records, author, cache) } } - values, err := general.FetchRawRecordCollection(logger, did, "app.bsky.feed.post", pdsURL) + records, err := general.FetchRawRecordCollection(logger, did, "app.bsky.feed.post", pdsURL) if err != nil { return nil, err } - return parseAppBskyFeedPostCollection(logger, values, profile, cache) + return parseAppBskyFeedPostCollection(logger, records, author, cache) } func FetchAppBskyFeedPostByURI(logger *slog.Logger, uri, slingshotURL string, cache *Cache) (*notes.Note, error) { @@ -52,9 +52,9 @@ func FetchAppBskyFeedPostByURI(logger *slog.Logger, uri, slingshotURL string, ca } // get author of the note - profile, found := cache.GetProfile(repo) + author, found := cache.GetProfile(repo) if !found { - profile, err = FetchProfile(logger, repo, pdsURL, slingshotURL, cache) + author, err = FetchAppBskyActorProfile(logger, repo, pdsURL, slingshotURL, cache) } if err != nil { return nil, err @@ -66,7 +66,7 @@ func FetchAppBskyFeedPostByURI(logger *slog.Logger, uri, slingshotURL string, ca if err != nil { logger.Error("fetching from slingshot failed", "error", err) } else { - return parseAppBskyFeedPostRecord(rawRecord, profile, cache) + return parseAppBskyFeedPostRecord(rawRecord, author, cache) } } @@ -76,10 +76,10 @@ func FetchAppBskyFeedPostByURI(logger *slog.Logger, uri, slingshotURL string, ca return nil, err } - return parseAppBskyFeedPostRecord(rawRecord, profile, cache) + return parseAppBskyFeedPostRecord(rawRecord, author, cache) } -func parseAppBskyFeedPostRecord(rawRecord *general.RawRecord, profile *general.Profile, cache *Cache) (*notes.Note, error) { +func parseAppBskyFeedPostRecord(rawRecord *general.RawRecord, author *notes.Author, cache *Cache) (*notes.Note, error) { if rawRecord == nil { return nil, fmt.Errorf("unable to parse nil record") } @@ -92,7 +92,7 @@ func parseAppBskyFeedPostRecord(rawRecord *general.RawRecord, profile *general.P return nil, err } - note, err := notes.NoteFromAppBskyFeedPostRecord(record, profile) + note, err := notes.NoteFromAppBskyFeedPostRecord(record, author) if err != nil { return nil, err } @@ -103,10 +103,10 @@ func parseAppBskyFeedPostRecord(rawRecord *general.RawRecord, profile *general.P return note, nil } -func parseAppBskyFeedPostCollection(logger *slog.Logger, records []general.RawRecord, profile *general.Profile, cache *Cache) ([]notes.Note, error) { +func parseAppBskyFeedPostCollection(logger *slog.Logger, records []general.RawRecord, author *notes.Author, cache *Cache) ([]notes.Note, error) { posts := make([]notes.Note, 0, len(records)) for _, record := range records { - note, err := parseAppBskyFeedPostRecord(&record, profile, cache) + note, err := parseAppBskyFeedPostRecord(&record, author, cache) if err != nil { logger.Error("failed to parse post record", "error", err) continue diff --git a/backend/fetchers/cache.go b/backend/fetchers/cache.go index b863d60..01c37fe 100644 --- a/backend/fetchers/cache.go +++ b/backend/fetchers/cache.go @@ -1,7 +1,6 @@ package fetchers import ( - "llostre/backend/general" "llostre/backend/notes" "sync" "time" @@ -14,14 +13,14 @@ type CacheEntry struct { type Cache struct { posts map[string]*CacheEntry - profiles map[string]*ProfileCacheEntry + profiles map[string]*AuthorCacheEntry mu sync.RWMutex } func NewCache() *Cache { return &Cache{ posts: make(map[string]*CacheEntry), - profiles: make(map[string]*ProfileCacheEntry), + profiles: make(map[string]*AuthorCacheEntry), } } @@ -77,12 +76,12 @@ func (c *Cache) GetMany(uris []string) map[string]*notes.Note { return result } -type ProfileCacheEntry struct { - Profile *general.Profile +type AuthorCacheEntry struct { + Author *notes.Author ExpiresAt time.Time } -func (c *Cache) GetProfile(did string) (*general.Profile, bool) { +func (c *Cache) GetProfile(did string) (*notes.Author, bool) { c.mu.Lock() defer c.mu.Unlock() @@ -96,15 +95,15 @@ func (c *Cache) GetProfile(did string) (*general.Profile, bool) { return nil, false } - return entry.Profile, true + return entry.Author, true } -func (c *Cache) SetProfile(did string, profile *general.Profile) { +func (c *Cache) SetAuthor(did string, author *notes.Author) { c.mu.Lock() defer c.mu.Unlock() - c.profiles[did] = &ProfileCacheEntry{ - Profile: profile, + c.profiles[did] = &AuthorCacheEntry{ + Author: author, ExpiresAt: time.Now().Add(15 * time.Minute), } } diff --git a/backend/general/profiles.go b/backend/general/profiles.go deleted file mode 100644 index b545e60..0000000 --- a/backend/general/profiles.go +++ /dev/null @@ -1,24 +0,0 @@ -package general - -type Profile struct { - Handle string - DisplayName string - AvatarURL string - BannerURL string - Pronouns string -} - -type ProfileRecord struct { - DisplayName string `json:"displayName"` - Avatar *struct { - Ref struct { - Link string `json:"$link"` - } `json:"ref"` - } `json:"avatar,omitempty"` - Banner *struct { - Ref struct { - Link string `json:"$link"` - } `json:"ref"` - } `json:"banner,omitempty"` - Pronouns string `json:"pronouns"` -} diff --git a/backend/notes/app.bsky.actor.go b/backend/notes/app.bsky.actor.go new file mode 100644 index 0000000..dc5d536 --- /dev/null +++ b/backend/notes/app.bsky.actor.go @@ -0,0 +1,40 @@ +package notes + +import ( + "fmt" +) + +type AppBskyActorProfileRecord struct { + DisplayName string `json:"displayName"` + Avatar *struct { + Ref struct { + Link string `json:"$link"` + } `json:"ref"` + } `json:"avatar,omitempty"` + Banner *struct { + Ref struct { + Link string `json:"$link"` + } `json:"ref"` + } `json:"banner,omitempty"` + Pronouns string `json:"pronouns"` +} + +func AuthorFromAppBskyActorProfileRecord(record AppBskyActorProfileRecord, did, handle string) (*Author, error) { + var avatarURL, bannerURL string + if record.Avatar != nil { + avatarURL = fmt.Sprintf("https://cdn.bsky.app/img/avatar/plain/%s/%s@jpeg", did, record.Avatar.Ref.Link) + } + if record.Banner != nil { + bannerURL = fmt.Sprintf("https://cdn.bsky.app/img/banner/plain/%s/%s@jpeg", did, record.Banner.Ref.Link) + } + + author := &Author{ + Handle: handle, + DisplayName: record.DisplayName, + AvatarURL: avatarURL, + BannerURL: bannerURL, + Pronouns: record.Pronouns, + } + + return author, nil +} diff --git a/backend/notes/app.bsky.feed.go b/backend/notes/app.bsky.feed.go new file mode 100644 index 0000000..b5cfd03 --- /dev/null +++ b/backend/notes/app.bsky.feed.go @@ -0,0 +1,102 @@ +package notes + +import ( + "fmt" + "llostre/backend/general" + "time" +) + +type AppBskyFeedPostRecord struct { + URI string `json:"uri"` + Value struct { + Text string `json:"text"` + CreatedAt string `json:"createdAt"` + Reply *struct { + Parent struct { + URI string `json:"uri"` + } `json:"parent"` + } `json:"reply,omitempty"` + Facets []struct { + Index struct { + ByteStart int `json:"byteStart"` + ByteEnd int `json:"byteEnd"` + } `json:"index"` + Features []struct { + Type string `json:"$type"` + URI string `json:"uri,omitempty"` + DID string `json:"did,omitempty"` + Tag string `json:"tag,omitempty"` + } `json:"features"` + } `json:"facets,omitempty"` + } `json:"value"` +} + +func NoteFromAppBskyFeedPostRecord(record AppBskyFeedPostRecord, author *Author) (*Note, error) { + if record.URI == "" { + return nil, fmt.Errorf("missing required field: uri") + } + + createdAt, err := time.Parse(time.RFC3339, record.Value.CreatedAt) + if err != nil { + return nil, fmt.Errorf("failed to parse timestamp: %w", err) + } + + var replyTo *string + if record.Value.Reply != nil { + replyTo = &record.Value.Reply.Parent.URI + } + + var facets []Facet + for _, f := range record.Value.Facets { + for _, feature := range f.Features { + var value string + var facetType string + + switch feature.Type { + case "app.bsky.richtext.facet#link": + facetType = "link" + value = feature.URI + case "app.bsky.richtext.facet#mention": + facetType = "mention" + value = feature.DID + case "app.bsky.richtext.facet#tag": + facetType = "tag" + value = feature.Tag + default: + continue + } + + facets = append(facets, Facet{ + ByteStart: f.Index.ByteStart, + ByteEnd: f.Index.ByteEnd, + Type: facetType, + Value: value, + }) + } + } + + var handle, displayName, avatarURL string + if author != nil { + handle = author.Handle + displayName = author.DisplayName + avatarURL = author.AvatarURL + } else { + handle = "handle.invalid" + displayName = "" + avatarURL = "" + } + + did, _, _, err := general.ParseURI(record.URI) + + return &Note{ + URI: record.URI, + AuthorDID: did, + AuthorHandle: handle, + AuthorName: displayName, + AvatarURL: avatarURL, + CreatedAt: createdAt, + Text: record.Value.Text, + ReplyTo: replyTo, + Facets: facets, + }, nil +} diff --git a/backend/notes/app.bsky.feed.post.go b/backend/notes/app.bsky.feed.post.go deleted file mode 100644 index 451633e..0000000 --- a/backend/notes/app.bsky.feed.post.go +++ /dev/null @@ -1,102 +0,0 @@ -package notes - -import ( - "fmt" - "llostre/backend/general" - "time" -) - -type AppBskyFeedPostRecord struct { - URI string `json:"uri"` - Value struct { - Text string `json:"text"` - CreatedAt string `json:"createdAt"` - Reply *struct { - Parent struct { - URI string `json:"uri"` - } `json:"parent"` - } `json:"reply,omitempty"` - Facets []struct { - Index struct { - ByteStart int `json:"byteStart"` - ByteEnd int `json:"byteEnd"` - } `json:"index"` - Features []struct { - Type string `json:"$type"` - URI string `json:"uri,omitempty"` - DID string `json:"did,omitempty"` - Tag string `json:"tag,omitempty"` - } `json:"features"` - } `json:"facets,omitempty"` - } `json:"value"` -} - -func NoteFromAppBskyFeedPostRecord(record AppBskyFeedPostRecord, profile *general.Profile) (*Note, error) { - if record.URI == "" { - return nil, fmt.Errorf("missing required field: uri") - } - - createdAt, err := time.Parse(time.RFC3339, record.Value.CreatedAt) - if err != nil { - return nil, fmt.Errorf("failed to parse timestamp: %w", err) - } - - var replyTo *string - if record.Value.Reply != nil { - replyTo = &record.Value.Reply.Parent.URI - } - - var facets []Facet - for _, f := range record.Value.Facets { - for _, feature := range f.Features { - var value string - var facetType string - - switch feature.Type { - case "app.bsky.richtext.facet#link": - facetType = "link" - value = feature.URI - case "app.bsky.richtext.facet#mention": - facetType = "mention" - value = feature.DID - case "app.bsky.richtext.facet#tag": - facetType = "tag" - value = feature.Tag - default: - continue - } - - facets = append(facets, Facet{ - ByteStart: f.Index.ByteStart, - ByteEnd: f.Index.ByteEnd, - Type: facetType, - Value: value, - }) - } - } - - var handle, displayName, avatarURL string - if profile != nil { - handle = profile.Handle - displayName = profile.DisplayName - avatarURL = profile.AvatarURL - } else { - handle = "handle.invalid" - displayName = "" - avatarURL = "" - } - - did, _, _, err := general.ParseURI(record.URI) - - return &Note{ - URI: record.URI, - AuthorDID: did, - AuthorHandle: handle, - AuthorName: displayName, - AvatarURL: avatarURL, - CreatedAt: createdAt, - Text: record.Value.Text, - ReplyTo: replyTo, - Facets: facets, - }, nil -} diff --git a/backend/notes/author.go b/backend/notes/author.go new file mode 100644 index 0000000..02afabf --- /dev/null +++ b/backend/notes/author.go @@ -0,0 +1,9 @@ +package notes + +type Author struct { + Handle string + DisplayName string + AvatarURL string + BannerURL string + Pronouns string +} -- cgit v1.2.3