Poor Dude’s Janky Bluesky Feed Reader CLI Via R & Python
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Lynn (of TITAA and general NLP wizardy fame) was gracious enough to lend me a Bluesky invite, so I could claim my handle on yet-another social media site. I’m still wary of it (as noted in one of this week’s Drops), but the AT protocol — whilst super (lacking a better word) “verbose” — is pretty usable, especially thanks to Ilya Siamionau’s atproto AT Protocol SDK for Python.
Longtime readers know I am most certainly not going to use Python directly, as such practice has been found to cause early onset dementia. But, that module is so well done that I’ll gladly use it from within R.
I whipped up a small R script CLI that will fetch my feed and display it via the terminal. While I also use the web app and the Raycast extension to read the feed, it’s a billion degrees outside, so used the need to stay indoors as an excuse to add this third way of checking what’s new.
Store your handle and app-password in BSKY_USER
and BSKY_KEY
, respectively, adjust the shebang accordingly, add execute permissions to the file and , you can do the same.
#!/usr/local/bin/Rscript suppressPackageStartupMessages({ library(reticulate, quietly = TRUE, warn.conflicts = FALSE) library(lubridate, include.only = c("as.period", "interval"), quietly = TRUE, warn.conflicts = FALSE) library(crayon, quietly = TRUE, warn.conflicts = FALSE) }) # Get where {reticlulate} thinks your python is via py_config()$python # then use the full path to # /full/path/to/python3 -m pip install atproto atproto <- import("atproto") client <- atproto$Client() profile <- client$login(Sys.getenv("BSKY_USER"), Sys.getenv("BSKY_KEY")) res <- client$bsky$feed$get_timeline(list(algorithm = "reverse-chronological")) for (item in rev(res$feed)) ( cat( blue(item$post$author$displayName), " • ", silver(gsub("\\.[[:digit:]]+", "", tolower(as.character(as.period(interval(item$post$record$createdAt, Sys.time()))))), "ago\n"), italic(paste0(strwrap(item$post$record$text, 50), collapse="\n")), "\n", ifelse( hasName(item$post$record$embed, "images"), sprintf( green("[%s IMAGE%s]\n"), length(item$post$record$embed$images), ifelse(length(item$post$record$embed$images) > 1, "s", "") ), "" ), ifelse( hasName(item$post$record$embed, "external"), yellow(sprintf( "\n%s\n │\n%s\n\n", bold(paste0(strwrap(item$post$embed$external$title, 47, prefix = " │"), collapse = "\n")), italic(paste0(strwrap(item$post$embed$external$description, 47, prefix = " │"), collapse = "\n")) )), "" ), "\n", sep = "" ) )
This is a sample of the output, showing how it handles embeds and images:
FIN
There’s tons of room for improvement in this hastily-crafted bit of code, and I’ll get it up on GitLab once their servers come back to life.
If you want to experience Bluesky but have no account, the firehose — which Elon charges $40K/month for on the birdsite — is free and can be accessed sans authentication:
library(reticulate) atproto <- import("atproto") hose <- atproto$firehose$FirehoseSubscribeReposClient() handler <- \(msg) { res <- atproto$firehose$parse_subscribe_repos_message(msg) print(res) # you need to do a bit more than this to get the actual commit type and contents } hose$start(handler)
You can find me over on bsky at @hrbrmstr.dev.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.