Skip to content

Commit

Permalink
Replaced twitteR with rtweet package (#4)
Browse files Browse the repository at this point in the history
* Consolidated utility functions
* Initial rtweet implementation
* Added rtweet based function GraphUserInfoTwitter
* Tidied up CreateActor S3 classes
  • Loading branch information
bryn-g authored Jan 11, 2019
1 parent 5397dfd commit ebd658a
Show file tree
Hide file tree
Showing 70 changed files with 2,126 additions and 2,713 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ vignettes/*.pdf

# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
.httr-oauth
.twitter_oauth_token

# knitr and R markdown default cache directories
/*_cache/
Expand Down
7 changes: 3 additions & 4 deletions vosonSML/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
Package: vosonSML
Version: 0.24.0
Version: 0.25.0
Title: Tools for Collecting Social Media Data and Generating Networks for Analysis
Description: A suite of tools for collecting and constructing networks from social media data.
Provides easy-to-use functions for collecting data across popular platforms (Instagram,
Facebook, Twitter, YouTube and Reddit) and generating different types of networks for analysis.
Type: Package
Imports: tm, stringr, twitteR, RCurl, bitops, rjson, plyr, igraph (>= 1.2.2), Rfacebook (>= 0.6.15),
Imports: tm, stringr, RCurl, bitops, rjson, plyr, igraph (>= 1.2.2), Rfacebook (>= 0.6.15),
Hmisc, data.table, httpuv, instaR, methods, httr, RedditExtractoR (>= 2.1.2), magrittr,
dplyr (>= 0.7.8), rlang (>= 0.3.0.1)
dplyr (>= 0.7.8), rlang (>= 0.3.0.1), rtweet (>= 0.6.8)
Depends: R (>= 3.2.0)
Suggests: testthat
Encoding: UTF-8
Author: Timothy Graham, Robert Ackland, Chung-hong Chan, Bryan Gertzel
Maintainer: Bryan Gertzel <[email protected]>
Expand Down
30 changes: 20 additions & 10 deletions vosonSML/NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
# Generated by roxygen2: do not edit by hand

S3method(CreateActorNetwork,default)
S3method(CreateActorNetwork,reddit)
S3method(CreateActorNetwork,twitter)
S3method(CreateActorNetwork,youtube)
S3method(CreateBimodalNetwork,facebook)
S3method(CreateBimodalNetwork,instagram)
S3method(CreateBimodalNetwork,twitter)
S3method(CreateDynamicNetwork,facebook)
S3method(CreateEgoNetworkFromData,instagram)
S3method(CreateSemanticNetwork,twitter)
export(Authenticate)
export(AuthenticateWithFacebookAPI)
export(AuthenticateWithInstagramAPI)
export(AuthenticateWithTwitterAPI)
export(Collect)
export(CollectDataFacebook)
export(CollectDataInstagram)
export(CollectDataTwitter)
export(CollectEgoInstagram)
export(Create)
export(CreateActorNetwork)
export(CreateEgoNetwork)
export(GetYoutubeVideoIDs)
export(GraphUserInfoTwitter)
export(ImportData)
export(LoadCredential)
export(PopulateUserInfo)
export(SaveCredential)
export(importData)
import(RCurl)
import(bitops)
import(data.table)
Expand All @@ -38,16 +38,23 @@ importFrom(Rfacebook,fbOAuth)
importFrom(Rfacebook,getPage)
importFrom(Rfacebook,getPost)
importFrom(Rfacebook,getUsers)
importFrom(dplyr,anti_join)
importFrom(dplyr,coalesce)
importFrom(dplyr,distinct)
importFrom(dplyr,ends_with)
importFrom(dplyr,filter)
importFrom(dplyr,funs)
importFrom(dplyr,group_by)
importFrom(dplyr,left_join)
importFrom(dplyr,mutate)
importFrom(dplyr,mutate_all)
importFrom(dplyr,mutate_at)
importFrom(dplyr,rename)
importFrom(dplyr,row_number)
importFrom(dplyr,select)
importFrom(dplyr,summarise)
importFrom(dplyr,ungroup)
importFrom(dplyr,vars)
importFrom(igraph,'V<-')
importFrom(igraph,V)
importFrom(igraph,delete.vertices)
Expand All @@ -57,6 +64,7 @@ importFrom(igraph,graph_from_data_frame)
importFrom(igraph,set.graph.attribute)
importFrom(igraph,set_graph_attr)
importFrom(igraph,simplify)
importFrom(igraph,vcount)
importFrom(igraph,write.graph)
importFrom(instaR,getComments)
importFrom(instaR,getFollowers)
Expand All @@ -65,17 +73,19 @@ importFrom(instaR,getLikes)
importFrom(instaR,getUser)
importFrom(instaR,instaOAuth)
importFrom(instaR,searchInstagram)
importFrom(magrittr,'%<>%')
importFrom(magrittr,'%>%')
importFrom(plyr,ldply)
importFrom(rlang,'.data')
importFrom(rtweet,create_token)
importFrom(rtweet,lookup_users)
importFrom(rtweet,rate_limit)
importFrom(rtweet,search_tweets)
importFrom(rtweet,users_data)
importFrom(stats,'na.omit')
importFrom(stringr,str_extract)
importFrom(stringr,str_match_all)
importFrom(stringr,str_replace_all)
importFrom(twitteR,lookupUsers)
importFrom(twitteR,searchTwitter)
importFrom(twitteR,setup_twitter_oauth)
importFrom(twitteR,twListToDF)
importFrom(utils,"flush.console")
importFrom(utils,"install.packages")
importFrom(utils,"read.csv")
Expand Down
181 changes: 55 additions & 126 deletions vosonSML/R/Authenticate.R
Original file line number Diff line number Diff line change
@@ -1,162 +1,91 @@
## The AuthenticateWithTwitterAPI is not functional because it relies on a "side effect". It is a twitteR design problem.
## AuthenticateWithFacebookAPI can be fixed to make it functional.

## TODO: Maybe need to unify the variable names, currently there are:
### facebook: appID, appSecret, extended_permissions, useCachedToken
### twitter: api_key, api_secret, access_token, access_token_secret, createToken <- inconsistent?
### youtube: apiKeyYoutube <- inconsistent?
### instagram: appID, appSecret, useCachedToken

## Maybe make it consistent with only camel, as the rest of the package uses camel, not underscore. But hadleyverse packages usually use underscores:
## Therefore, unified variable names:
## appID, appSecret, apiKey, apiSecret, accessToken, accessTokenSecret, useCachedToken, extendedPermissions, createToken

#' Create credential to access social media APIs
#' Create a credential to access social media APIs
#'
#' \code{Authenticate} creates a \code{credential} object that enables R to
#' make authenticated calls to social media APIs. A \code{credential} object
#' is a S3 object with the authentication-related information such as access
#' tokens and the information on the social media that grant authentication.
#' \code{Authenticate} is the first step of the \code{Authenticate},
#' \code{Collect}, \code{Create} workflow.
#' \code{Authenticate} creates a \code{credential} object that enables R to make authenticated calls to social media
#' APIs. A \code{credential} object is a S3 object with the authentication-related information such as access tokens
#' and the information on the social media that grant authentication. \code{Authenticate} is the first step of the
#' \code{Authenticate}, \code{\link{Collect}} and \code{\link{Create}} workflow.
#'
#' @param socialmedia character string, social media API to authenticate,
#' currently supports "facebook", "youtube", "twitter", "instagram" and "reddit"
#' @param ... additional parameters for authentication
#' \code{facebook}: appID, appSecret
#' \code{youtube}: apiKey
#' \code{twitter}: apiKey, apiSecret, accessToken, accessTokenSecret
#' \code{instagram}: appID, appSecret
#' \code{reddit}: appName, appKey, appSecret, useTokenCache
#' @param socialmedia Character string. Identifier for social media API to authenticate.\cr
#' Supports: \code{"twitter"}, \code{"youtube"}, \code{"reddit"}, \code{"instagram"} and \code{"facebook"}.
#' @param ... Additional parameters for authentication appropriate to \code{socialmedia} identifier.
#' \describe{
#' \item{twitter:}{\code{[appName], apiKey, apiSecret, accessToken,
#' accessTokenSecret, [useCachedToken]}}
#' \item{youtube:}{\code{apiKey}}
#' \item{reddit:}{\code{[appName], appKey, appSecret, [useCachedToken]}}
#' \item{instagram:}{\code{appID, appSecret, [useCachedToken]}}
#' \item{facebook:}{\code{appID, appSecret, [extendedPermissions, useCachedToken]}}
#' }
#'
#' @return credential object with authentication information
#' @return A \code{credential} object with authentication information.
#'
#' @note Currently, \code{Authenticate} with socialmedia = "twitter" generates
#' oauth information to be used in the current active session only (i.e.
#' "side-effect") and no authentication-related information will be stored in
#' the returned \code{credential} object.
#' @note Currently, \code{Authenticate} with \code{socialmedia = "twitter"} generates OAuth information to be used in
#' the current active session only (i.e. "side-effect") and no authentication-related information will be stored in the
#' returned \code{credential} object.
#'
#' @author Chung-hong Chan <chainsawtiney@@gmail.com>
#' @seealso \code{\link{AuthenticateWithFacebookAPI}},
#' \code{\link{AuthenticateWithInstagramAPI}},
#' \code{\link{AuthenticateWithTwitterAPI}},
#' \code{\link{SaveCredential}},
#' \code{\link{LoadCredential}}
#' @examples
#' For other social network API's it's useful to cache the credential to a file and then re-use it in future sessions.
#' Refer to \code{\link{SaveCredential}} and \code{\link{LoadCredential}} to do this.
#'
#' @seealso \code{\link{SaveCredential}}, \code{\link{Collect}}, \code{\link{Create}}
#' @keywords authenticate credential twitter youtube reddit instagram facebook
#'
#' @examples
#' \dontrun{
#' require(magrittr)
#' ## Instagram ego network example
#' myAppID <- "123456789098765"
#' myAppSecret <- "abc123abc123abc123abc123abc123ab"
#' myUsernames <- c("senjohnmccain","obama")
#'
#' Authenticate("instagram",
#' appID = myAappId,
#' appSecret = myAppSecret) %>% Collect(ego = TRUE,
#' username = myUsernames) %>% Create
#' ## youtube actor network example
#'
#' myYoutubeAPIKey <- "xxxxxxxxxxxxxxxxxxxxxx"
#' listYoutubeVideoIDs <- c("W2GZFeYGU3s", "mL27TAJGlWc")
#'
#' myActorNetwork <- Authenticate("youtube", apiKey = myYoutubeAPIKey) %>%
#' Collect(videoIDs = listYoutubeVideoIDs) %>% Create("actor")
#'
#' ## YouTube actor network example
#' my_apiKeyYoutube <- "314159265358979qwerty"
#' videoIDs <- c("W2GZFeYGU3s","mL27TAJGlWc")
#' ## instagram ego network example
#'
#' Authenticate("youtube",
#' apiKey = my_apiKeyYoutube) %>% Collect(videoIDs = videoIDs) %>% Create('actor')
#' myInstaAppID <- "xxxxxxxxxxx"
#' myInstaAppSecret <- "xxxxxxxxxxxxxxxxxxxxxx"
#' listInstaUsernames <- c("senjohnmccain", "obama")
#'
#' myEgoNetwork <- Authenticate("instagram", appID = myInstaAppID, appSecret = myInstaAppSecret) %>%
#' Collect(ego = TRUE, username = listInstaUsernames) %>% Create("ego")
#' }
#'
#' @export
Authenticate <- function(socialmedia, ...) {
authenticator <- switch(tolower(socialmedia),
facebook = facebookAuthenticator,
youtube = youtubeAuthenticator,
twitter = twitterAuthenticator,
instagram = instagramAuthenticator,
youtube = youtubeAuthenticator,
reddit = redditAuthenticator,
stop("Unknown socialmedia")
)
instagram = instagramAuthenticator,
facebook = facebookAuthenticator,
stop("Unknown socialmedia"))

auth <- authenticator(...)

credential <- list(socialmedia = tolower(socialmedia), auth = auth)
class(credential) <- append(class(credential), "credential")

return(credential)
}

### For the side effect of saving the credential into a file.
### Useful to cache the Credential to a file and then re-use it in the future session.
### i.e. Authenticate %>% SaveCredential %>% Collect
### and then, LoadCredential %>% Collect

#' Save and load credential information
#'
#' Functions to save and load credential information. Currently, credential
#' information will be stored as a RDS file. \code{SaveCredential} will return
#' the input \code{credential}, useful for working as a filter between the
#' \code{Authenticate} and \code{Collect}.
#'
#' @aliases LoadCredential SaveCredential
#' @param credential \code{credential} object
#' @param filename character, filename to be saved to or restored from
#' @return \code{credential} object
#' @note \code{credential} created from \code{Authenticate} with socialmedia =
#' 'twitter' will not be saved by SaveCredential
#' @examples
#'
#' \dontrun{
#' require(magrittr)
#' myAppID <- "123456789098765"
#' myAppSecret <- "abc123abc123abc123abc123abc123ab"
#' myUsernames <- c("senjohnmccain","obama")
#'
#' Authenticate("instagram",
#' appID = myAppId,
#' appSecret = myAppSecret) %>% SaveCredential("instagramCred.RDS") %>% Collect(ego = TRUE,
#' username = myUsernames) %>% Create
#'
#' ## Load the previously saved credential information
#' LoadCredential("instagramCred.RDS") %>% Collect(tag="obama",
#' distance=5000, n=100) %>% Create("bimodal")
#' }
#' @export
SaveCredential <- function(credential, filename = "credential.RDS") {
if (credential$socialmedia == "twitter") {
warning("Credential created for Twitter will not be saved.")
} else {
saveRDS(credential, filename)
}
return(credential)
}

#' @rdname SaveCredential
#' @export
LoadCredential <- function(filename = "credential.RDS") {
credential <- readRDS(filename)
return(credential)
twitterAuthenticator <- function(appName, apiKey, apiSecret, accessToken, accessTokenSecret, useCachedToken) {
return(AuthenticateWithTwitterAPI(appName, apiKey, apiSecret, accessToken, accessTokenSecret, useCachedToken))
}

### *Authenticator functions should not be exported. It is just a bunch of helper functions to bridge the AuthenticateWith* functions with Authenticate(), but with datasource as the first argument and always return an auth object

### As a convention, function starts with lower case shouldn't be exported.

youtubeAuthenticator <- function(apiKey) {
return(authenticateWithYoutubeAPI(apiKey))
return(AuthenticateWithYoutubeAPI(apiKey))
}

### Currently, this Authenticator will return nothing, only for its side effect
### SAD!!!!!!!!!!!!!!!!!!
### i.e. cannot use SaveCredential and LoadCredential!

twitterAuthenticator <- function(apiKey, apiSecret, accessToken, accessTokenSecret, createToken) {
AuthenticateWithTwitterAPI(api_key = apiKey, api_secret = apiSecret, access_token = accessToken, access_token_secret = accessTokenSecret, createToken = createToken) # ah, only for its side effect, really bad design decision, twitteR!
redditAuthenticator <- function(appName, appKey, appSecret, useCachedToken) {
# return(AuthenticateWithRedditAPI(appName, appKey, appSecret, useCachedToken))
return(NULL)
}

facebookAuthenticator <- function(appID, appSecret, extendedPermissions = FALSE) {
return(AuthenticateWithFacebookAPI(appID, appSecret, extended_permissions = extendedPermissions, useCachedToken = FALSE))
}

instagramAuthenticator <- function(appID, appSecret) {
return(AuthenticateWithInstagramAPI(appID, appSecret))
}

redditAuthenticator <- function(appName, appKey, appSecret, useTokenCache) {
# return(AuthenticateWithRedditAPI(appName, appKey, appSecret, useTokenCache))
return(NULL)
facebookAuthenticator <- function(appID, appSecret, extendedPermissions = FALSE) {
return(AuthenticateWithFacebookAPI(appID, appSecret, extendedPermissions, useCachedToken = FALSE))
}
12 changes: 6 additions & 6 deletions vosonSML/R/AuthenticateWithRedditAPI.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@
#' @param appName character string containing the reddit app name associated with the API key.
#' @param appKey character string containing the app key.
#' @param appSecret character string containing the app secret.
#' @param useTokenCache logical. Use cached authentication token if found.
#' @param useCachedToken logical. Use cached authentication token if found.
#'
#' @return a reddit authentication token
#'
AuthenticateWithRedditAPI <- function(appName, appKey, appSecret, useTokenCache) {
AuthenticateWithRedditAPI <- function(appName, appKey, appSecret, useCachedToken) {

if (missing(appName)) {
appName <- "reddit"
appName <- "vosonSML-reddit"
}

if (missing(appKey) | missing(appSecret)) {
cat("Error. One or more API credentials are missing.\nPlease specify these.\n")
return()
}

if (missing(useTokenCache)) {
useTokenCache <- FALSE
if (missing(useCachedToken)) {
useCachedToken <- FALSE
}

# sets up oauth2 for reddit
Expand All @@ -44,7 +44,7 @@ AuthenticateWithRedditAPI <- function(appName, appKey, appSecret, useTokenCache)
scope = c("read"),
use_basic_auth = TRUE,
config_init = user_agent("httr oauth"),
cache = useTokenCache)
cache = useCachedToken)

return(reddit_token)
}
Loading

0 comments on commit ebd658a

Please sign in to comment.