twitter4jads.internal.models4j.TwitterImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2007 Yusuke Yamamoto
* Copyright (C) 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package twitter4jads.internal.models4j;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import twitter4jads.auth.Authorization;
import twitter4jads.conf.Configuration;
import twitter4jads.internal.http.HttpParameter;
import twitter4jads.internal.http.HttpResponse;
import twitter4jads.internal.json.TwitterUploadMediaResponseImpl;
import twitter4jads.internal.util.z_T4JInternalStringUtil;
import twitter4jads.util.TwitterAdUtil;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* A java representation of the Twitter REST API
* This class is thread safe and can be cached/re-used and used concurrently.
* Currently this class is not carefully designed to be extended. It is suggested to extend this class only for mock testing purpose.
*
* @author Yusuke Yamamoto - yusuke at mac.com
*/
public class TwitterImpl extends TwitterBaseImpl implements Twitter {
private static final long serialVersionUID = -1486360080128882436L;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
//TODO: move these to a constant file along with other hardcoded strings
public static final String COMMAND = "command";
public static final String MEDIA_ID = "media_id";
public static final String SEGMENT_INDEX = "segment_index";
public static final String MEDIA = "media";
public static final String MEDIA_TYPE = "media_type";
public static final String MEDIA_CATEGORY = "media_category";
public static final String PARAM_ADDITIONAL_OWNERS = "additional_owners";
public static final String TOTAL_BYTES = "total_bytes";
public static final String INIT = "INIT";
public static final String APPEND = "APPEND";
public static final String FINALIZE = "FINALIZE";
public static final String STATUS = "STATUS";
private final String IMPLICIT_PARAMS_STR;
private final HttpParameter[] IMPLICIT_PARAMS;
private final HttpParameter INCLUDE_MY_RETWEET;
private final HttpParameter TWEET_MODE;
long WAIT_INTERVAL_TRANSCODING = TimeUnit.SECONDS.toMillis(5);
private static final Map implicitParamsMap = new ConcurrentHashMap<>();
private static final Map implicitParamsStrMap = new ConcurrentHashMap<>();
/*package*/
public TwitterImpl(Configuration conf, Authorization auth) {
super(conf, auth);
INCLUDE_MY_RETWEET = new HttpParameter("include_my_retweet", conf.isIncludeMyRetweetEnabled());
TWEET_MODE = new HttpParameter("tweet_mode", conf.getTweetMode());
HttpParameter[] implicitParams = implicitParamsMap.get(conf);
String implicitParamsStr = implicitParamsStrMap.get(conf);
if (implicitParams == null) {
String includeEntities = conf.isIncludeEntitiesEnabled() ? "1" : "0";
String includeRTs = conf.isIncludeRTsEnabled() ? "1" : "0";
boolean contributorsEnabled = conf.getContributingTo() != -1L;
implicitParamsStr = "include_entities=" + includeEntities + "&include_rts=" + includeRTs +
(contributorsEnabled ? "&contributingto=" + conf.getContributingTo() : "");
implicitParamsStrMap.put(conf, implicitParamsStr);
List params = new ArrayList();
params.add(new HttpParameter("include_entities", includeEntities));
params.add(new HttpParameter("include_rts", includeRTs));
if (contributorsEnabled) {
params.add(new HttpParameter("contributingto", conf.getContributingTo()));
}
implicitParams = params.toArray(new HttpParameter[params.size()]);
implicitParamsMap.put(conf, implicitParams);
}
IMPLICIT_PARAMS = implicitParams;
IMPLICIT_PARAMS_STR = implicitParamsStr;
}
/**
* {@inheritDoc}
*/
public ResponseList getRetweetsOfMe() throws TwitterException {
return factory.createStatusList(get(conf.getRestBaseURL() + "statuses/retweets_of_me.json", new HttpParameter[]{TWEET_MODE}));
}
/**
* {@inheritDoc}
*/
public ResponseList getRetweetsOfMe(Paging paging) throws TwitterException {
return factory.createStatusList(get(conf.getRestBaseURL() + "statuses/retweets_of_me.json",
mergeParameters(paging.asPostParameterArray(), new HttpParameter[]{TWEET_MODE})));
}
public ResponseList getScopedTimeLine(long userId, Paging paging) throws TwitterException {
return factory.createStatusList(get(conf.getRestBaseURL() + "statuses/scoped_timeline.json", mergeParameters(
mergeParameters(new HttpParameter[]{new HttpParameter("user_id", userId)}, paging.asPostParameterArray()),
new HttpParameter[]{TWEET_MODE})));
}
/* Tweets Resources */
/**
* {@inheritDoc}
*/
@Override
public ResponseList getRetweets(long statusId) throws TwitterException {
return factory
.createStatusList(get(conf.getRestBaseURL() + "statuses/retweets/" + statusId + ".json?count=100", new HttpParameter[]{TWEET_MODE}));
}
/**
* {@inheritDoc}
*/
@Override
public Status showStatus(long id) throws TwitterException {
return factory.createStatus(get(conf.getRestBaseURL() + "statuses/show/" + id + ".json",
mergeParameters(new HttpParameter[]{INCLUDE_MY_RETWEET}, new HttpParameter[]{TWEET_MODE})));
}
/**
* {@inheritDoc}
*/
@Override
public Status destroyStatus(long statusId) throws TwitterException {
return factory.createStatus(post(conf.getRestBaseURL() + "statuses/destroy/" + statusId + ".json", new HttpParameter[]{TWEET_MODE}));
}
/**
* {@inheritDoc}
*/
@Override
public Status updateStatus(String status) throws TwitterException {
return factory.createStatus(post(conf.getRestBaseURL() + "statuses/update.json",
mergeParameters(new HttpParameter[]{new HttpParameter("status", status)}, new HttpParameter[]{TWEET_MODE})));
}
/**
* {@inheritDoc}
*/
@Override
public Status updateStatus(StatusUpdate status) throws TwitterException {
String url = conf.getRestBaseURL() + (status.isWithMedia() ? "statuses/update_with_media.json" : "statuses/update.json");
return factory.createStatus(post(url, mergeParameters(status.asHttpParameterArray(), new HttpParameter[]{TWEET_MODE})));
}
/**
* {@inheritDoc}
*/
@Override
public Status retweetStatus(long statusId) throws TwitterException {
return factory.createStatus(post(conf.getRestBaseURL() + "statuses/retweet/" + statusId + ".json", new HttpParameter[]{TWEET_MODE}));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList lookupStatuses(long[] statusIds) throws TwitterException {
if (statusIds == null || statusIds.length > 100) {
throw new TwitterException("Illegal argument: Status Ids cannot be null, and maximum upto 100 tweets per request");
}
return factory.createStatusList(
get(conf.getRestBaseURL() + "statuses/lookup.json?id=" + z_T4JInternalStringUtil.join(statusIds), new HttpParameter[]{TWEET_MODE}));
}
@Override
public String uploadMediaInChunks(String filePath, long fileSize, String mediaType, String mediaCategory, int maxChunkSize,
long maxWaitTimeTranscoding, String accountUserId) throws TwitterException {
String mediaId = initializeChunkUpload(fileSize, mediaType, mediaCategory, accountUserId);
uploadVideoInChunks(mediaId, filePath, fileSize, maxChunkSize);
finalizeChunkUpload(mediaId);
//only when specifying media category, the process is made async otherwise no need to wait.
if (StringUtils.isNotBlank(mediaCategory)) {
waitForVideoTranscoding(mediaId, maxWaitTimeTranscoding);
}
return mediaId;
}
/**
* {@inheritDoc}
*/
@Override
public AccountSettings getAccountSettings() throws TwitterException {
return factory.createAccountSettings(get(conf.getRestBaseURL() + "account/settings.json"));
}
/**
* {@inheritDoc}
*/
@Override
public User verifyCredentials() throws TwitterException {
return super.fillInIDAndScreenName();
}
/**
* {@inheritDoc}
*/
@Override
public AccountSettings updateAccountSettings(Integer trend_locationWoeid, Boolean sleep_timeEnabled, String start_sleepTime, String end_sleepTime,
String time_zone, String lang) throws TwitterException {
List profile = new ArrayList(6);
if (trend_locationWoeid != null) {
profile.add(new HttpParameter("trend_location_woeid", trend_locationWoeid));
}
if (sleep_timeEnabled != null) {
profile.add(new HttpParameter("sleep_time_enabled", sleep_timeEnabled.toString()));
}
if (start_sleepTime != null) {
profile.add(new HttpParameter("start_sleep_time", start_sleepTime));
}
if (end_sleepTime != null) {
profile.add(new HttpParameter("end_sleep_time", end_sleepTime));
}
if (time_zone != null) {
profile.add(new HttpParameter("time_zone", time_zone));
}
if (lang != null) {
profile.add(new HttpParameter("lang", lang));
}
return factory
.createAccountSettings(post(conf.getRestBaseURL() + "account/settings.json", profile.toArray(new HttpParameter[profile.size()])));
}
/**
* {@inheritDoc}
*/
@Override
public User updateProfile(String name, String url, String location, String description) throws TwitterException {
List profile = new ArrayList(4);
addParameterToList(profile, "name", name);
addParameterToList(profile, "url", url);
addParameterToList(profile, "location", location);
addParameterToList(profile, "description", description);
return factory.createUser(post(conf.getRestBaseURL() + "account/update_profile.json", profile.toArray(new HttpParameter[profile.size()])));
}
/**
* {@inheritDoc}
*/
@Override
public User updateProfileBackgroundImage(File image, boolean tile) throws TwitterException {
checkFileValidity(image);
return factory.createUser(post(conf.getRestBaseURL() + "account/update_profile_background_image.json",
new HttpParameter[]{new HttpParameter("image", image), new HttpParameter("tile", tile)}));
}
/**
* {@inheritDoc}
*/
@Override
public User updateProfileBackgroundImage(InputStream image, boolean tile) throws TwitterException {
return factory.createUser(post(conf.getRestBaseURL() + "account/update_profile_background_image.json",
new HttpParameter[]{new HttpParameter("image", "image", image), new HttpParameter("tile", tile)}));
}
/**
* {@inheritDoc}
*/
@Override
public User updateProfileColors(String profileBackgroundColor, String profileTextColor, String profileLinkColor, String profileSidebarFillColor,
String profileSidebarBorderColor) throws TwitterException {
List colors = new ArrayList(6);
addParameterToList(colors, "profile_background_color", profileBackgroundColor);
addParameterToList(colors, "profile_text_color", profileTextColor);
addParameterToList(colors, "profile_link_color", profileLinkColor);
addParameterToList(colors, "profile_sidebar_fill_color", profileSidebarFillColor);
addParameterToList(colors, "profile_sidebar_border_color", profileSidebarBorderColor);
return factory
.createUser(post(conf.getRestBaseURL() + "account/update_profile_colors.json", colors.toArray(new HttpParameter[colors.size()])));
}
private void addParameterToList(List colors, String paramName, String color) {
if (color != null) {
colors.add(new HttpParameter(paramName, color));
}
}
/**
* {@inheritDoc}
*/
@Override
public User updateProfileImage(File image) throws TwitterException {
checkFileValidity(image);
return factory.createUser(
post(conf.getRestBaseURL() + "account/update_profile_image.json", new HttpParameter[]{new HttpParameter("image", image)}));
}
/**
* {@inheritDoc}
*/
@Override
public User updateProfileImage(InputStream image) throws TwitterException {
return factory.createUser(
post(conf.getRestBaseURL() + "account/update_profile_image.json", new HttpParameter[]{new HttpParameter("image", "image", image)}));
}
/**
* Check the existence, and the type of the specified file.
*
* @param image image to be uploaded
* @throws TwitterException when the specified file is not found (FileNotFoundException will be nested)
* , or when the specified file object is not representing a file(IOException will be nested).
*/
private void checkFileValidity(File image) throws TwitterException {
if (!image.exists()) {
//noinspection ThrowableInstanceNeverThrown
throw new TwitterException(new FileNotFoundException(image + " is not found."));
}
if (!image.isFile()) {
//noinspection ThrowableInstanceNeverThrown
throw new TwitterException(new IOException(image + " is not a file."));
}
}
/**
* {@inheritDoc}
*/
@Override
public PagableResponseList getBlocksList() throws TwitterException {
return getBlocksList(-1L);
}
/**
* {@inheritDoc}
*/
@Override
public PagableResponseList getBlocksList(long cursor) throws TwitterException {
return factory.createPagableUserList(get(conf.getRestBaseURL() + "blocks/list.json?cursor=" + cursor));
}
/**
* {@inheritDoc}
*/
@Override
public User createBlock(long userId) throws TwitterException {
return factory.createUser(post(conf.getRestBaseURL() + "blocks/create.json?user_id=" + userId));
}
/**
* {@inheritDoc}
*/
@Override
public User createBlock(String screenName) throws TwitterException {
return factory.createUser(post(conf.getRestBaseURL() + "blocks/create.json?screen_name=" + screenName));
}
/**
* {@inheritDoc}
*/
@Override
public User destroyBlock(long userId) throws TwitterException {
return factory.createUser(post(conf.getRestBaseURL() + "blocks/destroy.json?user_id=" + userId));
}
/**
* {@inheritDoc}
*/
@Override
public User destroyBlock(String screen_name) throws TwitterException {
return factory.createUser(post(conf.getRestBaseURL() + "blocks/destroy.json?screen_name=" + screen_name));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList lookupUsers(long[] ids) throws TwitterException {
return factory.createUserList(get(conf.getRestBaseURL() + "users/lookup.json",
new HttpParameter[]{new HttpParameter("user_id", z_T4JInternalStringUtil.join(ids))}));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList lookupUsers(String[] screenNames) throws TwitterException {
return factory.createUserList(get(conf.getRestBaseURL() + "users/lookup.json",
new HttpParameter[]{new HttpParameter("screen_name", z_T4JInternalStringUtil.join(screenNames))}));
}
/**
* {@inheritDoc}
*/
@Override
public User showUser(long userId) throws TwitterException {
return factory.createUser(get(conf.getRestBaseURL() + "users/show.json?user_id=" + userId));
}
/**
* {@inheritDoc}
*/
@Override
public User showUser(String screenName) throws TwitterException {
return factory.createUser(get(conf.getRestBaseURL() + "users/show.json?screen_name=" + screenName));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList searchUsers(String query, int page) throws TwitterException {
return factory.createUserList(get(conf.getRestBaseURL() + "users/search.json",
new HttpParameter[]{new HttpParameter("q", query), new HttpParameter("per_page", 20),
new HttpParameter("page", page)}));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList getContributees(long userId) throws TwitterException {
return factory.createUserList(get(conf.getRestBaseURL() + "users/contributees.json?user_id=" + userId));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList getContributees(String screenName) throws TwitterException {
return factory.createUserList(get(conf.getRestBaseURL() + "users/contributees.json?screen_name=" + screenName));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList getContributors(long userId) throws TwitterException {
return factory.createUserList(get(conf.getRestBaseURL() + "users/contributors.json?user_id=" + userId));
}
/**
* {@inheritDoc}
*/
@Override
public ResponseList getContributors(String screenName) throws TwitterException {
return factory.createUserList(get(conf.getRestBaseURL() + "users/contributors.json?screen_name=" + screenName));
}
/**
* {@inheritDoc}
*/
@Override
public void removeProfileBanner() throws TwitterException {
post(conf.getRestBaseURL() + "account/remove_profile_banner.json");
}
/**
* {@inheritDoc}
*/
@Override
public void updateProfileBanner(File image) throws TwitterException {
checkFileValidity(image);
post(conf.getRestBaseURL() + "account/update_profile_banner.json", new HttpParameter[]{new HttpParameter("banner", image)});
}
/**
* {@inheritDoc}
*/
@Override
public void updateProfileBanner(InputStream image) throws TwitterException {
post(conf.getRestBaseURL() + "account/update_profile_banner.json", new HttpParameter[]{new HttpParameter("banner", "banner", image)});
}
@Override
public RelatedResults getRelatedResults(long statusId) throws TwitterException {
return factory.createRelatedResults(get("https://api.twitter.com/1/related_results/show.json?id=" + Long.toString(statusId)));
}
/**
* upload status :- [pending] -> [in_progress] -> [succeeded | failed]
*
* @see Upload Status
*/
protected void waitForVideoTranscoding(String mediaId, long maxWaitTime) throws TwitterException {
long totalWaitTime = 0;
String url = getMediaUploadUrl();
HttpParameter[] parameters = createChunkedUploadStatusParams(mediaId);
while (totalWaitTime < maxWaitTime) {
TwitterAdUtil.reallySleep(WAIT_INTERVAL_TRANSCODING);
totalWaitTime += WAIT_INTERVAL_TRANSCODING;
Media media = checkVideoUploadStatus(url, parameters);
if (media.isStatePending()) {
continue;
}
if (media.isStateSucceeded()) {
return;
} else if (!media.isStateInProgress()) {
throw new TwitterException("Video transcoding error. Error code: " + media.getState());
}
}
}
private HttpParameter[] createChunkedUploadStatusParams(String mediaId) {
HttpParameter[] parameters = new HttpParameter[2];
parameters[0] = new HttpParameter(COMMAND, STATUS);
parameters[1] = new HttpParameter(MEDIA_ID, mediaId);
return parameters;
}
private void uploadVideoInChunks(String mediaId, String filePath, Long fileSize, int chunkSize) throws TwitterException {
try (InputStream inputStream = new FileInputStream(new File(filePath))) {
long totalBytesRead = 0l;
for (int segmentIndex = 0; totalBytesRead < fileSize; segmentIndex++) {
chunkSize = chunkSize < (int) (fileSize - totalBytesRead) ? chunkSize : (int) (fileSize - totalBytesRead);
byte[] chunk = new byte[chunkSize];
int bytesRead = inputStream.read(chunk);
if (bytesRead == -1) {
throw new IOException("EOF reached..");
}
post(getMediaUploadUrl(), createAppendParams(mediaId, segmentIndex, chunk));
totalBytesRead += bytesRead;
}
} catch (Exception e) {
throw new TwitterException(e);
}
}
private HttpParameter[] createAppendParams(String mediaId, int segmentIndex, byte[] chunk) {
HttpParameter[] parameters = new HttpParameter[4];
parameters[0] = new HttpParameter(COMMAND, APPEND);
parameters[1] = new HttpParameter(MEDIA_ID, mediaId);
parameters[2] = new HttpParameter(SEGMENT_INDEX, segmentIndex);
parameters[3] = new HttpParameter(MEDIA, "", new ByteArrayInputStream(chunk));
return parameters;
}
private String initializeChunkUpload(long fileSizeInBytes, String mediaType, String mediaCategory, String accountUserId) throws TwitterException {
List parameters = new ArrayList<>();
parameters.add(new HttpParameter(COMMAND, INIT));
parameters.add(new HttpParameter(MEDIA_TYPE, mediaType));
if (StringUtils.isNotBlank(mediaCategory)) {
parameters.add(new HttpParameter(MEDIA_CATEGORY, mediaCategory));
}
parameters.add(new HttpParameter(PARAM_ADDITIONAL_OWNERS, accountUserId));
parameters.add(new HttpParameter(TOTAL_BYTES, fileSizeInBytes));
HttpResponse response = post(getMediaUploadUrl(), parameters.toArray(new HttpParameter[parameters.size()]));
return new TwitterUploadMediaResponseImpl(response, conf).getMediaIdString();
}
private void finalizeChunkUpload(String mediaId) throws TwitterException {
List parameters = new ArrayList<>();
parameters.add(new HttpParameter(COMMAND, FINALIZE));
parameters.add(new HttpParameter(MEDIA_ID, mediaId));
post(getMediaUploadUrl(), parameters.toArray(new HttpParameter[parameters.size()]));
}
private String getMediaUploadUrl() {return conf.getMediaUploadBaseUrl() + "media/upload.json";}
@Override
String getImplicitParamsStr() {
return IMPLICIT_PARAMS_STR;
}
@Override
HttpParameter[] getImplicitParams() {
return IMPLICIT_PARAMS;
}
@Override
public String toString() {
return "TwitterImpl{" +
"INCLUDE_MY_RETWEET=" + INCLUDE_MY_RETWEET +
'}';
}
}