def replace_facets_with_markdown(text, facets): if not facets: return text # We need to work with bytes since ATProto facets use UTF-8 byte offsets text_bytes = text.encode('utf-8') # Sort facets by byteStart, descending so we can replace from back to front # to avoid messing up the offsets for earlier facets # Actually, one facet might have multiple features, but usually it's just one Link. # Let's extract all links with their offsets. links = [] for facet in facets: for feature in getattr(facet, 'features', []): if hasattr(feature, 'uri'): links.append({ 'start': facet.index.byte_start, 'end': facet.index.byte_end, 'uri': feature.uri }) links.sort(key=lambda x: x['start'], reverse=True) for link in links: start = link['start'] end = link['end'] uri = link['uri'] # Original text for the link link_text = text_bytes[start:end].decode('utf-8') # Replace with markdown link markdown_link = f"[{link_text}]({uri})".encode('utf-8') text_bytes = text_bytes[:start] + markdown_link + text_bytes[end:] return text_bytes.decode('utf-8') # test it import sys from src.void_tools.client import get_client client = get_client() response = client.get_posts(['at://did:plc:gfrmhdmjvxn2sjedzboeudef/app.bsky.feed.post/3mgtyb5h5fc25']) for post in response.posts: text = post.record.text facets = getattr(post.record, 'facets', []) print(replace_facets_with_markdown(text, facets))