Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add non-public tweet metrics and misc fields for API v2 #366

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/main/java/io/github/redouane59/twitter/ITwitterClientV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@

public interface ITwitterClientV2 {

public static enum REQUEST_TWEET_FIELDS_SCOPE {
/** Retrieve public fields of tweets only (any token)*/
PUBLIC,
/** Retrieve also non-public fields of tweets (needs user token) */
NON_PUBLIC,
/** Retrieve non-public and promoted fields of tweets (needs user token, tweet must be promoted or request fails) */
NON_PUBLIC_PROMOTED
}

/**
* Retreive a user from his screen name calling https://api.twitter.com/2/users/
*
Expand Down Expand Up @@ -101,20 +110,40 @@ public interface ITwitterClientV2 {

/**
* Get a tweet from its id calling https://api.twitter.com/2/tweets
* with public fields only
*
* @param tweetId id of the tweet
* @return a tweet object
*/
Tweet getTweet(String tweetId);

/**
* Get a tweet from its id calling https://api.twitter.com/2/tweets
* with given fields scope
*
* @param tweetId id of the tweet
* @return a tweet object
*/
Tweet getTweet(String tweetId, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope);

/**
* Get a tweet list from their id calling https://api.twitter.com/2/tweets
* with public fields only
*
* @param tweetIds the ids of the tweets
* @return a tweet object list
*/
TweetList getTweets(List<String> tweetIds);

/**
* Get a tweet list from their id calling https://api.twitter.com/2/tweets
* with given fields scope
*
* @param tweetIds the ids of the tweets
* @return a tweet object list
*/
TweetList getTweets(List<String> tweetIds, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope);

/**
* Hide/Unide a reply using https://api.twitter.com/labs/2/tweets/:id/hidden
*
Expand Down Expand Up @@ -304,6 +333,25 @@ public interface ITwitterClientV2 {
*/
TweetList getUserTimeline(String userId, AdditionalParameters additionalParameters);

/**
* Get the most recent Tweets posted by the user calling https://api.twitter.com/2/users/:id/tweets (time & tweet id arguments can be null)
*
* @param userId identifier of the Twitter account (user ID) for whom to return results.
* @param requestTweetFieldsScope
* @return a TweetList object containing a list of tweets and the next token if recursiveCall is set to false
*/
public TweetList getUserTimeline(final String userId, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope);

/**
* Get the most recent Tweets posted by the user calling https://api.twitter.com/2/users/:id/tweets (time & tweet id arguments can be null)
*
* @param userId identifier of the Twitter account (user ID) for whom to return results.
* @param additionalParameters accepted parameters recursiveCall, startTime, endTime, sinceId, untilId, maxResults
* @param requestTweetFieldsScope
* @return a TweetList object containing a list of tweets and the next token if recursiveCall is set to false
*/
public TweetList getUserTimeline(String userId, AdditionalParameters additionalParameters, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little question, isn't it possible to add the requestTweetFieldsScope parameter inside the additionalParameters object?
It will avoid creating new constructors no ?


/**
* Get the most recent mentions received posted by the user calling https://api.twitter.com/2/users/:id/mentions
*
Expand Down
97 changes: 75 additions & 22 deletions src/main/java/io/github/redouane59/twitter/TwitterClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,15 @@
@Slf4j
public class TwitterClient implements ITwitterClientV1, ITwitterClientV2, ITwitterClientArchive {

public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.findAndRegisterModules();
public static final String TWEET_FIELDS = "tweet.fields";
public static final String
ALL_TWEET_FIELDS =
"attachments,author_id,created_at,entities,geo,id,in_reply_to_user_id,lang,possibly_sensitive,public_metrics,referenced_tweets,source,text,withheld,context_annotations,conversation_id,reply_settings";
public static final String ALL_TWEET_FIELDS_PUBLIC =
"attachments,author_id,created_at,entities,geo,id,in_reply_to_user_id,lang,possibly_sensitive,referenced_tweets,source,text,withheld,context_annotations,conversation_id,reply_settings,public_metrics";
public static final String ALL_TWEET_FIELDS_NON_PUBLIC = ALL_TWEET_FIELDS_PUBLIC + ",non_public_metrics,organic_metrics";
public static final String ALL_TWEET_FIELDS_NON_PUBLIC_PROMOTED = ALL_TWEET_FIELDS_NON_PUBLIC + ",promoted_metrics";
public static final String EXPANSION = "expansions";
public static final String
ALL_EXPANSIONS =
Expand Down Expand Up @@ -159,7 +164,12 @@ public TwitterClient() {
public TwitterClient(TwitterCredentials credentials) {
this(credentials, new ServiceBuilder(credentials.getApiKey()).apiSecret(credentials.getApiSecretKey()));
}


public TwitterClient(TwitterCredentials credentials, boolean useConsumerKey) {
this(credentials, new ServiceBuilder(credentials.getApiKey()).apiSecret(credentials.getApiSecretKey()));
setUseConsumerKey(useConsumerKey);
}

public TwitterClient(TwitterCredentials credentials, HttpClient httpClient) {
this(credentials,
new ServiceBuilder(credentials.getApiKey()).apiSecret(credentials.getApiSecretKey()).httpClient(httpClient));
Expand Down Expand Up @@ -240,6 +250,15 @@ public void setAutomaticRetry(boolean automaticRetry) {
requestHelperV2.setAutomaticRetry(automaticRetry);
}

/**
* Set the behavior of authentication context used in requests.
*
* @param useConsumerKey Using consumer key for user context if true; or else app-only bearer token. Default is false.
*/
public void setUseConsumerKey(boolean useConsumerKey) {
requestHelperV2.setUseConsumerKey(useConsumerKey);
}

// can manage up to 5000 results / call . Max 15 calls / 15min ==> 75.000
// results max. / 15min
private List<String> getUserIdsByRelation(String url) {
Expand Down Expand Up @@ -520,7 +539,7 @@ public TweetList getLikedTweets(final String userId) {
public TweetList getLikedTweets(final String userId, AdditionalParameters additionalParameters) {
String url = getUrlHelper().getLikedTweetsUrl(userId);
Map<String, String> parameters = new HashMap<>();
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
if (!additionalParameters.isRecursiveCall()) {
return getRequestHelper().getRequestWithParameters(url, parameters, TweetList.class).orElseThrow(NoSuchElementException::new);
}
Expand Down Expand Up @@ -581,7 +600,7 @@ public UserList getMutedUsers() {
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(EXPANSION, PINNED_TWEET_ID);
parameters.put(MAX_RESULTS, "1000");
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
return requestHelperV1.getRequestWithParameters(url, parameters, UserList.class).orElseThrow(NoSuchElementException::new);
}

Expand Down Expand Up @@ -651,7 +670,7 @@ public UserList getSpaceBuyers(final String spaceId) {
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, PINNED_TWEET_ID);
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
return getRequestHelperV2().getRequestWithParameters(url, parameters, UserList.class)
.orElseThrow(NoSuchElementException::new);
}
Expand Down Expand Up @@ -756,7 +775,7 @@ public UserList getListMembers(final String listId) {
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, PINNED_TWEET_ID);
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
return getUsersRecursively(Integer.MAX_VALUE, url, parameters);
}

Expand Down Expand Up @@ -919,21 +938,31 @@ public PostDmResponse createUserDmConversation(String participantId, DmMessage m

@Override
public Tweet getTweet(String tweetId) {
return getTweet(tweetId, REQUEST_TWEET_FIELDS_SCOPE.PUBLIC);
}

@Override
public Tweet getTweet(String tweetId, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope) {
String url = getUrlHelper().getTweetUrl(tweetId);
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, ALL_EXPANSIONS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, getFieldsForRequestTweetFieldsScope(requestTweetFieldsScope));
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(MEDIA_FIELD, ALL_MEDIA_FIELDS);
return getRequestHelper().getRequestWithParameters(url, parameters, TweetV2.class).orElseThrow(NoSuchElementException::new);
}

@Override
public TweetList getTweets(List<String> tweetIds) {
return getTweets(tweetIds, REQUEST_TWEET_FIELDS_SCOPE.PUBLIC);
}

@Override
public TweetList getTweets(List<String> tweetIds, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope) {
String url = getUrlHelper().getTweetsUrl();
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, ALL_EXPANSIONS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, getFieldsForRequestTweetFieldsScope(requestTweetFieldsScope));
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(MEDIA_FIELD, ALL_MEDIA_FIELDS);
StringBuilder result = new StringBuilder();
Expand Down Expand Up @@ -972,7 +1001,7 @@ public TweetList searchTweets(String query) {
public TweetList searchTweets(String query, AdditionalParameters additionalParameters) {
Map<String, String> parameters = additionalParameters.getMapFromParameters();
parameters.put(QUERY, query);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(EXPANSION, ALL_EXPANSIONS);
parameters.put(MEDIA_FIELD, ALL_MEDIA_FIELDS);
Expand All @@ -996,10 +1025,10 @@ public TweetList searchAllTweets(final String query, AdditionalParameters additi
Map<String, String> parameters = additionalParameters.getMapFromParameters();
parameters.put(QUERY, query);
if (additionalParameters.getMaxResults() <= 100) {
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
} else {
LOGGER.warn("Removing context_annotations from tweet_fields because max_result is greater 100");
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS.replace(",context_annotations", ""));
LOGGER.warn("removing context_annotations from tweet_fields because max_result is greater 100");
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC.replace(",context_annotations", ""));
}
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(EXPANSION, ALL_EXPANSIONS);
Expand Down Expand Up @@ -1135,7 +1164,7 @@ public Future<Response> startFilteredStream(Consumer<Tweet> consumer) {
String url = urlHelper.getFilteredStreamUrl();
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, ALL_EXPANSIONS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(MEDIA_FIELD, ALL_MEDIA_FIELDS);
return requestHelperV2.getAsyncRequest(url, parameters, consumer);
Expand All @@ -1151,7 +1180,7 @@ public Future<Response> startFilteredStream(IAPIEventListener listener, int back
String url = urlHelper.getFilteredStreamUrl();
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, ALL_EXPANSIONS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(MEDIA_FIELD, ALL_MEDIA_FIELDS);
if (backfillMinutes > 0) {
Expand Down Expand Up @@ -1243,7 +1272,7 @@ public Future<Response> startSampledStream(Consumer<Tweet> consumer) {
String url = urlHelper.getSampledStreamUrl();
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, ALL_EXPANSIONS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(MEDIA_FIELD, ALL_MEDIA_FIELDS);
return requestHelperV2.getAsyncRequest(url, parameters, consumer);
Expand All @@ -1259,7 +1288,7 @@ public Future<Response> startSampledStream(IAPIEventListener listener, int backf
String url = urlHelper.getSampledStreamUrl();
Map<String, String> parameters = new HashMap<>();
parameters.put(EXPANSION, ALL_EXPANSIONS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(MEDIA_FIELD, ALL_MEDIA_FIELDS);
if (backfillMinutes > 0) {
Expand All @@ -1270,13 +1299,23 @@ public Future<Response> startSampledStream(IAPIEventListener listener, int backf

@Override
public TweetList getUserTimeline(final String userId) {
return getUserTimeline(userId, AdditionalParameters.builder().maxResults(100).build());
return getUserTimeline(userId, AdditionalParameters.builder().maxResults(100).build(), REQUEST_TWEET_FIELDS_SCOPE.PUBLIC);
}

@Override
public TweetList getUserTimeline(final String userId, AdditionalParameters additionalParameters) {
return getUserTimeline(userId, additionalParameters, REQUEST_TWEET_FIELDS_SCOPE.PUBLIC);
}

@Override
public TweetList getUserTimeline(String userId, AdditionalParameters additionalParameters) {
public TweetList getUserTimeline(final String userId, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope) {
return getUserTimeline(userId, AdditionalParameters.builder().maxResults(100).build(), requestTweetFieldsScope);
}

@Override
public TweetList getUserTimeline(String userId, AdditionalParameters additionalParameters, REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope) {
Map<String, String> parameters = additionalParameters.getMapFromParameters();
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, getFieldsForRequestTweetFieldsScope(requestTweetFieldsScope));
parameters.put(USER_FIELDS, ALL_USER_FIELDS);
parameters.put(PLACE_FIELDS, ALL_PLACE_FIELDS);
parameters.put(POLL_FIELDS, ALL_POLL_FIELDS);
Expand All @@ -1300,7 +1339,7 @@ public TweetList getUserMentions(final String userId) {
@Override
public TweetList getUserMentions(final String userId, AdditionalParameters additionalParameters) {
Map<String, String> parameters = additionalParameters.getMapFromParameters();
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS);
parameters.put(TWEET_FIELDS, ALL_TWEET_FIELDS_PUBLIC);
String url = urlHelper.getUserMentionsUrl(userId);
if (!additionalParameters.isRecursiveCall()) {
return getRequestHelperV2().getRequestWithParameters(url, parameters, TweetList.class).orElseThrow(NoSuchElementException::new);
Expand Down Expand Up @@ -1528,4 +1567,18 @@ public String getUserIdFromAccessToken() {
}
return accessToken.substring(0, accessToken.indexOf("-"));
}

public String getFieldsForRequestTweetFieldsScope(REQUEST_TWEET_FIELDS_SCOPE requestTweetFieldsScope) {
switch (requestTweetFieldsScope) {
case NON_PUBLIC:
return ALL_TWEET_FIELDS_NON_PUBLIC;
case NON_PUBLIC_PROMOTED:
return ALL_TWEET_FIELDS_NON_PUBLIC_PROMOTED;
case PUBLIC:
return ALL_TWEET_FIELDS_PUBLIC;
default:
throw new RuntimeException("REQUEST_TWEET_FIELDS_SCOPE " + requestTweetFieldsScope + " not implemented.");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ public class Attachments {

@JsonProperty("media_keys")
private String[] mediaKeys;
@JsonProperty("poll_ids")
private String[] pollIds;

}
Loading