14.7.3. Demo: Display Real Bluesky Comments and Replies#
Choose Social Media Platform: Reddit | Discord | Bluesky | No Coding
Now lets do the same thing we did on the last page (using recursion to display comments and replies), but do it on Bluesky! (Either for real or faked with the fake_atproto library).
Normal Bluesky Setup#
We’ll start by doing our normal steps including these helper functions:
helper function for atproto links#
NOTE: You don’t need to worry about the details of how this works, it just is here to make the code later easier to use.
import re #load a "regular expression" library for helping to parse text
from atproto import IdResolver # Load the atproto IdResolver library to get offical ATProto user IDs
def get_at_post_link_from_url(url):
# Initialize and log in with the client
# Extract username and post ID from the URL
match = re.search(r'https://bsky.app/profile/([^/]+)/post/([^/]+)', url)
if not match:
raise ValueError("Invalid Bluesky post URL format.")
user_handle, post_id = match.groups()
# Construct the at:// URI
post_uri = f"at://{user_handle}/app.bsky.feed.post/{post_id}"
return post_uri
# function to convert a feed from a weblink url to the special atproto "at" URI
def getATFeedLinkFromURL(url):
# Get the user did and feed id from the weblink url
match = re.search(r'https://bsky.app/profile/([^/]+)/feed/([^/]+)', url)
if not match:
raise ValueError("Invalid Bluesky feed URL format.")
user_handle, feed_id = match.groups()
# Get the official atproto user ID (did) from the handle
resolver = IdResolver()
did = resolver.handle.resolve(user_handle)
if not did:
raise ValueError(f'Could not resolve DID for handle "{user_handle}".')
# Construct the at:// URI
post_uri = f"at://{did}/app.bsky.feed.generator/{feed_id}"
Now we can continue logging in to Bluesky and look through multiple posts.
load atproto library#
# Load some code called "Client" from the "atproto" library that will help us work with Bluesky
from atproto import Client
(optional) make a fake Bluesky connection with the fake_atproto library#
For testing purposes, we”ve added this line of code, which loads a fake version of atproto, so it wont actually connect to Bluesky. If you want to try to actually connect to Bluesky, don’t run this line of code.
%run ../../fake_apis/fake_atproto.ipynb
login to Bluesky#
# Login to Bluesky
# TODO: put your account name and password below
client = Client(base_url="https://bsky.social")
client.login("your_account_name.bsky.social", "m#5@_fake_bsky_password_$%Ds")
Helper function to display text in an indented box#
(You don’t need to worry about how this works. This is that function that helps display posts in indented boxes)
from IPython.display import HTML, Image, display
import html
def display_indented(text, left_margin=0):
display(
HTML(
"<pre style='border:solid 1px;padding:3px;margin-top:3px;margin-bottom:3px;margin-left:"+ str(left_margin) + "px'>" +
html.escape(text) +
"</pre>"
)
)
Code to print a post with replies#
The print_post_thread
is a function that takes a Bluesky Post weblink (url) (instructions on where to get one below), downloads the thread that follows that post, and then uses the print_post_and_replies
function to print out that post and the replies to that post.
def print_post_thread(postUrl):
at_post_link = get_at_post_link_from_url(postUrl)
# Fetch the post details
post_data = client.get_post_thread(at_post_link)
print_post_and_replies(post_data.thread)
The print_post_and_replies
function takes a given post and recursively prints that post as well as all replies to that post (which will also print all the replies to those replies, etc.)
def print_post_and_replies(postInfo, num_indents=0):
# make sure this post isn't blocked (since we can't read blocked posts)
if not (hasattr(postInfo,'blocked') and postInfo.blocked):
post = postInfo.post
replies = postInfo.replies
# If replies is None, make it an empty array (so the for loop later doesn't crash)
if not replies:
replies = []
display_text = (
post.record.text + "\n" +
"-- " + str(post.author.display_name) + " (" + str(post.author.handle) + ")\n" +
" (likes: " + str(post.like_count) +
", replies: " + str(post.reply_count) +
", reposts: " + str(post.repost_count) +
", quotes: " + str(post.quote_count) + ") - "
)
display_indented(display_text, num_indents*20)
#print replies (and the replies of those, etc.)
for reply in replies:
print_post_and_replies(reply, num_indents = num_indents + 1)
Finding post IDs and testing our code#
In order to test it out, we need to find a link to a Bluesky post with replies. Once you find the post, find the ‘Copy Link to Post’ option to get a web url for the post.
It should be something like: https://bsky.app/profile/realgdt.bsky.social/post/3lihunicmds2y
Now we can test it out by calling the print_post_thread
, passing it the url as a string. Then you should see the comment tree.
print_post_thread('https://bsky.app/profile/realgdt.bsky.social/post/3lihunicmds2y')
This is a fake fact about movie costuming and I find it so interesting! -- Imaginary User (imaginary_user.bsky.social) (likes: 25, replies: 2, reposts: 13, quotes: 7) -
Wow! That is a cool fake fact! -- Fake User (fake_user.bsky.social) (likes: 6, replies: 2, reposts: 5, quotes: 1) -
You're totally right! I never saw that before! -- False User (false_user.bsky.social) (likes: 3, replies: 1, reposts: 0, quotes: 0) -
Yeah, and did you see this other detail too? -- Fake User (fake_user.bsky.social) (likes: 7, replies: 1, reposts: 4, quotes: 10) -
Wow! No way! -- False User (false_user.bsky.social) (likes: 4, replies: 0, reposts: 0, quotes: 0) -
It's not cool! You are a bad person for saying it's cool! -- Troll User (troll_user.bsky.social) (likes: 0, replies: 0, reposts: 0, quotes: 2) -
I saw a completely unrelated movie once and I liked it! -- Pretend User (pretend_user.bsky.social) (likes: 1, replies: 1, reposts: 0, quotes: 0) -
I don't see how that's relevant -- Fake User (fake_user.bsky.social) (likes: 2, replies: 0, reposts: 1, quotes: 1) -