
<!-- README.md is generated from README.Rmd. Please edit that file -->

# atrrr <img src="man/figures/logo.png" align="right" height="120">

<!-- badges: start -->

[![CRAN
status](https://www.r-pkg.org/badges/version/atrrr)](https://CRAN.R-project.org/package=atrrr)
[![CRAN_Download_Badge](https://cranlogs.r-pkg.org/badges/grand-total/atrrr)](https://cran.r-project.org/package=atrrr)
[![R-CMD-check](https://github.com/JBGruber/atrrr/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/JBGruber/atrrr/actions/workflows/R-CMD-check.yaml)
[![Codecov test
coverage](https://codecov.io/gh/JBGruber/atrrr/branch/main/graph/badge.svg)](https://app.codecov.io/gh/JBGruber/atrrr?branch=main)
<!-- badges: end -->

The goal of atrrr[^1] is to wrap the AT Protocol (Authenticated Transfer
Protocol) behind Bluesky. [*And we have actually already fulfilled this
goal!*](#want-to-help).

The entire protocol is open and documented in so-called
[lexicons](https://atproto.com/guides/lexicon), from which we
autogenerated `R` functions.

These are not exported, however, since dealing with them is a bit
advanced. Rather we have some nice human-generated functions with
documentation and examples.

## Installation

You can install atrrr from CRAN with:

``` r
install.packages("atrrr")
```

You can install the development version of `atrrr` like so (install the
`remotes` package first, with `install.packages("remotes")`, if you
don’t have that yet):

``` r
# install.packages("remotes")
remotes::install_github("JBGruber/atrrr")
```

## Load the package

``` r
library(atrrr)
```

## Authentication

The first time you make a request, you will be prompted automatically to
enter your user handle and an app password to authenticate `atrrr` to
communicate with BlueSky for you.

<figure>
<img src="vignettes/figures/password_popup.png" alt="RStudio Popup" />
<figcaption aria-hidden="true">RStudio Popup</figcaption>
</figure>

The page to generate app passwords is also automatically opened for you.

<figure>
<img src="vignettes/figures/app_password.png"
alt="page to create new app passwords" />
<figcaption aria-hidden="true">page to create new app
passwords</figcaption>
</figure>

However, you can also trigger this process manually:

``` r
auth("jbgruber.bsky.social")
```

This can be useful if you want to replace an old token as it is
permanently stored encrypted on disk.

## Retrieve Skeets (`get_skeets_authored_by`)

To fetch all the skeets by a specific user, use the
`get_skeets_authored_by` function. *Note this also includes quote skeets
and reskeets.* You can also opt not to parse the result by setting
`parse = FALSE`, however it is recommended to use the default parse
option which results in a (more) tidy tibble.

``` r
get_skeets_authored_by(actor = "benguinaudeau.bsky.social", parse = TRUE) |>
  dplyr::glimpse()
#> Rows: 25
#> Columns: 21
#> $ uri           <chr> "at://did:plc:ntd53albt5ffa4rgervvgibd/app.bsky.feed.pos…
#> $ cid           <chr> "bafyreiconry2rc74zkunyalbhwsxa347gxjnhb7uza7y4njnecu3ek…
#> $ author_handle <chr> "jbgruber.bsky.social", "jacobmontgomery.bsky.social", "…
#> $ author_name   <chr> "Johannes B. Gruber", "Jacob Montgomery", "Beatrice Magi…
#> $ text          <chr> "I didn't even notice CRAN already approved it, but our …
#> $ author_data   <list> ["did:plc:ntd53albt5ffa4rgervvgibd", "jbgruber.bsky.soc…
#> $ post_data     <list> ["app.bsky.feed.post", "2024-10-04T12:43:04.752Z", ["ap…
#> $ embed_data    <list> ["app.bsky.embed.record#view", ["app.bsky.embed.record#…
#> $ reply_count   <int> 0, 0, 0, 1, 4, 0, 0, 4, 0, 1, 9, 0, 1, 0, 1, 0, 0, 0, 0,…
#> $ repost_count  <int> 5, 3, 6, 6, 8, 0, 0, 15, 2, 1, 432, 3, 1, 5, 28, 0, 0, 0…
#> $ like_count    <int> 11, 13, 13, 20, 13, 1, 1, 42, 6, 1, 606, 7, 3, 10, 35, 1…
#> $ indexed_at    <dttm> 2024-10-04 12:43:04, 2024-03-18 21:09:28, 2024-02-16 17…
#> $ in_reply_to   <chr> NA, NA, NA, NA, NA, NA, "at://did:plc:eotrvt2wp6mqooxjf3…
#> $ in_reply_root <chr> NA, NA, NA, NA, NA, NA, "at://did:plc:eotrvt2wp6mqooxjf3…
#> $ quotes        <chr> "at://did:plc:vgvueqvmbqgoyxtcdebqdcgb/app.bsky.feed.pos…
#> $ tags          <list> "rstats", <NULL>, <NULL>, "rstats", "rstats", <NULL>, <…
#> $ mentions      <list> <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>,…
#> $ links         <list> <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>,…
#> $ langs         <list> ["en"], ["en"], ["it"], ["en"], ["en"], ["en"], ["en"],…
#> $ labels        <list> [], [], [], [], [], [], [], [], [], [], [], [], [], [],…
#> $ is_reskeet    <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, …
```

## Analyzing Feeds on Blue Sky

On Blue Sky users have the ability to create custom feeds based on
specific keywords. These feeds aggregate content, for instance, a user
might curate a feed around the hashtag `#rstats` to gather all relevant
content about. Let’s delve into the dynamics of such feeds.

Our starting point is to extract the posts from the `#rstats` feed
created by “andrew.heiss.phd”.

``` r
# Fetching the feed posts
feeds <- get_feeds_created_by(actor = "andrew.heiss.phd") 

# Filtering for a specific keyword, for example "#rstats"
rstat_feed <- feeds |>
  filter(displayName == "#rstats")

# Extracting posts from this curated feed
rstat_posts <- get_feed(rstat_feed$uri, limit = 200) |>
  dplyr::glimpse()
#> Rows: 292
#> Columns: 20
#> $ uri           <chr> "at://did:plc:7crs3axm67jhrc57gi7ojyjn/app.bsky.feed.pos…
#> $ cid           <chr> "bafyreicru2iryln3mqm6skdhym4illw2kn6z7qqig34trfvsp72zyi…
#> $ author_handle <chr> "timbulwidodostp.bsky.social", "bigbookofr.bsky.social",…
#> $ author_name   <chr> "Timbul Widodo, S.TP", "Big Book of R", "Craig Hamilton"…
#> $ text          <chr> "Sequential analysis poisson binomial data g estimation …
#> $ author_data   <list> ["did:plc:7crs3axm67jhrc57gi7ojyjn", "timbulwidodostp.b…
#> $ post_data     <list> ["app.bsky.feed.post", "2024-11-16T13:08:30.723Z", [[[[…
#> $ embed_data    <list> <NULL>, <NULL>, <NULL>, ["app.bsky.embed.record#view", …
#> $ reply_count   <int> 0, 0, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 0, 3, 1, 0, 0, 0, 0,…
#> $ repost_count  <int> 0, 0, 0, 3, 0, 19, 0, 1, 0, 2, 1, 4, 0, 11, 0, 0, 1, 0, …
#> $ like_count    <int> 0, 0, 0, 3, 1, 37, 0, 3, 1, 4, 1, 14, 1, 21, 1, 1, 4, 5,…
#> $ indexed_at    <dttm> 2024-11-16 13:08:30, 2024-11-16 12:31:26, 2024-11-16 12…
#> $ in_reply_to   <chr> NA, NA, "at://did:plc:ntd53albt5ffa4rgervvgibd/app.bsky.…
#> $ in_reply_root <chr> NA, NA, "at://did:plc:ntd53albt5ffa4rgervvgibd/app.bsky.…
#> $ quotes        <chr> NA, NA, NA, "at://did:plc:ntd53albt5ffa4rgervvgibd/app.b…
#> $ tags          <list> <"RStats", "rstats", "rsoftware", "rstatistics">, <NULL…
#> $ mentions      <list> <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>,…
#> $ links         <list> <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>, <NULL>,…
#> $ langs         <list> ["en"], <NULL>, ["en"], ["en"], ["en"], ["en"], <NULL>,…
#> $ labels        <list> [], [], [], [], [], [], [], [], [], [], [], [], [], [],…
```

## Learn More?

Start with the [Basic
Usage](https://jbgruber.github.io/atrrr/articles/Basic_Usage.html)
vignette to learn more.

# Want to help?

You can help by creating an
[issue](https://github.com/JBGruber/atrrr/issues/new/choose) requesting
new features or reporting bugs.

If you are a developer, we are happy to accept pull requests. It should
be fairly straightforward, as all endpoints are already covered by
automatically generated function. For example, the endpoint
[app.bsky.actor.getProfiles](https://docs.bsky.app/docs/api/app-bsky-actor-get-profiles)
is accessible via `atrrr:::app_bsky_actor_get_profiles()`. The function
`get_user_info()` is just a thin wrapper around that and calls an
optional parsing function:

    get_user_info <- function(actor,
                              parse = TRUE,
                              .token = NULL) {

      # we need to use do.call so objects are passed to the right environment
      res <- do.call( 
        what = app_bsky_actor_get_profiles,
        args = list(
          actor,
          .token = .token, # tokens are handled automatically under the hood
          .return = "json"
        )) |>
        purrr::pluck("profiles")

      if (parse) {
        res <- parse_actors(res)
      }
      return(res)
    }

If you find an endpoint at
<https://docs.bsky.app/docs/category/http-reference> that interests you,
you can write a similar wrapper and contribute it to the package (or
build something new on top of it). But please open an
[issue](https://github.com/JBGruber/atrrr/issues) first, so we don’t do
duplicated work.

[^1]: before 2024-01-04, this package was [called
    `atr`](https://github.com/JBGruber/atrrr/issues/12), meaning an R
    package for the AT protocol (similar to httr, which is a package for
    the HTTProtocol). Unfortunatley, when we wanted to release the
    package on CRAN, the name `atr` was rejected, as a package of the
    same name existed some time ago. So we added two “r” to make the
    package go brrr anyway!
