Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'main' of github.com:bluesky-social/social-app into main

+186 -11
+57
bskyweb/cmd/bskyweb/formating.go
··· 1 + package main 2 + 3 + import ( 4 + "fmt" 5 + "slices" 6 + "strings" 7 + 8 + appbsky "github.com/bluesky-social/indigo/api/bsky" 9 + ) 10 + 11 + // Function to expand shortened links in rich text back to full urls, replacing shortened urls in social card meta tags and the noscript output. 12 + // 13 + // This essentially reverses the effect of the typescript function `shortenLinks()` in `src/lib/strings/rich-text-manip.ts` 14 + func ExpandPostText(post *appbsky.FeedPost) string { 15 + postText := post.Text 16 + var charsAdded int = 0 17 + // iterate over facets, check if they're link facets, and if found, grab the uri 18 + for _, facet := range post.Facets { 19 + linkUri := "" 20 + if slices.ContainsFunc(facet.Features, func(feat *appbsky.RichtextFacet_Features_Elem) bool { 21 + if feat.RichtextFacet_Link == nil || feat.RichtextFacet_Link.LexiconTypeID != "app.bsky.richtext.facet#link" { 22 + return false 23 + } 24 + 25 + // bail out if bounds checks fail 26 + if int(facet.Index.ByteStart)+charsAdded > len(postText) || int(facet.Index.ByteEnd)+charsAdded > len(postText) { 27 + return false 28 + } 29 + linkText := postText[int(facet.Index.ByteStart)+charsAdded : int(facet.Index.ByteEnd)+charsAdded] 30 + linkUri = feat.RichtextFacet_Link.Uri 31 + 32 + // only expand uris that have been shortened (as opposed to those with non-uri anchor text) 33 + if strings.HasSuffix(linkText, "...") && strings.Contains(linkUri, linkText[0:len(linkText)-3]) { 34 + return true 35 + } 36 + return false 37 + }) { 38 + // replace the shortened uri with the full length one from the facet using utf8 byte offsets 39 + // NOTE: we already did bounds check above 40 + postText = postText[0:int(facet.Index.ByteStart)+charsAdded] + linkUri + postText[int(facet.Index.ByteEnd)+charsAdded:] 41 + charsAdded += len(linkUri) - int(facet.Index.ByteEnd-facet.Index.ByteStart) 42 + } 43 + } 44 + // if the post has an embeded link and its url doesn't already appear in postText, append it to 45 + // the end to avoid social cards with missing links 46 + if post.Embed != nil && post.Embed.EmbedExternal != nil && post.Embed.EmbedExternal.External != nil { 47 + externalURI := post.Embed.EmbedExternal.External.Uri 48 + if !strings.Contains(postText, externalURI) { 49 + postText = fmt.Sprintf("%s\n%s", postText, externalURI) 50 + } 51 + } 52 + // TODO: could embed the actual post text? 53 + if post.Embed != nil && (post.Embed.EmbedRecord != nil || post.Embed.EmbedRecordWithMedia != nil) { 54 + postText = fmt.Sprintf("%s\n\n[contains quote post or other embeded content]", postText) 55 + } 56 + return postText 57 + }
+39
bskyweb/cmd/bskyweb/formatting_test.go
··· 1 + package main 2 + 3 + import ( 4 + "encoding/json" 5 + "io" 6 + "os" 7 + "strings" 8 + "testing" 9 + 10 + appbsky "github.com/bluesky-social/indigo/api/bsky" 11 + ) 12 + 13 + func loadPost(t *testing.T, p string) appbsky.FeedPost { 14 + 15 + f, err := os.Open(p) 16 + if err != nil { 17 + t.Fatal(err) 18 + } 19 + defer func() { _ = f.Close() }() 20 + 21 + postBytes, err := io.ReadAll(f) 22 + if err != nil { 23 + t.Fatal(err) 24 + } 25 + var post appbsky.FeedPost 26 + if err := json.Unmarshal(postBytes, &post); err != nil { 27 + t.Fatal(err) 28 + } 29 + return post 30 + } 31 + 32 + func TestExpandPostText(t *testing.T) { 33 + post := loadPost(t, "testdata/atproto_embed_post.json") 34 + 35 + text := ExpandPostText(&post) 36 + if !strings.Contains(text, "https://github.com/snarfed/bridgy-fed") { 37 + t.Fail() 38 + } 39 + }
+5 -2
bskyweb/cmd/bskyweb/rss.go
··· 96 96 if err != nil { 97 97 return err 98 98 } 99 - rec := p.Post.Record.Val.(*appbsky.FeedPost) 99 + rec, ok := p.Post.Record.Val.(*appbsky.FeedPost) 100 + if !ok { 101 + continue 102 + } 100 103 // only top-level posts in RSS (no replies) 101 104 if rec.Reply != nil { 102 105 continue ··· 108 111 } 109 112 posts = append(posts, Item{ 110 113 Link: fmt.Sprintf("https://%s/profile/%s/post/%s", req.Host, pv.Handle, aturi.RecordKey().String()), 111 - Description: rec.Text, 114 + Description: ExpandPostText(rec), 112 115 PubDate: pubDate, 113 116 GUID: ItemGUID{ 114 117 Value: aturi.String(),
+21 -5
bskyweb/cmd/bskyweb/server.go
··· 336 336 postView := tpv.Thread.FeedDefs_ThreadViewPost.Post 337 337 data["postView"] = postView 338 338 data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path) 339 - if postView.Embed != nil && postView.Embed.EmbedImages_View != nil { 340 - var thumbUrls []string 341 - for i := range postView.Embed.EmbedImages_View.Images { 342 - thumbUrls = append(thumbUrls, postView.Embed.EmbedImages_View.Images[i].Thumb) 339 + if postView.Embed != nil { 340 + if postView.Embed.EmbedImages_View != nil { 341 + var thumbUrls []string 342 + for i := range postView.Embed.EmbedImages_View.Images { 343 + thumbUrls = append(thumbUrls, postView.Embed.EmbedImages_View.Images[i].Thumb) 344 + } 345 + data["imgThumbUrls"] = thumbUrls 346 + } else if postView.Embed.EmbedRecordWithMedia_View != nil && postView.Embed.EmbedRecordWithMedia_View.Media != nil && postView.Embed.EmbedRecordWithMedia_View.Media.EmbedImages_View != nil { 347 + var thumbUrls []string 348 + for i := range postView.Embed.EmbedRecordWithMedia_View.Media.EmbedImages_View.Images { 349 + thumbUrls = append(thumbUrls, postView.Embed.EmbedRecordWithMedia_View.Media.EmbedImages_View.Images[i].Thumb) 350 + } 351 + data["imgThumbUrls"] = thumbUrls 343 352 } 344 - data["imgThumbUrls"] = thumbUrls 353 + } 354 + 355 + if postView.Record != nil { 356 + postRecord, ok := postView.Record.Val.(*appbsky.FeedPost) 357 + if ok { 358 + data["postText"] = ExpandPostText(postRecord) 359 + } 345 360 } 361 + 346 362 return c.Render(http.StatusOK, "post.html", data) 347 363 } 348 364
+60
bskyweb/cmd/bskyweb/testdata/atproto_embed_post.json
··· 1 + { 2 + "$type": "app.bsky.feed.post", 3 + "createdAt": "2023-12-04T19:30:03.024Z", 4 + "embed": { 5 + "$type": "app.bsky.embed.external", 6 + "external": { 7 + "description": "🕸 Bridges the IndieWeb to Mastodon and the fediverse via ActivityPub. - GitHub - snarfed/bridgy-fed: 🕸 Bridges the IndieWeb to Mastodon and the fediverse via ActivityPub.", 8 + "thumb": { 9 + "$type": "blob", 10 + "ref": { 11 + "$link": "bafkreidplhjcnrl2c74r3xs7nh7k7q3ny6ul7cgxr2fophblvdeky6t64e" 12 + }, 13 + "mimeType": "image/jpeg", 14 + "size": 347998 15 + }, 16 + "title": "GitHub - snarfed/bridgy-fed: 🕸 Bridges the IndieWeb to Mastodon and the fediverse via ActivityPub...", 17 + "uri": "https://github.com/snarfed/bridgy-fed" 18 + } 19 + }, 20 + "facets": [ 21 + { 22 + "features": [ 23 + { 24 + "$type": "app.bsky.richtext.facet#link", 25 + "uri": "https://github.com/snarfed/bridgy-fed" 26 + } 27 + ], 28 + "index": { 29 + "byteEnd": 92, 30 + "byteStart": 66 31 + } 32 + }, 33 + { 34 + "features": [ 35 + { 36 + "$type": "app.bsky.richtext.facet#mention", 37 + "did": "did:plc:fdme4gb7mu7zrie7peay7tst" 38 + } 39 + ], 40 + "index": { 41 + "byteEnd": 149, 42 + "byteStart": 137 43 + } 44 + } 45 + ], 46 + "langs": [ 47 + "en" 48 + ], 49 + "reply": { 50 + "parent": { 51 + "cid": "bafyreifaidyl62p4snkdwsygviemsxyidi3cd7dxvjomh5644sovxhsppa", 52 + "uri": "at://did:plc:ewvi7nxzyoun6zhxrhs64oiz/app.bsky.feed.post/3kfqklhpalh2c" 53 + }, 54 + "root": { 55 + "cid": "bafyreibiimdwmsp5mqpm7utqcdmvo6fdqmofblp5obs3h7ub6652zyooci", 56 + "uri": "at://did:plc:ewvi7nxzyoun6zhxrhs64oiz/app.bsky.feed.post/3kfqkkjdkic2e" 57 + } 58 + }, 59 + "text": "Bridgy Fed is an open-source project — check out the code here: github.com/snarfed/brid...\n\nStay updated with the project by following @snarfed.org!" 60 + }
+4 -4
bskyweb/templates/post.html
··· 21 21 {% else %} 22 22 <meta property="og:title" content="@{{ postView.Author.Handle }}"> 23 23 {% endif -%} 24 - {%- if postView.Record.Val.Text %} 25 - <meta name="description" content="{{ postView.Record.Val.Text }}"> 26 - <meta property="og:description" content="{{ postView.Record.Val.Text }}"> 24 + {%- if postText %} 25 + <meta name="description" content="{{ postText }}"> 26 + <meta property="og:description" content="{{ postText }}"> 27 27 {% endif -%} 28 28 {%- if imgThumbUrls %} 29 29 {% for imgThumbUrl in imgThumbUrls %} ··· 47 47 <p id="bsky_display_name">{{ postView.Author.DisplayName }}</p> 48 48 <p id="bsky_handle">{{ postView.Author.Handle }}</p> 49 49 <p id="bsky_did">{{ postView.Author.Did }}</p> 50 - <p id="bsky_post_text">{{ postView.Record.Val.Text }}</p> 50 + <p id="bsky_post_text">{{ postText }}</p> 51 51 <p id="bsky_post_indexedat">{{ postView.IndexedAt }}</p> 52 52 </div> 53 53 {% endif -%}