├── DESCRIPTION ├── NAMESPACE ├── NEWS ├── R ├── account.R ├── allGenerics.R ├── base.R ├── comm.R ├── convert.R ├── db.R ├── db_connections.R ├── db_search.R ├── db_utils.R ├── df_columns.R ├── dm.R ├── followers.R ├── oauth.R ├── s4methods.R ├── search.R ├── statuses.R ├── toys.R ├── trends.R ├── users.R ├── utils.R └── zzz.R ├── README.md ├── inst ├── doc │ └── twitteR.pdf └── vignettes │ └── twitteR.Rnw └── man ├── decode_short_url.Rd ├── directMessage-class.Rd ├── dm.Rd ├── favorites.Rd ├── friendships.Rd ├── getCurRateLimitInfo.Rd ├── getTrends.Rd ├── getUser.Rd ├── get_latest_tweet_id.Rd ├── import_statuses.Rd ├── load_tweets_db.Rd ├── registerTwitterOAuth.Rd ├── register_db_backend.Rd ├── retweets.Rd ├── search.Rd ├── search_twitter_and_store.Rd ├── setup_twitter_oauth.Rd ├── showStatus.Rd ├── status-class.Rd ├── strip_retweets.Rd ├── taskStatus.Rd ├── timelines.Rd ├── twListToDF.Rd ├── updateStatus.Rd ├── use_oauth_token.Rd └── user-class.Rd /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: twitteR 2 | Title: R Based Twitter Client 3 | Description: Provides an interface to the Twitter web API. 4 | Version: 1.1.9 5 | Author: Jeff Gentry 6 | Maintainer: Jeff Gentry 7 | Depends: R (>= 2.12.0) 8 | Imports: methods, bit64, rjson, DBI (>= 0.3.1), httr (>= 1.0.0) 9 | Suggests: RSQLite, RMySQL 10 | License: Artistic-2.0 11 | LazyData: yes 12 | URL: http://lists.hexdump.org/listinfo.cgi/twitter-users-hexdump.org 13 | Collate: allGenerics.R base.R account.R statuses.R users.R trends.R 14 | s4methods.R convert.R dm.R oauth.R comm.R followers.R search.R db.R 15 | df_columns.R db_connections.R db_utils.R db_search.R toys.R utils.R 16 | zzz.R 17 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | import(httr, bit64, rjson, DBI, methods) 2 | importFrom("utils", "URLencode") 3 | exportClasses('status', 'user', 'directMessage') 4 | exportMethods(as.data.frame, screenName, show, favorited, replyToSN, 5 | statusesCount, tweetCount, followersCount, 6 | favoritesCount, friendsCount, userURL, created, 7 | protected, verified, location, lastStatus, show, 8 | description, name, id, statusText, truncated, statusSource) 9 | 10 | export("search_twitter_and_store") 11 | export("get_latest_tweet_id") 12 | export("setup_twitter_oauth") 13 | export("use_oauth_token") 14 | export("register_db_backend") 15 | export("register_sqlite_backend") 16 | export("register_mysql_backend") 17 | export("store_tweets_db") 18 | export("load_tweets_db") 19 | export("store_users_db") 20 | export("load_users_db") 21 | export("resource_families") 22 | export("strip_retweets") 23 | export("searchTwitteR") 24 | export("retweets") 25 | export("retweeters") 26 | export("favorites") 27 | exportPattern("^import*") 28 | exportPattern("^jsonTo*") 29 | export("getTwitterOAuth") 30 | export("decode_short_url") 31 | export("getCurRateLimitInfo") 32 | export('statusFactory') 33 | export('buildStatus') 34 | export('userFactory') 35 | export('buildUser') 36 | export('dmFactory') 37 | export('registerTwitterOAuth') 38 | export('dmGet') 39 | export('dmSent') 40 | export('dmDestroy') 41 | export('dmSend') 42 | export('Rtweets') 43 | export('searchTwitter') 44 | export('userTimeline') 45 | export('homeTimeline') 46 | export('mentions') 47 | export('retweetsOfMe') 48 | export('listTimeline') 49 | export('showStatus') 50 | export('lookup_statuses') 51 | export('getUser') 52 | export('lookupUsers') 53 | export("updateStatus") 54 | export('tweet') 55 | export('deleteStatus') 56 | export('taskStatus') 57 | export('getTrends') 58 | export('availableTrendLocations') 59 | export('closestTrendLocations') 60 | export('twListToDF') 61 | export('friendships') 62 | export('retweetStatus') 63 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | CHANGES IN VERSION 1.1.8 (2014-12-31) 2 | ----------------------- 3 | 4 | USER VISIBLE CHANGES 5 | o The use of ROAuth and RCurl has been removed in favor of the httr 6 | package. This changes how OAuth authentication is handled, with the 7 | new system going through setup_twitter_oauth() and load_twitter_oauth(). 8 | 9 | o Changed from using int64 to bit64 for large integer support 10 | 11 | o setup_twitter_oauth() will report if an authentication attempt fails 12 | 13 | BUG FIXES 14 | o getCurRateLimitInfo now requires at least one supported resource family 15 | 16 | NEW FEATURES 17 | o Added lookup_statuses, which will retrieve a vector of tweet IDs 18 | 19 | o Added resultType to searchTwitter, provided by Ajay Gopal 20 | 21 | o Added maxID to searchTwitter, provided by Paul Nulty 22 | 23 | o updateStatus now has an argument 'mediaPath' which can be used to 24 | upload images. Provided by Christopher Hackett. 25 | 26 | o Added a DB persistence layer, with store_tweets_db/load_tweets_db and 27 | store_users_db/load_users_db. 28 | 29 | o Added a convenience method search_twitter_and_store() which can be used 30 | to periodically update a database table with search results. 31 | 32 | o Added a function strip_retweets which will attempt to remove retweets 33 | from a list of Status objects. 34 | 35 | o Added a function 'retweeters' and a convenience method 'getRetweeters' 36 | to the Status class. This function takes an ID of a tweet and a count 37 | (default is 20) and will return the IDs of users who have retweeted this 38 | tweet. The getRetweeters convenience method only takes the count. 39 | 40 | o Added argument forceUtf8Conversion (accessible via ... in all exposed 41 | functionality) which if set to FALSE (default is TRUE) will disable the 42 | forced conversion to UTF-8. Note that doing this can cause bad behavior 43 | if responses pass on characters outside of UTF-8. 44 | 45 | CHANGES IN VERSION 1.1.7 (2013-7-8) 46 | ----------------------- 47 | 48 | NEW FEATURES 49 | o Added a 'urls' slot to the Status class. This is a data.frame containing 50 | information about any t.co shortened URLs, providing the expanded URL, 51 | a display string and the character positions the URL was extracted from. 52 | This only applies to t.co shortened URLs. 53 | 54 | o Added a function decode_short_urls as a utility to help users process 55 | shortened URLs which were not t.co shortened. 56 | 57 | o Included patch to force all characters to UTF-8 before passing along 58 | to rjson 59 | 60 | o Added function "favorites" which takes a user and returns a list of 61 | their n most recent favorited tweets. Also added convenience method 62 | to the user class getFavorites() 63 | 64 | o Added favoriteCount field to the status class, i.e. x$getFavoriteCount() 65 | 66 | o Added isRetweet field to the status class, i.e. x$getIsRetweet() 67 | 68 | o Added retweets function to get retweets of a status, and a 69 | $getRetweets() convenience method to the status class. 70 | 71 | BUG FIXES 72 | 73 | o Fixed a bug where userTimeline would fail if given a single user 74 | object (as opposed to simple screen names, IDs, etc) 75 | 76 | o Fixed a bug in lookupUsers - if the user arg was NULL an error would 77 | be thrown. Now returns empty list 78 | 79 | o Fixed a bug in the assignment of latitude/longitude for tweets, these 80 | will now be properly populated 81 | 82 | o users$toDataFrame will now honor the stringsAsFactors argument 83 | 84 | o getFavoritesCount() will now work properly on users. 85 | 86 | 87 | CHANGES IN VERSION 1.1.6 (2013-04-05) 88 | ------------------------ 89 | 90 | BUG FIXES 91 | 92 | o Fixed a bug in searchTwitter which would fill the tail end of a query 93 | with duplicated tweets if the user requested more than available 94 | 95 | o Changed showStatus to use character based input due to large IDs 96 | 97 | CHANGES IN VERSION 1.1.5 (2013-03-26) 98 | ------------------------ 99 | 100 | NEW FEATURES 101 | 102 | - Changed 'blockOnRateLimit' (boolean) to 'retryOnRateLimit' (integer). If 103 | this value is supplied (to any of the twitteR API wrappers) the system 104 | will retry up to N times if Twitter's rate limit in effect. If the 105 | retry limit is reached (even if it was 0) twitteR will now try to return 106 | any partial results that had been accumulated prior to the rate limit 107 | (with a warning) 108 | - Added function getTwitterOAuth(consumer_key, consumer_secret) which is a 109 | wrapper around the ROAuth creation/handshaking process which embeds the 110 | Twitter URLs. Will also call registerTwitterOAuth for you and then return 111 | the OAuth credential so you can save it for future use if you wish. 112 | 113 | USER VISIBLE CHANGES 114 | - ROAuth (>= 0.9.4) is now a Depends 115 | 116 | CHANGES IN VERSION 1.1.4 (2013-03-18) 117 | ------------------------ 118 | 119 | USER VISIBLE CHANGES 120 | 121 | - Allow for by-hour filtering in since/util 122 | 123 | CHANGES IN VERSION 1.1.3 (2013-03-17) 124 | ------------------------ 125 | 126 | BUG FIXES 127 | 128 | - Fixing bug from 1.1.2's bug fix, search now works properly w/ max_id 129 | 130 | CHANGES IN VERSION 1.1.2 (2013-03-16) 131 | ------------------------ 132 | 133 | BUG FIXES 134 | 135 | - Bug fixes from abicky 136 | - removed a browser call to the status class 137 | 138 | CHANGES IN VERSION 1.1.1 (2013-03-03) 139 | ------------------------ 140 | 141 | NEW FEATURES 142 | 143 | - Added 'longitude' and 'latitude' fields to the status class, pulls 144 | from the 'coordinates' field of a tweet (when available) 145 | 146 | CHANGES IN VERSION 1.1.0 (2013-03-01) 147 | ------------------------ 148 | 149 | USER VISIBLE CHANGES 150 | 151 | - Purely a version number bump to reflect the new API status 152 | 153 | CHANGES IN VERSION 0.99.28 (2013-02-23) 154 | -------------------------- 155 | 156 | NEW FEATURES 157 | 158 | - Added profileImageUrl to the User class, along w/ getProfileImageUrl() 159 | method 160 | 161 | CHANGES IN VERSION 0.99.27 (2013-01-21) 162 | -------------------------- 163 | 164 | NEW FEATURES 165 | 166 | - Added includeRts option to userTimeline 167 | 168 | CHANGES IN VERSION 0.99.26 (2013-01-02) 169 | -------------------------- 170 | 171 | USER VISIBLE CHANGES 172 | 173 | - now depending on rjson >= 0.2.24 174 | 175 | NEW FEATURES 176 | 177 | - supply argument blockOnRateLimit which does as the name suggests 178 | 179 | CHANGES IN VERSION 0.99.24 (2013-01-02) 180 | ------------------------------------- 181 | 182 | USER VISIBLE CHANGES 183 | 184 | - Convert search to using the 1.1 API 185 | 186 | CHANGES IN VERSION 0.99.23 (2012-12-27) 187 | --------------------------------------- 188 | 189 | CONVERSION TO THE 1.1 API 190 | 191 | - Multiple functions (e.g. publicTimeline) were removed due to the 192 | corresponding functionality being removed from the API 193 | 194 | - The trend system has been completely rewritten 195 | 196 | - The rate limit system has been completely rewritten 197 | 198 | - The friendships() function has been added 199 | 200 | -------------------------------------------------------------------------------- /R/account.R: -------------------------------------------------------------------------------- 1 | resource_families = c("account", "application", "blocks", "direct_message", "favorites", "followers", 2 | "friends", "friendships", "geo", "help", "lists", "saved_searches", "search", 3 | "statuses", "trends", "users") 4 | 5 | getCurRateLimitInfo <- function(resources=resource_families, ...) { 6 | if ((length(resources) == 0) || (!all(resources %in% resource_families))){ 7 | stop("Must provide at least one valid resource family: ", 8 | paste(resource_families, collapse=", ")) 9 | } 10 | params = list() 11 | if (length(resources) > 0) { 12 | params[["resources"]] = paste(resources, collapse=",") 13 | } 14 | json <- twInterfaceObj$doAPICall("application/rate_limit_status", params=params, ...) 15 | if (! "resources" %in% names(json)) { 16 | stop("Invalid response from server - no 'resources' field in JSON") 17 | } 18 | resources = unlist(json[["resources"]], recursive=FALSE) 19 | resource_names = sapply(strsplit(names(resources), "\\."), function(x) x[2]) 20 | resource_rows = lapply(resources, function(x) { 21 | return(c(limit=x$limit, remaining=x$remaining, reset=twitterDateToPOSIX(x$reset))) 22 | }) 23 | resource_matrix = cbind(resource=resource_names, do.call(rbind, resource_rows)) 24 | rownames(resource_matrix) = NULL 25 | resource_df = as.data.frame(resource_matrix, stringsAsFactors=FALSE) 26 | resource_df[, "reset"] = as.POSIXct(as.numeric(resource_df[, "reset"]), tz="UTC", origin="1970-01-01") 27 | return(resource_df) 28 | } 29 | 30 | get_authenticated_user = function(...) { 31 | buildUser(twInterfaceObj$doAPICall("account/verify_credentials", ...)) 32 | } 33 | 34 | can_access_other_account = function(other_id) { 35 | # The idea here is to first get our own username, and then make sure that we're 36 | # following screenName if they're protected. If they're protected and we're not 37 | # following, we can't see their details 38 | authenticated_user_id = get_authenticated_user()$getId() 39 | if (authenticated_user_id != other_id) { 40 | other_user = getUser(other_id) 41 | if (other_user$getProtected()) { 42 | relationship = friendships(user_ids = other_id) 43 | return(as.logical(relationship[1, "following"])) 44 | } 45 | } 46 | 47 | TRUE 48 | } 49 | 50 | -------------------------------------------------------------------------------- /R/allGenerics.R: -------------------------------------------------------------------------------- 1 | setGeneric("favorited", function(object, ...) 2 | standardGeneric("favorited")) 3 | 4 | setGeneric("screenName", function(object, ...) 5 | standardGeneric("screenName")) 6 | 7 | setGeneric("replyToSN", function(object, ...) 8 | standardGeneric("replyToSN")) 9 | 10 | setGeneric("created", function(object, ...) 11 | standardGeneric("created")) 12 | 13 | setGeneric("truncated", function(object, ...) 14 | standardGeneric("truncated")) 15 | 16 | setGeneric("replyToSID", function(object, ...) 17 | standardGeneric("replyToSID")) 18 | 19 | setGeneric("id", function(object, ...) 20 | standardGeneric("id")) 21 | 22 | setGeneric("replyToUID", function(object, ...) 23 | standardGeneric("replyToUID")) 24 | 25 | setGeneric("statusSource", function(object, ...) 26 | standardGeneric("statusSource")) 27 | 28 | setGeneric("user", function(object, ...) 29 | standardGeneric("user")) 30 | 31 | setGeneric("description", function(object, ...) 32 | standardGeneric("description")) 33 | 34 | setGeneric("statusesCount", function(object, ...) 35 | standardGeneric("statusesCount")) 36 | 37 | setGeneric("tweetCount", function(object, ...) 38 | standardGeneric("tweetCount")) 39 | 40 | setGeneric("followersCount", function(object, ...) 41 | standardGeneric("followersCount")) 42 | 43 | setGeneric("favoritesCount", function(object, ...) 44 | standardGeneric("favoritesCount")) 45 | 46 | setGeneric("friendsCount", function(object, ...) 47 | standardGeneric("friendsCount")) 48 | 49 | setGeneric("userURL", function(object, ...) 50 | standardGeneric("userURL")) 51 | 52 | setGeneric("name", function(object, ...) 53 | standardGeneric("name")) 54 | 55 | setGeneric("protected", function(object, ...) 56 | standardGeneric("protected")) 57 | 58 | setGeneric("verified", function(object, ...) 59 | standardGeneric("verified")) 60 | 61 | setGeneric("location", function(object, ...) 62 | standardGeneric("location")) 63 | 64 | setGeneric("lastStatus", function(object, ...) 65 | standardGeneric("lastStatus")) 66 | 67 | setGeneric("recipientSN", function(object, ...) 68 | standardGeneric("recipientSN")) 69 | 70 | setGeneric("recipientID", function(object, ...) 71 | standardGeneric("recipientID")) 72 | 73 | setGeneric("sender", function(object, ...) 74 | standardGeneric("sender")) 75 | 76 | setGeneric("recipient", function(object, ...) 77 | standardGeneric("recipient")) 78 | 79 | setGeneric("senderID", function(object, ...) 80 | standardGeneric("senderID")) 81 | 82 | setGeneric("senderSN", function(object, ...) 83 | standardGeneric("senderSN")) 84 | 85 | setGeneric("statusText", function(object, ...) 86 | standardGeneric("statusText")) 87 | -------------------------------------------------------------------------------- /R/base.R: -------------------------------------------------------------------------------- 1 | setRefClass('twitterObj', 2 | contains='VIRTUAL', 3 | methods = list( 4 | toDataFrame = function(row.names=NULL, optional=FALSE, 5 | stringsAsFactors=FALSE, fieldsToRemove=character()) { 6 | fields <- setdiff(names(.self$getRefClass()$fields()), 7 | fieldsToRemove) 8 | fieldList <- lapply(fields, function(x) { 9 | val <- .self$field(x) 10 | if (length(val) == 0) { 11 | NA 12 | } else { 13 | val 14 | } 15 | }) 16 | names(fieldList) <- fields 17 | as.data.frame(fieldList, row.names=row.names, 18 | optional=optional, stringsAsFactors=stringsAsFactors) 19 | } 20 | ) 21 | ) 22 | 23 | setMethod('as.data.frame', signature='twitterObj', 24 | function(x, row.names=NULL, optional=FALSE, stringsAsFactors=FALSE, ...) 25 | x$toDataFrame(row.names, optional, stringsAsFactors)) 26 | 27 | setRefClass('twitterObjList', 28 | contains = 'VIRTUAL', 29 | fields = list( 30 | objectList = 'list' 31 | ), 32 | methods = list() 33 | ) 34 | 35 | setMethod('show', signature='twitterObjList', function(object) { 36 | print(object$objectList) 37 | }) 38 | 39 | setMethod('[[', signature='twitterObjList', function(x, i) { 40 | x$objectList[[i]] 41 | }) 42 | 43 | listClassValidity <- function(object, objClass) { 44 | all(sapply(object$objectList, is, objClass)) 45 | } 46 | -------------------------------------------------------------------------------- /R/comm.R: -------------------------------------------------------------------------------- 1 | ## twitter API has multiple methods of handling paging issues, not to mention the search API 2 | ## has a completely different interface. Trying to manage all of these below using one unified 3 | ## approach to actually sending the data back & receiving response and then providing multiple 4 | ## mechanisms to page 5 | tw_from_response = function(response) { 6 | ## Will provide some basic error checking, as well as suppress 7 | ## warnings that always seem to come out of fromJSON, even 8 | ## in good cases. 9 | out <- try(suppressWarnings(fromJSON(content(response, as="text", encoding="UTF-8"))), silent=TRUE) 10 | if (inherits(out, "try-error")) { 11 | stop("Error: Malformed response from server, was not JSON.\n", 12 | "The most likely cause of this error is Twitter returning a character which\n", 13 | "can't be properly parsed by R. Generally the only remedy is to wait long\n", 14 | "enough for the offending character to disappear from searches (e.g. if\n", 15 | "using searchTwitter()).") 16 | } 17 | 18 | return(out) 19 | } 20 | 21 | doAPICall = function(cmd, params=NULL, method="GET", retryCount=5, 22 | retryOnRateLimit=0, debug=FALSE, ...) { 23 | if (debug) { 24 | browser() 25 | } 26 | 27 | if (!is.numeric(retryOnRateLimit)) { 28 | stop("retryOnRateLimit must be a number") 29 | } 30 | 31 | if (!is.numeric(retryCount)) { 32 | stop("retryCount must be a number") 33 | } 34 | 35 | recall_func = function(retryCount, rateLimitCount) { 36 | return(doAPICall(cmd, params=params, method=method, retryCount=retryCount, 37 | retryOnRateLimit=rateLimitCount, ...)) 38 | } 39 | url = getAPIStr(cmd) 40 | if (method == "POST") { 41 | out = try(POST(url, config(token=get_oauth_sig()), body=params), silent=TRUE) 42 | } else { 43 | if (is.null(params)) { 44 | query = NULL 45 | } else { 46 | query = lapply(params, function(x) URLencode(as.character(x))) 47 | } 48 | out = GET(url, query=query, config(token=get_oauth_sig())) 49 | } 50 | 51 | httr_status = out$status 52 | http_message = http_status(out)$message 53 | 54 | if (httr_status %in% c(500, 502)) { 55 | print(http_message) 56 | print(paste("This error is likely transient, retrying up to", retryCount, "more times ...")) 57 | ## These are typically fail whales or similar such things 58 | Sys.sleep(1) 59 | return(recall_func(retryCount - 1, rateLimitCount=retryOnRateLimit)) 60 | } else if (httr_status == 429) { 61 | if (retryOnRateLimit > 0) { 62 | ## We're rate limited. Wait a while and try again 63 | newRateLimit = retryOnRateLimit - 1 64 | print(paste("Rate limited .... blocking for a minute and retrying up to", newRateLimit, "times ...")) 65 | Sys.sleep(60) 66 | return(recall_func(retryCount, newRateLimit)) 67 | } else { 68 | ## FIXME: very experimental - the idea is that if we're rate limited, 69 | ## just give a warning and return. This should result in rate limited 70 | ## operations returning the partial result 71 | warning("Rate limit encountered & retry limit reached - returning partial results") 72 | return(NULL) 73 | } 74 | } else if (httr_status == 401) { 75 | stop("OAuth authentication error:\nThis most likely means that you have incorrectly called setup_twitter_oauth()'") 76 | } else { 77 | ## Generic catch-all for any other errors 78 | stop_for_status(out) 79 | } 80 | 81 | json = tw_from_response(out, ...) 82 | 83 | if (length(json[["errors"]]) > 0) { 84 | stop(json[["errors"]][[1]][["message"]]) 85 | } 86 | 87 | out = json 88 | } 89 | 90 | setRefClass('twAPIInterface', 91 | fields = list( 92 | maxResults = 'integer' 93 | ), 94 | methods = list( 95 | initialize=function(...) { 96 | maxResults <<- 100L 97 | callSuper(...) 98 | .self 99 | }, 100 | tw_from_response = tw_from_response, 101 | doAPICall = doAPICall 102 | ) 103 | ) 104 | 105 | 106 | tint <- getRefClass('twAPIInterface') 107 | tint$accessors(names(tint$fields())) 108 | twInterfaceObj <- tint$new() 109 | 110 | 111 | doPagedAPICall = function(cmd, num, params=NULL, method='GET', ...) { 112 | if (num <= 0) 113 | stop('num must be positive') 114 | else 115 | num <- as.integer(num) 116 | 117 | maxResults <- twInterfaceObj$getMaxResults() 118 | page <- 1 119 | total <- num 120 | count <- ifelse(num < maxResults, num, maxResults) 121 | jsonList <- list() 122 | params[['count']] <- count 123 | while (total > 0) { 124 | params[['page']] <- page 125 | results = twInterfaceObj$doAPICall(cmd, params, method, ...) 126 | if (is.null(results)) { 127 | return(jsonList) 128 | } 129 | 130 | jsonList <- c(jsonList, results) 131 | 132 | total <- total - count 133 | page <- page + 1 134 | } 135 | jLen <- length(jsonList) 136 | if ((jLen > 0) && (jLen > num)) 137 | jsonList <- jsonList[1:num] 138 | jsonList 139 | } 140 | 141 | doCursorAPICall = function(cmd, type, num=NULL, params=NULL, method='GET', ...) { 142 | cursor <- -1 143 | if (!is.null(num)) { 144 | if (num <= 0) 145 | stop("num must be positive") 146 | else 147 | num <- as.integer(num) 148 | } 149 | vals <- character() 150 | while(cursor != 0) { 151 | params[['cursor']] <- cursor 152 | curResults <- twInterfaceObj$doAPICall(cmd, params, method, ...) 153 | if (is.null(curResults)) { 154 | return(vals) 155 | } 156 | vals <- c(vals, curResults[[type]]) 157 | if ((!is.null(num)) && (length(vals) >= num)) 158 | break 159 | cursor <- curResults[['next_cursor_str']] 160 | } 161 | if ((!is.null(num)) && (length(vals) > num)) 162 | vals <- vals[1:num] 163 | vals 164 | } 165 | 166 | doRppAPICall = function(cmd, num, params, ...) { 167 | if (! 'q' %in% names(params)) 168 | stop("parameter 'q' must be supplied") 169 | maxResults <- twInterfaceObj$getMaxResults() 170 | params[['count']] <- ifelse(num < maxResults, num, maxResults) 171 | 172 | curDiff <- num 173 | jsonList <- list() 174 | ids = list() 175 | while (curDiff > 0) { 176 | fromJSON <- twInterfaceObj$doAPICall(cmd, params, 'GET', ...) 177 | if (is.null(fromJSON)) { 178 | return(jsonList) 179 | } 180 | newList <- fromJSON$statuses 181 | 182 | curIds = sapply(newList, function(x) x[["id"]]) 183 | dups = which(ids %in% ids) 184 | if (length(dups) > 0) { 185 | curIds = curIds[-dups] 186 | newList = newList[-dups] 187 | } 188 | 189 | if (length(curIds) == 0) { 190 | break 191 | } 192 | 193 | jsonList <- c(jsonList, newList) 194 | curDiff <- num - length(jsonList) 195 | if ((curDiff > 0)) { #&& (length(newList) == params[["count"]])) { 196 | params[["max_id"]] = as.character(as.integer64(min(curIds)) - 1) 197 | } else { 198 | break 199 | } 200 | } 201 | 202 | if (length(jsonList) > num) { 203 | jsonList = jsonList[seq_len(num)] 204 | } 205 | 206 | if (length(jsonList) < num) { 207 | warning(num, " tweets were requested but the API can only return ", length(jsonList)) 208 | } 209 | 210 | return(jsonList) 211 | } 212 | 213 | twitterDateToPOSIX <- function(dateStr) { 214 | ## In typical twitter fashion, there are multiple ways that they 215 | ## spit dates back at us. First, let's take a look at unix 216 | ## epoch time, and then try a few data string formats 217 | dateInt <- suppressWarnings(as.numeric(dateStr)) 218 | 219 | ## Locale must be set to something american-y in order to properly 220 | ## parse the Twitter dates. Get the current LC_TIME, reset it on 221 | ## exit and then change the locale 222 | curLocale <- Sys.getlocale("LC_TIME") 223 | on.exit(Sys.setlocale("LC_TIME", curLocale), add=TRUE) 224 | Sys.setlocale("LC_TIME", "C") 225 | 226 | if (!is.na(dateInt)) { 227 | posDate <- as.POSIXct(dateInt, tz='UTC', origin='1970-01-01') 228 | } else { 229 | posDate <- as.POSIXct(dateStr, tz='UTC', 230 | format="%a %b %d %H:%M:%S +0000 %Y") 231 | ## try again if necessary 232 | if (is.na(posDate)) 233 | posDate <- as.POSIXct(dateStr, tz='UTC', 234 | format="%a, %d %b %Y %H:%M:%S +0000") 235 | } 236 | ## might still be NA, but we tried 237 | return(posDate) 238 | } 239 | 240 | 241 | -------------------------------------------------------------------------------- /R/convert.R: -------------------------------------------------------------------------------- 1 | import_users = function(raw_data, conversion_func=json_to_users) { 2 | return(import_obj(raw_data, conversion_func)) 3 | } 4 | 5 | import_statuses = function(raw_data, conversion_func=json_to_statuses) { 6 | return(import_obj(raw_data, conversion_func)) 7 | } 8 | 9 | import_trends = function(raw_data, conversion_func=json_to_trends) { 10 | return(import_obj(raw_data, conversion_func)) 11 | } 12 | 13 | import_obj = function(raw_data, conversion_func, ...) { 14 | return(conversion_func(raw_data, ...)) 15 | } 16 | 17 | json_to_users = function(raw_data) { 18 | return(sapply(raw_data, buildUser)) 19 | } 20 | 21 | json_to_statuses = function(raw_data) { 22 | return(sapply(raw_data, buildStatus)) 23 | } 24 | 25 | json_to_trends = function(raw_data) { 26 | buildTrendLocationDf(raw_data) 27 | } 28 | 29 | df_to_statuses = function(df) { 30 | df_to_class(df, tweet_columns, buildStatus) 31 | } 32 | 33 | df_to_users = function(df) { 34 | df_to_class(df, user_columns, buildUser) 35 | } 36 | 37 | df_to_class = function(df, columns, builder, colname_converter=camel_case_to_twitter_names) { 38 | if (length(setdiff(colnames(df), columns)) > 0) { 39 | stop("Malformed tweet data.frame, columns don't match") 40 | } 41 | 42 | out = vector(mode="list", length=nrow(df)) 43 | colnames(df) = sapply(colnames(df), colname_converter) 44 | for (row in seq_along(df[, 1])) { 45 | out[[row]] = builder(as.list(df[row, ])) 46 | } 47 | 48 | out 49 | } -------------------------------------------------------------------------------- /R/db.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | store_tweets_db = function(tweets, table_name="tweets") { 4 | if (inherits(tweets, "status")) { 5 | tweets = list(tweets) 6 | } 7 | check_list_classes(tweets, "status") 8 | store_db(tweets, table_name) 9 | } 10 | 11 | load_tweets_db = function(as.data.frame=FALSE, table_name="tweets") { 12 | tweet_df = load_from_db(table_name) 13 | 14 | if (as.data.frame) { 15 | if (length(setdiff(colnames(tweet_df), tweet_columns)) > 0) { 16 | stop("Malformed tweet data.frame, columns don't match") 17 | } 18 | 19 | # Some column types need to be massaged from the DB if we're going straight to DF 20 | for (logical_col in c("favorited", "truncated", "isRetweet", "retweeted")) { 21 | tweet_df[, logical_col] = convert_logical_column(tweet_df[, logical_col]) 22 | } 23 | tweet_df$created = convert_date_column(tweet_df$created) 24 | 25 | tweet_df 26 | } else { 27 | # We don't need to do the checks & type massaging as the normal structure 28 | # of the import will take care of it 29 | import_statuses(tweet_df, df_to_statuses) 30 | } 31 | } 32 | 33 | store_users_db = function(users, table_name="users") { 34 | if (inherits(users, "user")) { 35 | users = list(users) 36 | } 37 | check_list_classes(users, "user") 38 | store_db(users, table_name) 39 | } 40 | 41 | load_users_db = function(as.data.frame=FALSE, table_name="users") { 42 | users_df = load_from_db(table_name) 43 | 44 | if (as.data.frame) { 45 | if (length(setdiff(colnames(users_df), user_columns)) > 0) { 46 | stop("Malformed user data.frame, columns don't match") 47 | } 48 | 49 | for (logical_col in c("protected", "verified", "followRequestSent")) { 50 | users_df[, logical_col] = convert_logical_column(users_df[, logical_col]) 51 | } 52 | users_df$created = convert_date_column(users_df$created) 53 | 54 | users_df 55 | } else { 56 | # We don't need to do the checks & type massaging as the normal structure 57 | # of the import will take care of it 58 | import_users(users_df, df_to_users) 59 | } 60 | } 61 | 62 | store_db = function(to_store, table_name) { 63 | db_handle = get_db_backend() 64 | df = twListToDF(to_store) 65 | df = escape_character_columns(df, db_handle) 66 | dbWriteTable(conn=db_handle, name=table_name, value=df, append=TRUE, overwrite=FALSE, 67 | row.names=FALSE) 68 | } 69 | 70 | load_from_db = function(table_name) { 71 | db_handle = get_db_handle_check_table(table_name) 72 | dbReadTable(db_handle, table_name) 73 | } 74 | 75 | get_db_handle_check_table = function(table_name) { 76 | if (!table_exists(table_name)) { 77 | stop("Supplied table_name does not exist: ", table_name) 78 | } 79 | 80 | get_db_backend() 81 | } 82 | 83 | table_exists = function(table_name) { 84 | db_handle = get_db_backend() 85 | table_name %in% dbListTables(db_handle) 86 | } 87 | 88 | convert_logical_column = function(column) { 89 | column == 1 90 | } 91 | 92 | escape_character_columns = function(df, db_handle) { 93 | escape_character_column = function(column) { 94 | nas = which(is.na(column)) 95 | new_column = RMySQL::dbEscapeStrings(db_handle, column) 96 | new_column[nas] = NA 97 | new_column 98 | } 99 | 100 | # Some DBI types have issues with stuff like \n in character fields, 101 | # strip these out if dbEscapeStrings exists 102 | if (attr(class(db_handle), "package") == "RMySQL") { 103 | char_columns = which(sapply(df, class) == "character") 104 | for (column in char_columns) { 105 | df[, column] = escape_character_column(df[, column]) 106 | } 107 | } 108 | 109 | df 110 | } 111 | 112 | convert_date_column = function(column) { 113 | as.POSIXct(column, tz="UTC", origin="1970-01-01") 114 | } 115 | 116 | check_list_classes = function(to_store, required_class) { 117 | if (!all(sapply(to_store, class) == required_class)) { 118 | stop("Invalid input, all entities in list must be of class ", required_class) 119 | } 120 | } -------------------------------------------------------------------------------- /R/db_connections.R: -------------------------------------------------------------------------------- 1 | require_package = function(package_name) { 2 | if (!require(package_name, character.only=TRUE, quietly=TRUE)) { 3 | stop(package_name, " is required to be installed for this activity") 4 | } 5 | } 6 | 7 | supported_db_handle_classes = c("SQLiteConnection", "MySQLConnection") 8 | 9 | # Based on the class of db_handle, make sure the appropriate dbi backend 10 | # package exists 11 | require_dbi_package = function(db_handle) { 12 | if (!inherits(db_handle, supported_db_handle_classes)) { 13 | stop("db_handle must be one of the following classes: ", 14 | paste(supported_db_handle_classes, collapse=", ")) 15 | } 16 | 17 | switch(class(db_handle), 18 | SQLiteConnection = require_package("RSQLite"), 19 | MySQLConnection = require_package("RMySQL") 20 | ) 21 | 22 | TRUE 23 | } 24 | 25 | # Convenience function which will wrap some of the DBI commands to get a SQLiteConnection 26 | register_sqlite_backend = function(sqlite_file, ...) { 27 | if (!requireNamespace("RSQLite")) { 28 | stop("RSQLite package must be installed to generate an SQLite handle") 29 | } 30 | 31 | register_db_backend(dbConnect(RSQLite::SQLite(), sqlite_file, ...)) 32 | } 33 | 34 | # Convenience function which will wrap the DBI command for a MySQL connection 35 | register_mysql_backend = function(db_name, host, user, password, ...) { 36 | if (!requireNamespace("RMySQL")) { 37 | stop("RMySQL package must be installed to generate a MySQL handle") 38 | } 39 | 40 | db_handle = register_db_backend(dbConnect(RMySQL::MySQL(), user=user, password=password, 41 | dbname=db_name, host=host, ...)) 42 | dbGetQuery(db_handle, "SET NAMES 'utf8'") 43 | 44 | invisible(db_handle) 45 | } 46 | 47 | 48 | register_db_backend = function(db_handle) { 49 | require_dbi_package(db_handle) 50 | assign("db_handle", db_handle, envir=db_backend_cache) 51 | } 52 | 53 | has_db_backend = function() { 54 | exists("db_handle", envir=db_backend_cache) 55 | } 56 | 57 | get_db_backend = function() { 58 | if (!has_db_backend()) { 59 | stop("No DB backend has been registered, see ?register_db_backend") 60 | } 61 | 62 | get("db_handle", envir=db_backend_cache) 63 | } -------------------------------------------------------------------------------- /R/db_search.R: -------------------------------------------------------------------------------- 1 | search_twitter_and_store = function(searchString, table_name="tweets", n = 5000, lang=NULL, 2 | locale=NULL, geocode=NULL, retryOnRateLimit=120, ...) { 3 | if (table_exists(table_name)) { 4 | since_id = get_latest_tweet_id(table_name) 5 | } else { 6 | since_id = NULL 7 | } 8 | 9 | new_tweets = suppressWarnings(searchTwitter(searchString, n=n, sinceID=since_id, lang=lang, 10 | locale=locale, retryOnRateLimit=retryOnRateLimit, ...)) 11 | if (length(new_tweets) > 0) { 12 | store_tweets_db(new_tweets, table_name) 13 | } 14 | 15 | length(new_tweets) 16 | } 17 | -------------------------------------------------------------------------------- /R/db_utils.R: -------------------------------------------------------------------------------- 1 | get_latest_tweet_id = function(table_name = "tweets") { 2 | db_handle = get_db_handle_check_table(table_name) 3 | max_id = dbGetQuery(db_handle, paste("select max(id) from", table_name)) 4 | if (nrow(max_id) == 0) { 5 | stop("No existing tweets in ", table_name) 6 | } else { 7 | max_id[1, 1] 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /R/df_columns.R: -------------------------------------------------------------------------------- 1 | tweet_columns = c("text", "favorited", "favoriteCount", "replyToSN", "created", 2 | "truncated", "replyToSID", "id", "replyToUID", "statusSource", 3 | "screenName", "retweetCount", "isRetweet", "retweeted", "longitude", 4 | "latitude", "location", "language", "profileImageURL") 5 | 6 | user_columns = c("description", "statusesCount", "followersCount", "favoritesCount", 7 | "friendsCount", "url", "name", "created", "protected", "verified", 8 | "screenName", "location", "id", "listedCount", "followRequestSent", 9 | "profileImageUrl") 10 | 11 | camel_case_to_twitter_names = function(name) { 12 | tolower(gsub("([A-Z])", "_\\1", name, perl=TRUE)) 13 | } 14 | -------------------------------------------------------------------------------- /R/dm.R: -------------------------------------------------------------------------------- 1 | setRefClass('dmList', 2 | contains='twitterObjList' 3 | ) 4 | 5 | setValidity('dmList', function(object) { 6 | listClassValidity(object, 'directMessage') 7 | }) 8 | 9 | setRefClass("directMessage", 10 | contains='twitterObj', 11 | fields = list( 12 | text = "character", 13 | recipientSN = "character", 14 | created = "POSIXct", 15 | recipientID = "character", 16 | sender = "user", 17 | recipient = "user", 18 | senderID = "character", 19 | id = "character", 20 | senderSN = "character"), 21 | methods = list( 22 | initialize = function(json, ...) { 23 | if (!missing(json)) { 24 | if (!is.null(json[['sender']])) 25 | sender <<- buildUser(json[['sender']]) 26 | if (!is.null(json[['recipient']])) 27 | recipient <<- buildUser(json[['recipient']]) 28 | if (!is.null(json[['text']])) 29 | text <<- json[['text']] 30 | if (!is.null(json[['recipient_screen_name']])) 31 | recipientSN <<- json[['recipient_screen_name']] 32 | if (!is.null(json[['created']])) 33 | created <<- twitterDateToPOSIX(json[['created']]) 34 | if (!is.null(json[['recipient']][['id_str']])) 35 | recipientID <<- json[['recipient']][['id_str']] 36 | if (!is.null(json[['sender']][['id_str']])) 37 | senderID <<- json[['sender']][['id_str']] 38 | if (!is.null(json[['sender_screen_name']])) 39 | senderSN <<- json[['sender_screen_name']] 40 | if (!is.null(json[['id_str']])) { 41 | id <<- json[['id_str']] 42 | } 43 | } 44 | callSuper(...) 45 | }, 46 | destroy = function() { 47 | dmDestroy(.self) 48 | } 49 | ) 50 | ) 51 | 52 | setMethod("show", signature="directMessage", function(object) { 53 | print(paste(screenName(object$sender), "->", 54 | screenName(object$recipient), ": ", 55 | object$text, sep="")) 56 | }) 57 | 58 | dmFactory <- getRefClass("directMessage") 59 | dmFactory$accessors(names(dmFactory$fields())) 60 | 61 | 62 | dmGet <- function(n=25, sinceID=NULL, maxID=NULL, ...) { 63 | dmGETBase(n, sinceID, maxID) 64 | } 65 | 66 | dmSent <- function(n=25, sinceID=NULL, maxID=NULL, ...) { 67 | dmGETBase(n, sinceID, maxID, "/sent") 68 | } 69 | 70 | dmGETBase <- function(n, sinceID, maxID, type='', ...) { 71 | if (!has_oauth_token()) 72 | stop("dmGet requires OAuth authentication") 73 | 74 | if (n <= 0) 75 | stop("n must be positive") 76 | else 77 | n <- as.integer(n) 78 | params <- buildCommonArgs(since_id=sinceID, max_id=maxID) 79 | jsonList <- doPagedAPICall(paste('direct_messages', type, sep=''), 80 | num=n, params=params, ...) 81 | sapply(jsonList, function(x) dmFactory$new(x)) 82 | } 83 | 84 | dmDestroy <- function(dm, ...) { 85 | if (!has_oauth_token()) 86 | stop("dmDestroy requires OAuth authentication") 87 | if (!inherits(dm, "directMessage")) 88 | stop("dm must be of class directMessage") 89 | json <- twInterfaceObj$doAPICall('direct_messages/destroy', 90 | params=list(id=dm$getId()), 91 | method='POST', ...) 92 | if (is.null(json$errors)) { 93 | TRUE 94 | } else { 95 | for (error in json$errors) { 96 | cat(error$message, error$code, fill = TRUE) 97 | } 98 | FALSE 99 | } 100 | } 101 | 102 | dmSend <- function(text, user, ...) { 103 | if (!has_oauth_token()) { 104 | stop("dmSend requires OAuth authentication") 105 | } 106 | if (inherits(user, "user")) { 107 | u.params <- c(list(...)[["uParams"]], list(screen_name = screenName(user))) 108 | } else { 109 | uParams <- parseUsers(user) 110 | } 111 | if (nchar(text) > 140) { 112 | stop("Maximum of 140 chars may be sent via a direct message") 113 | } 114 | params <- c(list(...)[["params"]], list(text=text)) 115 | params[["user_id"]] <- uParams[["user_id"]] 116 | params[["screen_name"]] <- uParams[["screen_name"]] 117 | res <- twInterfaceObj$doAPICall('direct_messages/new', 118 | params=params, method='POST', ...) 119 | dmFactory$new(res) 120 | } 121 | -------------------------------------------------------------------------------- /R/followers.R: -------------------------------------------------------------------------------- 1 | followers <- function(user_id, n=NULL, ...) { 2 | ffBase('followers', user_id, n, ...) 3 | } 4 | 5 | friends <- function(user_id, n=NULL, ...) { 6 | ffBase('friends', user_id, n, ...) 7 | } 8 | 9 | ffBase <- function(type, user_id, n=NULL, ...) { 10 | if (!can_access_other_account(user_id)) { 11 | warning("Cannot lookup relationships for user id ", user_id, ", query may fail!") 12 | } 13 | params <- c(parseUsers(user_id), list(stringify_ids=TRUE)) 14 | doCursorAPICall(paste(type, 'ids', sep='/'), 'ids', num=n, params=params, method='GET', ...) 15 | } 16 | 17 | friendships = function(screen_names=character(), user_ids=character(), ...) { 18 | if ((length(user_ids) + length(screen_names)) > 100) { 19 | stop("friendships() has a maximum of 100 total user ids and screen names") 20 | } 21 | 22 | user_list = buildUserList(user_ids, screen_names) 23 | 24 | friendships = twInterfaceObj$doAPICall("friendships/lookup", params=user_list, ...) 25 | friendship_dfs = lapply(friendships, function(x) { 26 | following = "following" %in% x$connections 27 | followed_by = "followed_by" %in% x$connections 28 | return(c(name=x$name, screen_name=x$screen_name, id=x$id_str, following=following, 29 | followed_by=followed_by)) 30 | }) 31 | 32 | return(as.data.frame(do.call(rbind, friendship_dfs), stringsAsFactors=FALSE)) 33 | } -------------------------------------------------------------------------------- /R/oauth.R: -------------------------------------------------------------------------------- 1 | getTwitterOAuth = function(consumer_key, consumer_secret) { 2 | stop("ROAuth is no longer used in favor of httr, please see ?setup_twitter_oauth") 3 | } 4 | 5 | registerTwitterOAuth <- function(oauth) { 6 | stop("ROAuth is no longer used in favor of httr, please see ?setup_twitter_oauth") 7 | } 8 | 9 | check_twitter_oauth = function() { 10 | req = try(stop_for_status(GET("https://api.twitter.com/1.1/account/settings.json", 11 | config(token=get_oauth_sig()))), silent=TRUE) 12 | 13 | if (inherits(req, "try-error")) { 14 | stop("OAuth authentication error:\nThis most likely means that you have incorrectly called setup_twitter_oauth()'") 15 | } 16 | } 17 | 18 | get_twitter_token_via_sign = function(app, access_token, access_secret) { 19 | print("Using direct authentication") 20 | params <- list(as_header = TRUE) 21 | credentials <- list(oauth_token = access_token, 22 | oauth_token_secret = access_secret) 23 | twitter_token <- Token1.0$new(endpoint = NULL, params = params, 24 | app = app, credentials = credentials) 25 | 26 | if (is.null(twitter_token)) 27 | stop("Invalid response for twitter_token") 28 | 29 | twitter_token 30 | } 31 | 32 | get_twitter_token_via_browser = function(app, ...) { 33 | print("Using browser based authentication") 34 | oauth1.0_token(oauth_endpoints('twitter'), app) 35 | } 36 | 37 | setup_twitter_oauth = function(consumer_key, consumer_secret, access_token=NULL, access_secret=NULL) { 38 | app <- oauth_app("twitter", key=consumer_key, secret=consumer_secret) 39 | 40 | if (is.null(access_token) || is.null(access_secret)) { 41 | token_func = get_twitter_token_via_browser 42 | } else { 43 | token_func = get_twitter_token_via_sign 44 | } 45 | 46 | twitter_token = token_func(app, access_token, access_secret) 47 | assign("oauth_token", twitter_token, envir=oauth_cache) 48 | 49 | check_twitter_oauth() 50 | } 51 | 52 | use_oauth_token = function(twitter_token) { 53 | assign("oauth_token", twitter_token, envir=oauth_cache) 54 | } 55 | 56 | has_oauth_token = function() { 57 | exists("oauth_token", envir=oauth_cache) 58 | } 59 | 60 | get_oauth_sig = function() { 61 | if (!has_oauth_token()) { 62 | stop("OAuth has not been registered for this session") 63 | } 64 | 65 | return(get("oauth_token", envir=oauth_cache)) 66 | } 67 | -------------------------------------------------------------------------------- /R/s4methods.R: -------------------------------------------------------------------------------- 1 | ## Bit of a misnomer as there are other S4 methods floating around. 2 | ## These are outdated S4 methods that were in use before the move 3 | ## towards reference classes and are only kept for backwards 4 | ## compatability 5 | setMethod("statusText", signature="status", function(object) { 6 | object$text 7 | }) 8 | 9 | setMethod("favorited", signature="status", function(object, ...) { 10 | object$favorited 11 | }) 12 | 13 | setMethod("replyToSN", signature="status", function(object, ...) { 14 | object$replyToSN 15 | }) 16 | 17 | setMethod("created", signature="status", function(object, ...) { 18 | object$created 19 | }) 20 | 21 | setMethod("truncated", signature="status", function(object, ...) { 22 | object$truncated 23 | }) 24 | 25 | setMethod("replyToSID", signature="status", function(object, ...) { 26 | object$replyToSID 27 | }) 28 | 29 | setMethod("id", signature="status", function(object, ...){ 30 | object$id 31 | }) 32 | 33 | setMethod("replyToUID", signature="status", function(object, ...) { 34 | object$replyToUID 35 | }) 36 | 37 | setMethod("statusSource", signature="status", function(object, ...) { 38 | object$statusSource 39 | }) 40 | 41 | setMethod("screenName", signature="status", function(object, ...) { 42 | object$screenName 43 | }) 44 | 45 | setMethod("screenName", signature="user", function(object, ...) { 46 | object$screenName 47 | }) 48 | 49 | setMethod("description", signature="user", function(object, ...) { 50 | object$description 51 | }) 52 | 53 | setMethod("statusesCount", signature="user", function(object, ...) { 54 | object$statusesCount 55 | }) 56 | 57 | setMethod("tweetCount", signature="user", function(object, ...) { 58 | statusesCount(object) 59 | }) 60 | 61 | setMethod("followersCount", signature="user", function(object, ...) { 62 | object$followersCount 63 | }) 64 | 65 | setMethod("favoritesCount", signature="user", function(object, ...) { 66 | object$favoritesCount 67 | }) 68 | 69 | setMethod("friendsCount", signature="user", function(object, ...) { 70 | object$friendsCount 71 | }) 72 | 73 | setMethod("userURL", signature="user", function(object, ...) { 74 | object$url 75 | }) 76 | 77 | setMethod("name", signature="user", function(object, ...) { 78 | object$name 79 | }) 80 | 81 | setMethod("created", signature="user", function(object, ...) { 82 | object$created 83 | }) 84 | 85 | setMethod("protected", signature="user", function(object, ...) { 86 | object$protected 87 | }) 88 | 89 | setMethod("verified", signature="user", function(object, ...) { 90 | object$verified 91 | }) 92 | 93 | setMethod("location", signature="user", function(object, ...) { 94 | object$location 95 | }) 96 | 97 | setMethod("id", signature="user", function(object, ...) { 98 | object$id 99 | }) 100 | 101 | setMethod("lastStatus", signature="user", function(object, ...) { 102 | object$lastStatus 103 | }) 104 | -------------------------------------------------------------------------------- /R/search.R: -------------------------------------------------------------------------------- 1 | Rtweets <- function(n=25, lang=NULL, since=NULL, ...) { 2 | searchTwitter("#rstats", n=n, lang=lang, since=since, ...) 3 | } 4 | 5 | searchTwitter <- function(searchString, n=25, lang=NULL, 6 | since=NULL, until=NULL, locale=NULL, 7 | geocode=NULL, sinceID=NULL, maxID=NULL, 8 | resultType=NULL, 9 | retryOnRateLimit=120, ...) { 10 | 11 | 12 | if (nchar(searchString) > 1000) { 13 | stop("searchString can only be up to 1000 characters") 14 | } 15 | 16 | if (n <= 0) 17 | stop("n must be positive") 18 | n <- as.integer(n) 19 | 20 | if (is.null(since)) { 21 | since_date = NULL 22 | } else { 23 | since_date = strsplit(since, " ")[[1]][1] 24 | } 25 | 26 | if (is.null(until)) { 27 | until_date = NULL 28 | } else { 29 | until_date = strsplit(until, " ")[[1]][1] 30 | if ((!is.null(since_date)) && (until_date == since_date)) { 31 | ## If since & until are on the same day nothing will be returned. Move 32 | ## until up a day and then we'll filter this later 33 | until_date = as.Date(since_date) + 1 34 | } 35 | } 36 | 37 | if (!is.null(geocode)) { 38 | geocheck = strsplit(geocode[[1]], ',')[[1]] 39 | lat = as.numeric(geocheck[1]) 40 | lon = as.numeric(geocheck[2]) 41 | if ((lon > 180) || (lon < -180)) { 42 | stop('Longitude need to be in range [180,-180].') 43 | } 44 | if ((lat > 90)||(lat < -90)) { 45 | stop('Latitude need to be in range [90.0,-90.0].') 46 | } 47 | } 48 | 49 | params <- buildCommonArgs(lang=lang, locale=locale, since=since_date, until=until_date, 50 | geocode=geocode, since_id=sinceID, max_id=maxID, 51 | result_type=resultType ) 52 | params[['q']] <- searchString 53 | params[["include_entities"]] = TRUE 54 | 55 | jsonList <- doRppAPICall("search/tweets", n, params=params, retryOnRateLimit=retryOnRateLimit, ...) 56 | statuses = import_statuses(jsonList) 57 | 58 | datetimes = sapply(statuses, function(x) as.Date(x$getCreated())) 59 | if (is.null(since)) { 60 | since_statuses = seq_along(statuses) 61 | } else { 62 | since_statuses = which(datetimes >= as.numeric(as.Date(since))) 63 | } 64 | if (is.null(until)) { 65 | until_statuses = seq_along(statuses) 66 | } else { 67 | until_statuses = which(datetimes <= as.numeric(as.Date(until))) 68 | } 69 | good_statuses = intersect(since_statuses, until_statuses) 70 | return(statuses[good_statuses]) 71 | } 72 | 73 | ## I don't know how many times I've typod this, making an alias 74 | searchTwitteR = searchTwitter 75 | -------------------------------------------------------------------------------- /R/statuses.R: -------------------------------------------------------------------------------- 1 | setRefClass('statusList', 2 | contains='twitterObjList' 3 | ) 4 | 5 | setValidity('statusList', function(object) { 6 | listClassValidity(object, 'status') 7 | }) 8 | 9 | setRefClass("status", 10 | contains='twitterObj', 11 | fields = list( 12 | text="character", 13 | favorited="logical", 14 | favoriteCount="numeric", 15 | replyToSN="character", 16 | created="POSIXct", 17 | truncated="logical", 18 | replyToSID="character", 19 | id="character", 20 | replyToUID="character", 21 | statusSource="character", 22 | screenName="character", 23 | retweetCount="numeric", 24 | isRetweet="logical", 25 | retweeted="logical", 26 | longitude="character", 27 | latitude="character", 28 | location="character", 29 | language="character", 30 | profileImageURL="character", 31 | urls="data.frame" 32 | ), 33 | methods=list( 34 | initialize = function(json, ...) { 35 | if (!missing(json)) { 36 | locationName <- 'Unknown' 37 | languageName <- 'Unknown' 38 | profileImageURLName <- 'Unknown' 39 | if ('user' %in% names(json)) { 40 | userObj <- userFactory$new(json[['user']]) 41 | screenName <<- userObj$getScreenName() 42 | locationName <- json$user$location 43 | languageName <- json$user$lang 44 | profileImageURLName <- json$user$profile_image_url 45 | } else if ('from_user' %in% names(json)) { 46 | screenName <<- json[['from_user']] 47 | } else if ("screen_name" %in% names(json)) { 48 | screenName <<- json[["screen_name"]] 49 | } else { 50 | screenName <<- "Unknown" 51 | } 52 | if ('location' %in% names(json)) { 53 | locationName <- json$location 54 | } 55 | if ('language' %in% names(json)) { 56 | languageName <- json$language 57 | } 58 | if ('profile_image_u_r_l' %in% names(json)) { 59 | profileImageURLName <- json$profile_image_u_r_l 60 | } 61 | 62 | location <<- locationName 63 | language <<- languageName 64 | profileImageURL <<- profileImageURLName 65 | 66 | if (!is.null(json[['text']])) { 67 | text <<- json[['text']] 68 | } 69 | 70 | if ((is.null(json[['favorited']])) || 71 | (json[["favorited"]] == FALSE)) { 72 | favorited <<- FALSE 73 | } else { 74 | favorited <<- TRUE 75 | } 76 | 77 | if ((is.null(json[['truncated']])) || 78 | (json[["truncated"]] == FALSE)) { 79 | truncated <<- FALSE 80 | } else { 81 | truncated <<- TRUE 82 | } 83 | 84 | status_source = get_json_value(json, c("source", "status_source")) 85 | if (!is.null(status_source)) { 86 | statusSource <<- status_source 87 | } 88 | 89 | created_at = get_json_value(json, c("created_at", "created")) 90 | if (is.null(created_at)) { 91 | created <<- Sys.time() 92 | } else { 93 | created <<- twitterDateToPOSIX(created_at) 94 | } 95 | 96 | in_reply_to_screen_name = get_json_value(json, c("reply_to_s_n", "in_reply_to_screen_name")) 97 | if (!is.null(in_reply_to_screen_name) && (!is.na(in_reply_to_screen_name))) { 98 | replyToSN <<- as.character(in_reply_to_screen_name) 99 | } 100 | 101 | in_reply_to_sid = get_json_value(json, c("reply_to_s_i_d", "in_reply_to_status_id_str")) 102 | if ((!is.null(in_reply_to_sid)) && (!is.na(in_reply_to_sid))) { 103 | replyToSID <<- as.character(in_reply_to_sid) 104 | } 105 | 106 | reply_to_uid = get_json_value(json, c("reply_to_u_i_d", "in_reply_to_user_id_str")) 107 | if ((!is.null(reply_to_uid)) && (!is.na(reply_to_uid))) { 108 | replyToUID <<- as.character(reply_to_uid) 109 | } 110 | 111 | # Note: Make sure id_str is first here, otherwise numeric id will be snagged 112 | id_field = get_json_value(json, c("id_str", "id")) 113 | if (!is.null(id_field)) { 114 | id <<- as.character(id_field) 115 | } 116 | 117 | if (!is.null(json[["retweet_count"]])) { 118 | retweetCount <<- as.numeric(json[["retweet_count"]]) 119 | } 120 | 121 | if ((is.null(json[['retweeted']])) || 122 | (json[["retweeted"]] == FALSE)) { 123 | retweeted <<- FALSE 124 | } else { 125 | retweeted <<- TRUE 126 | } 127 | if (!is.null(json[["favorite_count"]])) { 128 | favoriteCount <<- as.numeric(json[["favorite_count"]]) 129 | } 130 | if (!is.null(json[["coordinates"]]) && (!is.null(json[["coordinates"]][["coordinates"]]))) { 131 | longitude <<- as.character(json[["coordinates"]][["coordinates"]][1]) 132 | latitude <<- as.character(json[["coordinates"]][["coordinates"]][2]) 133 | } else { 134 | if (!is.null(json[["longitude"]])) { 135 | longitude <<- as.character(json[["longitude"]]) 136 | } 137 | if (!is.null(json[["latitude"]])) { 138 | latitude <<- as.character(json[["latitude"]]) 139 | } 140 | } 141 | 142 | ## If retweeted_status is provided (which contains the full original status), this is a retweet 143 | isRetweet <<- any(c("retweeted_status", "isRetweet") %in% names(json)) 144 | 145 | urls <<- build_urls_data_frame(json) 146 | } 147 | callSuper(...) 148 | }, 149 | getRetweets = function(n=20, ...) { 150 | return(retweets(self$getId(), n, ...)) 151 | }, 152 | getRetweeters = function(n=20, ...) { 153 | return(retweeters(self$getId(), n, ...)) 154 | }, 155 | toDataFrame = function(row.names=NULL, optional=FALSE, stringsAsFactors=FALSE) { 156 | callSuper(row.names=row.names, optional=optional, stringsAsFactors=stringsAsFactors, 157 | fieldsToRemove="urls") 158 | } 159 | ) 160 | ) 161 | 162 | 163 | statusFactory = getRefClass("status") 164 | statusFactory$accessors(names(statusFactory$fields())) 165 | 166 | buildStatus = function(json) { 167 | if (is.null(json)) { 168 | NULL 169 | } else { 170 | statusFactory$new(json) 171 | } 172 | } 173 | 174 | setMethod("show", signature="status", function(object) { 175 | print(paste(screenName(object), object$text, sep=": ")) 176 | }) 177 | 178 | updateStatus <- function(text, lat=NULL, long=NULL, placeID=NULL, 179 | displayCoords=NULL, inReplyTo=NULL, mediaPath=NULL, 180 | bypassCharLimit=FALSE, ...) { 181 | if (!has_oauth_token()) 182 | stop("updateStatus requires OAuth authentication") 183 | 184 | if (nchar(text) > 140 && !bypassCharLimit) 185 | stop("Status can not be more than 140 characters") 186 | 187 | params = buildCommonArgs(lat=lat, long=long, place_id=placeID, 188 | display_coordinates=displayCoords, 189 | in_reply_to_status_id=inReplyTo) 190 | params[['status']] <- text 191 | 192 | if (is.null(mediaPath)){ 193 | endpoint = 'statuses/update' 194 | } else { 195 | endpoint = 'statuses/update_with_media' 196 | params[['media']] <- upload_file(mediaPath) 197 | } 198 | json = twInterfaceObj$doAPICall(endpoint, 199 | params=params, method='POST', ...) 200 | return(buildStatus(json)) 201 | } 202 | 203 | tweet = function(text, ...) { 204 | return(updateStatus(text, ...)) 205 | } 206 | 207 | deleteStatus = function(status, ...) { 208 | if (!has_oauth_token()) { 209 | stop("deleteStatus requires OAuth authentication") 210 | } 211 | if (!inherits(status, 'status')) { 212 | stop("status argument must be of class status") 213 | } 214 | 215 | json = twInterfaceObj$doAPICall(paste('statuses/destroy', 216 | status$getId(), sep='/'), 217 | method='POST', ...) 218 | if (is.null(json$errors)) { 219 | return(TRUE) 220 | } else { 221 | for (error in json$errors) { 222 | cat(error$message, error$code, fill = TRUE) 223 | } 224 | return(FALSE) 225 | } 226 | } 227 | 228 | lookup_statuses = function(ids, ...) { 229 | sapply(ids, check_id) 230 | cmd = "statuses/lookup" 231 | params = list(id=paste(ids, collapse=",")) 232 | # FIXME: Note that this is set to GET but twitter recommends POST. See issue #78 233 | return(sapply(twInterfaceObj$doAPICall(cmd, params=params, method="GET", ...), buildStatus)) 234 | } 235 | 236 | showStatus = function(id, ...) { 237 | check_id(id) 238 | buildStatus(twInterfaceObj$doAPICall(paste('statuses', 'show', id, sep='/'), ...)) 239 | } 240 | 241 | retweets = function(id, n=20, ...) { 242 | check_id(id) 243 | 244 | if (n > 100) { 245 | stop("n must be less than 100, set to ", n) 246 | } 247 | 248 | cmd = "statuses/retweets" 249 | params = list(id=id, count=n) 250 | return(sapply(doAPICall(cmd, params=params), buildStatus)) 251 | } 252 | 253 | retweeters = function(id, n=20, ...) { 254 | check_id(id) 255 | 256 | cmd = "statuses/retweeters/ids" 257 | params = list(id=id, count=n) 258 | json = doCursorAPICall(cmd, "ids", num=n, params=params, method="GET", ...) 259 | json 260 | } 261 | 262 | favorites = function(user, n=20, max_id=NULL, since_id=NULL, ...) { 263 | uParams = parseUsers(user) 264 | cmd = "favorites/list" 265 | params = buildCommonArgs(max_id=max_id, since_id=since_id) 266 | params[["user_id"]] = uParams[["user_id"]] 267 | params[["screen_name"]] = uParams[["screen_name"]] 268 | return(statusBase(cmd, params, n, 200, ...)) 269 | } 270 | 271 | userTimeline = function(user, n=20, maxID=NULL, sinceID=NULL, includeRts=FALSE, excludeReplies=FALSE, ...) { 272 | uParams <- parseUsers(user) 273 | cmd <- 'statuses/user_timeline' 274 | params <- buildCommonArgs(max_id=maxID, since_id=sinceID) 275 | params[['user_id']] <- uParams[['user_id']] 276 | params[['screen_name']] <- uParams[['screen_name']] 277 | params[["include_rts"]] <- ifelse(includeRts == TRUE, "true", "false") 278 | params[["exclude_replies"]] <- ifelse(excludeReplies == TRUE, "true", "false") 279 | return(statusBase(cmd, params, n, 3200, ...)) 280 | } 281 | 282 | homeTimeline <- function(n=25, maxID=NULL, sinceID=NULL, ...) { 283 | return(authStatusBase(n, 'home_timeline', maxID=maxID, sinceID=sinceID, ...)) 284 | } 285 | 286 | mentions <- function(n=25, maxID=NULL, sinceID=NULL, ...) { 287 | return(authStatusBase(n, 'mentions_timeline', maxID=maxID, sinceID=sinceID, ...)) 288 | } 289 | 290 | retweetsOfMe <- function(n=25, maxID=NULL, sinceID=NULL, ...) { 291 | return(authStatusBase(n, 'retweets_of_me', maxID=maxID, sinceID=sinceID, ...)) 292 | } 293 | 294 | listTimeline <- function (user, list_name, n = 20, maxID = NULL, 295 | sinceID = NULL, includeRts = FALSE, 296 | excludeReplies = FALSE, ...) { 297 | uParams <- parseUsers(user) 298 | cmd <- "lists/statuses" 299 | params <- buildCommonArgs(max_id = maxID, since_id = sinceID) 300 | params[["slug"]] <- list_name 301 | params[["owner_screen_name"]] <- uParams[["screen_name"]] 302 | params[["include_rts"]] <- ifelse(includeRts == TRUE, "true", 303 | "false") 304 | params[["exclude_replies"]] <- ifelse(excludeReplies == TRUE, 305 | "true", "false") 306 | return(statusBase(cmd, params, n, 3200, ...)) 307 | } 308 | 309 | authStatusBase <- function(n, type, maxID=NULL, sinceID=NULL, ...) { 310 | if (!has_oauth_token()) { 311 | stop("OAuth is required for this functionality") 312 | } 313 | 314 | params <- buildCommonArgs(max_id=maxID, since_id=sinceID) 315 | cmd <- paste('statuses', type, sep='/') 316 | cmd <- paste('statuses', type, sep='/') 317 | return(statusBase(cmd, params, n, 800, ...)) 318 | } 319 | 320 | statusBase <- function(cmd, params, n, maxN, ...) { 321 | n <- as.integer(n) 322 | if (n > maxN) { 323 | warning(cmd, " has a cap of ", maxN, " statuses, clipping") 324 | n <- maxN 325 | } 326 | return(sapply(doPagedAPICall(cmd, n, params, ...), buildStatus)) 327 | } 328 | 329 | build_urls_data_frame = function(json) { 330 | ## takes a status JSON and will either return a data.frame of the URLs entity or an 331 | ## empty data.frame if there were none provided 332 | split_indices = function(urls_block) { 333 | urls_block$start_index = urls_block$indices[1] 334 | urls_block$stop_index = urls_block$indices[2] 335 | urls_block$indices = NULL 336 | urls_block 337 | } 338 | 339 | if (length(json$entities$urls) > 0) { 340 | urls = json$entities$urls 341 | massaged_urls = lapply(urls, split_indices) 342 | return(do.call("rbind", lapply(massaged_urls, as.data.frame, stringsAsFactors=FALSE))) 343 | } else { 344 | data.frame(url=character(), expanded_url=character(), dispaly_url=character(), indices=numeric(), stringsAsFactors=FALSE) 345 | } 346 | } 347 | 348 | retweetStatus <- function(status, ...) { 349 | if (!has_oauth_token()) { 350 | stop("retweetStatus requires OAuth authentication") 351 | } 352 | if (!inherits(status, 'status')) { 353 | stop("status argument must be of class status") 354 | } 355 | 356 | json = twInterfaceObj$doAPICall(paste('statuses/retweet', 357 | status$getId(), sep='/'), 358 | method='POST', ...) 359 | if (is.null(json$errors)) { 360 | return(TRUE) 361 | } else { 362 | for (error in json$errors) { 363 | cat(error$message, error$code, fill = TRUE) 364 | } 365 | return(FALSE) 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /R/toys.R: -------------------------------------------------------------------------------- 1 | taskStatus <- function(expr, to, msg="") { 2 | if (!has_oauth_token()) 3 | stop("taskStatus requires OAuth authentication") 4 | 5 | status <- try(expr, silent=TRUE) 6 | if (inherits(status, "try-error")) { 7 | out <- paste(paste("Your task failed with error message", 8 | status), msg, ":") 9 | dmSend(out, to) 10 | } 11 | else { 12 | out <- paste("Your task has completed successfully", 13 | msg, sep=":") 14 | dmSend(out, to) 15 | } 16 | status 17 | } 18 | 19 | -------------------------------------------------------------------------------- /R/trends.R: -------------------------------------------------------------------------------- 1 | availableTrendLocations = function(...) { 2 | locs = twInterfaceObj$doAPICall("trends/available", ...) 3 | if (is.null(locs)) { 4 | NULL 5 | } else { 6 | import_trends(locs) 7 | } 8 | } 9 | 10 | closestTrendLocations = function(lat, long, ...) { 11 | params=list(lat=lat, long=long) 12 | 13 | locs = twInterfaceObj$doAPICall("trends/closest", params=params, ...) 14 | if (is.null(locs)) { 15 | NULL 16 | } else { 17 | import_trends(locs) 18 | } 19 | } 20 | 21 | buildTrendLocationDf = function(loc_json) { 22 | loc_rows = lapply(loc_json, function(x) { 23 | return(c(name=x$name, country=x$country, woeid=x$woeid)) 24 | }) 25 | return(as.data.frame(do.call(rbind, loc_rows), stringsAsFactors=FALSE)) 26 | 27 | } 28 | 29 | getTrends <- function(woeid, exclude=NULL, ...) { 30 | params <- buildCommonArgs(exclude=exclude) 31 | params[["id"]] = woeid 32 | jsonList <- twInterfaceObj$doAPICall("trends/place", params=params, ...) 33 | if (is.null(jsonList)) { 34 | return(NULL) 35 | } 36 | trends <- jsonList[[1]][['trends']] 37 | trend_rows = lapply(trends, function(x) { 38 | return(c(name=x$name, url=x$url, promoted_content=x$promoted_content, 39 | query=x$query, events=x$events, woeid=woeid)) 40 | }) 41 | return(as.data.frame(do.call(rbind, trend_rows), stringsAsFactors=FALSE)) 42 | } 43 | -------------------------------------------------------------------------------- /R/users.R: -------------------------------------------------------------------------------- 1 | setRefClass('userList', 2 | contains='twitterObjList' 3 | ) 4 | 5 | setValidity('userList', function(object) { 6 | listClassValidity(object, 'user') 7 | }) 8 | 9 | setRefClass("user", 10 | contains='twitterObj', 11 | fields = list( 12 | description="character", 13 | statusesCount="numeric", 14 | followersCount="numeric", 15 | favoritesCount="numeric", 16 | friendsCount="numeric", 17 | url="character", 18 | name="character", 19 | created="POSIXct", 20 | protected="logical", 21 | verified="logical", 22 | screenName="character", 23 | location="character", 24 | lang="character", 25 | id="character", 26 | lastStatus="status", 27 | listedCount="numeric", 28 | followRequestSent="logical", 29 | profileImageUrl="character" 30 | ), 31 | methods = list( 32 | initialize = function(json, ...) { 33 | if (!missing(json)) { 34 | if (!is.null(json[['status']])) 35 | lastStatus <<- buildStatus(json[['status']]) 36 | 37 | if (is.character(json[['description']])) 38 | description <<- json[['description']] 39 | 40 | if (!is.null(json[['statuses_count']])) 41 | statusesCount <<- as.numeric(json[['statuses_count']]) 42 | 43 | if (!is.null(json[['followers_count']])) 44 | followersCount <<- as.numeric(json[['followers_count']]) 45 | 46 | if (!is.null(json[['friends_count']])) 47 | friendsCount <<- as.numeric(json[['friends_count']]) 48 | 49 | ## NOTE: Twitter uses the british spelling for historical reasons 50 | favorites_count = get_json_value(json, c("favourites_count", "favorites_count")) 51 | if (!is.null(favorites_count)) { 52 | favoritesCount <<- as.numeric(favorites_count) 53 | } 54 | 55 | if ((!is.null(json[['url']]))&&(!is.na(json[['url']]))) 56 | url <<- json[['url']] 57 | 58 | if (is.character(json[['name']])) 59 | name <<- json[['name']] 60 | 61 | created_at = get_json_value(json, c("created_at", "created")) 62 | if (is.null(created_at)) { 63 | created <<- Sys.time() 64 | } else { 65 | created <<- twitterDateToPOSIX(created_at) 66 | } 67 | 68 | if ((is.null(json[['protected']])) || 69 | (json[['protected']] == FALSE)) 70 | protected <<- FALSE 71 | else 72 | protected <<- TRUE 73 | 74 | if ((is.null(json[['verified']])) || 75 | (json[['verified']] == FALSE)) 76 | verified <<- FALSE 77 | else 78 | verified <<- TRUE 79 | 80 | if (is.character(json[['screen_name']])) 81 | screenName <<- json[['screen_name']] 82 | 83 | # Note: id_str must be checked first! 84 | id_field = get_json_value(json, c("id_str", "id")) 85 | if (!is.null(id_field)) { 86 | id <<- as.character(id_field) 87 | } 88 | 89 | if (!is.null(json[['location']])) { 90 | location <<- json[['location']] 91 | } 92 | 93 | if (!is.null(json[['lang']])) { 94 | lang <<- json[['lang']] 95 | } 96 | 97 | if (!is.null(json[["listed_count"]])) { 98 | listedCount <<- json[["listed_count"]] 99 | } 100 | 101 | if ((is.null(json[["followRequestSent"]])) || 102 | (json[["followRequestSent"]] == FALSE)) { 103 | followRequestSent <<- FALSE 104 | } else { 105 | followRequestSent <<- TRUE 106 | } 107 | 108 | if (!is.null(json[["profile_image_url"]])) { 109 | profileImageUrl <<- json[["profile_image_url"]] 110 | } 111 | } 112 | 113 | callSuper(...) 114 | }, 115 | getFollowerIDs = function(n=NULL, ...) { 116 | return(unlist(followers(.self$id, n, ...))) 117 | }, 118 | getFollowers = function(n=NULL, ...) { 119 | fol <- .self$getFollowerIDs(n, ...) 120 | lookupUsers(fol, ...) 121 | }, 122 | getFriendIDs = function(n=NULL, ...) { 123 | return(unlist(friends(.self$id, n, ...))) 124 | }, 125 | getFriends = function(n=NULL, ...) { 126 | fri <- .self$getFriendIDs(n, ...) 127 | lookupUsers(fri, ...) 128 | }, 129 | getFavouritesCount = function() { 130 | return(favoritesCount) 131 | }, 132 | getFavorites = function(n=20, max_id=NULL, since_id=NULL, ...) { 133 | return(favorites(screenName, n=n, max_id=max_id, since_id=since_id, ...)) 134 | }, 135 | toDataFrame = function(row.names=NULL, optional=FALSE, stringsAsFactors=FALSE) { 136 | callSuper(row.names=row.names, optional=optional, stringsAsFactors=stringsAsFactors, 137 | fieldsToRemove='lastStatus') 138 | } 139 | ) 140 | ) 141 | 142 | userFactory <- getRefClass("user") 143 | userFactory$accessors(names(userFactory$fields())) 144 | 145 | buildUser <- function(json) { 146 | if (is.null(json)) { 147 | NULL 148 | } else { 149 | userFactory$new(json) 150 | } 151 | } 152 | 153 | setMethod("show", signature="user", function(object) { 154 | print(screenName(object)) 155 | }) 156 | 157 | getUser <- function(user, ...) { 158 | params <- parseUsers(user) 159 | buildUser(twInterfaceObj$doAPICall(paste('users', 'show', sep='/'), 160 | params=params, ...)) 161 | } 162 | 163 | lookupUsers <- function(users, includeNA=FALSE, ...) { 164 | MatchLookedUpUsers <- function(vals) { 165 | order <- match(tolower(users), tolower(vals)) 166 | na.eles <- which(is.na(order)) 167 | 168 | if (length(na.eles) > 0) { 169 | if (!includeNA) { 170 | order <- order[-na.eles] 171 | users <- users[-na.eles] 172 | } 173 | } 174 | out <- out[order] 175 | names(out) <- users 176 | 177 | return(out) 178 | } 179 | 180 | if (is.null(users) || length(users) == 0) { 181 | return(list()) 182 | } 183 | 184 | batches <- split(users, ceiling(seq_along(users) / 100)) 185 | results <- lapply(batches, function(batch) { 186 | params <- parseUsers(batch) 187 | twInterfaceObj$doAPICall(paste('users', 'lookup', sep='/'), 188 | params=params, ...) 189 | }) 190 | out <- sapply(do.call(c, results), buildUser) 191 | 192 | ## Order these to match the users vector - if !includeNA, 193 | ## drop out the elements of the return list which weren't 194 | ## found 195 | sn.lookups <- MatchLookedUpUsers(sapply(out, 196 | function(x) x$getScreenName())) 197 | id.lookups <- MatchLookedUpUsers(sapply(out, function(x) x$getId())) 198 | 199 | ## The problem with doing it in the two batch way above is that 200 | ## anything that was SN will be NULL for ID and vice versa. 201 | ## If includeNA is TRUE, we can't just remove all empty 202 | ## entries. As a hack, only retain the NULL values that are shared 203 | ## between both lists 204 | if (includeNA) { 205 | sn.nulls <- sapply(sn.lookups, is.null) 206 | id.nulls <- sapply(id.lookups, is.null) 207 | false.nulls <- xor(sn.nulls, id.nulls) 208 | sn.lookups <- sn.lookups[!(sn.nulls & false.nulls)] 209 | id.lookups <- id.lookups[!(id.nulls & false.nulls)] 210 | } else { 211 | ## Otherwise, just strip out the names that have been 212 | ## taken out 213 | users <- intersect(users, union(names(sn.lookups), names(id.lookups))) 214 | } 215 | 216 | out <- c(sn.lookups, id.lookups) 217 | return(out[users]) 218 | } 219 | 220 | 221 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | get_json_value = function(json, names) { 2 | for (name in names) { 3 | if (!is.null(json[[name]])) { 4 | return(json[[name]]) 5 | } 6 | } 7 | 8 | NULL 9 | } 10 | 11 | check_id = function(id) { 12 | if (!is.character(id)) { 13 | warning("Using numeric id value can lead to unexpected results for very large ids") 14 | } 15 | if (is.na(as.numeric(id))) { 16 | stop("Malformed id, while it must be a string all ids must be representable as an integer") 17 | } 18 | 19 | return(TRUE) 20 | } 21 | 22 | decode_short_url <- function(url, ...) { 23 | request_url = paste("http://api.longurl.org/v2/expand?url=", url, "&format=json", sep="") 24 | response = GET(request_url, query=list(useragent="twitteR"), ...) 25 | parsed = content(response, as="parsed") 26 | if (! "long-url" %in% names(parsed)) { 27 | return(url) 28 | } else { 29 | return(parsed[["long-url"]]) 30 | } 31 | } 32 | 33 | getAPIStr <- function(cmd, version=1.1) { 34 | paste("https://api.twitter.com/", version, '/', cmd, '.json', sep='') 35 | } 36 | 37 | buildCommonArgs <- function(lang=NULL, since=NULL, until=NULL, locale=NULL, 38 | geocode=NULL, since_id=NULL, max_id=NULL, 39 | result_type=NULL, lat=NULL, long=NULL, place_id=NULL, 40 | display_coordinates=NULL, 41 | in_reply_to_status_id=NULL, exclude=NULL, 42 | date=NULL) { 43 | out <- list() 44 | for (arg in names(formals())) { 45 | val <- get(arg) 46 | if (length(val) > 0) 47 | out[[arg]] <- as.character(val) 48 | } 49 | out 50 | } 51 | 52 | parseUsers <- function(users) { 53 | ## many functions have 'user' input which can be one of class 'user', a UID or a screen name, 54 | ## try to do something rational here 55 | if ((length(users) == 1) && (inherits(users, "user"))) { 56 | users = users$getScreenName() 57 | } else { 58 | users <- sapply(users, function(x) { 59 | if (inherits(x, 'user')) 60 | x$getScreenName() 61 | else 62 | x 63 | }) 64 | } 65 | 66 | numUsers <- suppressWarnings(as.numeric(users)) 67 | uids <- c(numUsers[!is.na(numUsers) & as.character(numUsers) == as.character(users)], 68 | users[!is.na(numUsers) & as.character(numUsers) != as.character(users)]) 69 | screen.names <- setdiff(users, uids) 70 | 71 | return(buildUserList(uids, screen.names)) 72 | } 73 | 74 | buildUserList = function(uids, screen_names) { 75 | user_list = list() 76 | if (length(uids) > 0) { 77 | user_list$user_id = paste(uids, collapse=',') 78 | } 79 | if (length(screen_names) > 0) { 80 | user_list$screen_name = paste(screen_names, collapse=',') 81 | } 82 | 83 | return(user_list) 84 | } 85 | 86 | twListToDF <- function(twList) { 87 | if (length(twList) == 0) { 88 | stop("Empty list passed to twListToDF") 89 | } 90 | 91 | ## iff all elements of twList are from a class defined in this 92 | ## package, and all of the same class, will collapse these into 93 | ## a data.frame and return 94 | classes <- unique(sapply(twList, class)) 95 | if (length(classes) != 1) { 96 | stop("Not all elements of twList are of the same class") 97 | } 98 | if (! classes %in% c('status', 'user', 'directMessage')) { 99 | stop("Elements of twList are not of an appropriate class") 100 | } 101 | do.call('rbind', lapply(twList, as.data.frame)) 102 | } 103 | 104 | strip_retweets = function(tweets, strip_manual=TRUE, strip_mt=TRUE) { 105 | if ((!is.list(tweets)) || (!all(sapply(tweets, function(x) inherits(x, "status"))))) { 106 | stop("tweets argument must be a list of class status") 107 | } 108 | ## Find/remove the tweets flagged as retweets 109 | is_retweets = which(sapply(tweets, function(x) x$getIsRetweet())) 110 | 111 | if (length(is_retweets) > 0) { 112 | filtered_tweets = tweets[-is_retweets] 113 | } else { 114 | filtered_tweets = tweets 115 | } 116 | 117 | if (strip_manual) { 118 | statuses = sapply(filtered_tweets, function(x) x$getText()) 119 | 120 | if (strip_mt) { 121 | rt_pattern = "(RT|MT)" 122 | } else { 123 | rt_pattern = "RT" 124 | } 125 | 126 | ## Find and remove RT based retweets. This will be overeager but we're not losing many 127 | ## tweets anyways 128 | split_tweets = sapply(strsplit(statuses, paste0("[[:space:]]?", rt_pattern)), function(x) x[1]) 129 | manual_retweets = which(split_tweets == "") 130 | if (length(manual_retweets) > 0) { 131 | filtered_tweets = filtered_tweets[-manual_retweets] 132 | } 133 | } 134 | 135 | filtered_tweets 136 | } 137 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | oauth_cache <- new.env(hash=TRUE) 2 | db_backend_cache = new.env(hash=TRUE) 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATION 2 | 3 | This is the start of a relatively leisurely deprecation period for `twitteR`, in favor of using [`rtweet`](https://github.com/mkearney/rtweet). Please start looking to switch over to that package. If you have any questions contact myself or @mkearney 4 | 5 | ## twitteR 6 | twitteR is an R package which provides access to the Twitter API. Most functionality of the API is supported, with a bias towards API calls that are more useful in data analysis as opposed to daily interaction. 7 | 8 | ## Getting Started 9 | 10 | - Please read the [user vignette](http://geoffjentry.hexdump.org/twitteR.pdf), which admittedly can get a bit out of date 11 | - Create a Twitter application at http://dev.twitter.com. Make sure to give the app read, write and direct message authority. 12 | - Take note of the following values from the Twitter app page: "API key", "API secret", "Access token", and "Access token secret". 13 | - You can use the CRAN version (stable) via the standard `install.packages("twitteR")` or use the github version. To do the latter: 14 | - `install.packages(c("devtools", "rjson", "bit64", "httr"))` 15 | - Make sure to restart your R session at this point 16 | - `library(devtools)` 17 | - `install_github("geoffjentry/twitteR")` 18 | - At this point you should have `twitteR` installed and can proceed: 19 | - `library(twitteR)` 20 | - `setup_twitter_oauth("API key", "API secret")` 21 | - The `API key` and `API secret` are from the Twitter app page above. This will lead you through `httr`'s OAuth authentication process. I recommend you look at the man page for `Token` in `httr` for an explanation of how it handles caching. 22 | - You should be ready to go! 23 | - If you have any questions or issues, check out the [mailing list](http://lists.hexdump.org/listinfo.cgi/twitter-users-hexdump.org) 24 | -------------------------------------------------------------------------------- /inst/doc/twitteR.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffjentry/twitteR/815fecc7d5aae8a9cfad2e2ba283f85561ae99e2/inst/doc/twitteR.pdf -------------------------------------------------------------------------------- /inst/vignettes/twitteR.Rnw: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | %\VignetteIndexEntry{Twitter client for R} 3 | %\VignettePackage{twitteR} 4 | %\VignetteKeywords{Documentation} 5 | \usepackage{url} 6 | \newcommand{\Rfunction}[1]{{\texttt{#1}}} 7 | \newcommand{\Robject}[1]{{\texttt{#1}}} 8 | \newcommand{\Rpackage}[1]{{\textit{#1}}} 9 | \newcommand{\Rmethod}[1]{{\texttt{#1}}} 10 | \newcommand{\Rfunarg}[1]{{\texttt{#1}}} 11 | \newcommand{\Rclass}[1]{{\textit{#1}}} 12 | 13 | \begin{document} 14 | \SweaveOpts{concordance=TRUE} 15 | 16 | \title{Twitter client for R} 17 | \author{Jeff Gentry} 18 | \maketitle 19 | 20 | <>= 21 | library(twitteR) 22 | setup_twitter_oauth(consumer_key, consumer_secret, access_token, access_secret) 23 | @ 24 | 25 | \section{Introduction} 26 | 27 | Twitter is a popular service that allows users to broadcast short 28 | messages ('\emph{tweets}') for others to read. Over the years this has become 29 | a valuable tool not just for standard social media purposes but also for 30 | data mining experiments such as sentiment analysis. The \Rpackage{twitteR} package 31 | is intended to provide access to the Twitter API within R, allowing users to 32 | grab interesting subsets of Twitter data for their analyses. 33 | 34 | This document is not intended to be exhaustive nor comprehensive but rather 35 | a brief introduction to some of the more common bits of functionality and 36 | some basic examples of how they can be used. In the last section I've included 37 | a variety of links to people using \Rpackage{twitteR} to solve real world problems. 38 | 39 | \section{Some Initial Notes} 40 | 41 | \subsection{Support mailing list} 42 | 43 | While this package doesn't generate a huge volume of emails to me, I 44 | have found that the same questions tends to come up repeatedly (often 45 | when something has been broken!). I also field requests for advice on 46 | practical application of this package which is an area that I'm far 47 | from expert at. I've set up a mailing list to better manage emails from users as this way, 48 | with the idea being that there'll now be a searchable archive and 49 | perhaps other users might be able to chime in. The URL for this 50 | mailing list is \url{http://lists.hexdump.org/listinfo.cgi/twitter-users-hexdump.org} 51 | 52 | \section{Authentication with OAuth} 53 | 54 | As of March 2013 OAuth authentication is \emph{required} for all Twitter 55 | transactions. You will need to follow these instructions to continue. 56 | 57 | OAuth is an authentication mechanism gaining popularity which allows 58 | applications to provide client functionality to a web service without 59 | granting an end user's credentials to the client itself. This causes 60 | a few wrinkles for cases like ours, where we're accessing Twitter 61 | programatically. \Rpackage{twitteR} uses the \Rpackage{httr} package under 62 | the hood to manage this. 63 | 64 | The first step is to create a Twitter application for yourself. Go to 65 | \url{https://twitter.com/apps/new} and log in. After filling in the 66 | basic info, go to the ``Settings'' tab and select "Read, Write and Access direct messages". 67 | Make sure to click on the save button after doing this. In the 68 | ``Details'' tab, take note of your consumer key and consumer secret. 69 | 70 | In your R session, you'll want to do the following with the appropriate values from 71 | the web page: 72 | 73 | <>= 74 | setup_twitter_oauth("API key", "API secret") 75 | @ 76 | 77 | This will authenticate via \Rpackage{httr}, I recommend looking at that package's 78 | \Rfunction{Token} man page for more information regarding how to manage the 79 | authentication and caching processes. 80 | 81 | If you are in a headless environment or otherwise don't want to deal with the browser 82 | based authentication dance, you can get your access token and secret from your apps 83 | webpage and call \Rfunction{setup\_twitter\_oauth} a bit differently: 84 | 85 | <>= 86 | setup_twitter_oauth("API key", "API secret", "Access token", "Access secret") 87 | @ 88 | 89 | \section{Getting Started} 90 | 91 | This document is intended to demonstrate basic techniques rather than 92 | an exhaustive tour of the functionality. For more in depth examples I 93 | recommend exploring the mailing list or StackOverflow. I've also included 94 | some links to examples of \Robject{twitteR} being used in the real word 95 | at the end. 96 | 97 | <>= 98 | library(twitteR) 99 | @ 100 | 101 | <>= 102 | setup_twitter_oauth("API key", "API secret") 103 | @ 104 | 105 | \section{Exploring Twitter} 106 | \subsection{Searching Twitter} 107 | 108 | The \Rfunction{searchTwitter} function can be used to search for 109 | tweets that match a desired term. Example searches are such things as hashtags, 110 | @usernames, and other such things which can also be manipulated with 111 | basic boolean logic such as AND and OR. It is worth looking at \url{https://dev.twitter.com/docs/using-search} 112 | for an example of what can and can not be done here. The \Rfunarg{n} argument 113 | can be used to specify the number of tweets to return, defaulting to 25. Note 114 | that while \Rfunction{searchTwitter} will wrap an arbitrary number of actual 115 | search calls to provide the number of tweets requested, the Twitter API has limitations 116 | on just how much it will actually return. In general you can only go back a handful 117 | of days worth of tweets. 118 | 119 | <>= 120 | tweets = searchTwitter('#rstats', n=50) 121 | head(tweets) 122 | @ 123 | 124 | There's a handy utility method, \Rfunction{strip\_retweets} which attempts to 125 | do exactly what it describes. It takes a list of \Rclass{status} objects (e.g. 126 | from \Rfunction{searchTwitter}) and by default will remove official API-based 127 | retweets from Twitter, i.e. cases where the retweet button was pressed instead 128 | of the manual \emph{RT @soandso ....} method. However there are two arguments, 129 | \Rfunarg{strip\_manual} and \Rfunarg{strip\_mt}, corresponding to the manual retweets 130 | described above and modified retweets (\emph{MT}). If either of these are \Robject{TRUE}, 131 | the appropriate type of tweets will also be removed, but leaving everything to the 132 | left of the \emph{RT}/\emph{MT}. 133 | 134 | Note that this example may or may not do anything depending on the data available 135 | when this vignette was compiled. 136 | <>= 137 | head(strip_retweets(tweets, strip_manual=TRUE, strip_mt=TRUE)) 138 | @ 139 | 140 | \subsection{Looking at users} 141 | 142 | To take a closer look at a Twitter user (including yourself!), run the 143 | command \Rfunction{getUser}. This will only work correctly with users 144 | who have their profiles public, or if you're authenticated and granted 145 | access. You can also see things such as a user's followers, who they 146 | follow, retweets, and more. The \Rfunction{getUser} function returns 147 | a \Rclass{user} object, which can then be polled for further information. 148 | 149 | <>= 150 | crantastic = getUser('crantastic') 151 | crantastic$getDescription() 152 | crantastic$getFollowersCount() 153 | crantastic$getFriends(n=5) 154 | crantastic$getFavorites(n=5) 155 | @ 156 | 157 | \subsection{Conversion to data.frames} 158 | 159 | There are times when it is convenient to display the object lists as 160 | an \Robject{data.frame} structure. To do this, every class has a 161 | reference method \Rfunction{toDataFrame} as well as a corresponding S4 162 | method \Rfunction{as.data.frame} that works in the traditional sense. 163 | Converting a single object will typically not be particularly useful 164 | by itself but there is a convenience method to convert an entire list, 165 | \Rfunction{twListToDF} which takes a list of objects from a single 166 | \Rpackage{twitteR} class: 167 | 168 | <>= 169 | df = twListToDF(tweets) 170 | head(df) 171 | @ 172 | 173 | \subsection{Database Persistence} 174 | A question that I'm often asked is how to retrieve data from the past, generally 175 | people are doing a study on some major event that has already happened (e.g. 176 | Arab Spring, an election, etc). Using the Twitter API this is impossible as you 177 | can only go back a small amount. However, if you have the ability to look ahead, 178 | it is easy to enable a prospective study by collecting data and automatically 179 | persisting it to a database. This will then allow you to load everything into 180 | a later R session, including using tools such as \Rpackage{dplyr}. There's a 181 | full writeup of this functionality at \url{http://geoffjentry.blogspot.com/2014/02/twitter-now-supports-database.html}. 182 | 183 | Here's a brief example: 184 | 185 | <>= 186 | sql_lite_file = tempfile() 187 | register_sqlite_backend(sql_lite_file) 188 | store_tweets_db(tweets) 189 | 190 | from_db = load_tweets_db() 191 | head(from_db) 192 | @ 193 | 194 | 195 | \subsection{Timelines} 196 | A Twitter \emph{timeline} is simply a stream of tweets. We support two 197 | timelines, the \emph{user timeline} and the \emph{home timeline}. The former 198 | provides the most recent tweets of a specified user while the latter is used 199 | to display your own most recent tweets. These both return a list of \Rclass{status} 200 | objects. 201 | 202 | To look at a particular user's timeline that user must either have a public 203 | account or you must have access to their account. You can either pass in the 204 | user's name or an object of class \Rclass{user} (more on this 205 | later). For this example, let's use the user \emph{cranatic}. 206 | 207 | <>= 208 | cran_tweets = userTimeline('cranatic') 209 | cran_tweets[1:5] 210 | @ 211 | 212 | By default this command returns the 20 most recent tweet. 213 | As with most (but not all) of the functions, it also provides a mechanism 214 | to retrieve an arbitrarily large number of tweets up to limits set by 215 | the Twitter API, which vary based on the specific type of request. 216 | 217 | <>= 218 | cran_tweets_large = userTimeline('cranatic', n=100) 219 | length(cran_tweets_large) 220 | @ 221 | 222 | The \Rfunction{homeTimeline} function works nearly identically except you do not 223 | pass in a user, it uses your own timeline. 224 | 225 | \subsection{Trends} 226 | 227 | Twitter keeps track of topics that are popular at any given point of time, 228 | and allows one to extract that data. The \Rfunction{getTrends} function is 229 | used to pull current trend information from a given location, which is specified 230 | using a WOEID (see \url{http://developer.yahoo.com/geo/geoplanet/}). Luckily 231 | there are two other functions to help you identify WOEIDs that you might be 232 | interested in. The \Rfunction{availableTrendLocations} function will return 233 | a \Robject{data.frame} with a location in each row and the \Robject{woeid} giving 234 | that location's WOEID. Similarly the \Rfunction{closestTrendLocations} function 235 | is passed a latitude and longitude and will return the same style \Robject{data.frame}. 236 | 237 | <>= 238 | avail_trends = availableTrendLocations() 239 | head(avail_trends) 240 | close_trends = closestTrendLocations(-42.8, -71.1) 241 | head(close_trends) 242 | trends = getTrends(2367105) 243 | head(trends) 244 | @ 245 | 246 | \subsection{A simple example} 247 | 248 | Just a quick example of how one can interact with actual data. Here we 249 | will pull the most recent results from the public timeline and see the 250 | clients that were used to post those statuses. We can look at a plot of the 251 | most common clients, as well as seeing hos many others were used. 252 | 253 | Note that sources which are not the standard web interface will be 254 | presented as an anchored URL string (...). There are more 255 | efficient means to rip out the anchor string than how it is done 256 | below, but this is a bit more robust for the purposes of this vignette 257 | due to issues with character encoding, locales, etc. 258 | 259 | <>= 260 | library(ggplot2) 261 | r_tweets <- searchTwitter("#rstats", n=300) 262 | sources <- sapply(r_tweets, function(x) x$getStatusSource()) 263 | sources <- gsub("", "", sources) 264 | sources <- strsplit(sources, ">") 265 | sources <- sapply(sources, function(x) ifelse(length(x) > 1, x[2], x[1])) 266 | source_table = table(sources) 267 | filtered_sources = names(source_table[source_table < quantile(source_table, 0.9)]) 268 | sources[sources %in% filtered_sources] = "other" 269 | source_df = as.data.frame(sources) 270 | ggplot(source_df, aes(sources)) + geom_bar() + coord_flip() 271 | @ 272 | 273 | \section{Examples Of twitteR In The Wild} 274 | 275 | I've found some examples around the web of people using this package for various 276 | purposes, hopefully some of these can give you good ideas on how to do things. 277 | Unfortunately I didn't give the package the most easily searched name! If you 278 | know of a good example please let me know. 279 | 280 | NB: Many of these predate the changes to using the \Rpackage{httr} package, so 281 | specifics might have changed, rather view these as examples of things you could do. 282 | 283 | \begin{itemize} 284 | \item Jeffrey Stanton's free book on data science discusses \Rpackage{twitteR}: \url{http://ischool.syr.edu/media/documents/2012/3/DataScienceBook1_1.pdf} 285 | \item Rare bird siting twitter bot: \url{https://twitter.com/crd_rare_bird} 286 | \item Jeffrey Breen's sentiment analysis example: \url{http://www.inside-r.org/howto/mining-twitter-airline-consumer-sentiment} 287 | \item Mapping your followers: \url{http://simplystatistics.org/2011/12/21/an-r-function-to-map-your-twitter-followers/} 288 | \item Yangchao Zhao's book on data mining w/ R \url{http://www.amazon.com/Data-Mining-Examples-Case-Studies/dp/0123969638} 289 | \item Gary Miner et al's book on data mining \url{http://www.amazon.com/Practical-Statistical-Analysis-Non-structured-Applications/dp/012386979X} 290 | \item Mining Twitter with R \url{https://sites.google.com/site/miningtwitter/home} 291 | \item Organization or conversation in Twitter: A case study of chatterboxing \url{https://www.asis.org/asist2012/proceedings/Submissions/185.pdf} 292 | \end{itemize} 293 | 294 | \section{Session Information} 295 | 296 | The version number of R and packages loaded for generating the vignette were: 297 | 298 | \begin{verbatim} 299 | <>= 300 | sessionInfo() 301 | @ 302 | \end{verbatim} 303 | 304 | \end{document} 305 | -------------------------------------------------------------------------------- /man/decode_short_url.Rd: -------------------------------------------------------------------------------- 1 | \name{decode_short_url} 2 | \alias{decode_short_url} 3 | \title{ 4 | A function to decode shortened URLs 5 | } 6 | \description{ 7 | Will expand a URL that has been processed by a link shortener (e.g. bit.ly). Provided as a convenience 8 | function to users who may which to perform this operation. 9 | } 10 | \usage{ 11 | decode_short_url(url, ...) 12 | } 13 | \arguments{ 14 | \item{url}{A character string, the URL to decode} 15 | \item{\dots}{Optional arguments to pass along to RCurl} 16 | } 17 | \details{ 18 | Uses the \url{longapi.org} API 19 | } 20 | \value{ 21 | A character string containing either the original URL (if not shortened) or the full URL (if shortened) 22 | } 23 | \references{ 24 | \url{longapi.org} 25 | } 26 | \author{ 27 | Neil Jang 28 | } 29 | \examples{ 30 | \dontrun{ 31 | decode_short_url("http://bit.ly/23226se656") 32 | } 33 | } 34 | \keyword{utilities} 35 | -------------------------------------------------------------------------------- /man/directMessage-class.Rd: -------------------------------------------------------------------------------- 1 | \name{directMessage-class} 2 | \Rdversion{1.1} 3 | \docType{class} 4 | \alias{directMessage-class} 5 | \alias{directMessage} 6 | \alias{dmFactory} 7 | \alias{show,directMessage-method} 8 | \title{Class "directMessage": A class to represent Twitter Direct Messages } 9 | \description{ Provides a model representing direct messages (DMs) from Twitter} 10 | \section{Fields}{ 11 | \describe{ 12 | \item{\code{text}:}{Text of the DM} 13 | \item{\code{recipient}:}{A \code{user} object representing the 14 | recipient of the message} 15 | \item{\code{recipientSN}:}{Screen name of the recipient} 16 | \item{\code{recipientID}:}{ID number of the recipient} 17 | \item{\code{sender}:}{A \code{user} object representing the sender 18 | of the message} 19 | \item{\code{senderSN}:}{Screen name of the sender} 20 | \item{\code{senderID}:}{ID number of the sender} 21 | \item{\code{created}:}{When the messages was created} 22 | } 23 | } 24 | \section{Methods}{ 25 | \describe{ 26 | \item{\code{destroy}:}{Deletes this DM from Twitter. A wrapper 27 | around \code{\link{dmDestroy}}} 28 | \item{\code{toDataFrame}:}{Converts this into a one row 29 | \code{\link{data.frame}}, with each field representing a column. 30 | This can also be accomplished by the S4 style 31 | \code{as.data.frame(objectName)}.} 32 | } 33 | } 34 | \details{ 35 | The \code{directMessage} class is implemented as a reference class. 36 | As there should be no backwards compatibility issues, there are no S4 37 | methods provided as with the \code{user} and \code{status} classes. 38 | An instance of a generator for this class is provided as a convenience 39 | to the user as it is configured to handle most standard cases. To 40 | access this generator, use the object \code{dmFactory}. Accessor set 41 | & get methods are provided for every field using reference class 42 | \code{$accessors()} methodology (see \code{\link{setRefClass}} for 43 | more details). As an example, the \code{sender} field could be 44 | accessed using \code{object$getSender()} and \code{object$setSender()}. 45 | 46 | The constructor of this object assumes that the user is passing in a 47 | JSON encoded Twitter Direct Message. It is also possible to directly 48 | pass in the arguments. 49 | } 50 | \author{ Jeff Gentry } 51 | \seealso{\code{\link{dmGet}}, \code{\link{dmSend}}, \code{\link{dmDestroy}}, \code{\link{setRefClass}}} 52 | \examples{ 53 | \dontrun{ 54 | dm <- dmFactory$new(text='foo', recipientSN='blah') 55 | dm$getText() 56 | 57 | ## assume 'json' is the return from a Twitter call 58 | dm <- dmFactory$new(json) 59 | dm$getSenderID() 60 | } 61 | } 62 | \keyword{classes} 63 | -------------------------------------------------------------------------------- /man/dm.Rd: -------------------------------------------------------------------------------- 1 | \name{dmGet} 2 | \alias{dmGet} 3 | \alias{dmSent} 4 | \alias{dmDestroy} 5 | \alias{dmSend} 6 | \title{ Functions to manipulate Twitter direct messages } 7 | \description{ 8 | These functions allow you to interact with, send, and delete direct 9 | messages (DMs) in Twitter. 10 | } 11 | \usage{ 12 | dmGet(n=25, sinceID=NULL, maxID=NULL, ...) 13 | dmSent(n=25, sinceID=NULL, maxID=NULL, ...) 14 | dmDestroy(dm, ...) 15 | dmSend(text, user, ...) 16 | } 17 | \arguments{ 18 | \item{text}{The text of a message to send} 19 | \item{user}{The user to send a message to, either \code{character} or 20 | an \code{\link{user}} object.} 21 | \item{dm}{The message to delete, an object of class \code{\link{directMessage}}} 22 | \item{n}{ The maximum number of direct messages to return } 23 | \item{sinceID}{If not \code{NULL}, an ID representing the earliest 24 | boundary} 25 | \item{maxID}{If not \code{NULL}, an ID representing the newest ID you 26 | wish to retrieve} 27 | \item{...}{Further arguments to pass along the communication chain} 28 | } 29 | \value{ 30 | These functions will not work without \code{OAuth} authentication 31 | 32 | The \code{dmGet} and \code{dmSent} functions will return a list of 33 | \code{\link{directMessage}} objects. The former will retrieve DMs 34 | sent to the user while the latter retrieves messages sent from the user. 35 | 36 | The \code{dmDestroy} function takes a \code{\link{directMessage}} 37 | object (perhaps from either \code{dmGet} or \code{dmSent}) and will 38 | delete it from the Twitter server. 39 | 40 | The \code{dmSend} function will send a message to another Twitter user. 41 | } 42 | \author{Jeff Gentry} 43 | \seealso{\code{\link{directMessage}}, \code{\link{registerTwitterOAuth}}} 44 | \examples{ 45 | \dontrun{ 46 | dms <- dmGet() 47 | dms 48 | ## delete the first one 49 | dms[[1]]$destroy() 50 | dmDestroy(dms[[2]]) 51 | ## send a DM 52 | dmSend('Testing out twitteR!', 'twitter') 53 | } 54 | } 55 | \keyword{ interface } 56 | 57 | -------------------------------------------------------------------------------- /man/favorites.Rd: -------------------------------------------------------------------------------- 1 | \name{favorites} 2 | \alias{favorites} 3 | \title{ 4 | A function to get favorite tweets 5 | } 6 | \description{ 7 | Returns the n most recently favorited tweets from the specified user. 8 | } 9 | \usage{ 10 | favorites(user, n = 20, max_id = NULL, since_id = NULL, ...) 11 | } 12 | \arguments{ 13 | \item{user}{The Twitter user to detail, can be \code{character} or an \code{\link{user}} object.} 14 | \item{n}{Number of tweets to retrieve, up to a maximum of 200} 15 | \item{max_id}{Maximum ID to search for} 16 | \item{since_id}{Minimum ID to search for} 17 | \item{\dots}{Optional arguments to pass along to RCurl} 18 | } 19 | \value{ 20 | A list of \code{link{status}} objects corresponding to the \code{n} most recent tweets 21 | } 22 | \references{ 23 | \url{https://dev.twitter.com/rest/reference/get/favorites/list} 24 | } 25 | \author{ 26 | Jeff Gentry 27 | } 28 | \seealso{ 29 | \code{\link{getUser}}, \code{\link{status}} 30 | } 31 | \examples{ 32 | \dontrun{ 33 | fav = favorites("barackobama", n=100) 34 | } 35 | } 36 | \keyword{ interface } 37 | -------------------------------------------------------------------------------- /man/friendships.Rd: -------------------------------------------------------------------------------- 1 | \name{friendships} 2 | \alias{friendships} 3 | \title{ 4 | A function to detail relations between yourself & other users 5 | } 6 | \description{ 7 | This function will accept a list of other Twitter users and will detail if 8 | they follow you and/or you follow them. 9 | } 10 | \usage{ 11 | friendships(screen_names = character(), user_ids = character(), ...) 12 | } 13 | \arguments{ 14 | \item{screen_names}{ 15 | A vector of one or more Twitter screen names 16 | } 17 | \item{user_ids}{ 18 | A vector of one or more Twitter user id values 19 | } 20 | \item{\dots}{ 21 | Any other arguments to pass to RCurl 22 | } 23 | } 24 | \details{ 25 | The combined number of screen names and user ids may not exceed 100. Any 26 | non-existent users will be dropped from the output 27 | } 28 | \value{ 29 | A data.frame, one row for each user requested with columns \code{name}, 30 | \code{screen_name}, \code{id}, \code{following} and \code{followed_by}. The 31 | latter two columns will be \code{TRUE} or \code{FALSE} depending on that user's 32 | relations with your account. 33 | } 34 | \references{ 35 | https://dev.twitter.com/docs/api/1.1/get/friendships/lookup 36 | } 37 | \author{ 38 | Jeff Gentry 39 | } 40 | \seealso{ 41 | \code{\link{registerTwitterOAuth}} 42 | } 43 | \examples{ 44 | \dontrun{ 45 | friendships() 46 | } 47 | } 48 | \keyword{ interface } 49 | -------------------------------------------------------------------------------- /man/getCurRateLimitInfo.Rd: -------------------------------------------------------------------------------- 1 | \name{getCurRateLimitInfo} 2 | \alias{getCurRateLimitInfo} 3 | \alias{resource_families} 4 | \title{ 5 | A function to retrieve current rate limit information 6 | } 7 | \description{ 8 | Will retrieve the current rate limit information for the authenticated user, 9 | displayed as a data.frame displaying specifc information for every Twitter resource 10 | } 11 | \usage{ 12 | getCurRateLimitInfo(resources=resource_families, ...) 13 | } 14 | \arguments{ 15 | \item{resources}{A character vector of specific resources to get information for} 16 | \item{\dots}{Optional arguments to pass to cURL} 17 | } 18 | \details{ 19 | By default, all known resource families will be polled. These families are contained 20 | in the object \code{resource_families}. If you would like to filter this down you 21 | may tweak the \code{resources} argument. 22 | 23 | The full list of allowed values in \code{resources} is as follows: \code{lists}, 24 | \code{application}, \code{friendships}, \code{blocks}, \code{geo}, \code{users}, 25 | \code{followers}, \code{statuses}, \code{help}, \code{friends}, \code{direct_messages}, 26 | \code{account}, \code{favorites}, \code{saved_searches}, \code{search}, \code{trends}. 27 | } 28 | \value{ 29 | A four column data.frame with columns \code{resource}, \code{limit}, \code{remaining} 30 | and \code{reset}. These detail the specific resource name, the rate limit for that block, 31 | the number of calls remaining and the time the rate limit will be reset in UTC time. 32 | } 33 | \author{ 34 | Jeff Gentry 35 | } 36 | \examples{ 37 | \dontrun{ 38 | zz <- getCurRateLimitInfo(c("lists", "users")) 39 | } 40 | } 41 | \keyword{ interface } 42 | -------------------------------------------------------------------------------- /man/getTrends.Rd: -------------------------------------------------------------------------------- 1 | \name{getTrends} 2 | \alias{getTrends} 3 | \alias{availableTrendLocations} 4 | \alias{closestTrendLocations} 5 | \title{ 6 | Functions to view Twitter trends 7 | } 8 | \description{ 9 | These functions will allow you to interact with the trend portion of 10 | the Twitter API 11 | } 12 | \usage{ 13 | availableTrendLocations(...) 14 | closestTrendLocations(lat, long, ...) 15 | getTrends(woeid, exclude=NULL, ...) 16 | } 17 | \arguments{ 18 | \item{woeid}{A numerical identification code describing a location, a Yahoo! Where On Earth ID} 19 | \item{lat}{A numerical latitude value, between -180 and 180 inclusive. West is negative, East is positive} 20 | \item{long}{A numerical longitude value, between -180 and 180 inclusive. South is negative, North is positive} 21 | \item{exclude}{If set to \code{hashtags}, will exclude hashtags} 22 | \item{...}{Additional arguments to be passed to RCurl} 23 | } 24 | \details{ 25 | The \code{availableTrendLocations} and \code{closestTrendLocations} functions will return a 26 | data.frame with three columns - \code{name}, \code{country} and \code{woeid}. The \code{closestTrendLocations} 27 | function will return the locations closest to the specified latitude and longitude. 28 | 29 | The \code{getTrends} function takes a specified woeid and returns the trending topics associated 30 | with that woeid. It returns a data.frame with the columns being \code{name}, \code{url}, 31 | \code{promoted_content}, \code{query} and \code{woeid} - one row per trend. 32 | } 33 | \value{ 34 | A data.frame with the columns specified in \code{Details} above 35 | } 36 | \author{ 37 | Jeff Gentry 38 | } 39 | \examples{ 40 | \dontrun{ 41 | woeid = availableTrendLocations[1, "woeid"] 42 | t1 <- getTrends(woeid) 43 | } 44 | } 45 | \keyword{interface} 46 | -------------------------------------------------------------------------------- /man/getUser.Rd: -------------------------------------------------------------------------------- 1 | \name{getUser} 2 | \alias{getUser} 3 | \alias{lookupUsers} 4 | \title{ Functions to manage Twitter users } 5 | \description{ 6 | These functions allow you interact with information about a Twitter 7 | user - retrieving their base information, list of friends, list of 8 | followers, and an up to date timeline. 9 | } 10 | \usage{ 11 | getUser(user, ...) 12 | lookupUsers(users, includeNA=FALSE, ...) 13 | } 14 | \arguments{ 15 | \item{user}{ The Twitter user to detail, can be \code{character} or 16 | an \code{\link{user}} object.} 17 | \item{users}{A vector of either user IDs or screen names or a mix of both} 18 | \item{includeNA}{If \code{TRUE} will leave an NA element in the return list for users that don't exist} 19 | \item{...}{Optional arguments to be passed to \code{\link{GET}}} 20 | } 21 | \details{ 22 | These functions will only return fully formed objects if the 23 | authenticated user is allowed to see the requested user. If that 24 | person has a private account and has not allowed you to see them, you 25 | will not be able to extract that information. 26 | 27 | The \code{lookupUsers} function should be used in cases where there are 28 | multiple lookups going to take place, to reduce the API call load. This function 29 | requires OAuth authentication. 30 | } 31 | \value{ 32 | The \code{getUser} function returns an object of class 33 | \code{\link{user}}. 34 | 35 | The \code{lookupUsers} function will return a list of \code{\link{user}} objects, 36 | sorted in the order of the \code{users} argument, with names being the particular 37 | element of \code{users} that it matches to. If the \code{includeNA} argument is set 38 | to \code{FALSE} (default), any non-existing users will be dropped from the list. 39 | } 40 | \author{Jeff Gentry} 41 | \seealso{\code{\link{mentions}}} 42 | \examples{ 43 | \dontrun{ 44 | tuser <- getUser('geoffjentry') 45 | users <- lookupUsers(c('geoffjentry', 'whitehouse')) 46 | } 47 | } 48 | \keyword{ interface } 49 | -------------------------------------------------------------------------------- /man/get_latest_tweet_id.Rd: -------------------------------------------------------------------------------- 1 | \name{get_latest_tweet_id} 2 | \alias{get_latest_tweet_id} 3 | \title{ 4 | A function to retrieve the most recent tweet ID from a database 5 | } 6 | \description{ 7 | Given a registered database backend which contains a table of tweets, will return the 8 | ID of the most recent tweet stored in that table 9 | } 10 | \usage{ 11 | get_latest_tweet_id(table_name = "tweets") 12 | } 13 | \arguments{ 14 | \item{table_name}{The name of the table in the database containing tweets} 15 | } 16 | \details{ 17 | A wrapper around a \code{select max(id)} on the \code{table_name} 18 | } 19 | \value{ 20 | The ID of the most recent tweet in the table, or a \code{\link{stop}} if the table is empty 21 | } 22 | \author{Jeff Gentry} 23 | \seealso{\code{\link{register_db_backend}}} 24 | \examples{ 25 | \dontrun{ 26 | register_sqlite_backend("sqlit_file") 27 | get_latest_tweet_id("rstats_tweets") 28 | } 29 | } 30 | \keyword{utilities} -------------------------------------------------------------------------------- /man/import_statuses.Rd: -------------------------------------------------------------------------------- 1 | \name{import_statuses} 2 | \alias{import_statuses} 3 | \alias{import_obj} 4 | \alias{import_trends} 5 | \alias{import_users} 6 | \alias{json_to_statuses} 7 | \alias{json_to_trends} 8 | \alias{json_to_users} 9 | \title{ 10 | Functions to import twitteR objects from various sources 11 | } 12 | \description{ 13 | Functions designed to import data into twitteR objects from a variety of data 14 | sources. Currently only JSON is supported, and this entire branch of functionality 15 | should be considered experimental & under development. 16 | } 17 | \usage{ 18 | import_statuses(raw_data, conversion_func = json_to_statuses) 19 | import_trends(raw_data, conversion_func = json_to_trends) 20 | import_users(raw_data, conversion_func = json_to_users) 21 | import_obj(raw_data, conversion_func, ...) 22 | json_to_users(raw_data) 23 | json_to_statuses(raw_data) 24 | json_to_trends(raw_data) 25 | } 26 | \arguments{ 27 | \item{raw_data}{Data to be be parsed via the prescribed function} 28 | \item{conversion_func}{The function to convert \code{raw_data} into the specified twitteR object} 29 | \item{...}{Arguments to pass along to \code{conversion_func}} 30 | } 31 | \value{ 32 | A list of twitteR objects of the appropriate type, e.g. \code{\link{status}}, \code{\link{user}}, etc 33 | } 34 | \author{Jeff Gentry} 35 | \seealso{\code{\link{status}}, \code{\link{user}}} 36 | \examples{ 37 | \dontrun{ 38 | status_list = import_statuses(list_of_status_json) 39 | } 40 | } 41 | \keyword{interface} -------------------------------------------------------------------------------- /man/load_tweets_db.Rd: -------------------------------------------------------------------------------- 1 | \name{load_tweets_db} 2 | \alias{load_tweets_db} 3 | \alias{load_users_db} 4 | \alias{store_tweets_db} 5 | \alias{store_users_db} 6 | \title{ 7 | Functions to persist/load twitteR data to a database 8 | } 9 | \description{ 10 | These functions allow a user to store twitteR based data to a database backend 11 | as well as retrieving previously stored data 12 | } 13 | \usage{ 14 | store_tweets_db(tweets, table_name="tweets") 15 | store_users_db(users, table_name="users") 16 | load_users_db(as.data.frame = FALSE, table_name = "users") 17 | load_tweets_db(as.data.frame = FALSE, table_name = "tweets") 18 | } 19 | \arguments{ 20 | \item{tweets}{A list of \code{status} objects to persist to the database} 21 | \item{users}{A list of \code{user} objects to persist to the database} 22 | \item{as.data.frame}{if \code{TRUE}, data will be returned as a data.frame instead of twitteR objects} 23 | \item{table_name}{The database table to use for storing and loading} 24 | } 25 | \value{ 26 | \code{store_tweets_db} and \code{store_users_db} return \code{TRUE} of \code{FALSE} 27 | based on their success or not. The loading functions return either a \code{data.frame} 28 | of the data (representing the underlying table) or a list of the appropriate \code{twitteR} 29 | objects. 30 | } 31 | \author{Jeff Gentry} 32 | \seealso{ 33 | \code{\link{register_db_backend}}, \code{\link{register_sqlite_backend}}, \code{\link{register_mysql_backend}} 34 | } 35 | \examples{ 36 | \dontrun{ 37 | register_sqlite_backend("/path/to/sqlite/file") 38 | tweets = searchTwitter("#scala") 39 | store_tweets_db(tweets) 40 | from_db = load_tweets_db() 41 | } 42 | } 43 | \keyword{ utilities} -------------------------------------------------------------------------------- /man/registerTwitterOAuth.Rd: -------------------------------------------------------------------------------- 1 | \name{registerTwitterOAuth} 2 | \alias{registerTwitterOAuth} 3 | \alias{getTwitterOAuth} 4 | \title{ Register OAuth credentials to twitter R session } 5 | \description{ 6 | These functions are deprecated 7 | } 8 | \usage{ 9 | getTwitterOAuth(consumer_key, consumer_secret) 10 | registerTwitterOAuth(oauth) 11 | } 12 | \arguments{ 13 | \item{consumer_key}{The consumer key supplied by Twitter} 14 | \item{consumer_secret}{The consumer secret supplied by Twitter} 15 | \item{oauth}{An object of class \code{OAuth}} 16 | } 17 | \details{ 18 | These functions are deprecated, see \code{\link{setup_twitter_oauth}} 19 | } 20 | \value{ 21 | \code{TRUE} on success, otherwise an error will be thrown 22 | } 23 | \author{Jeff Gentry} 24 | \seealso{ \code{setup_twitter_oauth} } 25 | \examples{ 26 | \dontrun{ 27 | fakeExample = 5 28 | } 29 | } 30 | \keyword{ interface } 31 | -------------------------------------------------------------------------------- /man/register_db_backend.Rd: -------------------------------------------------------------------------------- 1 | \name{register_db_backend} 2 | \alias{register_db_backend} 3 | \alias{register_sqlite_backend} 4 | \alias{register_mysql_backend} 5 | \title{ 6 | Functions to setup a database backend for twitteR 7 | } 8 | \description{ 9 | twitteR can have a database backend registered from which to store and load 10 | tweet and user data. These functions provide mechanisms for setting up the 11 | connection within twitteR 12 | } 13 | \usage{ 14 | register_db_backend(db_handle) 15 | register_sqlite_backend(sqlite_file, ...) 16 | register_mysql_backend(db_name, host, user, password, ...) 17 | } 18 | \arguments{ 19 | \item{db_handle}{A DBI connection} 20 | \item{sqlite_file}{File path for a SQLite file} 21 | \item{db_name}{Name of the database to connect to} 22 | \item{host}{Hostname the database is on} 23 | \item{user}{username to connect to the database with} 24 | \item{password}{password to connect to the database with} 25 | \item{...}{extra arguments to pass to \code{dbConnect}} 26 | } 27 | \details{ 28 | Currently only \code{RSQLite} and \code{RMySQL} are supported. To use either 29 | of these DBI implementations the appropriate packages will need to be 30 | installed. 31 | 32 | The \code{register_sqlite_backend} and \code{register_mysql_backend} are 33 | convenience wrappers to both create the DBI connection and call \code{register_db_backend} 34 | for you. 35 | } 36 | \value{ 37 | The DBI connection, invisibly 38 | } 39 | \author{Jeff Gentry} 40 | \seealso{ 41 | \code{\link{store_tweets_db}}, \code{\link{store_users_db}}, \code{\link{load_tweets_db}}, \code{\link{load_users_db}} 42 | } 43 | \examples{ 44 | \dontrun{ 45 | register_sqlite_backend("/path/to/sqlite/file") 46 | tweets = searchTwitter("#scala") 47 | store_tweets_db(tweets) 48 | from_db = load_tweets_db() 49 | } 50 | } 51 | \keyword{ utilities } -------------------------------------------------------------------------------- /man/retweets.Rd: -------------------------------------------------------------------------------- 1 | \name{retweets} 2 | \alias{retweets} 3 | \alias{retweeters} 4 | \title{ 5 | Functions to work with retweets 6 | } 7 | \description{ 8 | These functions can be used to return retweets or users who retweeted a tweet 9 | } 10 | \usage{ 11 | retweets(id, n = 20, ...) 12 | } 13 | \arguments{ 14 | \item{id}{The ID of the tweet to get retweet information on} 15 | \item{n}{The number of results to return, up to 100} 16 | \item{\dots}{Further arguments to pass on to httr} 17 | } 18 | \value{ 19 | For \code{retweets} the n most recent retweets of the original tweet. 20 | 21 | For \code{retweeters} the n most recent users who have retweeted this tweet. 22 | } 23 | \author{ 24 | Jeff Gentry 25 | } 26 | \seealso{ 27 | \code{\link{showStatus}} 28 | } 29 | \examples{ 30 | \dontrun{ 31 | retweets("21947795900469248") 32 | 33 | st = showStatus("21947795900469248") 34 | retweeters(st$getId()) 35 | } 36 | } 37 | % Add one or more standard keywords, see file 'KEYWORDS' in the 38 | % R documentation directory. 39 | \keyword{ ~kwd1 } 40 | \keyword{ ~kwd2 }% __ONLY ONE__ keyword per line 41 | -------------------------------------------------------------------------------- /man/search.Rd: -------------------------------------------------------------------------------- 1 | \name{searchTwitter} 2 | \alias{searchTwitter} 3 | \alias{Rtweets} 4 | \alias{searchTwitteR} 5 | \title{ Search twitter } 6 | \description{ 7 | This function will issue a search of Twitter based on a supplied 8 | search string. 9 | } 10 | \usage{ 11 | searchTwitter(searchString, n=25, lang=NULL, since=NULL, until=NULL, 12 | locale=NULL, geocode=NULL, sinceID=NULL, maxID=NULL, 13 | resultType=NULL, retryOnRateLimit=120, ...) 14 | Rtweets(n=25, lang=NULL, since=NULL, ...) 15 | } 16 | \arguments{ 17 | \item{searchString}{Search query to issue to twitter. Use "+" to separate query terms.} 18 | \item{n}{The maximum number of tweets to return} 19 | \item{lang}{If not \code{NULL}, restricts tweets to the given 20 | language, given by an ISO 639-1 code} 21 | \item{since}{If not \code{NULL}, restricts tweets to those since the 22 | given date. Date is to be formatted as YYYY-MM-DD} 23 | \item{until}{If not \code{NULL}, restricts tweets to those up until the 24 | given date. Date is to be formatted as YYYY-MM-DD} 25 | \item{locale}{If not \code{NULL}, will set the locale for the search. 26 | As of 03/06/11 only \code{ja} is effective, as per the Twitter API} 27 | \item{geocode}{If not \code{NULL}, returns tweets by users located 28 | within a given radius of the given latitude/longitude. See 29 | \code{Details} below for more information} 30 | \item{sinceID}{If not \code{NULL}, returns tweets with IDs greater 31 | (ie newer) than the specified ID} 32 | \item{maxID}{If not \code{NULL}, returns tweets with IDs smaller 33 | (ie older) than the specified ID} 34 | \item{resultType}{If not \code{NULL}, returns filtered tweets as per value. 35 | See details for allowed values.} 36 | \item{retryOnRateLimit}{If non-zero the search command will block retry up to 37 | X times if the rate limit is experienced. This might lead to a much longer run 38 | time but the task will eventually complete if the retry count is high enough} 39 | \item{...}{Optional arguments to be passed to \code{\link{GET}}} 40 | } 41 | \details{ 42 | These commands will return any authorized tweets which match the 43 | search criteria. Note that there are pagination restrictions as well 44 | as other limits on what can be searched, so it is always possible to 45 | not retrieve as many tweets as was requested with the \code{n} 46 | argument. Authorized tweets are public tweets as well as those 47 | protected tweets that are available to the user after authenticating 48 | via \code{\link{registerTwitterOAuth}}. 49 | 50 | The \code{searchString} is always required. Terms can contain spaces, 51 | and multiple terms should be separated with "+". 52 | 53 | For the \code{geocode} argument, the values are given in the format 54 | \code{latitude,longitude,radius}, where the radius can have either 55 | \code{mi} (miles) or \code{km} (kilometers) as a unit. For example 56 | \code{geocode='37.781157,-122.39720,1mi'}. 57 | 58 | For the \code{sinceID} argument, if the requested ID value is older 59 | than the oldest available tweets, the API will return tweets starting 60 | from the oldest ID available. 61 | 62 | For the \code{maxID} argument, tweets upto this ID value will be returned 63 | starting from the oldest ID available. Useful for paging. 64 | 65 | The \code{resultType} argument specifies the type of search results received 66 | in API response. Default is \code{mixed}. 67 | Allowed values are \code{mixed} (includes popular + real time results), 68 | \code{recent} (returns the most recent results) and \code{popular} 69 | (returns only the most popular results). 70 | 71 | The \code{Rtweets} function is a wrapper around \code{searchTwitter} 72 | which hardcodes in a search for \code{#rstats}. 73 | } 74 | \value{ 75 | A list of \code{\link{status}} objects 76 | } 77 | \author{Jeff Gentry} 78 | \seealso{ \code{\link{status}}} 79 | \examples{ 80 | \dontrun{ 81 | searchTwitter("#beer", n=100) 82 | Rtweets(n=37) 83 | 84 | ## Search between two dates 85 | searchTwitter('charlie sheen', since='2011-03-01', until='2011-03-02') 86 | 87 | ## geocoded results 88 | searchTwitter('patriots', geocode='42.375,-71.1061111,10mi') 89 | 90 | ## using resultType 91 | searchTwitter('world cup+brazil', resultType="popular", n=15) 92 | searchTwitter('from:hadleywickham', resultType="recent", n=10) 93 | 94 | } 95 | } 96 | \keyword{ interface } 97 | -------------------------------------------------------------------------------- /man/search_twitter_and_store.Rd: -------------------------------------------------------------------------------- 1 | \name{search_twitter_and_store} 2 | \alias{search_twitter_and_store} 3 | \title{ 4 | A function to store searched tweets to a database 5 | } 6 | \description{ 7 | A convenience function designed to wrap the process of running a twitter search and pushing the results to a database. 8 | If this is called more than once, the search will start with the most recent tweet already stored. 9 | } 10 | \usage{ 11 | search_twitter_and_store(searchString, table_name = "tweets", lang = NULL, 12 | locale = NULL, geocode = NULL, retryOnRateLimit = 120, ...) 13 | } 14 | \arguments{ 15 | \item{searchString}{The search string to use, e.g. as one would in \code{\link{searchTwitter}}} 16 | \item{table_name}{The database to store the tweets to, see \code{\link{register_db_backend}}} 17 | \item{lang}{If not \code{NULL}, restricts tweets to the given 18 | language, given by an ISO 639-1 code} 19 | \item{locale}{If not \code{NULL}, will set the locale for the search. 20 | As of 03/06/11 only \code{ja} is effective, as per the Twitter API} 21 | \item{geocode}{If not \code{NULL}, returns tweets by users located 22 | within a given radius of the given latitude/longitude. See 23 | \code{Details} in \code{link{searchTwitter}}} 24 | \item{retryOnRateLimit}{If non-zero the search command will block retry up to 25 | X times if the rate limit is experienced. This might lead to a much longer run 26 | time but the task will eventually complete if the retry count is high enough} 27 | \item{...}{Optional arguments to be passed to \code{\link{GET}}} 28 | } 29 | \details{ 30 | All arguments but \code{table_name} are being passed directly to \code{\link{searchTwitter}}. 31 | 32 | This function will check if \code{table_name} exists, and if so will also use a \code{sinceID} of the 33 | most recent ID in the table. The search is performed, the returned tweets are stored 34 | in the database via \code{\link{store_tweets_db}}. 35 | } 36 | \value{ 37 | The number of tweets stored 38 | } 39 | \note{Jeff Gentry} 40 | \seealso{ 41 | \code{\link{register_db_backend}}, \code{\link{searchTwitter}}, \code{\link{store_tweets_db}} 42 | } 43 | \examples{ 44 | \dontrun{ 45 | register_sqlite_backend("sqlit_file") 46 | n = search_twitter_and_store("#rstats", "rstats_tweets") 47 | } 48 | } 49 | \keyword{ utilities } -------------------------------------------------------------------------------- /man/setup_twitter_oauth.Rd: -------------------------------------------------------------------------------- 1 | \name{setup_twitter_oauth} 2 | \alias{setup_twitter_oauth} 3 | \title{ 4 | Sets up the OAuth credentials for a twitteR session 5 | } 6 | \description{ 7 | This function wraps the OAuth authentication handshake functions from the 8 | httr package for a twitteR session 9 | } 10 | \usage{ 11 | setup_twitter_oauth(consumer_key, consumer_secret, access_token=NULL, access_secret=NULL) 12 | } 13 | \arguments{ 14 | \item{consumer_key}{The consumer key supplied by Twitter} 15 | \item{consumer_secret}{The consumer secret supplied by Twitter} 16 | \item{access_token}{The access token supplied by Twitter} 17 | \item{access_secret}{The access secret supplied by Twitter} 18 | } 19 | \details{ 20 | The \code{httr} package can cache authentication. See \code{\link{Token}} for 21 | details 22 | 23 | If both \code{access_token} and \code{access_secret} are set (i.e. not \code{NULL}), 24 | these will be supplied directly to the OAuth authentication instead of the browser 25 | based authentication dance one would normally experience. This requires you to already 26 | know the access tokens for your Twitter app. The usefuleness of this feature is primarily 27 | in a headless environment where a web browser is not available. 28 | } 29 | \value{ 30 | This is called for its side effect 31 | } 32 | \author{ 33 | Jeff Gentry 34 | } 35 | \seealso{ 36 | \code{\link{Token}}, \code{\link{GET}}, \code{\link{POST}} 37 | } 38 | \examples{ 39 | \dontrun{ 40 | setup_twitter_oauth("CONSUMER_KEY", "CONSUMER_SECRET") 41 | } 42 | } 43 | \keyword{interface} -------------------------------------------------------------------------------- /man/showStatus.Rd: -------------------------------------------------------------------------------- 1 | \name{showStatus} 2 | \alias{showStatus} 3 | \alias{lookup_statuses} 4 | \title{ 5 | Functions to return statuses 6 | } 7 | \description{ 8 | These functions can be used to retrieve specific tweets from the server 9 | } 10 | \usage{ 11 | showStatus(id, ...) 12 | lookup_statuses(ids, ...) 13 | } 14 | \arguments{ 15 | \item{id}{ID of a specific tweet, should be a String, but numbers are accepted} 16 | \item{ids}{A vector of IDs to lookup, should be Strings but numbers are accepted} 17 | \item{\dots}{Optional arguments to be passed to \code{\link{GET}} (or \code{\link{POST}}, see Details)} 18 | } 19 | \value{ 20 | For showStatus, an object of class \code{\link{status}} 21 | 22 | For lookup_statuses, a list of \code{\link{status}} objects. Note that these will not be in the 23 | same order as the \code{ids} argument and that any id which could not be retrieved will not be present. 24 | } 25 | \details{ 26 | Ideally a POST request would be used for lookup_statuses, however currently there is a problem 27 | (issue 78 on github) and GET is used. 28 | } 29 | \author{ 30 | Jeff Gentry 31 | } 32 | \seealso{ 33 | \code{\link{status}} 34 | } 35 | \examples{ 36 | \dontrun{ 37 | showStatus('123') 38 | lookup_statuses(c("123", "234", "456")) 39 | } 40 | } 41 | % Add one or more standard keywords, see file 'KEYWORDS' in the 42 | % R documentation directory. 43 | \keyword{ interface } 44 | 45 | -------------------------------------------------------------------------------- /man/status-class.Rd: -------------------------------------------------------------------------------- 1 | \name{status-class} 2 | \Rdversion{1.1} 3 | \docType{class} 4 | \alias{status-class} 5 | \alias{statusFactory} 6 | \alias{status} 7 | \alias{buildStatus} 8 | \alias{show,status-method} 9 | \alias{as.data.frame,status-method} 10 | \alias{text,status-method} 11 | \alias{favorited,status-method} 12 | \alias{favorited} 13 | \alias{replyToSN,status-method} 14 | \alias{replyToSN} 15 | \alias{created,status-method} 16 | \alias{truncated,status-method} 17 | \alias{truncated} 18 | \alias{replyToSID,status-method} 19 | \alias{replyToSID} 20 | \alias{id,status-method} 21 | \alias{id} 22 | \alias{replyToUID,status-method} 23 | \alias{replyToUID} 24 | \alias{statusSource,status-method} 25 | \alias{statusSource} 26 | \alias{screenName,status-method} 27 | \alias{statusText} 28 | \alias{statusText,status-method} 29 | \alias{retweetCount,status-method} 30 | \alias{retweetCount} 31 | \alias{retweeted,status-method} 32 | \alias{retweeted} 33 | \alias{[[,twitterObjList-method} 34 | \alias{as.data.frame,twitterObj-method} 35 | \alias{show,twitterObjList-method} 36 | \alias{retweetStatus} 37 | \title{Class to contain a Twitter status} 38 | \description{Container for Twitter status messages, including the text 39 | as well as basic information} 40 | \section{Fields}{ 41 | \describe{ 42 | \item{\code{text}:}{The text of the status} 43 | \item{\code{screenName}:}{Screen name of the user who posted this status} 44 | \item{\code{id}:}{ID of this status} 45 | \item{\code{replyToSN}:}{Screen name of the user this is in reply 46 | to} 47 | \item{\code{replyToUID}:}{ID of the user this was in reply to} 48 | \item{\code{statusSource}:}{Source user agent for this tweet} 49 | \item{\code{created}:}{When this status was created} 50 | \item{\code{truncated}:}{Whether this status was truncated} 51 | \item{\code{favorited}:}{Whether this status has been favorited} 52 | \item{\code{retweeted}:}{TRUE if this status has been retweeted} 53 | \item{\code{retweetCount}:}{The number of times this status has been retweeted} 54 | } 55 | } 56 | \section{Methods}{ 57 | \describe{ 58 | \item{\code{toDataFrame}:}{Converts this into a one row 59 | \code{\link{data.frame}}, with each field representing a column. 60 | This can also be accomplished by the S4 style 61 | \code{as.data.frame(objectName)}.} 62 | } 63 | } 64 | \details{ 65 | The \code{status} class is implemented as a reference class. This class 66 | was previously implemented as an S4 class, and for backward 67 | compatibility purposes the old S4 accessor methods have been left in, 68 | although new code should not be written with these. An instance of a 69 | generator for this class is provided as a convenience to the user as 70 | it is configured to handle most standard cases. To access this 71 | generator, use the object \code{statusFactory}. Accessor set & get 72 | methods are provided for every field using reference class 73 | \code{$accessors()} methodology (see \code{\link{setRefClass}} for 74 | more details). As an example, the \code{screenName} field could be 75 | accessed using \code{object$getScreenName} and 76 | \code{object$setScreenName}. 77 | 78 | The constructor of this object assumes that the user is passing in a 79 | JSON encoded Twitter status. It is also possible to directly pass in 80 | the arguments. 81 | 82 | } 83 | \author{Jeff Gentry} 84 | \seealso{ 85 | \code{\link{userTimeline}}, \code{\link{setRefClass}} 86 | } 87 | \examples{ 88 | \dontrun{ 89 | st <- statusFactory$new(screenName="test", text="test message") 90 | st$getScreenName() 91 | st$getText() 92 | 93 | ## Assume 'json' is the return from a Twitter call 94 | st <- statusFactory$new(json) 95 | st$getScreenName() 96 | } 97 | 98 | } 99 | \keyword{classes} 100 | -------------------------------------------------------------------------------- /man/strip_retweets.Rd: -------------------------------------------------------------------------------- 1 | \name{strip_retweets} 2 | \alias{strip_retweets} 3 | \title{ 4 | A function to remove retweets 5 | } 6 | \description{ 7 | Given a list of status objects, will remove retweets from the list 8 | to provide a "pure" set of tweets. 9 | } 10 | \usage{ 11 | strip_retweets(tweets, strip_manual = TRUE, strip_mt = TRUE) 12 | } 13 | \arguments{ 14 | \item{tweets}{A list of \code{\link{status}} objects} 15 | \item{strip_manual}{If \code{TRUE} will remove old style manual retweets} 16 | \item{strip_mt}{If \code{TRUE} will remove modified tweets (MT)} 17 | } 18 | \details{ 19 | Newer style retweets are summarily removed regardless of options. 20 | 21 | Older style retweets (aka manual retweets) are tweets of the form 22 | \code{RT @user blah blah}. If \code{strip_manual} is \code{TRUE}, tweets 23 | containing the \code{RT} string will have everything including and to the 24 | right of the \code{RT} will be removed. Everything to the left of the 25 | \code{RT} will remain, as this should be original content. 26 | 27 | If \code{strip_mt} is \code{TRUE}, tweets will be stripped in the same 28 | manner as \code{strip_manual} but using the string {\code{MT}} 29 | } 30 | \value{ 31 | A list of \code{status} objects with retweeted content removed 32 | } 33 | \author{ 34 | Jeff Gentry 35 | } 36 | \seealso{ 37 | \code{\link{status}} 38 | } 39 | \examples{ 40 | \dontrun{ 41 | tweets = searchTwitter("stuff") 42 | no_retweets = strip_retweets(tweets) 43 | } 44 | } 45 | \keyword{ utilities } 46 | -------------------------------------------------------------------------------- /man/taskStatus.Rd: -------------------------------------------------------------------------------- 1 | \name{taskStatus} 2 | \alias{taskStatus} 3 | \title{ 4 | A function to send a Twitter DM after completion of a task 5 | } 6 | \description{ 7 | This function will run an R expression and send a direct message to a 8 | specified user on success or failure. 9 | } 10 | \usage{ 11 | taskStatus(expr, to, msg="") 12 | } 13 | \arguments{ 14 | \item{expr}{An R expression that will be run} 15 | \item{to}{The user to send a message to, either \code{character} or 16 | an \code{\link{user}} object.} 17 | \item{msg}{An extra message to append to the standard DM} 18 | } 19 | \details{ 20 | This function will run \code{expr}, and send a Direct Message (DM) 21 | upon completion which will report the expression's success or failure. 22 | } 23 | \value{ 24 | Either the value of the expression or an object of class 25 | \code{try-error}. 26 | } 27 | \author{Jeff Gentry} 28 | \seealso{\code{\link{dmSend}}} 29 | \examples{ 30 | \dontrun{ 31 | taskStatus(z<-5, "username", session=sess) 32 | } 33 | } 34 | \keyword{interface} 35 | -------------------------------------------------------------------------------- /man/timelines.Rd: -------------------------------------------------------------------------------- 1 | \name{timelines} 2 | \alias{userTimeline} 3 | \alias{homeTimeline} 4 | \alias{mentions} 5 | \alias{retweetsOfMe} 6 | \title{ Functions to view Twitter timelines } 7 | \description{ 8 | These functions will allow you to retrieve various timelines within 9 | the Twitter universe 10 | } 11 | \usage{ 12 | userTimeline(user, n=20, maxID=NULL, sinceID=NULL, includeRts=FALSE, 13 | excludeReplies=FALSE, ...) 14 | homeTimeline(n=25, maxID=NULL, sinceID=NULL, ...) 15 | mentions(n=25, maxID=NULL, sinceID=NULL, ...) 16 | retweetsOfMe(n=25, maxID=NULL, sinceID=NULL, ...) 17 | listTimeline(user, list_name, n=20, maxID=NULL, sinceID=NULL, includeRts=FALSE, 18 | excludeReplies=FALSE, ...) 19 | } 20 | \arguments{ 21 | \item{user}{ The Twitter user to detail, can be \code{character} or 22 | an \code{\link{user}} object.} 23 | \item{n}{Number of tweets to retrieve, up to a maximum of 3200} 24 | \item{maxID}{Maximum ID to search for} 25 | \item{sinceID}{Minimum (not inclusive) ID to search for} 26 | \item{includeRts}{If \code{FALSE} any native retweets (not old style RT retweets) 27 | will be stripped from the results} 28 | \item{excludeReplies}{if \code{TRUE} any replies are stripped from the results} 29 | \item{list_name}{ Target list name, used for \code{listTimeline}} 30 | \item{...}{Optional arguments to be passed to \code{\link{GET}}} 31 | } 32 | \value{ 33 | A list of \code{\link{status}} objects 34 | } 35 | \author{ Jeff Gentry } 36 | \seealso{ \code{\link{getUser}}, \code{\link{status}}} 37 | \examples{ 38 | \dontrun{ 39 | ut <- userTimeline('barackobama', n=100) 40 | } 41 | } 42 | \keyword{ interface } 43 | -------------------------------------------------------------------------------- /man/twListToDF.Rd: -------------------------------------------------------------------------------- 1 | \name{twListToDF} 2 | \alias{twListToDF} 3 | \title{ 4 | A function to convert twitteR lists to data.frames 5 | } 6 | \description{ 7 | This function will take a list of objects from a single twitteR class and return a data.frame 8 | version of the members 9 | } 10 | \usage{ 11 | twListToDF(twList) 12 | } 13 | \arguments{ 14 | \item{twList}{A list of objects of a single twitteR class, restrictions are listed in \code{details}} 15 | } 16 | \details{ 17 | The classes supported by this function are \code{\link{status}}, \code{\link{user}}, 18 | and \code{\link{directMessage}}. 19 | } 20 | \value{ 21 | A \code{\link{data.frame}} with rows corresponding to the objects in the list and columns being the 22 | fields of the class 23 | } 24 | \author{ 25 | Jeff Gentry 26 | } 27 | \seealso{ 28 | \code{\link{status}}, \code{\link{user}}, \code{\link{directMessage}} 29 | } 30 | \examples{ 31 | \dontrun{ 32 | zz <- searchTwitter("#rstats") 33 | twListToDF(zz) 34 | } 35 | } 36 | \keyword{ interface } 37 | -------------------------------------------------------------------------------- /man/updateStatus.Rd: -------------------------------------------------------------------------------- 1 | \name{updateStatus} 2 | \Rdversion{1.1} 3 | \alias{updateStatus} 4 | \alias{deleteStatus} 5 | \alias{tweet} 6 | \title{ 7 | Functions to manipulate Twitter status 8 | } 9 | \description{ 10 | These functions can be used to set or delete a user's Twitter status 11 | } 12 | \usage{ 13 | tweet(text, ...) 14 | updateStatus(text, lat=NULL, long=NULL, placeID=NULL, 15 | displayCoords=NULL, inReplyTo=NULL, mediaPath=NULL, 16 | bypassCharLimit=FALSE, ...) 17 | deleteStatus(status, ...) 18 | } 19 | \arguments{ 20 | \item{text}{The text to use for a new status } 21 | \item{status}{An object of class \code{\link{status}}} 22 | \item{lat}{If not \code{NULL}, the latitude the status refers to. Ignored if no \code{long} 23 | parameter is provided} 24 | \item{long}{If not \code{NULL}, the longitude the status refers to. Ignored if no \code{lat} 25 | parameter is provided} 26 | \item{placeID}{If not \code{NULL}, provides a place in the world. See Twitter documentation for details} 27 | \item{displayCoords}{Whether or not to put a pin on the exact coordinates a tweet has been sent from, 28 | \code{true} or \code{false} if not \code{NULL}} 29 | \item{inReplyTo}{If not \code{NULL}, denotes the status this is in reply to. Either an object of 30 | class \code{\link{status}} or an ID value} 31 | \item{mediaPath}{If not \code{NULL}, file path to a supported media format (PNG, JPG and GIF) to be included in the status update} 32 | \item{bypassCharLimit}{If \code{TRUE} will not enforce the incoming tweet is less than 140 characters. This can be useful when dealing with autoshortened links} 33 | \item{...}{Optional arguments to be passed to \code{\link{GET}}} 34 | } 35 | \details{ 36 | These messages will only operate properly if the user is authenticated 37 | via \code{OAuth} 38 | 39 | The \code{tweet} and \code{updateStatus} functions are the same. 40 | 41 | To delete a status message, pass in an object of class 42 | \code{\link{status}}, such as from the return value of \code{updateStatus}. 43 | } 44 | \value{ 45 | The \code{updateStatus} function will return an object of class 46 | \code{\link{status}}. 47 | 48 | The \code{deleteStatus} returns \code{TRUE} on success and an error if 49 | failure occurs. 50 | } 51 | \author{ 52 | Jeff Gentry 53 | } 54 | \examples{ 55 | \dontrun{ 56 | ns <- updateStatus('this is my new status message') 57 | ## ooops, we want to remove it! 58 | deleteStatus(ns) 59 | } 60 | } 61 | \keyword{ interface } 62 | -------------------------------------------------------------------------------- /man/use_oauth_token.Rd: -------------------------------------------------------------------------------- 1 | \name{use_oauth_token} 2 | \alias{use_oauth_token} 3 | \title{ 4 | Sets up the OAuth credentials for a twitteR session from an existing Token object 5 | } 6 | \description{ 7 | This function uses an existing httr OAuth Token in the Twitter session 8 | } 9 | \usage{ 10 | use_oauth_token(twitter_token) 11 | } 12 | \arguments{ 13 | \item{twitter_token}{An httr Token object} 14 | } 15 | \details{ 16 | This function is an escape hatch for nonstandard OAuth scenarios. Use setup_twitter_token 17 | unless it doesn't work for your use case. 18 | } 19 | \value{ 20 | This is called for its side effect 21 | } 22 | \author{ 23 | Anand Patil 24 | } 25 | \seealso{ 26 | \code{\link{Token}} 27 | } 28 | \examples{ 29 | \dontrun{ 30 | library(httr) 31 | library(twitteR) 32 | token <- Token2.0$new( 33 | params = list(as_header=TRUE), 34 | app = oauth_app("fun.with.twitter", "no.key", "no.secret"), 35 | endpoint = oauth_endpoints("twitter"), 36 | credentials = list(access_token = "AAAAAAAAAAAAAAAAAAA\%3DAAAAAAAAAAAAAA"), 37 | cache = FALSE 38 | ) 39 | 40 | use_oauth_token(token) 41 | } 42 | } 43 | \keyword{interface} -------------------------------------------------------------------------------- /man/user-class.Rd: -------------------------------------------------------------------------------- 1 | \name{user-class} 2 | \Rdversion{1.1} 3 | \docType{class} 4 | \alias{userFactory} 5 | \alias{user-class} 6 | \alias{user} 7 | \alias{buildUser} 8 | \alias{screenName} 9 | \alias{screenName,user-method} 10 | \alias{show,user-method} 11 | \alias{as.data.frame,user-method} 12 | \alias{description,user-method} 13 | \alias{statusesCount,user-method} 14 | \alias{statusesCount} 15 | \alias{created} 16 | \alias{description} 17 | \alias{favoritesCount} 18 | \alias{followersCount} 19 | \alias{friendsCount} 20 | \alias{followersCount,user-method} 21 | \alias{favoritesCount,user-method} 22 | \alias{friendsCount,user-method} 23 | \alias{userURL,user-method} 24 | \alias{userURL} 25 | \alias{name,user-method} 26 | \alias{name} 27 | \alias{created,user-method} 28 | \alias{tweetCount,user-method} 29 | \alias{tweetCount} 30 | \alias{protected,user-method} 31 | \alias{protected} 32 | \alias{verified,user-method} 33 | \alias{verified} 34 | \alias{location,user-method} 35 | \alias{location} 36 | \alias{id,user-method} 37 | \alias{lastStatus,user-method} 38 | \alias{lastStatus} 39 | \alias{listedCount,user-method} 40 | \alias{listedCount} 41 | \alias{followRequestSent,user-method} 42 | \alias{followRequestSent} 43 | \alias{profileImageUrl} 44 | \alias{profileImageUrl,user-method} 45 | \title{A container object to model Twitter users } 46 | \description{This class is designed to represent a user on Twitter, 47 | modeling information available} 48 | \section{Fields}{ 49 | \describe{ 50 | \item{\code{name}:}{Name of the user} 51 | \item{\code{screenName}:}{Screen name of the user} 52 | \item{\code{id}:}{ID value for this user} 53 | \item{\code{lastStatus}:}{Last status update for the user} 54 | \item{\code{description}:}{User's description} 55 | \item{\code{statusesCount}:}{Number of status updates this user has 56 | had} 57 | \item{\code{followersCount}:}{Number of followers for this user} 58 | \item{\code{favoritesCount}:}{Number of favorites for this user} 59 | \item{\code{friendsCount}:}{Number of followees for this user} 60 | \item{\code{url}:}{A URL associated with this user} 61 | \item{\code{created}:}{When this user was created} 62 | \item{\code{protected}:}{Whether or not this user is protected} 63 | \item{\code{verified}:}{Whether or not this user is verified} 64 | \item{\code{location}:}{Location of the user} 65 | \item{\code{listedCount}:}{The number of times this user appears in public lists} 66 | \item{\code{followRequestSent}:}{If authenticated via OAuth, will be TRUE if you've sent a friend request to this user} 67 | \item{\code{profileImageUrl}:}{URL of the user's profile image, if one exists} 68 | } 69 | } 70 | \section{Methods}{ 71 | \describe{ 72 | \item{\code{getFollowerIDs(n=NULL, ...)}:}{Will return a vector of 73 | twitter user IDs representing followers of this user, up to a 74 | maximum of \code{n} values. If \code{n} is NULL, all followers 75 | will be returned} 76 | \item{\code{getFollowers(n=NULL, ...)}:}{Will return a list of 77 | \code{user} objects representing followers of this user, up to a 78 | maximum of \code{n} values. If \code{n} is NULL, all followers 79 | will be returned} 80 | \item{\code{getFriendIDs(n=NULL, ...)}:}{Will return a vector of 81 | twitter user IDs representing users this user follows, up to a 82 | maximum of \code{n} values. If \code{n} is NULL, all friends 83 | will be returned} 84 | \item{\code{getFriends(n=NULL, ...)}:}{Will return a list of 85 | \code{user} objects representing users this user follows, up to a 86 | maximum of \code{n} values. If \code{n} is NULL, all friendss 87 | will be returned} 88 | \item{\code{toDataFrame(row.names=NULL, optional=FALSE)}:}{Converts 89 | this into a one row 90 | \code{\link{data.frame}}, with each field except for 91 | \code{lastStatus} representing a column. 92 | This can also be accomplished by the S4 style 93 | \code{as.data.frame(objectName)}.} 94 | 95 | } 96 | } 97 | \details{ 98 | The \code{user} class is implemented as a reference class. This class 99 | was previously implemented as an S4 class, and for backward 100 | compatibility purposes the old S4 accessor methods have been left in, 101 | although new code should not be written with these. An instance of a 102 | generator for this class is provided as a convenience to the user as 103 | it is configured to handle most standard cases. To access this 104 | generator, use the object \code{userFactory}. Accessor set & get 105 | methods are provided for every field using reference class 106 | \code{$accessors()} methodology (see \code{\link{setRefClass}} for 107 | more details). As an example, the \code{screenName} field could be 108 | accessed using \code{object$getScreenName} and 109 | \code{object$setScreenName}. 110 | 111 | The constructor of this object assumes that the user is passing in a 112 | JSON encoded Twitter user. It is also possible to directly pass in 113 | the arguments. 114 | } 115 | \author{Jeff Gentry} 116 | \seealso{\code{\link{status}}, \code{\link{setRefClass}}} 117 | \examples{ 118 | ## This example is run, but likely not how you want to do things 119 | us <- userFactory$new(screenName="test", name="Joe Smith") 120 | us$getScreenName() 121 | us$getName() 122 | 123 | \dontrun{ 124 | ## Assume 'json' is the return from a Twitter call 125 | us <- userFactory$new(json) 126 | us$getScreenName() 127 | } 128 | } 129 | \keyword{classes} 130 | --------------------------------------------------------------------------------