package notes import ( "fmt" "llostre/general" "time" ) type EmbedImage struct { Alt string `json:"alt"` Image struct { Ref struct { CID string `json:"$link"` } `json:"ref"` } `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 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("/api/blob/%s/%s", did, img.Image.Ref.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, porxieURL string) (*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), }, nil }