From deb1eb6a0c7c8ee952a1b622109a3d0b84a4e9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Sun, 8 Oct 2023 12:32:23 +0200 Subject: [PATCH 01/13] Handle earlier the API v1.1 errors --- R/http.R | 71 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/R/http.R b/R/http.R index 64732605..4c8e6ced 100644 --- a/R/http.R +++ b/R/http.R @@ -1,10 +1,10 @@ TWIT_get <- function(token, api, params = NULL, ..., host = "api.twitter.com") { resp <- TWIT_method("GET", - token = token, - api = api, - params = params, - ..., - host = host + token = token, + api = api, + params = params, + ..., + host = host ) from_js(resp) @@ -12,12 +12,12 @@ TWIT_get <- function(token, api, params = NULL, ..., host = "api.twitter.com") { TWIT_post <- function(token, api, params = NULL, body = NULL, ..., host = "api.twitter.com") { TWIT_method("POST", - token = token, - api = api, - params = params, - body = body, - ..., - host = host + token = token, + api = api, + params = params, + body = body, + ..., + host = host ) } @@ -100,6 +100,7 @@ TWIT_method <- function(method, token, api, #' Twitter API. #' @param verbose Show progress bars and other messages indicating current #' progress? +#' @returns A list with the json output of the API. TWIT_paginate_max_id <- function(token, api, params, get_id = function(x) x$id_str, n = 1000, @@ -146,8 +147,8 @@ TWIT_paginate_max_id <- function(token, api, params, ) if (is_rate_limit(json)) { warn_early_term(json, - hint = paste0("Set `max_id = '", max_id, "' to continue."), - hint_if = !is.null(max_id) + hint = paste0("Set `max_id = '", max_id, "' to continue."), + hint_if = !is.null(max_id) ) break } @@ -482,29 +483,31 @@ handle_error <- function(x, params) { stop("Twitter API failed [", x$status_code, "]\n", chk_message, call. = FALSE) } json <- from_js(x) - error <- if (!is.null(json$error)) json$error else json$errors - if (length(error) == 1) { - if (any(c("screen_name", "user_id") %in% names(params))) { - account <- params$screen_name - if (is.null(account)) account <- params$user_id - warning("Skipping unauthorized account: ", account, call. = FALSE) + error <- if (!is.null(json[["error"]])) json[["error"]] else json[["errors"]] + if (x$status_code %in% c("401", "403") && is_developing()) { + testthat::skip("API v1.1 no longer works") + if (length(error) == 1) { + if (any(c("screen_name", "user_id") %in% names(params))) { + account <- params$screen_name + if (is.null(account)) + account <- params$user_id + warn(paste0("Skipping unauthorized account: ", account)) + } else { + warn(paste0("Something went wrong with the authentication:\n\t", error)) + } + } else if (length(error) == 2) { + abort(c(paste0("Twitter API failed [", x$status_code, "]:"), + paste0(error$message, " (", error$code, ")")), + call. = caller_call()) } else { - warning("Something went wrong with the authentication:\n\t", - error, call. = FALSE) - } - } else if (length(error) == 2) { - stop("Twitter API failed [", x$status_code, "]. ", chk_message, " \n", - paste0(" * ", error$message, " (", error$code, ")"), - call. = FALSE) - } else { - if (is_testing()) { - testthat::skip("Something went wrong with the requests") + if (is_testing()) { + testthat::skip("Something went wrong with the requests") + } + warn("Something went wrong with the requests") } - warning("Something went wrong with the requests", call. = FALSE) } } - # I don't love this interface because it returns either a httr response object # or a condition object, but it's easy to understand and avoids having to do # anything exotic to break from the correct frame. @@ -525,9 +528,9 @@ warn_early_term <- function(cnd, hint, hint_if) { check_status <- function(x, api) { switch(resp_type(x), - ok = NULL, - rate_limit = , - error = handle_error(x) + ok = NULL, + rate_limit = , + error = handle_error(x) ) } From d8165acf149f021528ade29682fcf66a3fee98c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Sun, 8 Oct 2023 12:31:23 +0200 Subject: [PATCH 02/13] Handle change in R of atomic and NULL --- R/coords.R | 3 ++- R/followers.R | 2 +- R/friends.R | 3 ++- R/post-block.R | 2 +- R/post-favorite.R | 4 ++-- R/post-list.R | 5 +++-- R/post-user.R | 4 ++-- R/search_tweets.R | 3 ++- R/search_users.R | 20 ++++++++++---------- R/stream.R | 2 +- R/trends.R | 2 +- R/ts_plot.R | 4 ++-- tests/testthat/_snaps/search_tweets.md | 2 +- 13 files changed, 30 insertions(+), 26 deletions(-) diff --git a/R/coords.R b/R/coords.R index 22ee8b94..8ac44cbe 100644 --- a/R/coords.R +++ b/R/coords.R @@ -56,7 +56,8 @@ #' @export lookup_coords <- function(address, components = NULL, apikey = NULL, ...) { if (missing(address)) stop("must supply address", call. = FALSE) - stopifnot(is.atomic(address), is.atomic(components)) + stopifnot(is.atomic(address) && !is.null(address), + is.atomic(components)) place <- address if (grepl("^us$|^usa$|^united states$|^u\\.s", address, ignore.case = TRUE)) { diff --git a/R/followers.R b/R/followers.R index 0e2863f1..41cc79a6 100644 --- a/R/followers.R +++ b/R/followers.R @@ -32,7 +32,7 @@ get_followers <- function(user, n = 5000, cursor <- page } - stopifnot(is.atomic(user), isTRUE(length(user) == 1)) + stopifnot(is.atomic(user) && !is.null(user), isTRUE(length(user) == 1)) params <- list(stringify_ids = TRUE) params[[user_type(user)]] <- user diff --git a/R/friends.R b/R/friends.R index 9d206bc4..a4791d46 100644 --- a/R/friends.R +++ b/R/friends.R @@ -139,7 +139,8 @@ lookup_friendships_ <- function(source, target, parse = TRUE, token = NULL) { - stopifnot(is.atomic(source), is.atomic(target)) + stopifnot(is.atomic(source) && !is.null(source), + is.atomic(target) && !is.null(target)) params <- list() params[[paste0("source_", user_type(source, "source"))]] <- source diff --git a/R/post-block.R b/R/post-block.R index 7ac20116..67e18817 100644 --- a/R/post-block.R +++ b/R/post-block.R @@ -20,7 +20,7 @@ user_block <- function(user, unblock = FALSE, token = NULL) { - stopifnot(is.atomic(user), is_logical(unblock)) + stopifnot(is.atomic(user) || is.null(user), is_logical(unblock)) if (!unblock) { query <- "/1.1/blocks/create" diff --git a/R/post-favorite.R b/R/post-favorite.R index ba1cb2d9..50647d8a 100644 --- a/R/post-favorite.R +++ b/R/post-favorite.R @@ -14,7 +14,7 @@ #' } #' @family post #' @export -#' @references +#' @references #' Create: #' Destroy: post_favorite <- function(status_id, @@ -22,7 +22,7 @@ post_favorite <- function(status_id, include_entities = FALSE, token = NULL) { - stopifnot(is.atomic(status_id)) + stopifnot(is.atomic(status_id) && !is.null(status_id)) if (destroy) { query <- "/1.1/favorites/destroy" diff --git a/R/post-list.R b/R/post-list.R index d24b351d..c5275055 100644 --- a/R/post-list.R +++ b/R/post-list.R @@ -100,7 +100,8 @@ post_list_create <- function(name, private = FALSE, token = NULL) { - stopifnot(is.atomic(name), length(name) == 1, is_logical(private)) + stopifnot(is.atomic(name) && !is.null(name), length(name) == 1, + is_logical(private)) if (private) { mode <- "private" @@ -193,7 +194,7 @@ post_list_destroy_all <- function(users, my_list_params <- function(token, slug = NULL, list_id = NULL, ..., users = NULL) { params <- list(...) - if (!is.null(list_id) && is.null(slug)) { + if (!is.null(list_id) && !is.null(slug)) { stopifnot(is.atomic(list_id), length(list_id) == 1) params$list_id <- list_id diff --git a/R/post-user.R b/R/post-user.R index aec553c8..6133d7ab 100644 --- a/R/post-user.R +++ b/R/post-user.R @@ -30,7 +30,7 @@ post_follow <- function(user, retweets = TRUE, token = NULL) { - stopifnot(is.atomic(user), is_logical(notify)) + stopifnot(is.atomic(user) && !is.null(user), is_logical(notify)) if (all(!destroy, !retweets)) { query <- "/1.1/friendships/update" @@ -89,7 +89,7 @@ post_friendship <- function(user, retweets = FALSE, token = NULL) { - stopifnot(is.atomic(user), is_logical(device), + stopifnot(is.atomic(user) && !is.null(user), is_logical(device), is_logical(retweets)) params <- list( diff --git a/R/search_tweets.R b/R/search_tweets.R index a52d2ce5..6a81186d 100644 --- a/R/search_tweets.R +++ b/R/search_tweets.R @@ -125,7 +125,8 @@ search_params <- function(q, if (missing(q) && !is.null(geocode)) { q <- "" } - stopifnot(is.atomic(q), length(q) == 1L, is.atomic(max_id)) + stopifnot(is.atomic(q) && !is.null(q) && length(q) == 1L, + is.atomic(max_id) && length(max_id) <= 1L) type <- arg_match(type) ## validate query length–char count might not always be same here as with diff --git a/R/search_users.R b/R/search_users.R index 7cb0176a..6c98723e 100644 --- a/R/search_users.R +++ b/R/search_users.R @@ -1,11 +1,11 @@ #' Search for users #' -#' Search for Twitter users. The Twitter API limits the results to at most +#' Search for Twitter users. The Twitter API limits the results to at most #' 1,000 users. #' #' @inheritParams TWIT_paginate_max_id -#' @param q As string providing the search query. Try searching by interest, -#' full name, company name, or location. Exact match searches are not +#' @param q As string providing the search query. Try searching by interest, +#' full name, company name, or location. Exact match searches are not #' supported. #' @examples #' if (auth_has_default()) { @@ -24,8 +24,8 @@ search_users <- function(q, n = 100, parse = TRUE, token = NULL, verbose = TRUE) { - - stopifnot(is_n(n), is.atomic(q)) + + stopifnot(is_n(n), is.atomic(q) && !is.null(q)) if (n > 1000) { abort("`n` must be <= 1,000 (the maximum allowed by Twitter)") } @@ -37,23 +37,23 @@ search_users <- function(q, n = 100, pb <- progress::progress_bar$new( format = "Searching for users :bar", total = pages - ) + ) withr::defer(pb$terminate()) } - + params <- list(q = q, count = 20) for (i in seq_len(pages)) { if (verbose) { pb$tick() - } + } params$page <- i results[[i]] <- TWIT_get(token, "/1.1/users/search", params) } - + if (parse) { results <- users_with_tweets(results) results$created_at <- format_date(results$created_at) } - + results } diff --git a/R/stream.R b/R/stream.R index a80d7b4b..6a4b5f86 100644 --- a/R/stream.R +++ b/R/stream.R @@ -170,7 +170,7 @@ whole_lines <- function(text, fragment = "") { stream_prep <- function(token, q = "", ..., filter_level = "none") { token <- check_token(token) - stopifnot(is.atomic(q) || inherits(q, "coords")) + stopifnot(is.atomic(q) && !is.null(q) || inherits(q, "coords")) if (identical(q, "")) { path <- "1.1/statuses/sample.json" diff --git a/R/trends.R b/R/trends.R index 542dd0ec..f39bb26f 100644 --- a/R/trends.R +++ b/R/trends.R @@ -75,7 +75,7 @@ get_trends <- function(woeid = 1, stop("could not find woe id for provided lat/lng coordinates", call. = FALSE) } } else { - stopifnot(is.atomic(woeid), length(woeid) == 1) + stopifnot(is.atomic(woeid) && !is.null(woeid), length(woeid) == 1) if (!is_n(woeid)) { trends <- trends_available(token = token) woeid <- trends$woeid[grep(woeid, trends$name, ignore.case = TRUE)[1]] diff --git a/R/ts_plot.R b/R/ts_plot.R index f14c46da..b26c0ed0 100644 --- a/R/ts_plot.R +++ b/R/ts_plot.R @@ -79,7 +79,7 @@ ts_plot <- function(data, by = "days", trim = 0L, tz ="UTC", ...) { #' #' @export ts_data <- function(data, by = "days", trim = 0L, tz ="UTC") { - stopifnot(is.data.frame(data), is.atomic(by)) + stopifnot(is.data.frame(data), is.atomic(by) && !is.null(by)) if (has_name_(data, "created_at")) { dtvar <- "created_at" } else { @@ -183,7 +183,7 @@ ts_data <- function(data, by = "days", trim = 0L, tz ="UTC") { } parse_unit <- function(by) { - stopifnot(is.atomic(by)) + stopifnot(is.atomic(by) && !is.null(by)) if (is.numeric(by)) { return(by) } else if (grepl("year", by)) { diff --git a/tests/testthat/_snaps/search_tweets.md b/tests/testthat/_snaps/search_tweets.md index 55fc42e1..dfcefa61 100644 --- a/tests/testthat/_snaps/search_tweets.md +++ b/tests/testthat/_snaps/search_tweets.md @@ -4,7 +4,7 @@ search_tweets(c(1:10), verbose = FALSE) Condition Error in `search_params()`: - ! length(q) == 1L is not TRUE + ! is.atomic(q) && !is.null(q) && length(q) == 1L is not TRUE --- From add5876e905b76f384436a4a2a95bb0493d58800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Sun, 8 Oct 2023 14:28:15 +0200 Subject: [PATCH 03/13] Fix citation problems --- inst/CITATION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inst/CITATION b/inst/CITATION index 8b927eed..086cf8b4 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -1,7 +1,7 @@ citHeader("To cite rtweet use:") -citEntry( - entry = "article", +bibentry( + bibtype = "article", title = "rtweet: Collecting and analyzing Twitter data", author = as.person("Michael W. Kearney"), year = "2019", From e023c85b4c63a7a619435470f574d15695a5383b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Sun, 8 Oct 2023 14:28:28 +0200 Subject: [PATCH 04/13] Improve documentation --- NEWS.md | 2 ++ R/search_tweets.R | 2 +- man/TWIT_paginate_max_id.Rd | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 98ea0da0..0c5b3ef0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,8 @@ * New `tweet_post()` and `tweet_delete()` to post and delete tweets to work with the [free product](https://developer.twitter.com/en/portal/products/free). +* Fix problems with changes on `is.atomic(NULL)`. + # rtweet 1.2.0 ## Authentication changes diff --git a/R/search_tweets.R b/R/search_tweets.R index 6a81186d..04301851 100644 --- a/R/search_tweets.R +++ b/R/search_tweets.R @@ -126,7 +126,7 @@ search_params <- function(q, q <- "" } stopifnot(is.atomic(q) && !is.null(q) && length(q) == 1L, - is.atomic(max_id) && length(max_id) <= 1L) + length(max_id) <= 1L) type <- arg_match(type) ## validate query length–char count might not always be same here as with diff --git a/man/TWIT_paginate_max_id.Rd b/man/TWIT_paginate_max_id.Rd index 03b4cab6..1c910d01 100644 --- a/man/TWIT_paginate_max_id.Rd +++ b/man/TWIT_paginate_max_id.Rd @@ -105,6 +105,9 @@ continue pagination from where it left off.} to return the "raw" list corresponding to the JSON returned from the Twitter API.} } +\value{ +A list with the json output of the API. +} \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} These are internal functions used for pagination inside of rtweet. From 5bf5c7ebffe1088cd27a217b9e5476cf03e056b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Sun, 8 Oct 2023 14:49:14 +0200 Subject: [PATCH 05/13] Fix documentation note and some errors --- DESCRIPTION | 2 +- R/coords.R | 2 +- R/data.R | 4 ++-- R/http.R | 3 --- R/post-tweet.R | 2 +- R/premium.R | 1 - man/TWIT_paginate_max_id.Rd | 4 ---- man/direct_messages.Rd | 4 ---- man/emojis.Rd | 2 +- man/get_favorites.Rd | 4 ---- man/get_followers.Rd | 4 ---- man/get_friends.Rd | 4 ---- man/get_mentions.Rd | 4 ---- man/get_retweets.Rd | 4 ---- man/get_timeline.Rd | 4 ---- man/get_trends.Rd | 4 ---- man/langs.Rd | 2 +- man/list_followers.Rd | 4 ---- man/list_get.Rd | 4 ---- man/list_members.Rd | 4 ---- man/list_membership.Rd | 4 ---- man/list_tweets.Rd | 4 ---- man/lists_members.Rd | 4 ---- man/lists_memberships.Rd | 4 ---- man/lists_statuses.Rd | 4 ---- man/lists_subscribers.Rd | 4 ---- man/lists_subscriptions.Rd | 4 ---- man/lists_users.Rd | 4 ---- man/lookup_friendships.Rd | 4 ---- man/lookup_tweets.Rd | 4 ---- man/lookup_users.Rd | 4 ---- man/my_friendships.Rd | 4 ---- man/post_tweet.Rd | 2 +- man/search_fullarchive.Rd | 7 ------- man/search_tweets.Rd | 4 ---- man/search_users.Rd | 4 ---- man/stream.Rd | 4 ---- man/trends_available.Rd | 4 ---- man/tweet_counts_recent.Rd | 4 ---- man/tweet_get.Rd | 4 ---- man/tweet_liking_users.Rd | 4 ---- man/tweet_quoted.Rd | 4 ---- man/tweet_retweeted_by.Rd | 4 ---- man/tweet_search_all.Rd | 4 ---- man/tweet_search_recent.Rd | 4 ---- man/user_blocked.Rd | 4 ---- man/user_bookmarks.Rd | 4 ---- man/user_by_username.Rd | 4 ---- man/user_followers.Rd | 4 ---- man/user_following.Rd | 4 ---- man/user_liked_tweets.Rd | 4 ---- man/user_list_follows.Rd | 4 ---- man/user_lists.Rd | 4 ---- man/user_mentions.Rd | 4 ---- man/user_muted.Rd | 4 ---- man/user_search.Rd | 4 ---- man/user_self.Rd | 4 ---- man/user_timeline.Rd | 4 ---- man/user_tweets.Rd | 4 ---- 59 files changed, 8 insertions(+), 215 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0609aa70..2223c949 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: rtweet Title: Collecting Twitter Data -Version: 1.2.0.9003 +Version: 1.2.0.9004 Authors@R: c( person("Michael W.", "Kearney", , "kearneymw@missouri.edu", role = "aut", comment = c(ORCID = "0000-0002-0730-4694")), diff --git a/R/coords.R b/R/coords.R index 8ac44cbe..56f33eac 100644 --- a/R/coords.R +++ b/R/coords.R @@ -57,7 +57,7 @@ lookup_coords <- function(address, components = NULL, apikey = NULL, ...) { if (missing(address)) stop("must supply address", call. = FALSE) stopifnot(is.atomic(address) && !is.null(address), - is.atomic(components)) + is.atomic(components) && !is.null(components)) place <- address if (grepl("^us$|^usa$|^united states$|^u\\.s", address, ignore.case = TRUE)) { diff --git a/R/data.R b/R/data.R index 92f7df6a..ea74853d 100644 --- a/R/data.R +++ b/R/data.R @@ -1,7 +1,7 @@ #' Defunct: Emojis codes and descriptions data. #' #' This data comes from "Unicode.org", -#' . The data are +#' . The data are #' codes and descriptions of Emojis. #' #' @docType data @@ -12,7 +12,7 @@ NULL #' Defunct: Language codes recognized by Twitter data. #' #' This data comes from the Library of Congress, -#' . The data are +#' . The data are #' descriptions and codes associated with internationally recognized languages. #' Variables include translations for each language represented as #' bibliographic, terminological, alpha, English, and French. diff --git a/R/http.R b/R/http.R index 4c8e6ced..5fcf07f7 100644 --- a/R/http.R +++ b/R/http.R @@ -95,9 +95,6 @@ TWIT_method <- function(method, token, api, #' If you expect a query to take hours or days to perform, you should not #' rely solely on `retryonratelimit` because it does not handle other common #' failure modes like temporarily losing your internet connection. -#' @param parse If `TRUE`, the default, returns a tidy data frame. Use `FALSE` -#' to return the "raw" list corresponding to the JSON returned from the -#' Twitter API. #' @param verbose Show progress bars and other messages indicating current #' progress? #' @returns A list with the json output of the API. diff --git a/R/post-tweet.R b/R/post-tweet.R index 30ef358f..837d4bbb 100644 --- a/R/post-tweet.R +++ b/R/post-tweet.R @@ -27,7 +27,7 @@ #' tweet refers to. Range should be between -90 and 90 (north). Note that you #' should enable the "Precise location" option in your account via *Settings #' and privacy > Privacy and Safety > Location*. See -#' [the official Help Center section](https://help.twitter.com/en/safety-and-security/twitter-location-services-for-mobile). +#' [the official Help Center section](https://help.twitter.com/en/safety-and-security/x-location-services-for-mobile). #' @param long A numeric value representing the longitude of the location the #' tweet refers to. Range should be between -180 and 180 (west). See #' `lat` parameter. diff --git a/R/premium.R b/R/premium.R index a88f7078..8110da6b 100644 --- a/R/premium.R +++ b/R/premium.R @@ -95,7 +95,6 @@ #' } #' #' @export -#' @references Endpoint: search_fullarchive <- function(q, n = 100, fromDate = NULL, toDate = NULL, continue = NULL, env_name = NULL, premium = FALSE, diff --git a/man/TWIT_paginate_max_id.Rd b/man/TWIT_paginate_max_id.Rd index 1c910d01..5b364009 100644 --- a/man/TWIT_paginate_max_id.Rd +++ b/man/TWIT_paginate_max_id.Rd @@ -100,10 +100,6 @@ progress?} \item{cursor}{Which page of results to return. The default will return the first page; you can supply the result from a previous call to continue pagination from where it left off.} - -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} } \value{ A list with the json output of the API. diff --git a/man/direct_messages.Rd b/man/direct_messages.Rd index b75da920..837fd04a 100644 --- a/man/direct_messages.Rd +++ b/man/direct_messages.Rd @@ -36,10 +36,6 @@ continue pagination from where it left off.} \item{next_cursor}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{cursor} instead.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/emojis.Rd b/man/emojis.Rd index 59189b63..b03d69df 100644 --- a/man/emojis.Rd +++ b/man/emojis.Rd @@ -9,6 +9,6 @@ A tibble with two variables and 2,623 observations. } \description{ This data comes from "Unicode.org", -\url{http://unicode.org/emoji/charts/full-emoji-list.html}. The data are +\url{https://unicode.org/emoji/charts/full-emoji-list.html}. The data are codes and descriptions of Emojis. } diff --git a/man/get_favorites.Rd b/man/get_favorites.Rd index 4bda65e4..e3365f71 100644 --- a/man/get_favorites.Rd +++ b/man/get_favorites.Rd @@ -39,10 +39,6 @@ find tweets \strong{newer} than \code{since_id}.} \item{max_id}{Supply a vector of ids or a data frame of previous results to find tweets \strong{older} than \code{max_id}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/get_followers.Rd b/man/get_followers.Rd index 32c9aa7f..ede28278 100644 --- a/man/get_followers.Rd +++ b/man/get_followers.Rd @@ -49,10 +49,6 @@ If you expect a query to take hours or days to perform, you should not rely solely on \code{retryonratelimit} because it does not handle other common failure modes like temporarily losing your internet connection.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{Show progress bars and other messages indicating current progress?} diff --git a/man/get_friends.Rd b/man/get_friends.Rd index 9b421418..d3ea2f9a 100644 --- a/man/get_friends.Rd +++ b/man/get_friends.Rd @@ -50,10 +50,6 @@ failure modes like temporarily losing your internet connection.} the first page; you can supply the result from a previous call to continue pagination from where it left off.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{Show progress bars and other messages indicating current progress?} diff --git a/man/get_mentions.Rd b/man/get_mentions.Rd index 5d81da89..a368d324 100644 --- a/man/get_mentions.Rd +++ b/man/get_mentions.Rd @@ -36,10 +36,6 @@ find tweets \strong{newer} than \code{since_id}.} \item{max_id}{Supply a vector of ids or a data frame of previous results to find tweets \strong{older} than \code{max_id}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/get_retweets.Rd b/man/get_retweets.Rd index 82b674c0..ce8f27c9 100644 --- a/man/get_retweets.Rd +++ b/man/get_retweets.Rd @@ -14,10 +14,6 @@ get_retweeters(status_id, n = 100, parse = TRUE, token = NULL) \item{n}{Number of results to retrieve. Must be <= 100.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/get_timeline.Rd b/man/get_timeline.Rd index bbbba3ba..b7eeb9bb 100644 --- a/man/get_timeline.Rd +++ b/man/get_timeline.Rd @@ -60,10 +60,6 @@ find tweets \strong{older} than \code{max_id}.} (the default, what a user has tweeted/retweeted) or a "home" timeline (what the user would see if they logged into twitter).} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{check}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}} \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait diff --git a/man/get_trends.Rd b/man/get_trends.Rd index 3c7d44b5..dcfa200a 100644 --- a/man/get_trends.Rd +++ b/man/get_trends.Rd @@ -38,10 +38,6 @@ included in returned trends.} \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} - -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} } \value{ Tibble data frame of trends data for a given geographical area. diff --git a/man/langs.Rd b/man/langs.Rd index 2a683e2a..9bd1498a 100644 --- a/man/langs.Rd +++ b/man/langs.Rd @@ -9,7 +9,7 @@ A tibble with five variables and 486 observations. } \description{ This data comes from the Library of Congress, -\url{http://www.loc.gov/standards/iso639-2/ISO-639-2_utf-8.txt}. The data are +\url{https://www.loc.gov/standards/iso639-2/ISO-639-2_utf-8.txt}. The data are descriptions and codes associated with internationally recognized languages. Variables include translations for each language represented as bibliographic, terminological, alpha, English, and French. diff --git a/man/list_followers.Rd b/man/list_followers.Rd index 16683887..1c93cbf8 100644 --- a/man/list_followers.Rd +++ b/man/list_followers.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_get.Rd b/man/list_get.Rd index 09420c21..815b881c 100644 --- a/man/list_get.Rd +++ b/man/list_get.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_members.Rd b/man/list_members.Rd index 74986902..9e626d41 100644 --- a/man/list_members.Rd +++ b/man/list_members.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_membership.Rd b/man/list_membership.Rd index a78b80dd..58bbd403 100644 --- a/man/list_membership.Rd +++ b/man/list_membership.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_tweets.Rd b/man/list_tweets.Rd index 1edfacec..54bc3e6d 100644 --- a/man/list_tweets.Rd +++ b/man/list_tweets.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/lists_members.Rd b/man/lists_members.Rd index d0d441c3..258ee93b 100644 --- a/man/lists_members.Rd +++ b/man/lists_members.Rd @@ -64,10 +64,6 @@ failure modes like temporarily losing your internet connection.} \item{verbose}{Show progress bars and other messages indicating current progress?} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{...}{Other arguments used as parameters in query composition.} } \description{ diff --git a/man/lists_memberships.Rd b/man/lists_memberships.Rd index ba29c12c..5a031222 100644 --- a/man/lists_memberships.Rd +++ b/man/lists_memberships.Rd @@ -45,10 +45,6 @@ authenticating user owns.} a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_statuses.Rd b/man/lists_statuses.Rd index 9021201b..1ac74fdf 100644 --- a/man/lists_statuses.Rd +++ b/man/lists_statuses.Rd @@ -55,10 +55,6 @@ addition to the standard stream of tweets. The output format of retweeted tweets is identical to the representation you see in home_timeline.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_subscribers.Rd b/man/lists_subscribers.Rd index 6b10ab31..def30c1a 100644 --- a/man/lists_subscribers.Rd +++ b/man/lists_subscribers.Rd @@ -39,10 +39,6 @@ size is 200, you'll get 200 results back.} the first page; you can supply the result from a previous call to continue pagination from where it left off.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_subscriptions.Rd b/man/lists_subscriptions.Rd index 797c8480..e55fe39e 100644 --- a/man/lists_subscriptions.Rd +++ b/man/lists_subscriptions.Rd @@ -37,10 +37,6 @@ size is 200, you'll get 200 results back.} the first page; you can supply the result from a previous call to continue pagination from where it left off.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_users.Rd b/man/lists_users.Rd index 0bd9414f..a03be650 100644 --- a/man/lists_users.Rd +++ b/man/lists_users.Rd @@ -17,10 +17,6 @@ how this parameter works.} \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} - -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} } \value{ data diff --git a/man/lookup_friendships.Rd b/man/lookup_friendships.Rd index 07d15a54..228c3450 100644 --- a/man/lookup_friendships.Rd +++ b/man/lookup_friendships.Rd @@ -11,10 +11,6 @@ lookup_friendships(source, target, parse = TRUE, token = NULL) \item{target}{Screen name or user id of target user.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/lookup_tweets.Rd b/man/lookup_tweets.Rd index 2688aedd..02b52452 100644 --- a/man/lookup_tweets.Rd +++ b/man/lookup_tweets.Rd @@ -16,10 +16,6 @@ lookup_tweets( \arguments{ \item{statuses}{User id or screen name of target user.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/lookup_users.Rd b/man/lookup_users.Rd index 0d892dd4..030d5b0c 100644 --- a/man/lookup_users.Rd +++ b/man/lookup_users.Rd @@ -15,10 +15,6 @@ lookup_users( \arguments{ \item{users}{User id or screen name of target user.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/my_friendships.Rd b/man/my_friendships.Rd index 4c2563ab..a08c9bfe 100644 --- a/man/my_friendships.Rd +++ b/man/my_friendships.Rd @@ -10,10 +10,6 @@ my_friendships(user, parse = FALSE, token = NULL) \item{user}{Character vector of screen names or user ids. See \code{\link[=as_screenname]{as_screenname()}} for more details.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/post_tweet.Rd b/man/post_tweet.Rd index 7c7e1929..3cb3ac29 100644 --- a/man/post_tweet.Rd +++ b/man/post_tweet.Rd @@ -57,7 +57,7 @@ for more information.} tweet refers to. Range should be between -90 and 90 (north). Note that you should enable the "Precise location" option in your account via \emph{Settings and privacy > Privacy and Safety > Location}. See -\href{https://help.twitter.com/en/safety-and-security/twitter-location-services-for-mobile}{the official Help Center section}.} +\href{https://help.twitter.com/en/safety-and-security/x-location-services-for-mobile}{the official Help Center section}.} \item{long}{A numeric value representing the longitude of the location the tweet refers to. Range should be between -180 and 180 (west). See diff --git a/man/search_fullarchive.Rd b/man/search_fullarchive.Rd index 7982e845..ae912238 100644 --- a/man/search_fullarchive.Rd +++ b/man/search_fullarchive.Rd @@ -70,10 +70,6 @@ saved. If the directory doesn't exist, it will be created. If NULL (the default) then a dir will be created in the current working directory. To override/deactivate safedir set this to FALSE.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} @@ -168,8 +164,5 @@ rt <- search_30day("#rstats", n = 300, env_name = "SetYourLabel", toDate = toDate) } -} -\references{ -Endpoint: \url{https://developer.twitter.com/en/docs/twitter-api/premium/search-api/api-reference/premium-search} } \concept{premium endpoints} diff --git a/man/search_tweets.Rd b/man/search_tweets.Rd index d3868f99..4d960f10 100644 --- a/man/search_tweets.Rd +++ b/man/search_tweets.Rd @@ -84,10 +84,6 @@ find tweets \strong{newer} than \code{since_id}.} \item{max_id}{Supply a vector of ids or a data frame of previous results to find tweets \strong{older} than \code{max_id}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/search_users.Rd b/man/search_users.Rd index dbf729b4..587350b4 100644 --- a/man/search_users.Rd +++ b/man/search_users.Rd @@ -25,10 +25,6 @@ You will get more results if you ask for a number of tweets that's not a multiple of page size, e.g. if you request \code{n = 150} and the page size is 200, you'll get 200 results back.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/stream.Rd b/man/stream.Rd index ae6a37d3..8fe2c002 100644 --- a/man/stream.Rd +++ b/man/stream.Rd @@ -55,10 +55,6 @@ are better of changing the default for all calls via \code{\link[=auth_as]{auth_ \item{append}{Append streaming to the file? Default does but it is recommended to have a new file for each call.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{query}{If \code{NULL} returns the current rules, else depending: \itemize{ \item In stream_add_rule it should be a list of value and tag. diff --git a/man/trends_available.Rd b/man/trends_available.Rd index 458bab67..61b49941 100644 --- a/man/trends_available.Rd +++ b/man/trends_available.Rd @@ -10,10 +10,6 @@ trends_available(token = NULL, parse = TRUE) \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} - -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} } \value{ Data frame with WOEID column. WOEID is a Yahoo! Where On diff --git a/man/tweet_counts_recent.Rd b/man/tweet_counts_recent.Rd index 773837a2..f784a5b9 100644 --- a/man/tweet_counts_recent.Rd +++ b/man/tweet_counts_recent.Rd @@ -18,10 +18,6 @@ tweet_counts_all(query, ..., token = NULL, parse = TRUE, verbose = FALSE) \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_get.Rd b/man/tweet_get.Rd index de330885..dc33b4d3 100644 --- a/man/tweet_get.Rd +++ b/man/tweet_get.Rd @@ -29,10 +29,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \item{token}{This endpoint accepts a OAuth2.0 authentication (can be created via \code{\link[=rtweet_oauth2]{rtweet_oauth2()}}) or a bearer token (can be created via \code{\link[=rtweet_app]{rtweet_app()}}).} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_liking_users.Rd b/man/tweet_liking_users.Rd index c47a721f..f657664d 100644 --- a/man/tweet_liking_users.Rd +++ b/man/tweet_liking_users.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/tweet_quoted.Rd b/man/tweet_quoted.Rd index a8c569ae..1036d6b9 100644 --- a/man/tweet_quoted.Rd +++ b/man/tweet_quoted.Rd @@ -32,10 +32,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \item{token}{This endpoint accepts a OAuth2.0 authentication (can be created via \code{\link[=rtweet_oauth2]{rtweet_oauth2()}}) or a bearer token (can be created via \code{\link[=rtweet_app]{rtweet_app()}}).} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_retweeted_by.Rd b/man/tweet_retweeted_by.Rd index 01dd3f22..562bdf59 100644 --- a/man/tweet_retweeted_by.Rd +++ b/man/tweet_retweeted_by.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_search_all.Rd b/man/tweet_search_all.Rd index c67df09f..174431b5 100644 --- a/man/tweet_search_all.Rd +++ b/man/tweet_search_all.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_search_recent.Rd b/man/tweet_search_recent.Rd index a80c6357..1c1bd298 100644 --- a/man/tweet_search_recent.Rd +++ b/man/tweet_search_recent.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/user_blocked.Rd b/man/user_blocked.Rd index e0705fd7..dbb355bc 100644 --- a/man/user_blocked.Rd +++ b/man/user_blocked.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_bookmarks.Rd b/man/user_bookmarks.Rd index 1d07af06..8e583992 100644 --- a/man/user_bookmarks.Rd +++ b/man/user_bookmarks.Rd @@ -29,10 +29,6 @@ expansions, or provide a vector with the expansions you want (create it with \item{fields}{Set \code{NULL} to not use any field, get all allowed fields with \code{NA}, provide a list with the fields you want (create it with \code{\link[=set_fields]{set_fields()}}).} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{token}{This endpoint only accept a OAuth2.0 authentication (can be created via \code{\link[=rtweet_oauth2]{rtweet_oauth2()}}).} diff --git a/man/user_by_username.Rd b/man/user_by_username.Rd index 39c39e96..5b03f46c 100644 --- a/man/user_by_username.Rd +++ b/man/user_by_username.Rd @@ -30,10 +30,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_followers.Rd b/man/user_followers.Rd index c66c0781..8619bf1e 100644 --- a/man/user_followers.Rd +++ b/man/user_followers.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_following.Rd b/man/user_following.Rd index 22b3c5fb..10b8fa28 100644 --- a/man/user_following.Rd +++ b/man/user_following.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_liked_tweets.Rd b/man/user_liked_tweets.Rd index 619eee07..6eec927f 100644 --- a/man/user_liked_tweets.Rd +++ b/man/user_liked_tweets.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_list_follows.Rd b/man/user_list_follows.Rd index b6592351..b3d1dcc8 100644 --- a/man/user_list_follows.Rd +++ b/man/user_list_follows.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_lists.Rd b/man/user_lists.Rd index 9fb53894..03bf1a53 100644 --- a/man/user_lists.Rd +++ b/man/user_lists.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_mentions.Rd b/man/user_mentions.Rd index 81dd0fbd..d3d37c99 100644 --- a/man/user_mentions.Rd +++ b/man/user_mentions.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_muted.Rd b/man/user_muted.Rd index c6d62bec..4faedb1c 100644 --- a/man/user_muted.Rd +++ b/man/user_muted.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/user_search.Rd b/man/user_search.Rd index 0bccf104..76a53aa6 100644 --- a/man/user_search.Rd +++ b/man/user_search.Rd @@ -30,10 +30,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/user_self.Rd b/man/user_self.Rd index 3e486f8b..83809e92 100644 --- a/man/user_self.Rd +++ b/man/user_self.Rd @@ -27,10 +27,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/user_timeline.Rd b/man/user_timeline.Rd index cfaaf4dd..8fc8b279 100644 --- a/man/user_timeline.Rd +++ b/man/user_timeline.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_tweets.Rd b/man/user_tweets.Rd index 6a2ebf09..22969fe2 100644 --- a/man/user_tweets.Rd +++ b/man/user_tweets.Rd @@ -33,10 +33,6 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} -\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} -to return the "raw" list corresponding to the JSON returned from the -Twitter API.} - \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ From 7246e68313f7bf130a7c71dba2e2c04144286089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Thu, 12 Oct 2023 13:50:50 +0200 Subject: [PATCH 06/13] Fix problems with missing documentation of parse --- DESCRIPTION | 2 +- R/direct_messages.R | 1 + R/favorites.R | 9 +++++---- R/followers.R | 1 + R/friends.R | 2 ++ R/lists_members.R | 1 + R/lists_parsing.R | 8 +++++++- R/lists_statuses.R | 5 +++-- R/lists_subscribers.R | 9 +++++---- R/mentions.R | 1 + R/premium.R | 1 + R/retweets.R | 3 ++- R/rt_stream.R | 4 +++- R/search_tweets.R | 1 + R/search_users.R | 1 + R/statuses.R | 1 + R/timeline.R | 1 + R/trends.R | 1 + R/users.R | 2 +- inst/WORDLIST | 2 ++ man/direct_messages.Rd | 4 ++++ man/get_favorites.Rd | 6 +++++- man/get_followers.Rd | 4 ++++ man/get_friends.Rd | 4 ++++ man/get_mentions.Rd | 4 ++++ man/get_retweets.Rd | 6 +++++- man/get_timeline.Rd | 4 ++++ man/get_trends.Rd | 4 ++++ man/list_followers.Rd | 4 ++++ man/list_get.Rd | 4 ++++ man/list_members.Rd | 4 ++++ man/list_membership.Rd | 4 ++++ man/list_tweets.Rd | 4 ++++ man/lists_members.Rd | 4 ++++ man/lists_memberships.Rd | 4 ++++ man/lists_statuses.Rd | 4 ++++ man/lists_subscribers.Rd | 4 ++++ man/lists_subscriptions.Rd | 4 ++++ man/lists_users.Rd | 4 ++++ man/lookup_friendships.Rd | 4 ++++ man/lookup_tweets.Rd | 4 ++++ man/lookup_users.Rd | 4 ++++ man/my_friendships.Rd | 4 ++++ man/search_fullarchive.Rd | 4 ++++ man/search_tweets.Rd | 4 ++++ man/search_users.Rd | 4 ++++ man/stream.Rd | 4 ++++ man/trends_available.Rd | 4 ++++ man/tweet_counts_recent.Rd | 4 ++++ man/tweet_get.Rd | 4 ++++ man/tweet_liking_users.Rd | 4 ++++ man/tweet_quoted.Rd | 4 ++++ man/tweet_retweeted_by.Rd | 4 ++++ man/tweet_search_all.Rd | 4 ++++ man/tweet_search_recent.Rd | 4 ++++ man/user_blocked.Rd | 4 ++++ man/user_bookmarks.Rd | 4 ++++ man/user_by_username.Rd | 4 ++++ man/user_followers.Rd | 4 ++++ man/user_following.Rd | 4 ++++ man/user_liked_tweets.Rd | 4 ++++ man/user_list_follows.Rd | 4 ++++ man/user_lists.Rd | 4 ++++ man/user_mentions.Rd | 4 ++++ man/user_muted.Rd | 4 ++++ man/user_search.Rd | 4 ++++ man/user_self.Rd | 4 ++++ man/user_timeline.Rd | 4 ++++ man/user_tweets.Rd | 4 ++++ 69 files changed, 239 insertions(+), 17 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 2223c949..fef1d7cb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: rtweet Title: Collecting Twitter Data -Version: 1.2.0.9004 +Version: 1.2.0.9005 Authors@R: c( person("Michael W.", "Kearney", , "kearneymw@missouri.edu", role = "aut", comment = c(ORCID = "0000-0002-0730-4694")), diff --git a/R/direct_messages.R b/R/direct_messages.R index 611b4657..276aeaf9 100644 --- a/R/direct_messages.R +++ b/R/direct_messages.R @@ -6,6 +6,7 @@ #' about the sender and recipient. #' #' @inheritParams TWIT_paginate_cursor +#' @inheritParams stream #' @param next_cursor `r lifecycle::badge("deprecated")` Use `cursor` instead. #' @return A list with one element for each page of results. #' @examples diff --git a/R/favorites.R b/R/favorites.R index 5215b44b..eb70c7f0 100644 --- a/R/favorites.R +++ b/R/favorites.R @@ -4,13 +4,14 @@ #' #' @inheritParams TWIT_paginate_max_id #' @inheritParams get_timeline +#' @inheritParams stream #' @return A tibble with one row for each tweet. #' @examples #' if (auth_has_default()) { #' # get likes for a single user #' kfc <- get_favorites("KFC") #' kfc -#' # get newer likes since last request +#' # get newer likes since last request #' newer <- get_favorites("KFC", since_id = kfc) #' #' # get likes from multiple users @@ -28,7 +29,7 @@ get_favorites <- function(user, retryonratelimit = NULL, verbose = TRUE, token = NULL) { - rt <- lapply(user, get_favorites_user, + rt <- lapply(user, get_favorites_user, n = n, since_id = since_id, max_id = max_id, @@ -51,7 +52,7 @@ get_favorites_user <- function(user, ..., parse = TRUE, token = NULL) { tweet_mode = "extended" ) params[[user_type(user)]] <- user - + results <- TWIT_paginate_max_id(token, "/1.1/favorites/list", params, page_size = 200, ... @@ -62,6 +63,6 @@ get_favorites_user <- function(user, ..., parse = TRUE, token = NULL) { results$created_at <- format_date(results$created_at) results$favorited_by <- rep(user, nrow(results)) } - + results } diff --git a/R/followers.R b/R/followers.R index 41cc79a6..8ff82127 100644 --- a/R/followers.R +++ b/R/followers.R @@ -5,6 +5,7 @@ #' #' @inheritParams TWIT_paginate_cursor #' @inheritParams get_timeline +#' @inheritParams stream #' @param page `r lifecycle::badge("deprecated")` Please use `cursor` instead. #' @references #' @examples diff --git a/R/friends.R b/R/friends.R index a4791d46..e15aba8f 100644 --- a/R/friends.R +++ b/R/friends.R @@ -12,6 +12,7 @@ #' #' @inheritParams TWIT_paginate_cursor #' @inheritParams get_followers +#' @inheritParams stream #' @param users Screen name or user ID of target user from which the #' user IDs of friends (accounts followed BY target user) will be #' retrieved. @@ -114,6 +115,7 @@ my_friendships <- function(user, #' Gets information on friendship between two Twitter users. #' #' @inheritParams lookup_users +#' @inheritParams stream #' @param source Screen name or user id of source user. #' @param target Screen name or user id of target user. #' @family friends diff --git a/R/lists_members.R b/R/lists_members.R index c59e8831..8dcef4a2 100644 --- a/R/lists_members.R +++ b/R/lists_members.R @@ -8,6 +8,7 @@ #' owner_user parameters. #' @param owner_user optional The screen name or user ID of the user #' @param ... Other arguments used as parameters in query composition. +#' @inheritParams stream #' @examples #' if (auth_has_default()) { #' diff --git a/R/lists_parsing.R b/R/lists_parsing.R index a6ba6a4b..205b14ed 100644 --- a/R/lists_parsing.R +++ b/R/lists_parsing.R @@ -41,7 +41,13 @@ as_lists_users <- function(x) { structure(x, class = "lists_users") } -as.data.frame.lists_users <- function(x) { +as.data.frame.lists_users <- function(x, row.names, optional, ...) { + if (!missing(row.names)) { + warning("`row.names` argument is ignored.") + } + if (!missing(optional)) { + warning("`optional` argument is ignored.") + } if (has_name_(x, "lists")) { x <- x[["lists"]] } diff --git a/R/lists_statuses.R b/R/lists_statuses.R index 426c22db..c3a045d2 100644 --- a/R/lists_statuses.R +++ b/R/lists_statuses.R @@ -13,10 +13,11 @@ #' retweeted tweets is identical to the representation you see in #' home_timeline. #' @inheritParams TWIT_paginate_max_id +#' @inheritParams stream #' @family lists #' @family tweets #' @return data -#' @examples +#' @examples #' if (auth_has_default()) { #' (rladies <- lists_statuses(list_id = "839186302968848384")) #' (rladies <- lists_statuses(slug = "rladies1", owner_user = "RLadiesGlobal")) @@ -51,7 +52,7 @@ lists_statuses <- function(list_id = NULL, retryonratelimit = retryonratelimit, verbose = verbose ) - + if (parse) { results <- tweets_with_users(results) results$created_at <- format_date(results$created_at) diff --git a/R/lists_subscribers.R b/R/lists_subscribers.R index a2356fb8..3d6cbb48 100644 --- a/R/lists_subscribers.R +++ b/R/lists_subscribers.R @@ -1,8 +1,9 @@ #' Get subscribers of a specified list. #' #' @inheritParams TWIT_paginate_cursor +#' @inheritParams stream #' @param list_id required The numerical id of the list. -#' @param slug,owner_user The list name (slug) and owner. +#' @param slug,owner_user The list name (slug) and owner. #' @examples #' if (auth_has_default()) { #' ## get subscribers of rladies list @@ -29,9 +30,9 @@ lists_subscribers <- function(list_id = NULL, owner_user = owner_user ) - r <- TWIT_paginate_cursor(token, "/1.1/lists/subscribers", params, + r <- TWIT_paginate_cursor(token, "/1.1/lists/subscribers", params, n = n, - cursor = cursor, + cursor = cursor, retryonratelimit = retryonratelimit, verbose = verbose, page_size = if (n >= 5000) 5000 else n, @@ -49,6 +50,6 @@ parse_lists_users <- function(x) { dfs <- lapply(users, wrangle_into_clean_data, type = "user") dfs <- lapply(dfs, tibble::as_tibble) df <- do.call(rbind, dfs) - + copy_cursor(df, x) } diff --git a/R/mentions.R b/R/mentions.R index 978193ce..9798a4d0 100644 --- a/R/mentions.R +++ b/R/mentions.R @@ -6,6 +6,7 @@ #' your mentions on twitter.com. #' #' @inheritParams TWIT_paginate_max_id +#' @inheritParams stream #' @param ... Other arguments passed as parameters in composed API #' query. #' @return Tibble of mentions data. diff --git a/R/premium.R b/R/premium.R index 8110da6b..ca21b4e5 100644 --- a/R/premium.R +++ b/R/premium.R @@ -9,6 +9,7 @@ #' Note: The `env_name` must match the ones you set up for the token you are using. #' #' @inheritParams TWIT_paginate_max_id +#' @inheritParams stream #' @param q Search query on which to match/filter tweets. See details for #' information about available search operators. #' @param continue A character string with the next results of a query. You diff --git a/R/retweets.R b/R/retweets.R index dc8e8096..834909eb 100644 --- a/R/retweets.R +++ b/R/retweets.R @@ -8,7 +8,8 @@ #' @param n Number of results to retrieve. Must be <= 100. #' @param ... Other arguments used as parameters in the query sent to #' Twitter's rest API, for example, `trim_user = TRUE`. -#' @return Tweets data of the most recent retweets/retweeters of a given status +#' @return Tweets data of the most recent retweets/retweeters of a given status. +#' @inheritParams stream #' @export #' @references get_retweets <- function(status_id, n = 100, parse = TRUE, token = NULL, ...) { diff --git a/R/rt_stream.R b/R/rt_stream.R index 864d913a..24baeb60 100644 --- a/R/rt_stream.R +++ b/R/rt_stream.R @@ -12,7 +12,9 @@ #' @param token These endpoints only accept a bearer token (can be created via #' [rtweet_app()]). In most cases you #' are better of changing the default for all calls via [auth_as()]. -#' @inheritParams TWIT_paginate_max_id +#' @param parse If `TRUE`, the default, returns a tidy data frame. Use `FALSE` +#' to return the "raw" list corresponding to the JSON returned from the +#' Twitter API. #' @param timeout time, in seconds, of the recording stream. #' @param expansions Set `NULL` to not use any expansion, set `NA` to get all #' expansions, or provide a vector with the expansions you want (create it with diff --git a/R/search_tweets.R b/R/search_tweets.R index 04301851..1145ef9f 100644 --- a/R/search_tweets.R +++ b/R/search_tweets.R @@ -34,6 +34,7 @@ #' } #' #' @inheritParams TWIT_paginate_max_id +#' @inheritParams stream #' @param type Character string specifying which type of search #' results to return from Twitter's REST API. The current default is #' `type = "recent"`, other valid types include `type = diff --git a/R/search_users.R b/R/search_users.R index 6c98723e..235266ec 100644 --- a/R/search_users.R +++ b/R/search_users.R @@ -4,6 +4,7 @@ #' 1,000 users. #' #' @inheritParams TWIT_paginate_max_id +#' @inheritParams stream #' @param q As string providing the search query. Try searching by interest, #' full name, company name, or location. Exact match searches are not #' supported. diff --git a/R/statuses.R b/R/statuses.R index 9325a38f..5b427c59 100644 --- a/R/statuses.R +++ b/R/statuses.R @@ -1,6 +1,7 @@ #' Get tweets data for given statuses (status IDs). #' #' @inheritParams lookup_users +#' @inheritParams stream #' @param statuses User id or screen name of target user. #' @references #' @examples diff --git a/R/timeline.R b/R/timeline.R index 9cbebab2..fdeb5891 100644 --- a/R/timeline.R +++ b/R/timeline.R @@ -7,6 +7,7 @@ #' At most up to 3,200 of a user's most recent Tweets can be retrieved. #' #' @inheritParams TWIT_paginate_max_id +#' @inheritParams stream #' @param user Character vector of screen names or user ids. #' See [as_screenname()] for more details. #' @param home Logical, indicating whether to return a "user" timeline diff --git a/R/trends.R b/R/trends.R index f39bb26f..3f38bb6f 100644 --- a/R/trends.R +++ b/R/trends.R @@ -18,6 +18,7 @@ #' @param exclude_hashtags Logical, indicating whether or not to #' exclude hashtags. Defaults to FALSE--meaning, hashtags are #' included in returned trends. +#' @inheritParams stream #' @examples #' if (auth_has_default()) { #' diff --git a/R/users.R b/R/users.R index 76998f46..add0d92c 100644 --- a/R/users.R +++ b/R/users.R @@ -1,7 +1,7 @@ #' Get Twitter users data for given users (user IDs or screen names). #' #' @inheritParams TWIT_paginate_max_id -#' +#' @inheritParams stream #' @param users User id or screen name of target user. #' @references #' diff --git a/inst/WORDLIST b/inst/WORDLIST index 8bf162b1..0d2ac534 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -102,3 +102,5 @@ unmute usa webshot wholely +Screenshot +URI diff --git a/man/direct_messages.Rd b/man/direct_messages.Rd index 837fd04a..b75da920 100644 --- a/man/direct_messages.Rd +++ b/man/direct_messages.Rd @@ -36,6 +36,10 @@ continue pagination from where it left off.} \item{next_cursor}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{cursor} instead.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/get_favorites.Rd b/man/get_favorites.Rd index e3365f71..8ca2d50a 100644 --- a/man/get_favorites.Rd +++ b/man/get_favorites.Rd @@ -39,6 +39,10 @@ find tweets \strong{newer} than \code{since_id}.} \item{max_id}{Supply a vector of ids or a data frame of previous results to find tweets \strong{older} than \code{max_id}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate @@ -69,7 +73,7 @@ if (auth_has_default()) { # get likes for a single user kfc <- get_favorites("KFC") kfc -# get newer likes since last request +# get newer likes since last request newer <- get_favorites("KFC", since_id = kfc) # get likes from multiple users diff --git a/man/get_followers.Rd b/man/get_followers.Rd index ede28278..32c9aa7f 100644 --- a/man/get_followers.Rd +++ b/man/get_followers.Rd @@ -49,6 +49,10 @@ If you expect a query to take hours or days to perform, you should not rely solely on \code{retryonratelimit} because it does not handle other common failure modes like temporarily losing your internet connection.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{Show progress bars and other messages indicating current progress?} diff --git a/man/get_friends.Rd b/man/get_friends.Rd index d3ea2f9a..9b421418 100644 --- a/man/get_friends.Rd +++ b/man/get_friends.Rd @@ -50,6 +50,10 @@ failure modes like temporarily losing your internet connection.} the first page; you can supply the result from a previous call to continue pagination from where it left off.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{Show progress bars and other messages indicating current progress?} diff --git a/man/get_mentions.Rd b/man/get_mentions.Rd index a368d324..5d81da89 100644 --- a/man/get_mentions.Rd +++ b/man/get_mentions.Rd @@ -36,6 +36,10 @@ find tweets \strong{newer} than \code{since_id}.} \item{max_id}{Supply a vector of ids or a data frame of previous results to find tweets \strong{older} than \code{max_id}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/get_retweets.Rd b/man/get_retweets.Rd index ce8f27c9..93bb97b5 100644 --- a/man/get_retweets.Rd +++ b/man/get_retweets.Rd @@ -14,6 +14,10 @@ get_retweeters(status_id, n = 100, parse = TRUE, token = NULL) \item{n}{Number of results to retrieve. Must be <= 100.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} @@ -22,7 +26,7 @@ default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} Twitter's rest API, for example, \code{trim_user = TRUE}.} } \value{ -Tweets data of the most recent retweets/retweeters of a given status +Tweets data of the most recent retweets/retweeters of a given status. } \description{ \code{get_retweets()} returns the 100 most recent retweets of a tweet; diff --git a/man/get_timeline.Rd b/man/get_timeline.Rd index b7eeb9bb..bbbba3ba 100644 --- a/man/get_timeline.Rd +++ b/man/get_timeline.Rd @@ -60,6 +60,10 @@ find tweets \strong{older} than \code{max_id}.} (the default, what a user has tweeted/retweeted) or a "home" timeline (what the user would see if they logged into twitter).} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{check}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}} \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait diff --git a/man/get_trends.Rd b/man/get_trends.Rd index dcfa200a..3c7d44b5 100644 --- a/man/get_trends.Rd +++ b/man/get_trends.Rd @@ -38,6 +38,10 @@ included in returned trends.} \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} + +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} } \value{ Tibble data frame of trends data for a given geographical area. diff --git a/man/list_followers.Rd b/man/list_followers.Rd index 1c93cbf8..16683887 100644 --- a/man/list_followers.Rd +++ b/man/list_followers.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_get.Rd b/man/list_get.Rd index 815b881c..09420c21 100644 --- a/man/list_get.Rd +++ b/man/list_get.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_members.Rd b/man/list_members.Rd index 9e626d41..74986902 100644 --- a/man/list_members.Rd +++ b/man/list_members.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_membership.Rd b/man/list_membership.Rd index 58bbd403..a78b80dd 100644 --- a/man/list_membership.Rd +++ b/man/list_membership.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/list_tweets.Rd b/man/list_tweets.Rd index 54bc3e6d..1edfacec 100644 --- a/man/list_tweets.Rd +++ b/man/list_tweets.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/lists_members.Rd b/man/lists_members.Rd index 258ee93b..d0d441c3 100644 --- a/man/lists_members.Rd +++ b/man/lists_members.Rd @@ -64,6 +64,10 @@ failure modes like temporarily losing your internet connection.} \item{verbose}{Show progress bars and other messages indicating current progress?} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{...}{Other arguments used as parameters in query composition.} } \description{ diff --git a/man/lists_memberships.Rd b/man/lists_memberships.Rd index 5a031222..ba29c12c 100644 --- a/man/lists_memberships.Rd +++ b/man/lists_memberships.Rd @@ -45,6 +45,10 @@ authenticating user owns.} a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_statuses.Rd b/man/lists_statuses.Rd index 1ac74fdf..9021201b 100644 --- a/man/lists_statuses.Rd +++ b/man/lists_statuses.Rd @@ -55,6 +55,10 @@ addition to the standard stream of tweets. The output format of retweeted tweets is identical to the representation you see in home_timeline.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_subscribers.Rd b/man/lists_subscribers.Rd index def30c1a..6b10ab31 100644 --- a/man/lists_subscribers.Rd +++ b/man/lists_subscribers.Rd @@ -39,6 +39,10 @@ size is 200, you'll get 200 results back.} the first page; you can supply the result from a previous call to continue pagination from where it left off.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_subscriptions.Rd b/man/lists_subscriptions.Rd index e55fe39e..797c8480 100644 --- a/man/lists_subscriptions.Rd +++ b/man/lists_subscriptions.Rd @@ -37,6 +37,10 @@ size is 200, you'll get 200 results back.} the first page; you can supply the result from a previous call to continue pagination from where it left off.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{retryonratelimit}{If \code{TRUE}, and a rate limit is exhausted, will wait until it refreshes. Most Twitter rate limits refresh every 15 minutes. If \code{FALSE}, and the rate limit is exceeded, the function will terminate diff --git a/man/lists_users.Rd b/man/lists_users.Rd index a03be650..0bd9414f 100644 --- a/man/lists_users.Rd +++ b/man/lists_users.Rd @@ -17,6 +17,10 @@ how this parameter works.} \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} + +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} } \value{ data diff --git a/man/lookup_friendships.Rd b/man/lookup_friendships.Rd index 228c3450..07d15a54 100644 --- a/man/lookup_friendships.Rd +++ b/man/lookup_friendships.Rd @@ -11,6 +11,10 @@ lookup_friendships(source, target, parse = TRUE, token = NULL) \item{target}{Screen name or user id of target user.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/lookup_tweets.Rd b/man/lookup_tweets.Rd index 02b52452..2688aedd 100644 --- a/man/lookup_tweets.Rd +++ b/man/lookup_tweets.Rd @@ -16,6 +16,10 @@ lookup_tweets( \arguments{ \item{statuses}{User id or screen name of target user.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/lookup_users.Rd b/man/lookup_users.Rd index 030d5b0c..0d892dd4 100644 --- a/man/lookup_users.Rd +++ b/man/lookup_users.Rd @@ -15,6 +15,10 @@ lookup_users( \arguments{ \item{users}{User id or screen name of target user.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/my_friendships.Rd b/man/my_friendships.Rd index a08c9bfe..4c2563ab 100644 --- a/man/my_friendships.Rd +++ b/man/my_friendships.Rd @@ -10,6 +10,10 @@ my_friendships(user, parse = FALSE, token = NULL) \item{user}{Character vector of screen names or user ids. See \code{\link[=as_screenname]{as_screenname()}} for more details.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/search_fullarchive.Rd b/man/search_fullarchive.Rd index ae912238..f6221190 100644 --- a/man/search_fullarchive.Rd +++ b/man/search_fullarchive.Rd @@ -70,6 +70,10 @@ saved. If the directory doesn't exist, it will be created. If NULL (the default) then a dir will be created in the current working directory. To override/deactivate safedir set this to FALSE.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/search_tweets.Rd b/man/search_tweets.Rd index 4d960f10..d3868f99 100644 --- a/man/search_tweets.Rd +++ b/man/search_tweets.Rd @@ -84,6 +84,10 @@ find tweets \strong{newer} than \code{since_id}.} \item{max_id}{Supply a vector of ids or a data frame of previous results to find tweets \strong{older} than \code{max_id}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/search_users.Rd b/man/search_users.Rd index 587350b4..dbf729b4 100644 --- a/man/search_users.Rd +++ b/man/search_users.Rd @@ -25,6 +25,10 @@ You will get more results if you ask for a number of tweets that's not a multiple of page size, e.g. if you request \code{n = 150} and the page size is 200, you'll get 200 results back.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} diff --git a/man/stream.Rd b/man/stream.Rd index 8fe2c002..ae6a37d3 100644 --- a/man/stream.Rd +++ b/man/stream.Rd @@ -55,6 +55,10 @@ are better of changing the default for all calls via \code{\link[=auth_as]{auth_ \item{append}{Append streaming to the file? Default does but it is recommended to have a new file for each call.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{query}{If \code{NULL} returns the current rules, else depending: \itemize{ \item In stream_add_rule it should be a list of value and tag. diff --git a/man/trends_available.Rd b/man/trends_available.Rd index 61b49941..458bab67 100644 --- a/man/trends_available.Rd +++ b/man/trends_available.Rd @@ -10,6 +10,10 @@ trends_available(token = NULL, parse = TRUE) \item{token}{Use this to override authentication for a single API call. In many cases you are better off changing the default for all calls. See \code{\link[=auth_as]{auth_as()}} for details.} + +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} } \value{ Data frame with WOEID column. WOEID is a Yahoo! Where On diff --git a/man/tweet_counts_recent.Rd b/man/tweet_counts_recent.Rd index f784a5b9..773837a2 100644 --- a/man/tweet_counts_recent.Rd +++ b/man/tweet_counts_recent.Rd @@ -18,6 +18,10 @@ tweet_counts_all(query, ..., token = NULL, parse = TRUE, verbose = FALSE) \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_get.Rd b/man/tweet_get.Rd index dc33b4d3..de330885 100644 --- a/man/tweet_get.Rd +++ b/man/tweet_get.Rd @@ -29,6 +29,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \item{token}{This endpoint accepts a OAuth2.0 authentication (can be created via \code{\link[=rtweet_oauth2]{rtweet_oauth2()}}) or a bearer token (can be created via \code{\link[=rtweet_app]{rtweet_app()}}).} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_liking_users.Rd b/man/tweet_liking_users.Rd index f657664d..c47a721f 100644 --- a/man/tweet_liking_users.Rd +++ b/man/tweet_liking_users.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/tweet_quoted.Rd b/man/tweet_quoted.Rd index 1036d6b9..a8c569ae 100644 --- a/man/tweet_quoted.Rd +++ b/man/tweet_quoted.Rd @@ -32,6 +32,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \item{token}{This endpoint accepts a OAuth2.0 authentication (can be created via \code{\link[=rtweet_oauth2]{rtweet_oauth2()}}) or a bearer token (can be created via \code{\link[=rtweet_app]{rtweet_app()}}).} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_retweeted_by.Rd b/man/tweet_retweeted_by.Rd index 562bdf59..01dd3f22 100644 --- a/man/tweet_retweeted_by.Rd +++ b/man/tweet_retweeted_by.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_search_all.Rd b/man/tweet_search_all.Rd index 174431b5..c67df09f 100644 --- a/man/tweet_search_all.Rd +++ b/man/tweet_search_all.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/tweet_search_recent.Rd b/man/tweet_search_recent.Rd index 1c1bd298..a80c6357 100644 --- a/man/tweet_search_recent.Rd +++ b/man/tweet_search_recent.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about the paginated queries (if any) and to store the data of each page.} } diff --git a/man/user_blocked.Rd b/man/user_blocked.Rd index dbb355bc..e0705fd7 100644 --- a/man/user_blocked.Rd +++ b/man/user_blocked.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_bookmarks.Rd b/man/user_bookmarks.Rd index 8e583992..1d07af06 100644 --- a/man/user_bookmarks.Rd +++ b/man/user_bookmarks.Rd @@ -29,6 +29,10 @@ expansions, or provide a vector with the expansions you want (create it with \item{fields}{Set \code{NULL} to not use any field, get all allowed fields with \code{NA}, provide a list with the fields you want (create it with \code{\link[=set_fields]{set_fields()}}).} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{token}{This endpoint only accept a OAuth2.0 authentication (can be created via \code{\link[=rtweet_oauth2]{rtweet_oauth2()}}).} diff --git a/man/user_by_username.Rd b/man/user_by_username.Rd index 5b03f46c..39c39e96 100644 --- a/man/user_by_username.Rd +++ b/man/user_by_username.Rd @@ -30,6 +30,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_followers.Rd b/man/user_followers.Rd index 8619bf1e..c66c0781 100644 --- a/man/user_followers.Rd +++ b/man/user_followers.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_following.Rd b/man/user_following.Rd index 10b8fa28..22b3c5fb 100644 --- a/man/user_following.Rd +++ b/man/user_following.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_liked_tweets.Rd b/man/user_liked_tweets.Rd index 6eec927f..619eee07 100644 --- a/man/user_liked_tweets.Rd +++ b/man/user_liked_tweets.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_list_follows.Rd b/man/user_list_follows.Rd index b3d1dcc8..b6592351 100644 --- a/man/user_list_follows.Rd +++ b/man/user_list_follows.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_lists.Rd b/man/user_lists.Rd index 03bf1a53..9fb53894 100644 --- a/man/user_lists.Rd +++ b/man/user_lists.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_mentions.Rd b/man/user_mentions.Rd index d3d37c99..81dd0fbd 100644 --- a/man/user_mentions.Rd +++ b/man/user_mentions.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_muted.Rd b/man/user_muted.Rd index 4faedb1c..c6d62bec 100644 --- a/man/user_muted.Rd +++ b/man/user_muted.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/user_search.Rd b/man/user_search.Rd index 76a53aa6..0bccf104 100644 --- a/man/user_search.Rd +++ b/man/user_search.Rd @@ -30,6 +30,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/user_self.Rd b/man/user_self.Rd index 83809e92..3e486f8b 100644 --- a/man/user_self.Rd +++ b/man/user_self.Rd @@ -27,6 +27,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \value{ diff --git a/man/user_timeline.Rd b/man/user_timeline.Rd index 8fc8b279..cfaaf4dd 100644 --- a/man/user_timeline.Rd +++ b/man/user_timeline.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ diff --git a/man/user_tweets.Rd b/man/user_tweets.Rd index 22969fe2..6a2ebf09 100644 --- a/man/user_tweets.Rd +++ b/man/user_tweets.Rd @@ -33,6 +33,10 @@ provide a list with the fields you want (create it with \code{\link[=set_fields] \code{\link[=rtweet_app]{rtweet_app()}}). In most cases you are better of changing the default for all calls via \code{\link[=auth_as]{auth_as()}}.} +\item{parse}{If \code{TRUE}, the default, returns a tidy data frame. Use \code{FALSE} +to return the "raw" list corresponding to the JSON returned from the +Twitter API.} + \item{verbose}{A logical value to provide more information about paginated queries.} } \description{ From 22af44ab89381b3ad855fe4a725cab9fddb11e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Thu, 12 Oct 2023 15:51:30 +0200 Subject: [PATCH 07/13] Increment version number to 1.2.0.9006 --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index fef1d7cb..aaeadaaa 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: rtweet Title: Collecting Twitter Data -Version: 1.2.0.9005 +Version: 1.2.0.9006 Authors@R: c( person("Michael W.", "Kearney", , "kearneymw@missouri.edu", role = "aut", comment = c(ORCID = "0000-0002-0730-4694")), From f1d79bc03cfabbddd3cf635cd8a90c96196957e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Thu, 12 Oct 2023 16:36:02 +0200 Subject: [PATCH 08/13] Update docs --- DESCRIPTION | 2 +- R/auth.R | 5 +++-- R/client.R | 4 ++-- R/coords.R | 3 +-- man/rtweet_client.Rd | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index aaeadaaa..4869c0ba 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -50,7 +50,7 @@ Suggests: maps (>= 3.4.0), openssl (>= 2.0.2), rmarkdown (>= 2.14), - spelling, + spelling (>= 2.2.1), testthat (>= 3.1.0), vcr (>= 0.6.0), webshot (>= 0.5.3) diff --git a/R/auth.R b/R/auth.R index 80fdbc0b..2a050f6f 100644 --- a/R/auth.R +++ b/R/auth.R @@ -392,7 +392,7 @@ twitter_init_oauth1.0 <- function(endpoint, app, permission = NULL, private_key = NULL) { # Twitter requirements only allow numbers # local environment for httr::oauth_callback to find the localhost IP. - # Which is also modified to http://127.0.0.1:1410/ + # Which is also modified to `http://127.0.0.1:1410/` withr::local_envvar("HTTR_SERVER" = "127.0.0.1") httr::init_oauth1.0( endpoint, @@ -410,7 +410,8 @@ auth_path <- function(...) { } #' Some endpoints require OAuth 2.0 with specific permissions in order to work. -#' In order to work, the developer must configure the app with callback url: `http://127.0.0.1:1410` +#' In order to work, the developer must configure the app with callback url: +#' `http://127.0.0.1:1410/` #' @param client Which client app will be used, see [rtweet_client()] for details. #' @param scopes The permissions of the app, see [set_scopes()] for details. #' By default it uses the client's scopes. Provided here in case you want to modify them. diff --git a/R/client.R b/R/client.R index d53e04ca..0ec4ad06 100644 --- a/R/client.R +++ b/R/client.R @@ -180,8 +180,8 @@ no_client <- function(call = caller_env()) { #' @param scopes Default scopes allowed for users using this client. #' Leave `NULL` to allow everything or choose yours with `set_scopes()`. #' @param app Name of the client, it helps if you make it match with the name of your app. -#' On the Twitter app the Callback URI must be "http://127.0.0.1:1410/" (the trailing / must be -#' included). +#' On the Twitter app the Callback URI must be `http://127.0.0.1:1410/` +#' (the trailing / must be included). #' @seealso scopes #' @export #' @examples diff --git a/R/coords.R b/R/coords.R index 56f33eac..7f9ddfbb 100644 --- a/R/coords.R +++ b/R/coords.R @@ -56,8 +56,7 @@ #' @export lookup_coords <- function(address, components = NULL, apikey = NULL, ...) { if (missing(address)) stop("must supply address", call. = FALSE) - stopifnot(is.atomic(address) && !is.null(address), - is.atomic(components) && !is.null(components)) + stopifnot(is.atomic(address) && !is.null(address)) place <- address if (grepl("^us$|^usa$|^united states$|^u\\.s", address, ignore.case = TRUE)) { diff --git a/man/rtweet_client.Rd b/man/rtweet_client.Rd index e3b08c82..0cfd7573 100644 --- a/man/rtweet_client.Rd +++ b/man/rtweet_client.Rd @@ -12,8 +12,8 @@ These are generally not required for \code{rtweet_user()} since the defaults wil the built-in rtweet app.} \item{app}{Name of the client, it helps if you make it match with the name of your app. -On the Twitter app the Callback URI must be "http://127.0.0.1:1410/" (the trailing / must be -included).} +On the Twitter app the Callback URI must be \verb{http://127.0.0.1:1410/} +(the trailing / must be included).} \item{scopes}{Default scopes allowed for users using this client. Leave \code{NULL} to allow everything or choose yours with \code{set_scopes()}.} From 2bb404d461ea74aeecb9c08391558974e0fd887d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Thu, 12 Oct 2023 16:36:15 +0200 Subject: [PATCH 09/13] Update vignettes --- README.Rmd | 74 +----- vignettes/auth.Rmd | 18 +- vignettes/rtweet.Rmd | 552 +++---------------------------------------- 3 files changed, 44 insertions(+), 600 deletions(-) diff --git a/README.Rmd b/README.Rmd index 710b4452..fbfe1c48 100644 --- a/README.Rmd +++ b/README.Rmd @@ -44,7 +44,8 @@ install.packages("rtweet", repos = 'https://ropensci.r-universe.dev') ## Usage -All users must be authenticated to interact with Twitter's APIs. The easiest way to authenticate is to use your personal twitter account - this will happen automatically (via a browser popup) the first time you use an rtweet function. See `auth_setup_default()` for details. Using your personal account is fine for casual use, but if you are trying to collect a lot of data it's a good idea to authentication with your own Twitter "app". See `vignette("auth", package = "rtweet")` for details. +All users must be authenticated to interact with Twitter's APIs. +See `vignette("auth", package = "rtweet")` for details. ```{r} library(rtweet) @@ -56,76 +57,17 @@ rtweet should be used in strict accordance with Twitter's [developer terms](htt auth_as(rtweet:::rtweet_test()) ``` +## Usage -### Search tweets or users - -Search for up to 1000 tweets containing #rstats, the common hashtag used to refer to the R language, excluding retweets: - -```{r} -rt <- search_tweets("#rstats", n = 1000, include_rts = FALSE) -``` - -Twitter rate limits cap the number of search results returned to 18,000 every 15 minutes. To request more than that, set `retryonratelimit = TRUE` and rtweet will wait for rate limit -resets for you. - -Search for 200 users with the #rstats in their profile: - -```{r} -useRs <- search_users("#rstats", n = 200) -``` - -### Stream tweets - -Randomly sample (approximately 1%) from the live stream of all tweets: - -```{r, eval=FALSE} -random_stream <- stream_tweets("") -``` - -Stream all geo-located tweets from London for 60 seconds: - -```{r, eval=FALSE} -stream_london <- stream_tweets(location = lookup_coords("london"), timeout = 60) -``` - -### Get friends and followers - -Get all accounts followed by a user: - -```{r} -## get user IDs of accounts followed by R Foundation -R_Foundation_fds <- get_friends("_R_Foundation") - -## lookup data on those accounts -R_Foundation_fds_data <- lookup_users(R_Foundation_fds$to_id) -``` - -Get all accounts following a user: - -```{r} -## get user IDs of accounts following R Foundation -R_Foundation_flw <- get_followers("_R_Foundation", n = 100) -R_Foundation_flw_data <- lookup_users(R_Foundation_flw$from_id) -``` - -If you want *all* followers, you'll need you'll need to set `n = Inf` and `retryonratelimit = TRUE` but be warned that this might take a *long* time. +Depending on if you are a paid user or not you can do more or less things. -### Get timelines +# Free -Get the most recent 100 tweets from R Foundation: +You can post (`tweet_post()`) and retrieve information about yourself (`user_self()`). -```{r} -## get most recent 100 tweets from R Foundation -tmls <- get_timeline("_R_Foundation", n = 100) -``` +# Paid -### Get favorites - -Get the 10 most recently favorited statuses by R Foundation: - -```{r} -favs <- get_favorites("_R_Foundation", n = 10) -``` +You can do all the other things: search tweets (`tweet_search_recent()`), retrieve your own bookmarks (`user_bookmarks()`), check who follows who, (`user_following()`, or `user_followers()`), .... ## Contact diff --git a/vignettes/auth.Rmd b/vignettes/auth.Rmd index 54e4de65..fc8a2572 100644 --- a/vignettes/auth.Rmd +++ b/vignettes/auth.Rmd @@ -21,7 +21,7 @@ rtweet's default authentication is shared by all user. If it is just for a test, a workshop or a lecture it is fine. But if you plan to use it more than a day you will benefit of authenticating yourself. -The **recommended authentication** mechanism is using `auth_setup_default()`. +The **authentication** mechanism is using your own app authentication. It allows you to act on behalf of your personal Twitter account, as if you were performing actions on twitter.com. If you want to collect a lot of data or implement a bot, you should instead use one of rtweet's two other authentication mechanisms: @@ -78,7 +78,7 @@ auth_setup_default() It will call `rtweet_user()` to use your current logged account on your default browser as the authentication used by `rtweet` and save it as "default" (See [Saving and loading](#save)). -#### Alternative authorization protocol +#### OAuth2 protocol Some new functions require a different authentication mechanism[^1]. This functions will need you to first set up a client, which requires your client id and secret. @@ -89,7 +89,7 @@ You can get them in your [developer dashboard](https://developer.twitter.com/en/ Choose one of the **App permissions**: Read, Read and write, or Read and write and Direct message In **Type of App** select Native app -In **App info** set the callback URI to: http://127.0.0.1:1410/ +In **App info** set the callback URI to: `http://127.0.0.1:1410/` The callback URI is important as this is what rtweet will use to validate. @@ -148,14 +148,7 @@ Again, you'll want to record this data in a secure place because you only get to ## Default -Now you have an auth object that you can provide to the `token` argument of any rtweet function: - -```{r, eval = FALSE} -df <- search_tweets("#rstats", token = auth) -``` - -It's a good idea to do this once to check that you've entered all the app data correctly, but it'd be annoying if you had to pass this object around every single time. -Instead, you can call `auth_as()` to set this as the default for the remainder of the session: +You can call `auth_as()` to set this as the default for the remainder of the session: ```{r, eval = FALSE} auth_as(auth) @@ -188,7 +181,6 @@ You can see all the authentication options you have saved with `auth_list()`. `auth_list()` reports all the available authentications at the default location (See `auth_save()` details). If you use an authentication saved on a different path you can directly use it `auth_as("../authentications/rtweet.rds")` -`auth_setup_default()` saves the authentication as default. So, after your initial setup you can start all your scripts with `auth_as("default")` to load it. Clients work similarly, but the client will be saved with the name of the app you provided: @@ -249,7 +241,7 @@ First looks up old authentications rtweet saved at your home directory (`~`, or Then it reports the authentications found on the new location (rtweet \>= 1.0.0). For each folder it reports apps and then users and bots authentications. It is safe to use in public, as instead of the tokens or keys it reports a letter. -For users authentications it reports the user_id, so that you can check who is that user (`search_users("1251053384")`). +For users authentications it reports the user_id, so that you can check who is that user (`user_search("1251053384")`). This makes it easier to see if there is a saved authentication with a name not matching the user_id. It also warns you if there is the same key or token for multiple files, as this indicates a misunderstanding or a duplication of the authentication. diff --git a/vignettes/rtweet.Rmd b/vignettes/rtweet.Rmd index 2b07cb22..91a384dc 100644 --- a/vignettes/rtweet.Rmd +++ b/vignettes/rtweet.Rmd @@ -15,7 +15,7 @@ vignette: > -This vignette provides a quick tour of the R package. +This vignette provides a quick tour of the R package, describing what could be possible to do. ```r @@ -24,591 +24,101 @@ library("rtweet") ## Authenticate -First you should set up your own credentials, this should be done just once ever: - - -```r -auth_setup_default() -``` - -Which will look up your account on your browser and create a token and save it as default. -From now on, on this R session on others we can use this authentication with: - - -```r -auth_as("default") -``` - -Automatically rtweet will use that token in all the API queries it will do in the session. - -If you want to set up a bot or collect a lot of information, please read the `vignette("auth", "rtweet")`. +First you should set up your own credentials, lease read the `vignette("auth", "rtweet")`. +## Free +### Posting statuses -## Search tweets - -You can search tweets: - +You can post tweets with: ```r -## search for 18000 tweets using the rstats hashtag -rstats <- search_tweets("#rstats", n = 100, include_rts = FALSE) -colnames(rstats) -#> [1] "created_at" "id" "id_str" -#> [4] "text" "full_text" "truncated" -#> [7] "entities" "source" "in_reply_to_status_id" -#> [10] "in_reply_to_status_id_str" "in_reply_to_user_id" "in_reply_to_user_id_str" -#> [13] "in_reply_to_screen_name" "geo" "coordinates" -#> [16] "place" "contributors" "is_quote_status" -#> [19] "retweet_count" "favorite_count" "favorited" -#> [22] "favorited_by" "retweeted" "scopes" -#> [25] "lang" "possibly_sensitive" "display_text_width" -#> [28] "display_text_range" "retweeted_status" "quoted_status" -#> [31] "quoted_status_id" "quoted_status_id_str" "quoted_status_permalink" -#> [34] "quote_count" "timestamp_ms" "reply_count" -#> [37] "filter_level" "metadata" "query" -#> [40] "withheld_scope" "withheld_copyright" "withheld_in_countries" -#> [43] "possibly_sensitive_appealable" -rstats[1:5, c("created_at", "text", "id_str")] -#> # A tibble: 5 × 3 -#> created_at text id_str -#> -#> 1 2023-03-26 06:26:01 "A Top List of Algorithms to Know. #BigData #Analytics #DataScience #AI #M… 16398… -#> 2 2023-03-26 21:26:01 "Tools Regularly used by Data Scientists. #BigData #Analytics #DataScience… 16400… -#> 3 2023-03-26 21:26:01 "Free eBook: Introduction to Modern #Statistics. #BigData #Analytics #Data… 16400… -#> 4 2023-03-28 00:10:18 "📺 New #rstatsvideo: ISLR: Resampling Methods Part 1 (islr05 5)\n▶ R4DS O… 16404… -#> 5 2023-03-28 00:07:28 "لاختراق أو استرداد أي حساب Dm الآن! #Roblox #RobloxDown #TikTok #Discord… 16404… -#> ℹ Users data at users_data() +tweet_post(paste0("My first tweet with #rtweet #rstats at ", Sys.time())) ``` -The `include_rts = FALSE` excludes retweets from the search. - -Twitter rate limits the number of calls to the endpoints you can do. -See `rate_limit()` and the [rate limit section](#Rate-limit) below. -If your query requires more calls like the example below, simply set `retryonratelimit = TRUE` and rtweet will wait for rate limit resets for you. - +### Your own data ```r -## search for 250,000 tweets containing the word data -tweets_peace <- search_tweets("peace", n = 250000, retryonratelimit = TRUE) +user_self() ``` -Search by geo-location, for example tweets in the English language sent from the United States. - - -```r -# search for tweets sent from the US -# lookup_coords requires Google maps API key for maps outside usa, canada and world -geo_tweets <- search_tweets("lang:en", geocode = lookup_coords("usa"), n = 100) -geo_tweets[1:5, c("created_at", "text", "id_str", "lang", "place")] -#> # A tibble: 5 × 5 -#> created_at text id_str lang place -#> -#> 1 2023-03-28 00:12:21 I hate crossing bridges and my ass had to cross the Channelvi… 16404… en -#> 2 2023-03-28 00:12:21 I'm at United Dairy Farmers - @udf_official in Blacklick, OH … 16404… en -#> 3 2023-03-28 00:12:20 it's always when we mad at each other, when i get the hornies… 16404… en -#> 4 NA -#> 5 NA -#> ℹ Users data at users_data() -``` -You can check the location of these tweets with `lat_lng()`. -Or quickly visualize frequency of tweets over time using `ts_plot()` (if `ggplot2` is installed). +## Paid +### Search tweets -```r -## plot time series of tweets -ts_plot(rstats) + - theme_minimal() + - theme(plot.title = element_text(face = "bold")) + - labs( - x = NULL, y = NULL, - title = "Frequency of #rstats Twitter statuses from past days", - subtitle = "Twitter status (tweet) counts aggregated using three-hour intervals", - caption = "Source: Data collected from Twitter's REST API via rtweet" - ) -``` - -![Last 100 tweets by hour.](plot1-1.png) - -## Posting statuses - -You can post tweets with: +You can search tweets: ```r -post_tweet(paste0("My first tweet with #rtweet #rstats at ", Sys.time())) -#> Your tweet has been posted! +rstats <- tweet_search_recent("#rstats", n = 100) ``` -It can include media and alt text: - ```r -path_file <- tempfile(fileext = ".png") -png(filename = path_file) -plot(mpg ~ cyl, mtcars, col = gear, pch = gear) -dev.off() -#> png -#> 2 -post_tweet("my first tweet with #rtweet with media #rstats", media = path_file, media_alt_text = "Plot of mtcars dataset, showing cyl vs mpg colored by gear. The lower cyl the higher the mpg is.") -#> Your tweet has been posted! +tweets_peace <- tweet_search_all("peace", n = 250000, retryonratelimit = TRUE) ``` -You can also reply to a previous tweet, retweet and provide additional information. - -## Get friends +### Get friends Retrieve a list of all the accounts a **user follows**. ```r -## get user IDs of accounts followed by R Foundation -R_foundation_fds <- get_friends("_R_Foundation") -R_foundation_fds -#> # A tibble: 30 × 2 -#> from_id to_id -#> -#> 1 _R_Foundation 1448728978370535426 -#> 2 _R_Foundation 889777924991307778 -#> 3 _R_Foundation 1300656590 -#> 4 _R_Foundation 1280779280579022848 -#> 5 _R_Foundation 1229418786085888001 -#> 6 _R_Foundation 1197874989367779328 -#> 7 _R_Foundation 1102763906714554368 -#> 8 _R_Foundation 1560929287 -#> 9 _R_Foundation 46782674 -#> 10 _R_Foundation 16284661 -#> # ℹ 20 more rows -``` - -Using `get_friends()` we can retrieve which users are being followed by the R Foundation. - -## Get followers - -If you really want all the users that follow the account we can use `get_followers()`: - -```r -R_foundation_flw <- get_followers("_R_Foundation", n = 30000, - retryonratelimit = TRUE) -#> Downloading multiple pages -#> ========================>-------------------------------------------------- Downloading multiple pages -#> =====================================>------------------------------------- Downloading multiple pages -#> =================================================>------------------------- Downloading multiple pages -#> =============================================================>------------- +user_following("1234565") ``` -Note that the `retryonratelimit` option is intended for when you need more queries than provided by Twitter on a given period. -You might want to check with `rate_limit()` how many does it provide for the endpoints you are using. -If exceeded `retryonratelimit` waits till the there are more calls available and then resumes the query. - -## Lookup users - -As seen above we can use `lookup_users()` to check their +### Get followers +If you really want all the users that follow the account we can use `user_follower()`: ```r -# Look who is following R Foundation -R_foundation_fds_data <- lookup_users(R_foundation_fds$to_id, verbose = FALSE) -R_foundation_fds_data[, c("name", "screen_name", "created_at")] -#> # A tibble: 30 × 3 -#> name screen_name created_at -#> -#> 1 R Contributors R_Contributors 2021-10-14 21:15:12 -#> 2 Sebastian Meyer bastistician 2017-07-25 11:22:43 -#> 3 Naras b_naras 2013-03-25 19:48:12 -#> 4 useR! 2022 _useRconf 2020-07-08 10:22:55 -#> 5 useR2021zrh useR2021zrh 2020-02-17 15:54:39 -#> 6 useR2020muc useR2020muc 2019-11-22 14:50:55 -#> 7 useR! 2020 useR2020stl 2019-03-05 03:52:58 -#> 8 Roger Bivand @rsbivand@fosstodon.org RogerBivand 2013-07-01 18:19:42 -#> 9 Henrik Bengtsson henrikbengtsson 2009-06-13 02:11:14 -#> 10 Gabriela de Queiroz ☁️ 🥑 gdequeiroz 2008-09-14 18:55:29 -#> # ℹ 20 more rows -#> ℹ Tweets data at tweets_data() - -# Look 100 R Foundation followers -R_foundation_flw_data <- lookup_users(head(R_foundation_flw$from_id, 100), verbose = FALSE) -R_foundation_flw_data[1:5, c("name", "screen_name", "created_at")] -#> # A tibble: 5 × 3 -#> name screen_name created_at -#> -#> 1 René Kotzé ReneSKotze 2012-06-15 22:34:54 -#> 2 Caio Lente @clente@fosstodon.org ctlente 2020-11-19 02:11:57 -#> 3 Цикл Флекса c_fleksa 2022-06-15 16:22:43 -#> 4 Benjamin Dupuis bjm_dupuis 2019-09-29 21:03:28 -#> 5 Jean Baptiste Fankam Fankam (PhD) JeanBap24011135 2021-04-05 12:26:45 -#> ℹ Tweets data at tweets_data() +R_foundation_flw <- user_follower("1599030512919650304") ``` -We have now the information from those followed by the R Foundation and its followers. -We can retrieve their latest tweets from these users: - - -```r -tweets_data(R_foundation_fds_data)[, c("created_at", "text")] -#> # A tibble: 30 × 2 -#> created_at text -#> -#> 1 Thu Mar 09 14:52:35 +0000 2023 "Reminder that the deadline for applications to participate in R Proj… -#> 2 Wed Sep 28 15:06:22 +0000 2022 "RT @pdalgd: #rstats 4.2.2 \"Innocent and Trusting\" scheduled for O… -#> 3 Fri Mar 24 20:11:27 +0000 2023 "RT @GarethMJam: new website just dropped\n\n@daniela_witten @HastieT… -#> 4 Sat Nov 12 16:06:40 +0000 2022 "RT @HeathrTurnr: Wondering about setting up a data science/open scie… -#> 5 Fri Jul 29 05:30:06 +0000 2022 "RT @kidssindwichtig: Wenn du in deiner Arbeitszeit noch nie einen Le… -#> 6 Fri Apr 16 11:03:21 +0000 2021 "RT @_useRconf: It is a good time to remember some of our keydates!\n… -#> 7 Mon Jan 18 17:36:22 +0000 2021 "Give us a follow at @_useRconf to stay updated on *all* future useR!… -#> 8 Mon Mar 06 19:42:31 +0000 2023 "@RConsortium @RConsortium It would be super-useful to have the missi… -#> 9 Mon Mar 27 21:47:50 +0000 2023 "@kaneplusplus Some of these issues could be solved by foreach() tigh… -#> 10 Thu Mar 23 03:17:30 +0000 2023 "@sascha_wolfer GitHub Copilot, Codespaces and other extensions. But … -#> # ℹ 20 more rows -``` +### Search users -## Search users +We can use `user_search()`: -Search for 1,000 users with the rstats hashtag in their profile bios. ```r -## search for users with #rstats in their profiles -useRs <- search_users("#rstats", n = 100, verbose = FALSE) -useRs[, c("name", "screen_name", "created_at")] -#> # A tibble: 100 × 3 -#> name screen_name created_at -#> -#> 1 R for Data Science rstats4ds 2018-12-18 13:55:25 -#> 2 Rstats rstatstweet 2018-06-27 05:45:02 -#> 3 FC rSTATS FC_rstats 2018-02-08 21:03:08 -#> 4 #RStats Question A Day data_question 2019-10-21 19:15:24 -#> 5 R Tweets rstats_tweets 2020-09-17 18:12:09 -#> 6 Data Science with R Rstats4Econ 2012-04-21 04:37:12 -#> 7 Ramiro Bentes NbaInRstats 2019-11-05 03:44:32 -#> 8 Baseball with R BaseballRstats 2013-11-02 16:07:05 -#> 9 Will steelRstats 2019-07-23 16:48:00 -#> 10 LIRR Statistics (Unofficial) LIRRstats 2017-01-25 00:31:55 -#> # ℹ 90 more rows -#> ℹ Tweets data at tweets_data() +users <- user_search(c("1599030512919650304", "2244994945")) ``` -If we want to know what have they tweeted about we can use `tweets_data()`: - -```r -useRs_twt <- tweets_data(useRs) -useRs_twt[1:5, c("id_str", "created_at", "text")] -#> # A tibble: 5 × 3 -#> id_str created_at text -#> -#> 1 1640473819436417026 Mon Mar 27 22:00:11 +0000 2023 RT @DataSciencetut: How to compare variances in R … -#> 2 1640475644277719041 Mon Mar 27 22:07:26 +0000 2023 RT @joey_stan: Does someone (in the #rstats commun… -#> 3 1639344660849086464 Fri Mar 24 19:13:19 +0000 2023 @_DrOsama @StatsBomb @mixedknuts 2018 I believe -#> 4 1640330542674739200 Mon Mar 27 12:30:51 +0000 2023 Sharpen your programming skills with a question a … -#> 5 1640466489407840257 Mon Mar 27 21:31:04 +0000 2023 RT @ibddoctor: If you are preparing a workshop for… -``` -## Get timelines +### Get timelines Get the most recent tweets from R Foundation. ```r -## get user IDs of accounts followed by R Foundation -R_foundation_tline <- get_timeline("_R_Foundation") -latest_R_tweets <- filter(R_foundation_tline, created_at > "2017-10-29") - -## plot the frequency of tweets for each user over time -plot <- ts_plot(latest_R_tweets, by = "month", trim = 1L) + - geom_point() + - theme_minimal() + - theme( - legend.title = element_blank(), - legend.position = "bottom", - plot.title = element_text(face = "bold")) + - labs( - x = NULL, y = NULL, - title = "Frequency of Twitter statuses posted by the R Foundation", - subtitle = "Twitter status (tweet) counts aggregated by month from October/November 2017", - caption = "Source: Data collected from Twitter's REST API via rtweet" - ) -plot +R_foundation_tline <- user_timeline("1599030512919650304") ``` -![Frequency of Twitter statuses posted by the R Foundation since 2017](plot2-1.png) - -## Get favorites +### Get favorites Get the 10 recently favorited statuses by R Foundation. ```r -R_foundation_favs <- get_favorites("_R_Foundation", n = 10) -R_foundation_favs[, c("text", "created_at", "id_str")] -#> # A tibble: 10 × 3 -#> text created_at id_str -#> -#> 1 "We're into August, which hopefully means you've had time to enjoy conten… 2020-08-03 09:51:33 12901… -#> 2 "Gret meeting of #useR2020 passing the torch to #useR2021! 🔥 \nThank you… 2020-07-16 17:14:25 12837… -#> 3 "Also thanks to the @_R_Foundation, @R_Forwards, @RLadiesGlobal, MiR and … 2020-05-28 08:57:24 12658… -#> 4 "Such an honour to be acknowledged this way at #useR2019. I'm happy that … 2019-07-12 18:36:27 11497… -#> 5 "R-3.4.4 Windows installer is on CRAN now: https://t.co/h35EcsIEuF https:… 2018-03-15 18:16:13 97433… -#> 6 "Gala dinner with a table with people in cosmology, finance, psychology, … 2017-07-07 09:10:41 88322… -#> 7 "AMAZING #RLadies at #useR2017 💜🌍 inspiring #rstats work around the wor… 2017-07-05 13:25:27 88256… -#> 8 "Fame at last: https://t.co/x4wIePKR6b -- it's always nice to get a bit o… 2017-06-07 23:25:37 87256… -#> 9 "We are excited to let you know that the full Conference Program is onlin… 2017-05-31 14:37:23 86989… -#> 10 ". @statsYSS and @RSSGlasgow1 to hold joint event celebrating 20 years o… 2017-04-10 10:50:11 85135… -#> ℹ Users data at users_data() +R_foundation_favs <- user_liked_tweets("1599030512919650304") ``` -## Get trends +### Muting users -Discover what's currently trending in San Francisco. - -```r -world <- get_trends("world") -world -#> # A tibble: 50 × 9 -#> trend url promoted_content query tweet_volume place woeid as_of created_at -#> -#> 1 Nashvi… http… NA Nash… 493618 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 2 #IRLFRA http… NA %23I… 40327 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 3 #فوازي… http… NA %23%… 189780 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 4 #LaIsl… http… NA %23L… 12140 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 5 #Yedek… http… NA %23Y… NA Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 6 #Pomem… http… NA %23P… 28037 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 7 Maignan http… NA Maig… 43778 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 8 Lamar http… NA Lamar 106642 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 9 Janne http… NA Janne NA Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> 10 Audrey… http… NA %22A… 42877 Worl… 1 2023-03-27 22:12:36 2023-03-25 21:57:33 -#> # ℹ 40 more rows -``` - -## Following users - -You can follow users and unfollow them: +You can list those users muted: ```r -post_follow("_R_Foundation") -#> Response [https://api.twitter.com/1.1/friendships/create.json?notify=FALSE&screen_name=_R_Foundation] -#> Date: 2023-03-27 22:12 -#> Status: 200 -#> Content-Type: application/json;charset=utf-8 -#> Size: 3.77 kB -post_unfollow_user("rtweet_test") -#> Response [https://api.twitter.com/1.1/friendships/destroy.json?notify=FALSE&screen_name=rtweet_test] -#> Date: 2023-03-27 22:12 -#> Status: 200 -#> Content-Type: application/json;charset=utf-8 -#> Size: 3.3 kB +um <- user_muted(user_self()$id) ``` -## Managing data - -There are some functions to help analyze the data extracted from the API. - -With `clean_tweets()` you can remove users mentions, hashtags, urls and media to keep only the text if you want to do sentiment analysis (you might want to remove emojis too). - - -```r -clean_tweets(head(R_foundation_favs), clean = c("users", "hashtags", "urls", "media")) -#> [1] "We're into August, which hopefully means you've had time to enjoy content from !\n\nPlease help us find out who participated in the conference and what you thought of it by answering our survey: ." -#> [2] "Gret meeting of passing the torch to ! 🔥 \nThank you so much, everyone!🙏🏽\nParticularly,\n🌟 from \n🌟, chair\n🌟 & , chairs\n🌟 & , @useR2021global chairs" -#> [3] "Also thanks to the , , , MiR and many others in supporting us in this endeavour!" -#> [4] "Such an honour to be acknowledged this way at . I'm happy that folks like , , , and so many others have got on board with my ideas for the community and helped them come to fruition - even better than I could imagine. 💜 " -#> [5] "R-3.4.4 Windows installer is on CRAN now: " -#> [6] "Gala dinner with a table with people in cosmology, finance, psychology, demography, medical doctor 😊" -``` - -With `entity()` you can access any entity of the tweets. -It returns the id of the tweet and the corresponding data field in the selected entity. - - -```r -head(entity(R_foundation_favs, "urls")) -#> id_str url expanded_url -#> 1 1290193576169803776 https://t.co/HYLl6rMySc http://bit.ly/useR2020survey -#> 2 1283782043021774850 -#> 3 1265899960228360195 -#> 4 1149719180314316800 https://t.co/dg2Dh49tug https://twitter.com/alice_data/status/1149680375817494529 -#> 5 974333459085672448 https://t.co/h35EcsIEuF https://cran.r-project.org/bin/windows/base/ -#> 6 974333459085672448 https://t.co/7xko0aUS2w https://twitter.com/pdalgd/status/974214402097508353 -#> display_url unwound -#> 1 bit.ly/useR2020survey NA -#> 2 NA -#> 3 NA -#> 4 twitter.com/alice_data/sta… NA -#> 5 cran.r-project.org/bin/windows/ba… NA -#> 6 twitter.com/pdalgd/status/… NA -head(entity(R_foundation_favs, "hashtags")) -#> id_str text -#> 1 1290193576169803776 useR2020 -#> 2 1283782043021774850 useR2020 -#> 3 1283782043021774850 useR2021 -#> 4 1265899960228360195 -#> 5 1149719180314316800 useR2019 -#> 6 1149719180314316800 rstats -head(entity(R_foundation_favs, "symbols")) -#> id_str text -#> 1 1290193576169803776 NA -#> 2 1283782043021774850 NA -#> 3 1265899960228360195 NA -#> 4 1149719180314316800 NA -#> 5 974333459085672448 NA -#> 6 883221715777720320 NA -head(entity(R_foundation_favs, "user_mentions")) -#> id_str screen_name name user_id -#> 1 1290193576169803776 NA -#> 2 1283782043021774850 HeathrTurnr Heather Turner 3.367337e+09 -#> 3 1283782043021774850 _R_Foundation The R Foundation 7.944582e+17 -#> 4 1283782043021774850 HeidiBaya Heidi Seibold @HeidiSeibold@fosstodon.org 5.321151e+08 -#> 5 1283782043021774850 useR2020muc useR2020muc 1.197875e+18 -#> 6 1283782043021774850 chrisprener Chris Prener 5.075829e+08 -#> user_id_str -#> 1 -#> 2 3367336625 -#> 3 794458165987438592 -#> 4 532115122 -#> 5 1197874989367779328 -#> 6 507582860 -head(entity(R_foundation_favs, "media")) -#> id_str id id_str media_url media_url_https url display_url expanded_url type sizes -#> 1 1290193576169803776 NA NA -#> 2 1283782043021774850 NA NA -#> 3 1265899960228360195 NA NA -#> 4 1149719180314316800 NA NA -#> 5 974333459085672448 NA NA -#> 6 883221715777720320 NA NA -#> ext_alt_text -#> 1 NA -#> 2 NA -#> 3 NA -#> 4 NA -#> 5 NA -#> 6 NA -``` -To avoid having two columns with the same name, `user_mentions()` renames to the ids from "id" and "id_str" to "user_id" and "user_id_str". - -[1]: Also applies to users ids - -## Muting users - -You can mute and unmute users: - - -```r -post_follow("rtweet_test", mute = TRUE) -post_follow("rtweet_test", mute = FALSE) -``` - - -## Blocking users +### Blocking users You can block users and unblock them: ```r -user_block("RTweetTest1") -#> Response [https://api.twitter.com/1.1/blocks/create.json?screen_name=RTweetTest1] -#> Date: 2023-03-27 22:12 -#> Status: 200 -#> Content-Type: application/json;charset=utf-8 -#> Size: 1.35 kB -user_unblock("RTweetTest1") -#> Response [https://api.twitter.com/1.1/blocks/destroy.json?screen_name=RTweetTest1] -#> Date: 2023-03-27 22:12 -#> Status: 200 -#> Content-Type: application/json;charset=utf-8 -#> Size: 1.35 kB -``` - - - -## Rate limits - -Twitter sets a limited number of calls to their endpoints for different authentications (check `vignette("auth", "rtweet")` to find which one is better for your use case). -To consult those limits you can use `rate_limt()` - - -```r -rate_limit() -#> # A tibble: 263 × 5 -#> resource limit remaining reset_at reset -#> -#> 1 /lists/list 15 15 2023-03-28 00:27:38 15 mins -#> 2 /lists/:id/tweets&GET 900 900 2023-03-28 00:27:38 15 mins -#> 3 /lists/:id/followers&GET 180 180 2023-03-28 00:27:38 15 mins -#> 4 /lists/memberships 75 75 2023-03-28 00:27:38 15 mins -#> 5 /lists/:id&DELETE 300 300 2023-03-28 00:27:38 15 mins -#> 6 /lists/subscriptions 15 15 2023-03-28 00:27:38 15 mins -#> 7 /lists/members 900 900 2023-03-28 00:27:38 15 mins -#> 8 /lists/:id&GET 75 75 2023-03-28 00:27:38 15 mins -#> 9 /lists/subscribers/show 15 15 2023-03-28 00:27:38 15 mins -#> 10 /lists/:id&PUT 300 300 2023-03-28 00:27:38 15 mins -#> # ℹ 253 more rows -# Search only those related to followers -rate_limit("followers") -#> # A tibble: 5 × 5 -#> resource limit remaining reset_at reset -#> -#> 1 /lists/:id/followers&GET 180 180 2023-03-28 00:27:38 15 mins -#> 2 /users/:id/followers 15 15 2023-03-28 00:27:38 15 mins -#> 3 /users/by/username/:username/followers 15 15 2023-03-28 00:27:38 15 mins -#> 4 /followers/ids 15 9 2023-03-28 00:27:25 15 mins -#> 5 /followers/list 15 15 2023-03-28 00:27:38 15 mins +user_block("rtweet") +user_unblock("rtweet") ``` - -The remaining column shows the number of times that you can call and endpoint (not the numbers of followers you can search). -After a query the number should decrease until it is reset again. - -If your queries return an error, check if you already exhausted your quota and try after the time on "reset_at". - -## Stream tweets - -Please, see the vignette on `vignette("stream", "rtweet")` for more information. - -## SessionInfo - -To provide real examples the vignette is precomputed before submission. -Also note that results returned by the API will change. - - -```{.r .fold-hide} -sessionInfo() -#> R version 4.2.2 (2022-10-31) -#> Platform: x86_64-pc-linux-gnu (64-bit) -#> Running under: Ubuntu 22.04.2 LTS -#> -#> Matrix products: default -#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 -#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so -#> -#> locale: -#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=es_ES.UTF-8 -#> [4] LC_COLLATE=en_US.UTF-8 LC_MONETARY=es_ES.UTF-8 LC_MESSAGES=en_US.UTF-8 -#> [7] LC_PAPER=es_ES.UTF-8 LC_NAME=C LC_ADDRESS=C -#> [10] LC_TELEPHONE=C LC_MEASUREMENT=es_ES.UTF-8 LC_IDENTIFICATION=C -#> -#> attached base packages: -#> [1] stats graphics grDevices utils datasets methods base -#> -#> other attached packages: -#> [1] dplyr_1.1.0 ggplot2_3.4.1 rtweet_1.1.0.9011 BiocManager_1.30.20 cyclocomp_1.1.0 -#> [6] testthat_3.1.7 devtools_2.4.5 usethis_2.1.6 -#> -#> loaded via a namespace (and not attached): -#> [1] fs_1.6.1 bit64_4.0.5 progress_1.2.2 httr_1.4.5 rprojroot_2.0.3 -#> [6] tools_4.2.2 profvis_0.3.7 utf8_1.2.3 R6_2.5.1 colorspace_2.1-0 -#> [11] urlchecker_1.0.1 withr_2.5.0 tidyselect_1.2.0 prettyunits_1.1.1 processx_3.8.0 -#> [16] bit_4.0.5 curl_5.0.0 compiler_4.2.2 httr2_0.2.2 cli_3.6.1 -#> [21] webmockr_0.9.0 desc_1.4.2 labeling_0.4.2 triebeard_0.4.1 scales_1.2.1 -#> [26] callr_3.7.3 askpass_1.1 rappdirs_0.3.3 stringr_1.5.0 digest_0.6.31 -#> [31] rmarkdown_2.20 base64enc_0.1-3 pkgconfig_2.0.3 htmltools_0.5.4 sessioninfo_1.2.2 -#> [36] highr_0.10 fastmap_1.1.1 htmlwidgets_1.6.1 rlang_1.1.0 rstudioapi_0.14 -#> [41] httpcode_0.3.0 shiny_1.7.4 generics_0.1.3 farver_2.1.1 jsonlite_1.8.4 -#> [46] magrittr_2.0.3 fauxpas_0.5.0 Rcpp_1.0.10 munsell_0.5.0 fansi_1.0.4 -#> [51] lifecycle_1.0.3 stringi_1.7.12 whisker_0.4.1 yaml_2.3.7 brio_1.1.3 -#> [56] pkgbuild_1.4.0 grid_4.2.2 promises_1.2.0.1 crayon_1.5.2 miniUI_0.1.1.1 -#> [61] hms_1.1.3 knitr_1.42 ps_1.7.2 pillar_1.9.0 pkgload_1.3.2 -#> [66] crul_1.3 glue_1.6.2 evaluate_0.20 remotes_2.4.2 vctrs_0.6.1 -#> [71] httpuv_1.6.9 urltools_1.7.3 gtable_0.3.1 openssl_2.0.6 purrr_1.0.1 -#> [76] cachem_1.0.7 xfun_0.37 mime_0.12 xtable_1.8-4 later_1.3.0 -#> [81] vcr_1.2.0 tibble_3.2.1 memoise_2.0.1 ellipsis_0.3.2 -``` - From d2aba5fb4790dc61b3befcac8cb7cd2269d02fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Fri, 13 Oct 2023 16:15:23 +0200 Subject: [PATCH 10/13] Update vignettes --- README.md | 89 ++--------- revdep/README.md | 79 +++++----- revdep/cran.md | 2 +- revdep/failures.md | 231 +++++++++++++++++++++++++++ tests/testthat/test-auth.R | 8 +- vignettes/auth.Rmd | 36 +---- vignettes/keys-tokens.png | Bin 81685 -> 0 bytes vignettes/plot1-1.png | Bin 10640 -> 0 bytes vignettes/plot2-1.png | Bin 26705 -> 0 bytes vignettes/rtweet.Rmd.orig | 311 ------------------------------------- vignettes/stream.Rmd.orig | 149 ------------------ 11 files changed, 298 insertions(+), 607 deletions(-) delete mode 100644 vignettes/keys-tokens.png delete mode 100644 vignettes/plot1-1.png delete mode 100644 vignettes/plot2-1.png delete mode 100644 vignettes/rtweet.Rmd.orig delete mode 100644 vignettes/stream.Rmd.orig diff --git a/README.md b/README.md index 253d2aaa..540cfac1 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,8 @@ install.packages("rtweet", repos = 'https://ropensci.r-universe.dev') ## Usage -All users must be authenticated to interact with Twitter’s APIs. The -easiest way to authenticate is to use your personal twitter account - -this will happen automatically (via a browser popup) the first time you -use an rtweet function. See `auth_setup_default()` for details. Using -your personal account is fine for casual use, but if you are trying to -collect a lot of data it’s a good idea to authentication with your own -Twitter “app”. See `vignette("auth", package = "rtweet")` for details. +All users must be authenticated to interact with Twitter’s APIs. See +`vignette("auth", package = "rtweet")` for details. ``` r library(rtweet) @@ -49,80 +44,22 @@ library(rtweet) rtweet should be used in strict accordance with Twitter’s [developer terms](https://developer.twitter.com/en/developer-terms/more-on-restricted-use-cases). -### Search tweets or users - -Search for up to 1000 tweets containing \#rstats, the common hashtag -used to refer to the R language, excluding retweets: - -``` r -rt <- search_tweets("#rstats", n = 1000, include_rts = FALSE) -``` - -Twitter rate limits cap the number of search results returned to 18,000 -every 15 minutes. To request more than that, set -`retryonratelimit = TRUE` and rtweet will wait for rate limit resets for -you. - -Search for 200 users with the \#rstats in their profile: - -``` r -useRs <- search_users("#rstats", n = 200) -``` - -### Stream tweets - -Randomly sample (approximately 1%) from the live stream of all tweets: - -``` r -random_stream <- stream_tweets("") -``` - -Stream all geo-located tweets from London for 60 seconds: - -``` r -stream_london <- stream_tweets(location = lookup_coords("london"), timeout = 60) -``` - -### Get friends and followers - -Get all accounts followed by a user: - -``` r -## get user IDs of accounts followed by R Foundation -R_Foundation_fds <- get_friends("_R_Foundation") - -## lookup data on those accounts -R_Foundation_fds_data <- lookup_users(R_Foundation_fds$to_id) -``` - -Get all accounts following a user: - -``` r -## get user IDs of accounts following R Foundation -R_Foundation_flw <- get_followers("_R_Foundation", n = 100) -R_Foundation_flw_data <- lookup_users(R_Foundation_flw$from_id) -``` - -If you want *all* followers, you’ll need you’ll need to set `n = Inf` -and `retryonratelimit = TRUE` but be warned that this might take a -*long* time. - -### Get timelines +## Usage -Get the most recent 100 tweets from R Foundation: +Depending on if you are a paid user or not you can do more or less +things. -``` r -## get most recent 100 tweets from R Foundation -tmls <- get_timeline("_R_Foundation", n = 100) -``` +# Free -### Get favorites +You can post (`tweet_post()`) and retrieve information about yourself +(`user_self()`). -Get the 10 most recently favorited statuses by R Foundation: +# Paid -``` r -favs <- get_favorites("_R_Foundation", n = 10) -``` +You can do all the other things: search tweets +(`tweet_search_recent()`), retrieve your own bookmarks +(`user_bookmarks()`), check who follows who, (`user_following()`, or +`user_followers()`), …. ## Contact diff --git a/revdep/README.md b/revdep/README.md index 2bb13d3d..c75ed889 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -1,60 +1,67 @@ # Platform -|field |value | -|:--------|:----------------------------| -|version |R version 4.2.2 (2022-10-31) | -|os |Ubuntu 22.04.2 LTS | -|system |x86_64, linux-gnu | -|ui |X11 | -|language |(EN) | -|collate |en_US.UTF-8 | -|ctype |en_US.UTF-8 | -|tz |Europe/Madrid | -|date |2023-03-16 | -|pandoc |2.9.2.1 @ /usr/bin/pandoc | +|field |value | +|:--------|:----------------------------------------------------------------------------| +|version |R version 4.3.1 (2023-06-16) | +|os |Ubuntu 22.04.3 LTS | +|system |x86_64, linux-gnu | +|ui |RStudio | +|language |(EN) | +|collate |en_US.UTF-8 | +|ctype |en_US.UTF-8 | +|tz |Europe/Madrid | +|date |2023-10-13 | +|rstudio |2023.06.1+524 Mountain Hydrangea (desktop) | +|pandoc |3.1.1 @ /usr/lib/rstudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown) | # Dependencies |package |old |new |Δ | |:-----------|:-----|:----------|:--| -|rtweet |1.1.0 |1.1.0.9009 |* | -|askpass |1.1 |1.1 | | +|rtweet |1.1.0 |1.2.0.9006 |* | +|askpass |1.2.0 |1.2.0 | | |bit |4.0.5 |4.0.5 | | |bit64 |4.0.5 |4.0.5 | | -|cli |3.6.0 |3.6.0 | | +|cli |3.6.1 |3.6.1 | | |crayon |1.5.2 |1.5.2 | | -|curl |5.0.0 |5.0.0 | | -|ellipsis |0.3.2 |0.3.2 | | -|fansi |1.0.4 |1.0.4 | | +|curl |5.1.0 |5.1.0 | | +|fansi |1.0.5 |1.0.5 | | |glue |1.6.2 |1.6.2 | | -|hms |1.1.2 |1.1.2 | | -|httr |1.4.5 |1.4.5 | | -|httr2 |0.2.2 |0.2.2 | | -|jsonlite |1.8.4 |1.8.4 | | +|hms |1.1.3 |1.1.3 | | +|httr |1.4.7 |1.4.7 | | +|httr2 |0.2.3 |0.2.3 | | +|jsonlite |1.8.7 |1.8.7 | | |lifecycle |1.0.3 |1.0.3 | | |magrittr |2.0.3 |2.0.3 | | |mime |0.12 |0.12 | | -|openssl |2.0.6 |2.0.6 | | -|pillar |1.8.1 |1.8.1 | | +|openssl |2.1.1 |2.1.1 | | +|pillar |1.9.0 |1.9.0 | | |pkgconfig |2.0.3 |2.0.3 | | -|prettyunits |1.1.1 |1.1.1 | | +|prettyunits |1.2.0 |1.2.0 | | |progress |1.2.2 |1.2.2 | | |R6 |2.5.1 |2.5.1 | | |rappdirs |0.3.3 |0.3.3 | | -|rlang |1.1.0 |1.1.0 | | -|sys |3.4.1 |3.4.1 | | -|tibble |3.2.0 |3.2.0 | | +|rlang |1.1.1 |1.1.1 | | +|sys |3.4.2 |3.4.2 | | +|tibble |3.2.1 |3.2.1 | | |utf8 |1.2.3 |1.2.3 | | -|vctrs |0.5.2 |0.5.2 | | -|withr |2.5.0 |2.5.0 | | +|vctrs |0.6.4 |0.6.4 | | +|withr |2.5.1 |2.5.1 | | # Revdeps -## Failed to check (3) +## Failed to check (10) -|package |version |error |warning |note | -|:---------|:-------|:-----|:-------|:----| -|epitweetr |? | | | | -|saotd |? | | | | -|tidytags |? | | | | +|package |version |error |warning |note | +|:-----------|:-------|:-----|:-------|:----| +|bdpar |? | | | | +|carbonate |? | | | | +|epitweetr |? | | | | +|graphTweets |? | | | | +|imgrec |? | | | | +|saotd |? | | | | +|tidytags |? | | | | +|Twitmo |? | | | | +|VOSONDash |? | | | | +|vosonSML |? | | | | diff --git a/revdep/cran.md b/revdep/cran.md index cd44a0fb..9651f2e6 100644 --- a/revdep/cran.md +++ b/revdep/cran.md @@ -1,6 +1,6 @@ ## revdepcheck results -We checked 13 reverse dependencies (10 from CRAN + 3 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. +We checked 13 reverse dependencies (3 from CRAN + 10 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. * We saw 0 new problems * We failed to check 0 packages diff --git a/revdep/failures.md b/revdep/failures.md index 68b1e78d..3a2bb10c 100644 --- a/revdep/failures.md +++ b/revdep/failures.md @@ -1,3 +1,69 @@ +# bdpar + +
+ +* Version: +* GitHub: https://github.com/ropensci/rtweet +* Source code: NA +* Number of recursive dependencies: 0 + +
+ +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# carbonate + +
+ +* Version: +* GitHub: https://github.com/ropensci/rtweet +* Source code: NA +* Number of recursive dependencies: 0 + +
+ +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` # epitweetr
@@ -30,6 +96,72 @@ +``` +# graphTweets + +
+ +* Version: +* GitHub: https://github.com/ropensci/rtweet +* Source code: NA +* Number of recursive dependencies: 0 + +
+ +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# imgrec + +
+ +* Version: +* GitHub: https://github.com/ropensci/rtweet +* Source code: NA +* Number of recursive dependencies: 0 + +
+ +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + ``` # saotd @@ -96,4 +228,103 @@ +``` +# Twitmo + +
+ +* Version: +* GitHub: https://github.com/ropensci/rtweet +* Source code: NA +* Number of recursive dependencies: 0 + +
+ +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# VOSONDash + +
+ +* Version: +* GitHub: https://github.com/ropensci/rtweet +* Source code: NA +* Number of recursive dependencies: 0 + +
+ +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# vosonSML + +
+ +* Version: +* GitHub: https://github.com/ropensci/rtweet +* Source code: NA +* Number of recursive dependencies: 0 + +
+ +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + ``` diff --git a/tests/testthat/test-auth.R b/tests/testthat/test-auth.R index b68fcd16..5df89a04 100644 --- a/tests/testthat/test-auth.R +++ b/tests/testthat/test-auth.R @@ -54,6 +54,7 @@ test_that("is_developing works", { test_that("rtweet_user works", { skip("requires manual testing") + skip("API v1.1 no longer works") # Avoid saving it but check that it is redirected in the browser. withr::local_options("rtweet:::config_dir" = tempfile()) expect_error(rtweet_user(), NA) @@ -66,10 +67,13 @@ test_that("rtweet_bot works", { }) test_that("rtweet_oauth2 works", { - skip("requires manual testing") # Avoid saving it but check that it is redirected in the browser. withr::local_options("rtweet:::config_dir" = tempfile()) - expect_error(b <- rtweet_client(app = "academic_dev"), NA) + expect_error(b <- rtweet_client(app = "academic_dev")) + skip("requires manual testing") + expect_error(b <- rtweet_client(client_id = "", + client_secret = "", + app = "academic_dev"), NA) client_as(b) expect_error(rtweet_oauth2(), NA) }) diff --git a/vignettes/auth.Rmd b/vignettes/auth.Rmd index fc8a2572..eab0cf88 100644 --- a/vignettes/auth.Rmd +++ b/vignettes/auth.Rmd @@ -32,7 +32,7 @@ If you want to collect a lot of data or implement a bot, you should instead use - **Bot authentication** allows you to create a fully automated Twitter bot that performs actions on its own behalf rather than on behalf of a human. In either case, you'll need to create your own Twitter app (yes, it can be confusing), so we'll start by discussing what an app is and how to create one on the Twitter website. -Next, you'll learn how to use the `rtweet_app()` and `rtweet_bot()` functions to tell rtweet about your app config. +Next, you'll learn how to use the `rtweet_app()` function to tell rtweet about your app config. You'll then learn how to set the default authentication mechanism for the current R session, and how to save it so you can use it in a future session. ```{r} @@ -61,22 +61,7 @@ If you regenerate the previous values will cease to work, so do not use it to ge ## Setup -Now that you have an app registered on twitter.com , you have to tell rtweet about it. -You'll use either `rtweet_app()` or `rtweet_bot()` depending on whether you want app-style authentication or bot-style authentication as described above. - -### User - -By default `rtweet` uses a common user for all the users. -Using the default authentication might cause problems while using rtweet. -You are sharing the same resource with many people! - -If you use rtweet for more than a day or a learning how to use the package it is recommended to set up your own authentication and you don't want to set up a bot or an app you can use `auth_setup_default()`: - -```{r, eval=FALSE} -auth_setup_default() -``` - -It will call `rtweet_user()` to use your current logged account on your default browser as the authentication used by `rtweet` and save it as "default" (See [Saving and loading](#save)). +Now that you have an app registered on twitter.com, you have to tell rtweet about it. #### OAuth2 protocol @@ -96,7 +81,7 @@ The callback URI is important as this is what rtweet will use to validate. The clients are under "Key and tokens" tab, check the top. At the last section of the tab, there is the section \"Auth 2.0 Client ID and Client Secret\": -![Screenshoot showing what to look to get the OAuth credentials](oauth2.png){width="548"} +![Screenshot showing what to look to get the OAuth credentials](oauth2.png){width="548"} Use them to set up your client app: @@ -132,19 +117,6 @@ This will prompt you to enter the *bearer token* that you recorded earlier. It's good practice to only provide secrets interactively, because that makes it harder to accidentally share them in either your `.Rhistory` or an `.R` file. -### Bots - -Bot based authentication works similarly: - -```{r, eval = FALSE} -auth <- rtweet_bot() -``` - -But you'll need more data --- as well as the *API key* and *API secret* you recorded earlier, you'll also need to generate a "*Access token and secret*" which you can get by clicking the "Generate" button on the Keys and Tokens page: - -![](keys-tokens.png){width="362"} - -Again, you'll want to record this data in a secure place because you only get to see it once. ## Default @@ -163,7 +135,7 @@ client_as(client) ## Saving and loading {#save} -`auth_as()` only lasts for a single session; if you close and re-open R, you'd need to repeat the whole process (generate the tokens and pass them to `rtweet_user()`, `rtweet_app()` or `rtweet_bot()`). +`auth_as()` only lasts for a single session; if you close and re-open R, you'd need to repeat the whole process (generate the tokens and pass them to `rtweet_app()` or `rtweet_oauth2()`). This would be annoying (!) so rtweet also provides a way to save and reload authentications across sessions: ```{r, eval = FALSE} diff --git a/vignettes/keys-tokens.png b/vignettes/keys-tokens.png deleted file mode 100644 index 6cd57552883dcd166d7c4efc84d188aab603d7ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81685 zcmeFZbyQo+7e9&>N-0tSTD*91cL~MagOe6_cMFtKti|0zaf%iQuB9y!++B)0#UTV< zdhczoyx;rd{qxp(Ykk8?ax!Q3nLTs%Z2cU<)l_70?~&X?K|#Tlmy^;!K|zl}z76kU zB3qtY0CrGN?k!kLN~+0AN>Zu0I9gcSnWLb{g(oIsscCKz1rD}sJ^OHnAm*XZqr67| zg-z7QdBH_*XaS6zv>1dcyAMPwJ-s-4t;N*#*&^}$!YE4KEcE+j2^fr;FK-s@*!Ji6Y zpjZ}uVk=jP#<@?re8PFd^|?6roeueFc=%D*)Z;*<6&JcK6dvEcfxeA=Q)jE#C$o_k zFCKh@ah|Q4J6%38^?s$GGSK{hqJ8S&F){iOFNa0`&2x(@(Dy>7@2(A7_b7I3wYpJX z6bqy`9o~wh-LeW!SN$McpUN?pdy&IY_C9klxYYaAJ)-=9uqT$!z+>P9ZnI~fR{G+v z7~XS3NC^t2(nV?fGDhD>kV$8VsPqZo_*p%Q9BkD+vF?NDGaOG88jgu@Igl5}|C=*9x8Ktls&wVKb z+5=ze0v-uv`p#2eNSe2 zH}vJp7c6Fy6mo$$5>yxu?jB*_9Lc2pfG`*{J}9tU<(!^+|A<47I&1!ML2gSfdV4p5 z98SAn{eD4FYG?b6y*6*)BzA*gfXinhpGQkp_fVh#pDhGQ0Z`(lqvu&lapD%8(IR&N zcL;}2$T?6RRbm)8VdxxUfbPCg3k!TA_eQWi_69hl<)t$!@*mi-eYN$;w@yP-^ zo$iw?Kh6kNbi#G`Y~*!6a|pfSCBZ@`$7?FuH&Gk}La&rM6B@_ZK}Nj%4?&fj&qIv_D_2agq@*Aj8>+j%=q}9&vg& zr(wuheqSe~>3#1ioG<1v9+;yu>)}qu|9H?Jql)$RWyQO8B#>C zI&rXWtRBnmy6%?MR}oBK-+h+|&pv#P^L*#I@ALiy(gccL4(<0g5~n%e)VPb8i`K^U zx23lqZ41-U(V5bD=BFyW$X`=fQ;Ib{RKJ|5GcFl4v2esDxYyFTMlY-B{)Le~hjRF_LwYR;2 zOw!|q6>l;@n^hm(}MNi=ts@yLxXK}#;(TXx7W9Ww*5|DoQiI5j5j?M zc#GW$=`bxd*)=ybJFz0*W#JJZNhP5p3FV#P#pE65&E#F-A+`eAWKHajeYQ@ub*`y` zPS}!LT%SBSsN zHea%hv96q6dMDBLtaE5q&8mL-uyk3mfIhyz&mxJNodfIr$-5Wt%w(G0&%A&8p7ovS zdmWO=r{cUH(khikl=#wX((b2`q^a;5+3A~Jb-y$D#s%Y;wVALu>zMm2vfu~x&h>_b z)ZXdJY0vrgJ)CZSq>F2^s`n_0a5%y|(Hz>jUBGLzXOj8K$lAXX6Y30oFtb~^mGUCx zyO=ajzD=*cK^p5U-(u~zT9^uqZO$RzD0hCRxIi<$z#(IX?=-z7(!UmA4DZ6*#`}QB zOrA!*AfzvRM8-^hATXTyIwMJtSFpvo)|p7)z%5|uvs3DR)lB~QT1iQI$rtDZbg_So z^{ai;tbYM2tR zQVJ1@Gs!$W%uW+YdZImSJ<>hr76yIB9PnPUdBJH{l|k)PSf;N?NX$cs3&$D`|7(M< z=MP<{Qb;jJZ0?j%>J4_;J2zWdfx(vK)TQT;-AJf zFds_-r6z7zygmFpjPL{*=r-QS870s)atyv)cu2`wtTsk=HC|u8 zm(%UiH6>FkY9`mRaq9u=OD(+*kI z>9`ztiKx!O6~%4KA^eP2#kRKW64=Mys&4|)03MmMI_`~Ak_9l{O0=@s8^L`hPlfpj z_`~?-W;d&U0E+dkC9Ka2w$e%XxcP}4Z%koxtUA3JFc15DheZ257JPQc8H+~y&RTZD zr-Yu&2&SFd9eYqUq7&~X%0SrPb7FsFQM+pAIQlas?Uej|U$epL@n#(czihX+!YH{_2gU zOd*r1ZI&_ktxv(>yKB3S=?_HCu=a)%*ZDG614@Xh0C?kbAI=_eC!2BWW4A3o(x$y# z^9opDxtF1!bD}%+N7@eb?GD@I4%qoJzjYqkvix@TeL?y@f4Xpidq%^W>(1O>;B4}~ z)4pMo@QOoM%akwvKEvXHUpsi{oCCg`E_)gMmXaO(!b{^?>(qLhyTwwwO&_5@xi#b6 z$eg`%b-cL|-Q+!A5fFcKmS1>y2O+Ij@ygL1V!Bv-lYNuUZrQTyO&7o`qIQB01>1q` zFRwNh{lRTwDI8PBGDk8u9(%%>p72|zn_PEBm(6K-#(5`Yg!s0f$YJ`FH`td0`O%EOAgv=?9}rh@VEB{zoq#FU>XQ}>j32V)2L z`=_UVT85}LoV;8lmvty)9Z8oiD1@NE*iVe#h}9SvzFsBEjb@N_nG9EM#T}}%feY~g zHkcPov9M6j?lsW4$yYH9s~+<79oa7^@UE16%=&9meCmz%MRU9LTg_KJxqL3+YV%r2gw0J?isznx-!1<_@k_j&67ooubGFY$rKAR}>T?x}R@Uc@5e_6cjX9Yb{+jU1cR9Q%8HY zS7weT=4@W}PCu`MBH|^4JhV4=dqw4CZ|C4D$Q>nf{J zNjkciQ}MBJuyIg}-J_zS5^*uJ5Ymv6`HLL+OO)Em&CN-Oo!!&Zlg*Qx&C$h@ol{Uy zke!2zor{YV*@M;9+rjOX7psFS&7T+fbsZ^lS5p^jCpT+H2dbafeP!b4?j}l2{d1t- zuRr-T_p<(bBnQ{OriGj!`_D7%oNOHI|0gmxYm5H}vY%)EB>Q7te-0<|^I}5UuI4V1 zj`sHE4sK$9ueit`BmL{--+cZ_sAlbDZl@<@jihu%&PhywkK-?@|DO3jQtJLqDagt7 zPs)Fs`3L3CDF~^$SR?s-^>YoyI7Qh1Z*BkWuVL=$Xy^X3yS9V1n;3GKKS=)5{1=r7 z`_J3*kK6R84E)iG6auk(BJ95fO6=b5Wtlh%3IIi3NsWo(S7{YU4B?}1TT z2(_+S0yW=bt;eKH)M0CSMM;HI%q9}*kT9dtu-9Q!RE#DcaB_;W(G(8kLe0I;&3a-| zFbA00Jt)teuQ?HDiG!G4^-OofBgE{9>|W0CI?b7d-1QK2&e*ADgHCgAsql^D>7W^ z-&aJ#h?ztGPg5(BBY)0V?0+&(00=~h^ZoZAfc9h*)I4^2u4n(`5$!Gkzy<$bY@^-1 zct{m^@>PodAItG`#%OZ;{~iPd6@=udHKqH_zvl@+=@I?+AXH0=fhZ%yk5d1$a>&KR z`cKpU7W03L`KO5gPcr}3C20RIwmAS5)Ii;L0{{N?Xg%E#c7grKtUnFvveX__`4ESY zg>i2Nnux=`%d+7nU=TsbOq3LQ=Pqq<`$CIv{$k)Po$2jO<>=OIT^*0j6o;Mn=4;Eh zA`FU2EZ9bN{;jLu*+yCs*5y08VSkG$fEMjiiG!G;Us@x_bQVIymf(Bf4+u!%?EJ(7 zuQP`w#L7|xsIfJye&6MeM-LLAuQcBQT2F0SP38uiXV$ItajSS5S+e`>o${}iHywNu z$7}&s0F~*L$=C^*f%o+XLN9Q3XOr0sZszF?>gU?z5aI3$5do2$#b5*4k7kpnS%u(|-J za1w>)NJsXD)h*I`xs8VpelELpNKQQ_>b(XL!K%Q(vk3BU?yYk zMa|ls-zv9T5sed@3KFoc=WTKr|3FwNRY<$Mv9amh^^)jwjW?iu8qEAa5!On-OP4M8 zdEB_HED8^b%cHe;VhC7;gkOx_jvU8?z0!BkIQ8Q6oXh>nZo0`4vVm7;zxaH%_LfyK z>4r7F06}G0D=I&e;g<23O^RwO{M)_1^s^jeP!U$lq93;rrue1Sn2O7{cd0Ij_xwGx zj;$0>uIG1yn&~S|yYRLqS_+ziH~ggiiwp;x3)G6w^zRTEF94IWAM#9Mx5|k{9s|PC-(-dtff)m zvMS%yH#;Hfmqi5^Jv+Gj1ZXg&z zU{O}sdU?RrTkrh@!n?FF02MgEhJVf0=PVBUQLB?^B+h`is_|2?zCT&@`)c#_Y8zhV zs$-#vAQ@+Z@g`?6jF$j=UAmZcD&qNN1L&E7WzeHUCOt!kE$@agp4s?c_9aM)D9i@M zd$8pkcBo@~Dzb=*fGe~G_#43yn}NbQpm|APnekp@3txXamv@epX3K6Ip7AG-JrX^r z4Rn{2#u!+vq4!a{jOlA6@u0&fS=nN>Wpt@wg95NzpN>x9aq?SS5~&*eG9&#uhfKSv zdU-A`t~|d67mZdw?_xv29rj#7q0J|j3k|M?*NF4QZ{WxG052 zuOAPmYpmn7_$EIVOtZzQd!06x>VDDyR_K||+m3(KiDyb=Rt!QLE_0gH?*QLib_qdk z#y`UMOVUSCG}41@tDWk`O06|7?RGcG_b*_H(Z!Bf>G?i-x9BhI_G_GUv_>xd198Y1mntcfnKY&-KM5LX_2Jp)qF{$b9f z@ag(&F(08y*z+=#BnC`TdS2U!`w4}PVx;E{BBv+Z7D5r?uWO%c6~$!us$dQKu7}ZV zW7@BrN*pxdi(kvhyz`Pxy8a=n%ZGRkK9XnckA6I#XWHdz1pZRV-@9+vc43kuxc@9i z+-FL@0x>tuU79^@yndP%kD~FuW-)j6WltiLc3jURhOh<70F6AEYK#6P)Vjs3#*&?i zCSaqHd;X4)?{WySksU(71Y8Rm6t9S%3orn~b5BXiHmHk+7{O1Dj+&}2D%OC-lg7h( zQI6|kRSq6anbj|2o6i=WFsJkRw*0V>feZ}1JN?UVbh^OE0n%~%U%3q~{g|@o&x52c zvNh#_!Va1dd?gK81`7?x>kXJOj~JAb3>@}Y4eD-oYV}^G4oF-!V^5q^OhQH1I zKy7w*{p(xGjay`7nvcr~$!rgWH-E@V2eVk}S6$#wni{tS1aliq!6D#F>00|)qFTS5 zq%sLu6<4Yl!eDD*MSqw|j0b7I6k9X6v}dwB-tud96#8*<}Ws#Q8dDsG%& zv{5cYr&SR18ibGhgfw{|IF=2=ez&6)ifA1x&dSk8_bK@8W~f)_!KQs@u&{-Fkp;qA;rC5-*%Bt^0%S%y~b|nxL=i^eYnY@xoC@fBuG9O&rYv$&nR(iBOjJz z(3NFgz*sg{@0@GsudF(ZGRKPRz5aZXQgiM|1}2xTS!X>uV^5qDY-G-B9*r5|AfHa? z$;JEGqnCN`YrXuUZfI%03j2Xyt^dWViul5Qj!~a$yE~!reZCcW=2m)Xt(UK|boi2J zei{2vqn}zyXYaL&RlxT1GCzn_0Qc)<^z%jAaSN;WZ;vYyt?z9q=_}?dW6!6Z3m$v7 zK)5Z-PxkoRZQZAiVrgU>CPdzvi_`<)T@9z$<6H8^*Nch`Vh-~+r<02(Gxmnb;FDoS zX2z3hP5Y#&)83L*1isiw{Csb<$(lj~BC5&o7<}Dh<$c!hp?s&xpeSfsnSasdbh3}F zqQyA9T&5{$m~Hm*V}FOQv2Tmz(j0uk0O>pt3-bT)qqNUuBlhr^Pd+QYUdB2=*(#}M z^_W*dAE`2j>F*r%x_$~+vAEn=TH+^$c*-RC6Ty<$2n7yG3$}*>qVu{PkSIJOcU|#CQK#%{j!w}hJ1=cE4_cXb z;0t0Gx=*YhnPtY0m({9Qj9M2$t34^b#=Rf31iak#>$nW^^ImJvwMwh7VKQ5H7~2x6 zQYg&yd2L449rL=N;B+i$A5pir2W4S(Jy`J1feY**0ckX42J}n zlZ@FwV8_Cs$H3!!)Nv4)wcmv!8z8|Mkc&Px=Q}uWahM&Y@zKrtasb25AC7<6EaoFX zr;rfCpadH5*qhOxv`WKHYPsUvUu**rv0m{OUFJ>fZolMl*jf&y;OIDY57O3`M0Dpy z8%#1*9xkP)a$AN^NKt~Xoj;U_i0|C}^wZmq5-txRb01R&a*vANTySra_&Q|tNANFr zjc#*qC+_9nw#4ie)t=RU;uzWSIMcK2Ei@Ic_I%`p*xqqm?PxEQbUdN(ur)C(dMU9o z^(=<7LWHfA%GJU$=N`3LqG*Oo;Mx&aBViW+;yHV$yPL( z1L~?{8-m^^)2+tT@@V_ENSvamu%P+G(2jG|!|J1MYw2m)xOm(3O=$DQH#ufSh_TUH zH>O@RlU;IyGq_vAaGcVjX^vaNnsTMT>A5*4?XgYUC=C1FXdIIFCtHk-Xk&mUdv`i&NX zz{3x8BsHUL8(}z-5FB`kL7j@#L_K(-ed?*m43^Vp>@EXW#~DawMHhvyo!X&Qh4677 z_h*@8=%k=+Wx%y_tL-g-JVqKm*hq&?KSB|{lH0PAKSFK!)z-} zQ)#K}uhbS^BtXlC3h9rNi4N`EZeH(lnGaiK$sFn=@OK}Eh| z3m*=P_&2)}<&n9#0Mv5*{D@LRD3jf3`Fs;Pj3>t0`bwbj4y({m_b1ke-yjPdT|DdYj&l>*ezC-^zn-eiUpK8jPS+zUsux8PzdMBx3K`4JJxsm_PA6`ItJ^ z+_=D6W?cPEzYspiwBaw&gk^!+k=p9JT(q-o!d@5Wp~Ms3(hor|g3d3Yg9h z`Z_ZAwEnI}!9|~hZhP!#vpG1Ep1qdDmm9}xh z=gjjJLa)w49CBzJWrWK(K^OCJ0nc$WOT~CV!B?k7=yDYjff=*5fCTN2CiHHeI8>5# z){EttwkXAyHCrF>cvi^le)_d(HzAo)>E1s5Mo$`}z5L5Ho02xlPkid>nglCm8{_Cc zZ}i~wCyzrLp`MFFyVC&^ttC5?jxoFIT6We~V-)*&=b>3EH5IK1ada(ZCiZTz*59^? z05%|?s0{91-f4*Byb3=wWd<^miZpQQt%eqRJ8zYzCmDqX2M2wd`bP4bE6N})-NC)eJR$pmwp;=<{{TFpx z3oYqZ)mm*^6>Ukqu9A>skG;VdO@q2>8N$;hd#?3k*D@EM1%1sVqmM=<79M&zwZ>q^ zp_^t0>#q@nF6V&rf<})UT~esfCGBLO0hr^;die#M+QH(PIV|9vX(t!Zjr zdWUmPVhszcre5Bma=C95`At+X-nh3p6UN26YG1%@o<@H z-kGEr?8FmS8EGZ0{LI#))tb(C6CnY5WIiMfH>Kv*scFb82G!(R<+IB30ai-T@1@kxIGMrdUdeS;$D1$oCZv77>6za^CN+? z2Q^n_jl9@Vp|O0X{?=26m6GrorH$b_RmV%D$7Xn&o;~OcgkJveV*89IgoVST&B%be zJrPHzc$*XpQLdE%zONblt}+VIfQ@Li*w798$NZfcczpMUh`i&$HnC^Lx{a#1sMK4L zBHv(Dpd6IQN z-h%o~f-Gw%t*1%t(X9G3=X2GAaaHxxSla@F9p&&+=?n=$wK9#vi1b5XHtx44t~Yrjd8j9`b5 z8>{6x0Bop=$6X{B>PmR5;3q6B2DPO%j^MPPk*W7>%Y&+mI#8(~ zRm+w8&0*)ecJ)>ErEx5GZz|Qzbjl6(yG98NV!lQt(vbnsG|`Rl_(Os2p&*}+Iwkbc zBG)mp4zcp_Z3zz!nn`QLjAe+8yk1SUp6@9pJGQ}%TU}B|B(VkrSKf_w*7n#)&<;ui z*JP^pFDzsgS7^9xZ!HlQ)hvfry)!vmvA|Y~E}j#+q>r;UPt&%?i=XwA@<*%=O%eSL zvWfv?HdLpI#_4=z=G$WxpYe2UVQg*J_nz`@OIpw5$xrzhWg|!_DS32_eR^6j<-t9Z zLBc$lbK7RNXh&rR`2C+O$~bQCH*bguo+bdxH9>RE8{n_@r1KMk`GH127Go&TA_~Ss z-JiybyF*+MTx2*~9qMOlL&BhRLA~oVQEEi03vA8sjpxriMBKplah0YV9J7@Tu<*}x znC%?n87s>APRFq~B`ek(xQ%*Y7HN^`4voX7YNuj;o6EAFYe&Vrqs7m{cKE_8)e653 ziB*7qwh|83bXKbCj)ZNciuf+7GK}w5q8Ku{j1gHr_4pllTvV zxY!u6J|d27qA$driqf;Ru??-e6hdt`8rf!Wc)i@G0zmaS4^+A6ukU-GlvrYm1k>^&xmbZpr zy1Le6`68p^`)j47n@mARyV%bmt8Yu#ld8-Wq>l;ykpgH0XrXuKMhR(LogXj+XFb{V z$<~+5d-P!kGVrR9_d!5`>QFP{>I{#m+H%m=Y*jYbkRcRuz4lnJpyyLg{2Vb~P%7W} zT&rj`SY7QImiWo$u+crDcT6F(4yER029WP0fbDjH`SR9pX>Sm#@hVj|@i#YcM_6we zPzT3$;d-h)Kt-%L-&Rwm|8V|X{HIHlOu}o^eRJXla@EGXr!vH^&%NV;fLffsC3WO- z=*&MYF4R4KZA01e3GzPvXL9n?&gXP{t5GDDXgiug6_0|prEf{z!8!?>4dnF$oa4{9 z_n4iAT#11vuqL|W@+0(HVdlgDGIb~h9F%^E#p$D22jp$oD=1kDnG}n`O|R8OUCteB zrSGEjts(POqA50=NMKThKctHjDd&Q{7$!dxmKq?a7n$Li4+03kHDE5jVW}& zZ=X(Ul+D?IdEh)nHTOVmZrhE2B z9AOYuZdW}%SH5zbs^g-u8wr8(53UgjX9};wXFC+t9=ksV1<8eU&x>Zabw!CC;1Oq4 zy%t}_g8a)9ix>Noo{w6FM-^RlW5Oj z%)br3ZBUKX0RU^{Den>b)>yIW7in9)PEdgSOs)NjKJYfcf3=ScuX~wAPWB_&*+^s- z$R^V}_wuH=FpJ~Z0@&>yJHvm}@N5v{ly20b9-#zCjL!>-pV(g?&x4LQ9AjUJLWJCH zU`+?jnJWzfh$PBLzu>;>55_d5+tFD3^DZS;_O+ScU*#$1s&XRYsuic-w~|C|P&b)I zOk7b*-!lkW%DrvT8xSuk8!Ok-c1xwlCCltE#pPBGjjFk7-{bH7n%S2?FC8hh!@-F4 zTcbAs#?a+NO)^*Zc)2q_@73YPT(9e#&CzDYEyLtN-3=CQI>=|! zYk8Bmdvyyj$ofHqig#>$p30S^EV7*AS~dM@(4bc7srUNUp}r2h-EIGC7pCYda7|X5 z*ArxbQyM4Q4Fr0doEn-z2dyebvwvias>a?fj7Yu}5ddJ~?y}aU)0emSx+nU=jTltL zhd`KTd*+yP)~su=q^Ps~=lK7asS;At+8|z31kKM0a+k4R7(?j?xW}l}Y`NU_n|^wU z!?KiXhI?1Wb;AJd;C`fP{Dk?%x!;P&#@PP*d7AY@;q6>}wBVyR>+EKjHBO2l_sl04 z>&r&XXBJ0T8{P{Qqt4Y-6n0ljX4Ts0wqSM)AB}0G(}=y4(aaQF5iY?loeij=EX2e) zUvRCndZbgA*KY`2Kieq==>vtlq7Clx-bEm7Sfc#1(XMe;?>)_9pZ%_tv`yZY#G@bUFdNYv$2n(!xJnZ85lhW~veu5I$OA<;U2*Y0U&#H8pdi;V4jMRQJx ztNIZ9Rp(n+L?Saf%;eQ$#_OeY74m8a=b_@2hOE(R0a(OW*JT#*g#*Q8rf0Ha<nT6#_k-YO330#wP+=yxjWPgx^8qwfPpFObtPu`m#<3_WkPU> zKc%f|^YYpX5lR4q5)?nZimbU@K;}}O3yo}k)GjGq5C7UvMWpXqs7&;z;hQ|e+uPH+ zMf0BTbQ)7*W{-I~jPGbl76iGe7}ZcNMO*AEnAR?n3gLB=hkHEiDLeOmx! zA*%re*S*O!YZV@=Qp4gn-ev#O?QvH_O>&Q5x{lYnPqxBZEwk3<^~~VBK0d^ zCg?UmQ>P5!b<7l#jYL5cBpmXVcc-0l!k zkST0_eSuxo*__0v{-QUL)m9llcQkLvkXN3Kt1#$Sr0Ipk!1%ORKKVW3AB#n$i@Z_? zh2MX$I)_NK&Pm`a&R>f3&vZ1P5(U+)5)1P8a{X~6g2d*CzSMp9YotGMHUumHl&8BL z48N?>U-t$d39!L`Li9Vq{b#)f1BU>B>3jJ1%Kp*2T@neWL4W)8KUkm+Bpl~|EdOtn z|0fv#|J{D~yjY^GexO#L-XNaF#X1AH@HQD@(Xl{f_}9O++#eF2R|yakJ@>n$Wo3n| z))|kUn0_wwqpyU$JIrw_A`vLCG{cNmXNoiVmre@GBdSE}so{AZBmRjrT!uApjFHDE zxa$NV@Bd0r@Lw8<3W!DmAnJyQzp|ytC0ckWg3RaT82+=193d)m`3$5!(M27b>O7sk zWK5)>m$l#fcclfXo&YR&xPrUi>F!fcn#+;_W|VbY{W0u*hm3#iRod)CH+Aw-`l6FL zaF{?~jjTaG;#no#zm;sDW_#N3XN=hJBx*dW(=T%WQGUaOQ;@(hyV0?|G{XIfo!tI)ZNM2?_>>;U zcN8x8C_UD{nF4Sjtp|Ol_A1%G*I5AB{0%kL=+6oMDq8kX0Z>x!>%_YL8}9iX+58mC z%;UF7`h$u#7->|v45bH9|7DrNem0ZEOyBtzBQf`py1&PXP!Q{%cL+d*hirBRSP=hv zLjPm>Unco~EB*hScmRUT1hD@WX*u^G)+YS!@3>o40kUuHX50a_yS;w%cEHcQQ_(0PD$;}?ObQ^`HDi7Za~{9f?L3a7 z(Bg7Q2a4#5m&PG}_l9SC_8~wfGXd?FvIv^O-c=E&iocy=j!)W6gwArC)X^x&P@!Tl zCCkNElRS>&-XV^ozN5h%M;oXVNE$2odz&AnwJ#vB%yO_vx>#mtX3x|nQzpwPyGT<3 zJ)bZ0j)z0m>>eo*^+NQ6Xi?3~&hZR>yMfQ?vOj!3hn@F?QF^Agz8@1P82h?m)``)Z zdJ{LFMjQ)-;wXH46N?e^sty(DEB@t7V$5Lx9bqxc)gC?UJ0tT*e;Qeu5Gc?+jz5zC^^K$k-gcXb znWA-@wKisu{!%>zAKJx672GdMGr8=a)6C1t^{hXq9;$Va&?MDUx<_wqj+{-zTz3Zj zFM=4&Akl(a=RuYggrJuMeClr z(h&MJ=#t{n`-Y~~F6~s_=%;?zXXY~W7+L@bvQR&^Aq%0=u)A}Y%5+C@>(?d#)S?N! z9H8ay%Du$1C?zd#v-MN{o0U3+y+P?T| zT5D(qXxD5H2srT`*@$>d`jee{4W?i0#FVv{`(v$I@70;h7}Grq1h`@R?5F@?*Em6P zlIy4t{)>}9_MkfO94n~@f#}s|4iylJFW15vnqk`jJ{`7a)( z)SE?@PJgS@2kDxUu({T(@nZbWX$5e%A$2x`F_OdF& z-O+%@7;gIb{x@>5IFG}c!*>P*i|m$AIC>>yLgXg9os_5hoO^a3BLOcvLhoJ`}|QHI7ELs1kvp$=Hcpa zFO^(sm(dSiZYB)b}bx>j5Ufi;ir z7+hULi;^^!Roxt7Vl*@q4&znD@PrUQR=QyM$;Ko#EGNOmR4_iooJY!n{dy7kI zzB#m^@t)pcqX~Q)U_H6dGd#K8pAL&;ZzZVnuckq2W-7uvZcIVmB}WU%MoeF{$tLig!tJRjsJX27Pj?Vco#Ta0u{x>IrEKZC-l_br` z9q&9PWT~~MixT|sjj+vFksW$>Hf~>N6B0yJ$=J!0XAHGTygq`lb9wF?+Flkyq7vV| zyYIc5H}M!EZgOmRWSfhEA=v#zYZHnOBKlvn5uRI#hK#a@mTE znri1fX6;>e%IfrY+HQQ(u#eb3Ega+jxs|;2md;P%Wu2;HOU`EzEmdFq8YZ+D!D7Wm z^lfCI+!(?`eH{E7SG{TzuKl|Cs#(9M49{Y8Fm^Zg`H#tvw$Ec+gFf*3Vx@1rf<(t*6j~J>nS%%ZNng9 zR0I5c#K1$$r(AzFPg!i=~d@r8e(P!1h}}j<@8W=QuKY4nx*<`|z=u`x_{qxo?l1 zjdzVy7y-Kq_h&>rqn|2mrQ}8@AqV%dnB2(~m$DiWYn;K?ihlLu``0SFX_sP9Tb5PZ z>`)#Mj5tjmXQ?-Aa9O|JmP00TC9Cz6f$jv1^U;|n`Zq}I+l1_1r$MuqvoB%AAIePq zp>Crfci=k>XLa^<7+upEe67QuEc&CC8pV#9P*D#k7VR=me(iM*Y9V(bYZ|>JKa0to z%tR${V_eASDrgk!lKHgR?Awv((Hf<%6kO9MpUVP%9K}Q}n_k6Cs2cD8*7h<9!R5b2 zap^HlvE^m9F60Y$pXFXHYgk^9Oo^bkew8+tHC|bX{TfHkTQli$vB+*J^_e1+y-;uv z0z9aW#BK(%XM)NF_%4LUgvIw0KGUZ-7}dq_l<3PvF(!lf4titS`kj?Me%yO(u^R+H2+Pp`T)cG_YK_EdUbWI)~qwOy6g zIra_Nw^8ak_`N6!7XyXYj)kCa(6(H9^5GiA> zipkqKDGcQ5wYHPxK|xkNC!4RLmT$5VDk&TiS>6_5gnE z_k?G<3d0e^Air0Q}xD z;;|kX)6YZQ8Oa9=>Ks%2j0vgkp_Sa6;$`Ki`6)$QxX+8F)_B!r*uLhsbiu37f$gMa zo5x`DM5eZPruL?0%iczn>H>r)HMRCrDz$;Pq!e{Tzw))ZuTkFQEZc@t z^ZM6~Lni@q=-^TlOG$>eS0<)}u!q^irrW+>kan)?f_G*q_lP=h?fC-mqJce)Fj4)7X+qFija@(@K;udPe{fL6Gmf z@-55!LhlFi(GJC~sLWNQA;-N(;ck%mTDnk15Wb5C>^HLV7KGx=2Ymk`e+0Rmqy5m4 zyFN*Qfb`rPO*s1%v-`e-RN|MP2E-xkD2 z-gnU%+WIy0{i!?xP3I>X5)x^yEv=JOBTq%%me+}cT>YFUmxhfU+iW;|tw)z;WPCG^ zp_ABOt3}vwOVhQm%UeeGa{bAYd2sP4GP|a-U!XR0U4&pp4~U%kiY|y1noY3Gz+A3K zwoV7OZgMikE5t&FJ;L¨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

yaOM*LC+1_m0; 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 diff --git a/vignettes/plot2-1.png b/vignettes/plot2-1.png deleted file mode 100644 index 9df110f0960c6ae4f9a36d1046429a7b79dfe16c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26705 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 diff --git a/vignettes/rtweet.Rmd.orig b/vignettes/rtweet.Rmd.orig deleted file mode 100644 index 0240346a..00000000 --- a/vignettes/rtweet.Rmd.orig +++ /dev/null @@ -1,311 +0,0 @@ ---- -title: "Intro to rtweet" -output: - rmarkdown::html_vignette: - fig_caption: true - code_folding: show - toc_float: - collapsed: true - toc_depth: 3 -vignette: > - %\VignetteIndexEntry{Intro to rtweet} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE, eval = TRUE, comment = "#>", - collapse = TRUE, fig.path = "vignettes/", fig.process = function(x) { - basename(x) - }) -knitr::opts_knit$set(root.dir = "vignettes") -``` - -This vignette provides a quick tour of the R package. - -```{r} -library("rtweet") -``` - -## Authenticate - -First you should set up your own credentials, this should be done just once ever: - -```{r, eval=FALSE} -auth_setup_default() -``` - -Which will look up your account on your browser and create a token and save it as default. -From now on, on this R session on others we can use this authentication with: - -```{r, eval=FALSE} -auth_as("default") -``` - -Automatically rtweet will use that token in all the API queries it will do in the session. - -If you want to set up a bot or collect a lot of information, please read the `vignette("auth", "rtweet")`. - -```{r include=FALSE, purl=FALSE} -library("ggplot2") -library("dplyr") -library("rtweet") -auth_as(rtweet:::rtweet_test()) -``` - -## Search tweets - -You can search tweets: - -```{r search_tweets0} -## search for 18000 tweets using the rstats hashtag -rstats <- search_tweets("#rstats", n = 100, include_rts = FALSE) -colnames(rstats) -rstats[1:5, c("created_at", "text", "id_str")] -``` - -The `include_rts = FALSE` excludes retweets from the search. - -Twitter rate limits the number of calls to the endpoints you can do. -See `rate_limit()` and the [rate limit section](#Rate-limit) below. -If your query requires more calls like the example below, simply set `retryonratelimit = TRUE` and rtweet will wait for rate limit resets for you. - -```{r, eval=FALSE} -## search for 250,000 tweets containing the word data -tweets_peace <- search_tweets("peace", n = 250000, retryonratelimit = TRUE) -``` - -Search by geo-location, for example tweets in the English language sent from the United States. - -```{r search_tweets} -# search for tweets sent from the US -# lookup_coords requires Google maps API key for maps outside usa, canada and world -geo_tweets <- search_tweets("lang:en", geocode = lookup_coords("usa"), n = 100) -geo_tweets[1:5, c("created_at", "text", "id_str", "lang", "place")] -``` - -You can check the location of these tweets with `lat_lng()`. -Or quickly visualize frequency of tweets over time using `ts_plot()` (if `ggplot2` is installed). - -```{r plot1} -#| fig.cap: "Last 100 tweets by hour." -## plot time series of tweets -ts_plot(rstats) + - theme_minimal() + - theme(plot.title = element_text(face = "bold")) + - labs( - x = NULL, y = NULL, - title = "Frequency of #rstats Twitter statuses from past days", - subtitle = "Twitter status (tweet) counts aggregated using three-hour intervals", - caption = "Source: Data collected from Twitter's REST API via rtweet" - ) -``` - -## Posting statuses - -You can post tweets with: - -```{r post_tweet} -post_tweet(paste0("My first tweet with #rtweet #rstats at ", Sys.time())) -``` - -It can include media and alt text: - -```{r baseplot} -path_file <- tempfile(fileext = ".png") -png(filename = path_file) -plot(mpg ~ cyl, mtcars, col = gear, pch = gear) -dev.off() -post_tweet("my first tweet with #rtweet with media #rstats", media = path_file, media_alt_text = "Plot of mtcars dataset, showing cyl vs mpg colored by gear. The lower cyl the higher the mpg is.") -``` - -You can also reply to a previous tweet, retweet and provide additional information. - -## Get friends - -Retrieve a list of all the accounts a **user follows**. -```{r get_friends} -## get user IDs of accounts followed by R Foundation -R_foundation_fds <- get_friends("_R_Foundation") -R_foundation_fds -``` - -Using `get_friends()` we can retrieve which users are being followed by the R Foundation. - -## Get followers - -If you really want all the users that follow the account we can use `get_followers()`: -```{r get_followers} -R_foundation_flw <- get_followers("_R_Foundation", n = 30000, - retryonratelimit = TRUE) -``` - -Note that the `retryonratelimit` option is intended for when you need more queries than provided by Twitter on a given period. -You might want to check with `rate_limit()` how many does it provide for the endpoints you are using. -If exceeded `retryonratelimit` waits till the there are more calls available and then resumes the query. - -## Lookup users - -As seen above we can use `lookup_users()` to check their - -```{r lookup_users} -# Look who is following R Foundation -R_foundation_fds_data <- lookup_users(R_foundation_fds$to_id, verbose = FALSE) -R_foundation_fds_data[, c("name", "screen_name", "created_at")] - -# Look 100 R Foundation followers -R_foundation_flw_data <- lookup_users(head(R_foundation_flw$from_id, 100), verbose = FALSE) -R_foundation_flw_data[1:5, c("name", "screen_name", "created_at")] -``` - -We have now the information from those followed by the R Foundation and its followers. -We can retrieve their latest tweets from these users: - -```{r tweets_data} -tweets_data(R_foundation_fds_data)[, c("created_at", "text")] -``` - -## Search users - -Search for 1,000 users with the rstats hashtag in their profile bios. -```{r search_users} -## search for users with #rstats in their profiles -useRs <- search_users("#rstats", n = 100, verbose = FALSE) -useRs[, c("name", "screen_name", "created_at")] -``` - -If we want to know what have they tweeted about we can use `tweets_data()`: -```{r tweets_data2} -useRs_twt <- tweets_data(useRs) -useRs_twt[1:5, c("id_str", "created_at", "text")] -``` - -## Get timelines - -Get the most recent tweets from R Foundation. -```{r plot2} -#| fig.cap: "Frequency of Twitter statuses posted by the R Foundation since 2017" -#| eval: requireNamespace("ggplot2", quietly = TRUE) -## get user IDs of accounts followed by R Foundation -R_foundation_tline <- get_timeline("_R_Foundation") -latest_R_tweets <- filter(R_foundation_tline, created_at > "2017-10-29") - -## plot the frequency of tweets for each user over time -plot <- ts_plot(latest_R_tweets, by = "month", trim = 1L) + - geom_point() + - theme_minimal() + - theme( - legend.title = element_blank(), - legend.position = "bottom", - plot.title = element_text(face = "bold")) + - labs( - x = NULL, y = NULL, - title = "Frequency of Twitter statuses posted by the R Foundation", - subtitle = "Twitter status (tweet) counts aggregated by month from October/November 2017", - caption = "Source: Data collected from Twitter's REST API via rtweet" - ) -plot -``` - -## Get favorites - -Get the 10 recently favorited statuses by R Foundation. - -```{r get_favorites} -R_foundation_favs <- get_favorites("_R_Foundation", n = 10) -R_foundation_favs[, c("text", "created_at", "id_str")] -``` - - -## Get trends - -Discover what's currently trending in San Francisco. -```{r get_trends} -world <- get_trends("world") -world -``` - -## Following users - -You can follow users and unfollow them: - -```{r} -post_follow("_R_Foundation") -post_unfollow_user("rtweet_test") -``` - -## Managing data - -There are some functions to help analyze the data extracted from the API. - -With `clean_tweets()` you can remove users mentions, hashtags, urls and media to keep only the text if you want to do sentiment analysis (you might want to remove emojis too). - -```{r clean_tweets} -clean_tweets(head(R_foundation_favs), clean = c("users", "hashtags", "urls", "media")) -``` - -With `entity()` you can access any entity of the tweets. -It returns the id of the tweet and the corresponding data field in the selected entity. - -```{r helpers} -head(entity(R_foundation_favs, "urls")) -head(entity(R_foundation_favs, "hashtags")) -head(entity(R_foundation_favs, "symbols")) -head(entity(R_foundation_favs, "user_mentions")) -head(entity(R_foundation_favs, "media")) -``` - -To avoid having two columns with the same name, `user_mentions()` renames to the ids from "id" and "id_str" to "user_id" and "user_id_str". - -[1]: Also applies to users ids - -## Muting users - -You can mute and unmute users: - -```{r post_follow, eval=FALSE} -post_follow("rtweet_test", mute = TRUE) -post_follow("rtweet_test", mute = FALSE) -``` - - -## Blocking users - -You can block users and unblock them: - -```{r user_block} -user_block("RTweetTest1") -user_unblock("RTweetTest1") -``` - - - -## Rate limits - -Twitter sets a limited number of calls to their endpoints for different authentications (check `vignette("auth", "rtweet")` to find which one is better for your use case). -To consult those limits you can use `rate_limt()` - -```{r rate_limit} -rate_limit() -# Search only those related to followers -rate_limit("followers") -``` - -The remaining column shows the number of times that you can call and endpoint (not the numbers of followers you can search). -After a query the number should decrease until it is reset again. - -If your queries return an error, check if you already exhausted your quota and try after the time on "reset_at". - -## Stream tweets - -Please, see the vignette on `vignette("stream", "rtweet")` for more information. - -## SessionInfo - -To provide real examples the vignette is precomputed before submission. -Also note that results returned by the API will change. - -```{r session, class.source = "fold-hide"} -sessionInfo() -``` - diff --git a/vignettes/stream.Rmd.orig b/vignettes/stream.Rmd.orig deleted file mode 100644 index 556613e2..00000000 --- a/vignettes/stream.Rmd.orig +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: "Live streaming tweets" -subtitle: "rtweet: Collecting Twitter Data" -output: - rmarkdown::html_vignette: - fig_caption: true - code_folding: show - toc_float: - collapsed: true - toc_depth: 3 -vignette: > - %\VignetteIndexEntry{Live streaming tweets} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE, eval = TRUE, comment = "#>", - collapse = TRUE, fig.path = "vignettes/", fig.process = function(x) { - basename(x) - }) -knitr::opts_knit$set(root.dir = "vignettes") -``` - -## Installing and loading package - -Prior to streaming, make sure to install and load rtweet. -This vignette assumes users have already setup app access tokens (see: the "auth" vignette, `vignette("auth", package = "rtweet")`). - -```{r library} -## Load rtweet -library(rtweet) -``` - -```{r auth, include=FALSE, purl=FALSE, message=FALSE} -rtweet::auth_as("bearer_academic_dev") -``` - -## Overview - -In addition to accessing Twitter's REST API (e.g., `search_tweets`, `get_timeline`), rtweet makes it possible to capture live streams of Twitter data[^1]. This requires an app authentication see `vignette("auth", package = "rtweet")`. - -[^1]: Till November 2022 it was possible with API v1.1, currently this is no longer possible and uses API v2. - -There are two ways of having a stream: - -- [A stream collecting data from a set of rules](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream), which can be collected via `filtered_stream()`. - -- [A stream of a 1% of tweets published](https://developer.twitter.com/en/docs/twitter-api/tweets/volume-streams/api-reference/get-tweets-sample-stream), which can be collected via `sample_stream()`. - -In either case we need to choose how long should the streaming connection hold, and in which file it should be saved to. - -```{r stream-setup} -## Stream time in seconds so for one minute set timeout = 60 -## For larger chunks of time, I recommend multiplying 60 by the number -## of desired minutes. This method scales up to hours as well -## (x * 60 = x mins, x * 60 * 60 = x hours) -## Stream for 5 seconds -streamtime <- 5 -## Filename to save json data (backup) -filename <- "rstats.json" -``` - -## Filtered stream - -The filtered stream collects tweets for all rules that are currently active, not just one rule or query. - -### Creating rules - -Streaming rules in rtweet need a value and a tag. -The value is the query to be performed, and the tag is the name to identify tweets that match a query. -You can use multiple words and hashtags as value, please [read the official documentation](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/integrate/build-a-rule). -Multiple rules can match to a single tweet. - -```{r stream-add-rule} -## Stream rules used to filter tweets -new_rule <- stream_add_rule(list(value = "#rstats", tag = "rstats")) -``` - -### Listing rules - -To know current rules you can use `stream_add_rule()` to know if any rule is currently active: - -```{r stream-list-rule} -rules <- stream_add_rule(NULL) -rules -rules(rules) -``` - -With the help of `rules()` the id, value and tag of each rule is provided. - -### Removing rules - -To remove rules use `stream_rm_rule()` - -```{r stream-rm-rule, eval = FALSE} -# Not evaluated now -stream_rm_rule(ids(new_rule)) -``` - -Note, if the rules are not used for some time, Twitter warns you that they will be removed. -But given that `filtered_stream()` collects tweets for all rules, it is advisable to keep the rules list short and clean. - -### filtered_stream() - -Once these parameters are specified, initiate the stream. -Note: Barring any disconnection or disruption of the API, streaming will occupy your current instance of R until the specified time has elapsed. -It is possible to start a new instance or R ---streaming itself usually isn't very memory intensive--- but operations may drag a bit during the parsing process which takes place immediately after streaming ends. - -```{r stream-rstats} -## Stream election tweets -stream_rstats <- filtered_stream(timeout = streamtime, file = filename, parse = FALSE) -``` - -If no tweet matching the rules is detected a warning will be issued. - -Parsing larger streams can take quite a bit of time (in addition to time spent streaming) due to a somewhat time-consuming simplifying process used to convert a json file into an R object. - -Don't forget to clean the streaming rules: - -```{r stream-rm-rule-real} -stream_rm_rule(ids(new_rule)) -``` - -## Sample stream - -The `sample_stream()` function doesn't need rules or anything. - -```{r sample_stream} -stream_random <- sample_stream(timeout = streamtime, file = filename, parse = FALSE) -length(stream_random) -``` - - -## Saving files - -Users may want to stream tweets into json files upfront and parse those files later on. -To do this, simply add `parse = FALSE` and make sure you provide a path (file name) to a location you can find later. - -You can also use `append = TRUE` to continue recording a stream into an already existing file. - -Currently parsing the streaming data file with `parse_stream()` is not functional. -However, you can read it back in with `jsonlite::stream_in(file)`. - -## Returned data object - -The parsed object should be the same whether a user parses up-front or from a json file in a later session. - -Currently the returned object is a raw conversion of the feed into a nested list depending on the fields and extensions requested. From aecb97c2e6065bc541280be211f18dfc2c5ff596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Mon, 16 Oct 2023 22:03:38 +0200 Subject: [PATCH 11/13] Increment version number to 1.2.1 --- DESCRIPTION | 2 +- NEWS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4869c0ba..4defe305 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: rtweet Title: Collecting Twitter Data -Version: 1.2.0.9006 +Version: 1.2.1 Authors@R: c( person("Michael W.", "Kearney", , "kearneymw@missouri.edu", role = "aut", comment = c(ORCID = "0000-0002-0730-4694")), diff --git a/NEWS.md b/NEWS.md index 0c5b3ef0..33c27598 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# rtweet (development version) +# rtweet 1.2.1 * Fix `auth_sitrep()` to work well with OAuth2 tokens. From 8c6380b625ed9423551fa233cccb4130803da7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Tue, 17 Oct 2023 19:46:17 +0200 Subject: [PATCH 12/13] Increment version number to 1.2.1.9000 --- DESCRIPTION | 2 +- NEWS.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4defe305..c0adaa87 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: rtweet Title: Collecting Twitter Data -Version: 1.2.1 +Version: 1.2.1.9000 Authors@R: c( person("Michael W.", "Kearney", , "kearneymw@missouri.edu", role = "aut", comment = c(ORCID = "0000-0002-0730-4694")), diff --git a/NEWS.md b/NEWS.md index 33c27598..f6cc16b7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,5 @@ +# rtweet (development version) + # rtweet 1.2.1 * Fix `auth_sitrep()` to work well with OAuth2 tokens. From 8e89e7a49ee89a4246b680745fc290d493acd2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs?= Date: Mon, 20 Nov 2023 18:01:28 +0100 Subject: [PATCH 13/13] Add the auth_clean functions [skip ci] --- NAMESPACE | 1 + NEWS.md | 2 ++ R/auth_clean.R | 42 ++++++++++++++++++++++++++++++++++++++++++ R/auth_sitrep.R | 21 ++++++++++++++++----- cran-comments.md | 6 ++++-- man/auth_clean.Rd | 27 +++++++++++++++++++++++++++ 6 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 R/auth_clean.R create mode 100644 man/auth_clean.Rd diff --git a/NAMESPACE b/NAMESPACE index badb2f67..b6e02652 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -34,6 +34,7 @@ S3method(rules,rules) export(as_screenname) export(as_userid) export(auth_as) +export(auth_clean) export(auth_get) export(auth_has_default) export(auth_list) diff --git a/NEWS.md b/NEWS.md index f6cc16b7..f178f8b6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # rtweet (development version) +* Fix problem with `auth_sitrep()` not correctly handling old tokens. + # rtweet 1.2.1 * Fix `auth_sitrep()` to work well with OAuth2 tokens. diff --git a/R/auth_clean.R b/R/auth_clean.R new file mode 100644 index 00000000..60d0ebd2 --- /dev/null +++ b/R/auth_clean.R @@ -0,0 +1,42 @@ +#' Remove tokens +#' +#' If there is a file with saved tokens it will delete it. +#' +#' This functions helps to comply with CRAN policy to remove files created by +#' the package. +#' @param old A logical value if you want to remove old tokens. +#' @param new A logical value if you want to remove new tokens. +#' +#' @return An invisible logical value showing the success of the operation. +#' If no tokens need to be deleted it will return FALSE too. +#' `NULL` if there is nothing to do. +#' @export +#' @examples +#' auth_clean(FALSE, FALSE) +auth_clean <- function(old = TRUE, new = FALSE) { + stopifnot(is_logical(old)) + stopifnot(is_logical(new)) + + if (isFALSE(old) && isFALSE(new)) { + inform(c("Nothing to do", + i = "Did you meant to set `old = TRUE`?")) + return(invisible(NULL)) + } + old_tokens <- find_old_tokens() + tools_tokens <- find_tools_tokens() + all_tokens_files <- c(old_tokens, tools_tokens) + if (is.null(all_tokens_files)) { + inform("No tokens were found! Nothing to do.") + return(invisible(NULL)) + } + ot <- FALSE + nt <- FALSE + if (length(old_tokens) != 0 && old) { + ot <- unlink(old_tokens) + } + if (length(tools_tokens) != 0 && new) { + nt <- unlink(tools_tokens) + } + out <- old && ot || new && nt + invisible(out) +} diff --git a/R/auth_sitrep.R b/R/auth_sitrep.R index 2c884cc0..00c0ed9c 100644 --- a/R/auth_sitrep.R +++ b/R/auth_sitrep.R @@ -20,7 +20,6 @@ auth_sitrep <- function() { old_tokens <- find_old_tokens() tools_tokens <- find_tools_tokens() all_tokens_files <- c(old_tokens, tools_tokens) - # FIXME: Deal with Oauth2 tokens if (is.null(all_tokens_files)) { inform("No tokens were found! See ?auth_as for more details.") return(NULL) @@ -88,7 +87,12 @@ token_auth <- function(tokens) { key = character(n)) df <- as.data.frame(t(list2DF(tokens))) rownames(df) <- names(tokens) - colnames(df) <- c("app", "user_id", "key") + if (ncol(df) < 3L) { + x <- seq_len(ncol(df)) + } else { + x <- 1L:3L + } + colnames(df) <- c("app", "user_id", "key")[x] uk <- unique(df$key) length_levels <- length(uk) - sum(any(uk == "")) df$key <- factor(df$key, labels = LETTERS[seq_len(length_levels)], exclude = "") @@ -97,8 +101,16 @@ token_auth <- function(tokens) { #' @importFrom methods is type_auth <- function(tokens) { - class_tokens <- vapply(tokens, is, character(1L)) - class_tokens <- ifelse(endsWith(class_tokens, "Token1.0"), "token", "bearer") + + class_tokens <- vapply(tokens, function(x){is(x)[1]}, character(1L)) + class_tokens2 <- character(length(class_tokens)) + class_tokens2[class_tokens %in% c("Token1.0", "TwitterToken1.0")] <- "token" + class_tokens2[class_tokens == "rtweet_bearer"] <- "bearer" + class_tokens2[class_tokens == "httr2_token"] <- "httr2_token" + if (any(!nzchar(class_tokens2))) { + warn("Detected some file which doesn't seems created by rtweet.", parent = current_call()) + } + class_tokens2 } move_tokens <- function(tokens, folder) { @@ -179,7 +191,6 @@ handle_token <- function(tokens) { action_tokens } - auth_check <- function(tokens) { type_auth <- type_auth(tokens) diff --git a/cran-comments.md b/cran-comments.md index a19ba621..0da55e96 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,3 +1,5 @@ -Update to add some functions, fix a bug and remove some links to removed documentation. +There might be some errors following links, sometimes Twitter.com or rOpenSci block the CRAN checks of urls. -Checks passed locally, in R-Hub there is a NOTE about lastMiKTeXException which apparently could be ignored. +This submission so close to the previous one is because there is an ERROR in one of the CRAN checks after acceptance. +This is probably caused by a previous version not following CRAN's policy of not writing to user directory. +The example detected a file with the pattern .rtweet_token.*rds in the user directory. diff --git a/man/auth_clean.Rd b/man/auth_clean.Rd new file mode 100644 index 00000000..881af04c --- /dev/null +++ b/man/auth_clean.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/auth_clean.R +\name{auth_clean} +\alias{auth_clean} +\title{Remove tokens} +\usage{ +auth_clean(old = TRUE, new = FALSE) +} +\arguments{ +\item{old}{A logical value if you want to remove old tokens.} + +\item{new}{A logical value if you want to remove new tokens.} +} +\value{ +An invisible logical value showing the success of the operation. +If no tokens need to be deleted it will return FALSE too. +} +\description{ +If there is a file with saved tokens it will delete it. +} +\details{ +This functions helps to comply with CRAN policy to remove files created by +the package. +} +\examples{ +auth_clean() +}