package bsky import ( "encoding/json" "fmt" "sort" "strings" "time" ) type Post struct { Text string `json:"text,omitempty"` Type string `json:"$type,omitempty"` Embed *Embed `json:"embed,omitempty"` Langs []string `json:"langs,omitempty"` Labels *Labels `json:"labels,omitempty"` Reply *Reply `json:"reply,omitempty"` Facets *[]Facets `json:"facets,omitempty"` CreatedAt time.Time `json:"createdAt"` } type Ref struct { Link string `json:"$link,omitempty"` } type Thumb struct { Type string `json:"$type,omitempty"` Ref *Ref `json:"ref,omitempty"` MimeType string `json:"mimeType,omitempty"` Size int `json:"size,omitempty"` } type External struct { URI string `json:"uri,omitempty"` Thumb *Thumb `json:"thumb,omitempty"` Title string `json:"title,omitempty"` Description string `json:"description,omitempty"` } type Video struct { Type string `json:"$type,omitempty"` Ref *Ref `json:"ref,omitempty"` MimeType string `json:"mimeType,omitempty"` Size int `json:"size,omitempty"` } type Image struct { Type string `json:"$type,omitempty"` Ref *Ref `json:"ref,omitempty"` MimeType string `json:"mimeType,omitempty"` Size int `json:"size,omitempty"` } type AspectRatio struct { Width int `json:"width,omitempty"` Height int `json:"height,omitempty"` } type Images struct { Alt string `json:"alt,omitempty"` Image *Image `json:"image,omitempty"` AspectRatio *AspectRatio `json:"aspectRatio,omitempty"` } type Media struct { Type string `json:"$type,omitempty"` External *External `json:"external,omitempty"` Video *Video `json:"video,omitempty"` Images *[]Images `json:"images,omitempty"` AspectRatio *AspectRatio `json:"aspectRatio,omitempty"` } type Record struct { Cid string `json:"cid,omitempty"` URI string `json:"uri,omitempty"` } type PostRecord struct { Type string `json:"$type,omitempty"` Cid string `json:"cid,omitempty"` URI string `json:"uri,omitempty"` Record *Record `json:"record,omitempty"` } type Embed struct { Type string `json:"$type,omitempty"` Media *Media `json:"media,omitempty"` Images *[]Images `json:"images,omitempty"` Video *Video `json:"video,omitempty"` Record *PostRecord `json:"record,omitempty"` External *External `json:"external,omitempty"` } type Values struct { Val string `json:"val,omitempty"` } type Labels struct { Type string `json:"$type,omitempty"` Values *[]Values `json:"values,omitempty"` } type Root struct { Cid string `json:"cid,omitempty"` URI string `json:"uri,omitempty"` } type Parent struct { Cid string `json:"cid,omitempty"` URI string `json:"uri,omitempty"` } type Reply struct { Root *Root `json:"root,omitempty"` Parent *Parent `json:"parent,omitempty"` } type Index struct { ByteEnd int `json:"byteEnd,omitempty"` ByteStart int `json:"byteStart,omitempty"` } type Features struct { Did string `json:"did,omitempty"` URI string `json:"uri,omitempty"` Tag string `json:"tag,omitempty"` Type string `json:"$type,omitempty"` } type Facets struct { Type string `json:"$type"` Index *Index `json:"index,omitempty"` Features *[]Features `json:"features,omitempty"` } type ParsedEmbeds struct { Type string MimeType string Ref string URI string Width int64 Height int64 } func (b *BSky) ParsePost(post []byte) (*Post, error) { var p = &Post{} err := json.Unmarshal(post, &p) if err != nil { return nil, err } return p, nil } func (post *Post) ProcessFacets() string { if post == nil { return "" } if post.Facets == nil { return post.Text } sort.Slice((*post.Facets), func(i, j int) bool { return (*post.Facets)[i].Index.ByteStart < (*post.Facets)[j].Index.ByteStart }) var result strings.Builder lastIndex := 0 // post.Text = html.EscapeString(post.Text) for _, facet := range *post.Facets { start := facet.Index.ByteStart end := facet.Index.ByteEnd result.WriteString(post.Text[lastIndex:start]) for _, feature := range *facet.Features { switch feature.Type { case "app.bsky.richtext.facet#mention": link := fmt.Sprintf(`%s`, feature.Did, post.Text[start:end]) result.WriteString(link) case "app.bsky.richtext.facet#link": link := fmt.Sprintf(`%s`, feature.URI, post.Text[start:end]) result.WriteString(link) case "app.bsky.richtext.facet#tag": link := fmt.Sprintf(`%s`, feature.Tag, post.Text[start:end]) result.WriteString(link) default: result.WriteString(post.Text[start:end]) } } lastIndex = end } result.WriteString(post.Text[lastIndex:]) return result.String() } func (p *Post) GetEmbeds() *[]ParsedEmbeds { var parsedEmbeds = &[]ParsedEmbeds{} if p.Embed != nil { if p.Embed.Video != nil { parsedEmbed := ParsedEmbeds{ URI: p.Embed.Video.Ref.Link, Type: "video", } *parsedEmbeds = append(*parsedEmbeds, parsedEmbed) } if p.Embed.External != nil { if strings.Contains(p.Embed.External.URI, "media.tenor.com") { parsedEmbed := ParsedEmbeds{ URI: p.Embed.External.URI, Type: "external", } *parsedEmbeds = append(*parsedEmbeds, parsedEmbed) } } if p.Embed.Media != nil { if p.Embed.Media.Images != nil { for _, image := range *p.Embed.Media.Images { parsedEmbed := ParsedEmbeds{ URI: image.Image.Ref.Link, Type: "image", } *parsedEmbeds = append(*parsedEmbeds, parsedEmbed) } } if p.Embed.Media.Video != nil { parsedEmbed := ParsedEmbeds{ URI: p.Embed.Media.Video.Ref.Link, Type: "video", } *parsedEmbeds = append(*parsedEmbeds, parsedEmbed) } if p.Embed.Media.External != nil { parsedEmbed := ParsedEmbeds{ URI: p.Embed.Media.External.URI, Type: "external", } *parsedEmbeds = append(*parsedEmbeds, parsedEmbed) } } if p.Embed.Images != nil { for _, image := range *p.Embed.Images { parsedEmbed := ParsedEmbeds{ URI: image.Image.Ref.Link, Type: "image", } *parsedEmbeds = append(*parsedEmbeds, parsedEmbed) } } } return parsedEmbeds } func (p *Post) GetMedia() *Media { if p.GetEmbeds() != nil { if p.Embed.Media != nil { return p.Embed.Media } } return nil } func (p *Post) GetMediaImages() *[]Images { if p.GetMedia() != nil { return p.GetMedia().Images } return nil } func (p *Post) GetExternal() *External { if p.GetMedia() != nil { if p.GetMedia().External != nil { return p.GetMedia().External } } return nil } func (p *Post) IsReply() bool { return p.Reply != nil } func (p *Post) IsQuotePost() bool { if p.Embed != nil { if p.Embed.Record != nil { return true } } return false }