Skip to content

Commit acb509d

Browse files
author
Jonny Johannes
committed
facet for links
1 parent 5f9af45 commit acb509d

2 files changed

Lines changed: 56 additions & 12 deletions

File tree

social_posters/bluesky.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -149,21 +149,35 @@ def format_post(self, pet):
149149
)
150150
def _build_text_and_facets(self, post: Post) -> tuple[str, list]:
151151
body = post.text
152-
facets = []
153-
154-
if not post.tags:
155-
return body[:300], facets
156-
157-
tag_strings = [f"#{tag}" for tag in post.tags if tag]
158-
tags_section = " ".join(tag_strings)
152+
facets: list = []
159153
separator = "\n\n"
154+
limit = 300
160155

161-
# Truncate body so the full text (body + separator + tags) fits in 300 chars.
162-
max_body = 300 - len(separator) - len(tags_section)
163-
full_text = f"{body[:max_body]}{separator}{tags_section}"
156+
tag_strings = [f"#{tag}" for tag in (post.tags or []) if tag]
157+
if tag_strings:
158+
tags_section = " ".join(tag_strings)
159+
# Truncate body so the full text (body + separator + tags) fits in limit chars.
160+
max_body = limit - len(separator) - len(tags_section)
161+
full_text = f"{body[:max_body]}{separator}{tags_section}"
162+
else:
163+
full_text = body[:limit]
164164

165-
# Compute byte offsets (AT Protocol facets use UTF-8 byte positions).
166165
encoded = full_text.encode("utf-8")
166+
167+
if post.link:
168+
link_bytes = post.link.encode("utf-8")
169+
link_idx = encoded.find(link_bytes)
170+
if link_idx != -1:
171+
facets.append({
172+
"index": {
173+
"byteStart": link_idx,
174+
"byteEnd": link_idx + len(link_bytes),
175+
},
176+
"features": [
177+
{"$type": "app.bsky.richtext.facet#link", "uri": post.link}
178+
],
179+
})
180+
167181
search_from = 0
168182
for tag_str in tag_strings:
169183
tag_bytes = tag_str.encode("utf-8")
@@ -175,5 +189,6 @@ def _build_text_and_facets(self, post: Post) -> tuple[str, list]:
175189
})
176190
search_from = idx + len(tag_bytes)
177191

192+
facets.sort(key=lambda f: f["index"]["byteStart"])
178193
return full_text, facets
179194

tests/test_bluesky.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,41 @@ class TestBuildTextAndFacets:
66
def setup_method(self):
77
self.poster = PosterBluesky.__new__(PosterBluesky)
88

9-
def test_no_tags_produces_no_facets(self):
9+
def test_no_tags_no_link_produces_no_facets(self):
1010
post = Post(text="Hello, world!")
1111
text, facets = self.poster._build_text_and_facets(post)
1212
assert text == "Hello, world!"
1313
assert facets == []
1414

15+
def test_link_without_tags_produces_link_facet(self):
16+
url = "https://example.com/adopt"
17+
post = Post(text=f"Meet Poppy!\n\n{url}", link=url)
18+
text, facets = self.poster._build_text_and_facets(post)
19+
20+
assert text == post.text
21+
assert len(facets) == 1
22+
enc = text.encode("utf-8")
23+
f = facets[0]
24+
assert enc[f["index"]["byteStart"] : f["index"]["byteEnd"]] == url.encode("utf-8")
25+
assert f["features"][0]["$type"] == "app.bsky.richtext.facet#link"
26+
assert f["features"][0]["uri"] == url
27+
28+
def test_link_and_tags_facets_sorted_by_byte_start(self):
29+
url = "https://rg.org/pet/1"
30+
post = Post(
31+
text=f"Adopt me!\n\n{url}",
32+
tags=["AdoptDontShop", "Boston", "DogsOfBluesky"],
33+
link=url,
34+
)
35+
text, facets = self.poster._build_text_and_facets(post)
36+
37+
assert text.endswith("\n\n#AdoptDontShop #Boston #DogsOfBluesky")
38+
assert len(facets) == 4
39+
assert facets[0]["features"][0]["$type"] == "app.bsky.richtext.facet#link"
40+
assert facets[0]["features"][0]["uri"] == url
41+
for facet in facets[1:]:
42+
assert facet["features"][0]["$type"] == "app.bsky.richtext.facet#tag"
43+
1544
def test_tags_produce_facets_with_correct_byte_offsets(self):
1645
post = Post(text="Adopt me!", tags=["AdoptDontShop", "Boston", "DogsOfBluesky"])
1746
text, facets = self.poster._build_text_and_facets(post)

0 commit comments

Comments
 (0)