package org.getlantern.firetweet.extension.twitlonger; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.util.ArrayList; /** * A helper class for using the Twitlonger API. * * @author Aidan Follestad */ public class TwitLonger { private final String app_name, api_key; private static final String TAG_TWITLONGER = "twitlonger"; private static final String TAG_ERROR = "error"; private static final String TAG_POST = "post"; private static final String TAG_ID = "id"; private static final String TAG_LINK = "link"; private static final String TAG_SHORT = "short"; private static final String TAG_CONTENT = "content"; private static final String TAG_USER = "user"; private static final String TWITLONGER_API_POST = "http://www.twitlonger.com/api_post"; private static final String TWITLONGER_API_CALLBACK = "http://www.twitlonger.com/api_set_id"; private static final String TWITLONGER_API_READ = "http://www.twitlonger.com/api_read/"; public TwitLonger(final String app_name, final String api_key) { this.app_name = app_name; this.api_key = api_key; } /** * Performs the Twitlonger callback, should be done after successfully using * the 'post' method. * * @param status_id The ID of the tweet posted directly to Twitter by you * using the results of 'post'. * @param twitlonger_message_id The ID of shortened content * @return True if successful. * @throws Exception */ public void callback(final long status_id, final String twitlonger_message_id) throws Exception { final ArrayList<NameValuePair> args = new ArrayList<>(2); args.add(new BasicNameValuePair("application", app_name)); args.add(new BasicNameValuePair("api_key", api_key)); args.add(new BasicNameValuePair("message_id", twitlonger_message_id)); args.add(new BasicNameValuePair("twitter_id ", Long.toString(status_id))); try { final HttpClient httpclient = new DefaultHttpClient(); final HttpPost httppost = new HttpPost(TWITLONGER_API_CALLBACK); httppost.setEntity(new UrlEncodedFormEntity(args, "UTF-8")); final HttpResponse response = httpclient.execute(httppost); final String responseStr = EntityUtils.toString(response.getEntity(), "UTF-8"); final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); final XmlPullParser parser = factory.newPullParser(); parser.setInput(new StringReader(responseStr)); int eventType = parser.getEventType(); String tagName; boolean lookingForEndOfUnknownTag = false; String unknownTagName = null; // This loop will skip to the result start tag do { if (eventType == XmlPullParser.START_TAG) { tagName = parser.getName(); if (TAG_TWITLONGER.equals(tagName)) { // Go to next tag eventType = parser.next(); break; } throw new RuntimeException("Expecting " + TAG_TWITLONGER + " , got " + tagName); } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); boolean endOfDocument = false; while (!endOfDocument) { switch (eventType) { case XmlPullParser.START_TAG: { if (lookingForEndOfUnknownTag) { break; } tagName = parser.getName(); if (TAG_POST.equalsIgnoreCase(tagName)) { } else if (TAG_ERROR.equalsIgnoreCase(tagName)) throw new TwitLongerException("Server returned error response: " + parser.nextText()); else { lookingForEndOfUnknownTag = true; unknownTagName = tagName; } break; } case XmlPullParser.END_TAG: { tagName = parser.getName(); if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) { lookingForEndOfUnknownTag = false; unknownTagName = null; } break; } case XmlPullParser.END_DOCUMENT: { endOfDocument = true; } } eventType = parser.next(); } } catch (final IOException e) { throw new TwitLongerException(e); } catch (final XmlPullParserException e) { throw new TwitLongerException(e); } } /** * Makes a post to Twitlonger, returning the text that should be posted in a * tweet on Twitter. * * @param status The full content of the tweet, should be over 140 * characters since you're using Twitlonger. * @param in_reply_to_status_id The optional ID of the tweet this post is in * reply to. * @param in_reply_to_screen_name The optional username of the user that * posted the tweet that this post is in reply to. * @return The text that should be posted on Twitter. * @throws Exception */ public TwitLongerResponse post(final String status, final String user_name, final long in_reply_to_status_id, final String in_reply_to_screen_name) throws TwitLongerException { final ArrayList<NameValuePair> args = new ArrayList<>(); args.add(new BasicNameValuePair("application", app_name)); args.add(new BasicNameValuePair("api_key", api_key)); args.add(new BasicNameValuePair("username", user_name)); args.add(new BasicNameValuePair("message", status)); if (in_reply_to_status_id > 0) { args.add(new BasicNameValuePair("in_reply", Long.toString(in_reply_to_status_id))); if (in_reply_to_screen_name != null && in_reply_to_screen_name.trim().length() != 0) { args.add(new BasicNameValuePair("in_reply_user", in_reply_to_screen_name)); } } try { final HttpClient httpclient = new DefaultHttpClient(); final HttpPost httppost = new HttpPost(TWITLONGER_API_POST); httppost.setEntity(new UrlEncodedFormEntity(args, HTTP.UTF_8)); final HttpResponse response = httpclient.execute(httppost); final StatusLine statusLine = response.getStatusLine(); final int statusCode = statusLine.getStatusCode(); if (statusCode >= 200 && statusCode <= 202) return parseTwitLongerResponse(response.getEntity().getContent()); throw new TwitLongerException(statusLine.getReasonPhrase(), statusCode); } catch (final IOException e) { throw new TwitLongerException(e); } } /** * Retrieves the full expanded content of a tweet longer post. * * @param id The ID (at the end of a shortened URL such as tl.gd/id). * @return The full expanded content. * @throws Exception */ public TwitLongerResponse readPost(final String id) throws TwitLongerException { try { final HttpClient httpclient = new DefaultHttpClient(); final HttpGet httpget = new HttpGet(TWITLONGER_API_READ + id); final HttpResponse response = httpclient.execute(httpget); return parseTwitLongerResponse(response.getEntity().getContent()); } catch (final IOException e) { throw new TwitLongerException(e); } } private TwitLongerResponse parseTwitLongerResponse(final InputStream response) throws TwitLongerException { try { final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); final XmlPullParser parser = factory.newPullParser(); parser.setInput(new InputStreamReader(response)); int eventType = parser.getEventType(); String tagName; boolean lookingForEndOfUnknownTag = false; String unknownTagName = null; // This loop will skip to the result start tag do { if (eventType == XmlPullParser.START_TAG) { tagName = parser.getName(); if (TAG_TWITLONGER.equals(tagName)) { // Go to next tag eventType = parser.next(); break; } throw new RuntimeException("Expecting " + TAG_TWITLONGER + " , got " + tagName); } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); boolean endOfDocument = false; String id = null, link = null, short_link = null, content = null, user = null; while (!endOfDocument) { switch (eventType) { case XmlPullParser.START_TAG: { if (lookingForEndOfUnknownTag) { break; } tagName = parser.getName(); if (TAG_POST.equalsIgnoreCase(tagName)) { } else if (TAG_ERROR.equalsIgnoreCase(tagName)) throw new TwitLongerException("Server returned error response: " + parser.nextText()); else if (TAG_ID.equalsIgnoreCase(tagName)) { id = parser.nextText(); } else if (TAG_LINK.equalsIgnoreCase(tagName)) { link = parser.nextText(); } else if (TAG_SHORT.equalsIgnoreCase(tagName)) { short_link = parser.nextText(); } else if (TAG_CONTENT.equalsIgnoreCase(tagName)) { content = parser.nextText(); } else if (TAG_USER.equalsIgnoreCase(tagName)) { user = parser.nextText(); } else { lookingForEndOfUnknownTag = true; unknownTagName = tagName; } break; } case XmlPullParser.END_TAG: { tagName = parser.getName(); if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) { lookingForEndOfUnknownTag = false; unknownTagName = null; } break; } case XmlPullParser.END_DOCUMENT: { endOfDocument = true; if (id == null || content == null) throw new TwitLongerException("Server returned unknown reponse."); } } eventType = parser.next(); } return new TwitLongerResponse(id, content, link, short_link, user); } catch (final IOException | XmlPullParserException e) { throw new TwitLongerException(e); } } public static class TwitLongerException extends Exception { private static final long serialVersionUID = 1020016463204888157L; private final int errorCode; public TwitLongerException() { this(null, null, -1); } public TwitLongerException(final String detailMessage) { this(detailMessage, null, -1); } public TwitLongerException(final String message, final int errorCode) { this(message, null, errorCode); } public TwitLongerException(final String detailMessage, final Throwable throwable, final int errorCode) { super(detailMessage, throwable); this.errorCode = errorCode; } public TwitLongerException(final Throwable throwable) { this(null, throwable, -1); } public int getErrorCode() { return errorCode; } } public static class TwitLongerResponse { public final String id, content, link, short_link, user; private TwitLongerResponse(final String id, final String content, final String link, final String short_link, final String user) { this.id = id; this.content = content; this.link = link; this.short_link = short_link; this.user = user; } } }