From 9f0a9a6779f00786e4728ad5c3fad36814a293b0 Mon Sep 17 00:00:00 2001 From: Guillermo Serrahima Date: Sat, 11 Apr 2026 07:40:30 +0200 Subject: project restructure --- backend/notes/app.bsky.actor.go | 58 ---------------- backend/notes/app.bsky.feed.go | 150 ---------------------------------------- backend/notes/author.go | 11 --- backend/notes/note.go | 80 --------------------- 4 files changed, 299 deletions(-) delete mode 100644 backend/notes/app.bsky.actor.go delete mode 100644 backend/notes/app.bsky.feed.go delete mode 100644 backend/notes/author.go delete mode 100644 backend/notes/note.go (limited to 'backend/notes') diff --git a/backend/notes/app.bsky.actor.go b/backend/notes/app.bsky.actor.go deleted file mode 100644 index 00a1ff1..0000000 --- a/backend/notes/app.bsky.actor.go +++ /dev/null @@ -1,58 +0,0 @@ -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"` - Labels *struct { - Type string `json:"$type"` - Values []struct { - Val string `json:"val"` - } `json:"values"` - } `json:"labels,omitempty"` -} - -func AuthorFromAppBskyActorProfileRecord(record AppBskyActorProfileRecord, did, handle, pds string) (*Author, error) { - var avatarURL, bannerURL string - if record.Avatar != nil { - avatarURL = fmt.Sprintf("%s/xrpc/com.atproto.sync.getBlob?did=%s&cid=%s", pds, did, record.Avatar.Ref.Link) - } - if record.Banner != nil { - bannerURL = fmt.Sprintf("%s/xrpc/com.atproto.sync.getBlob?did=%s&cid=%s", pds, did, record.Banner.Ref.Link) - } - - // check for !no-unauthenticated label - noUnauth := false - if record.Labels != nil && record.Labels.Type == "com.atproto.label.defs#selfLabels" { - for _, label := range record.Labels.Values { - if label.Val == "!no-unauthenticated" { - noUnauth = true - } - } - } - - author := &Author{ - PDS: pds, - Handle: handle, - DisplayName: record.DisplayName, - AvatarURL: avatarURL, - BannerURL: bannerURL, - Pronouns: record.Pronouns, - NoUnauth: noUnauth, - } - - return author, nil -} diff --git a/backend/notes/app.bsky.feed.go b/backend/notes/app.bsky.feed.go deleted file mode 100644 index 3dd7b95..0000000 --- a/backend/notes/app.bsky.feed.go +++ /dev/null @@ -1,150 +0,0 @@ -package notes - -import ( - "fmt" - "llostre/backend/general" - "time" -) - -type EmbedImage struct { - Alt string `json:"alt"` - Image struct { - CID string `json:"$link"` - } `json:"image"` - AspectRatio *struct { - Width int `json:"width"` - Height int `json:"height"` - } `json:"aspectRatio,omitempty"` -} - -type EmbedImages struct { - Type string `json:"$type"` - Images []EmbedImage `json:"images,omitempty"` -} - -type AppBskyFeedPostEmbed struct { - Type string `json:"$type"` - Images []EmbedImage `json:"images,omitempty"` - Media *EmbedImages `json:"media,omitempty"` -} - -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"` - Embed *AppBskyFeedPostEmbed `json:"embed,omitempty"` - } `json:"value"` -} - -func extractImages(embed *AppBskyFeedPostEmbed, did, pdsURL string) []Media { - if embed == nil { - return nil - } - images := embed.Images - if embed.Media != nil { - images = embed.Media.Images - } - var media []Media - for _, img := range images { - m := Media{ - URL: fmt.Sprintf("%s/xrpc/com.atproto.sync.getBlob?did=%s&cid=%s", pdsURL, did, img.Image.CID), - Alt: img.Alt, - } - if img.AspectRatio != nil { - m.Width = img.AspectRatio.Width - m.Height = img.AspectRatio.Height - } - media = append(media, m) - } - return media -} - -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, - AuthorNoUnauth: author.NoUnauth, - AvatarURL: avatarURL, - CreatedAt: createdAt, - Text: record.Value.Text, - ReplyTo: replyTo, - Facets: facets, - Media: extractImages(record.Value.Embed, did, author.PDS), - }, nil -} diff --git a/backend/notes/author.go b/backend/notes/author.go deleted file mode 100644 index faf4f7b..0000000 --- a/backend/notes/author.go +++ /dev/null @@ -1,11 +0,0 @@ -package notes - -type Author struct { - PDS string - Handle string - DisplayName string - AvatarURL string - BannerURL string - Pronouns string - NoUnauth bool -} diff --git a/backend/notes/note.go b/backend/notes/note.go deleted file mode 100644 index 15f6f67..0000000 --- a/backend/notes/note.go +++ /dev/null @@ -1,80 +0,0 @@ -package notes - -import ( - "time" -) - -type Media struct { - URL string `json:"url"` - Alt string `json:"alt"` - Width int `json:"width,omitempty"` - Height int `json:"height,omitempty"` -} - -type Note struct { - URI string `json:"uri"` - AuthorDID string `json:"authorDID"` - AuthorHandle string `json:"authorHandle"` - AuthorName string `json:"authorName"` - AuthorNoUnauth bool `json:"authorNoUnauth"` // author has !no-unauthenticated label - AvatarURL string `json:"avatarURL"` - Text string `json:"text"` - CreatedAt time.Time `json:"createdAt"` - ReplyTo *string `json:"replyTo,omitempty"` - Facets []Facet `json:"facets,omitempty"` - Parents []Note `json:"parents,omitempty"` - Media []Media `json:"media,omitempty"` -} - -type Facet struct { - ByteStart int `json:"byteStart"` - ByteEnd int `json:"byteEnd"` - Type string `json:"type"` - Value string `json:"value"` // the link destination, whether url, profile or tag -} - -func MissingNote(uri string) *Note { - return &Note{ - URI: uri, - AuthorDID: "MISSING", - Text: "This post could not be fetched. It may have been deleted.", - CreatedAt: time.Time{}, - } -} - -func isChainContained(smallChainPost, bigChainPost Note) bool { - if len(smallChainPost.Parents) > len(bigChainPost.Parents) { - return false - } - - // if the thread is going to differ at any post, it's going to be at the end - // thus if the posts match their parents MUST match too - // (not that I expect to ever compare a post to itself but this way it won't break if I do. And technically a thread contains itself) - if len(smallChainPost.Parents) == len(bigChainPost.Parents) { - return smallChainPost.URI == bigChainPost.URI - } - - // check if the last post of the smaller chain is the (n+1)-th post of the bigger chain - // where n is the number of parents in the smaller reply chain - offset := len(bigChainPost.Parents) - len(smallChainPost.Parents) - return smallChainPost.URI == bigChainPost.Parents[offset-1].URI -} - -func DeduplicateThreads(posts []Note) []Note { - filtered := []Note{} - for _, post := range posts { - isRedundant := false - for _, kept := range filtered { - // check if this post's chain is contained in any kept post - if isChainContained(post, kept) { - isRedundant = true - break - } - } - if !isRedundant { - filtered = append(filtered, post) - } - } - - return filtered -} -- cgit v1.2.3