R/tweet.R

Defines functions check_length tweet_collapse tweet_splitter auth_rtweet tweet_photo

Documented in auth_rtweet tweet_collapse tweet_photo

#' Tweet a photo out
#'
#' @param photo_df a dataframe with information about the photo to be tweeted
#' @param path the path to the `phototweetr` directory (the one above "orig"
#' typically)
#' @param ... arguments passed to `rtweet::post_tweet`
#'
#' @return the `photo_df`, updated with tweet time, etc.
#' @export
tweet_photo <- function(photo_df, path = NULL, ...) {
  message(glue("Attempting to tweet photo {photo_df$tweet_file}"))
  if (!is.null(path)) {
    image_path <- file.path(path, photo_df$tweet_file)
  } else {
    image_path <- photo_df$tweet_file
  }

  tweet_info <- unlist(photo_df[,c("title", "comment", "tags", "exposure")])
  tweet_text <- tweet_splitter(tweet_info)
  image_alt <- photo_df$alt_text

  if (length(tweet_text) == 1) {
    tweeted_text <- tweet_collapse(tweet_text)
    tweet_response <- rtweet::post_tweet(status = tweeted_text, media = image_path, ..., media_alt_text = image_alt)
  } else {
    text <- tweet_collapse(tweet_text[[1]])
    tweet_response <- rtweet::post_tweet(status = text, media = image_path, ..., media_alt_text = image_alt)
    reply_id <- httr::content(tweet_response)$id_str

    # Iterate through each reply after the first
    for (tweet in tweet_text[-1]) {
      tweet_response <- rtweet::post_tweet(
        status = tweet_collapse(tweet),
        in_reply_to_status_id = reply_id,
        auto_populate_reply_metadata = TRUE,
        ...
      )
      reply_id <- httr::content(tweet_response)$id_str
    }
    tweeted_text <- paste0(lapply(tweet_text, tweet_collapse), collapse = "\\\\")
  }

  if (httr::status_code(tweet_response) == 200L) {
    photo_df$tweeted <- TRUE
    photo_df$tweeted_text <- tweeted_text
    photo_df$date_tweeted <- Sys.time()
  } else {
    photo_df$tweet_error <- httr::content(tweet_response)
  }

  return(photo_df)
}

#' Ensure that `rtweet` is authenticated
#'
#' This will call `rtweet::create_token()`. If a token is being created, the
#' following environment variables are used/needed:
#'
#' * `rtweet_api_key`
#' * `rtweet_api_secret_key`
#' * `rtweet_access_token`
#' * `rtweet_access_token_secret`
#'
#' @export
auth_rtweet <- function() {
  return(rtweet::rtweet_bot(
    api_key = Sys.getenv("rtweet_api_key"),
    api_secret = Sys.getenv("rtweet_api_secret_key"),
    access_token = Sys.getenv("rtweet_access_token"),
    access_secret = Sys.getenv("rtweet_access_token_secret")
  ))
}

twitter_max <- 280
tweet_breaker <- "\\"

tweet_splitter <- function(text) {
  # TODO: how to count emojis as 2 characters, CJK as 2 characters, etc.
  # https://developer.twitter.com/en/docs/basics/counting-characters
  # currently this uses utf8::utf8_width, which is close, but over counts
  # combining emojis

  # if the text all together is within one tweet, we're done. But ensure that
  # the order is title comment exposure tags
  if (check_length(text)) {
    return(list(text[c("title", "comment", "exposure", "tags")]))
  }

  # first, try to put the photo settings in the next tweet
  # the settings should always be under one tweet, so no need to check those
  if (check_length(text[c("title", "exposure", "tags")]) &
      check_length(text["comment"])) {
    return(list(text[c("title", "exposure", "tags")], text["comment"]))
  }

  # finally, start with the title tags and then comment, and split them in to
  # chunks that are under the limit the tags are second here so the first tweet
  # has at least some tags
  chunks <- as.list(c(
    chunker(text[c("title", "tags")]),
    chunker(text[c("comment")]),
    text["exposure"]
  ))

  return(chunks)
}

#' Collapse tweet text
#'
#' @param text text to flatten
#'
#' @return the text flattened with new lines
#' @export
tweet_collapse <- function(text) {
  text <- unlist(text)
  text <- Filter(Negate(is.na), text)

  return(glue::glue_collapse(text, sep = "\n"))
}

check_length <- function(input, max = twitter_max) {
  input <- tweet_collapse(input)

  n_char <- sum(utf8::utf8_width(as.character(input)), na.rm = TRUE)

  return(n_char <= twitter_max)
}
jonkeane/phototweetr documentation built on Jan. 10, 2023, 1:21 p.m.