Come on, man! What used to a be a simple task to send a tweet is now a OAuth nightmare.
A year or so ago, I built that
tweet-a-w/e thing that sniffed XBee chirps and sent them to a twitter account that kindly routed them to my cell phone.
I have a need now to receive an XML stream, parse out a few tidbits and then send the results out as a SMS text message. Remembering that tweet-a-w/e app, I thought that I would again leverage Twitter to send the text message. All I need to do is send a tweet via a Twitter API and have my account set up to send the content to a phone number.
This time my XML receiver is in Java/Groovy, so I grabbed the latest Twitter4J, glanced at the UpdateStatus example and tried a quick test with Groovy.
import twitter4j.Twitter
import twitter4j.TwitterFactory
import twitter4j.Status
Twitter twitter = new TwitterFactory().getInstance("myAcct","myPassword");
Status status = twitter.updateStatus("from Groovy");
System.out.println("Successfully updated the status to [" + status.getText() + "].");
Did that work? Of course not. Authentication FAIL.
Caught: 401:Authentication credentials were missing or incorrect.
{"errors":[{"code":53,"message":"Basic authentication is not supported"}]}
TwitterException{exceptionCode=[15bb6564-00e5bee0], statusCode=401, retryAfter=0, rateLimitStatus=null, version=2.1.7}
What the heck? Basic Authentication is not supported? Darn it, what is this stupid OAuth thing about? Here's the Twitter page "
Authenticating Requests with OAuth" that looks real interesting to read. Arghhhhh. I just want to send a flippin' tweet, I really don't have time to research this at
Hueniverse.
After some more quick Googling, this isn't as bad as it first looks.It boils down to getting a key and secret pair. The steps are:
- Goto Twitter and register your app.
- You'll get a consumer key and secret which are similar to the public and private keys used in protocols such as ssh.
- Use the key and secret to sign every request you make to the Twitter API
import twitter4j.Twitter
import twitter4j.Twitter
import twitter4j.TwitterFactory
import twitter4j.Status
import twitter4j.http.AccessToken
import twitter4j.http.RequestToken
import java.io.BufferedReader
try{
Twitter twitter = new TwitterFactory().getInstance();
// set key and secret that you get from Twitter app registeration at:
// http://dev.twitter.com/pages/auth#register
twitter.setOAuthConsumer("Your Consumer key", "Your Consumer secret");
// get the URL to request access to Twitter acct
RequestToken requestToken = twitter.getOAuthRequestToken();
String authUrl = requestToken.getAuthorizationURL()
System.out.println("Open the following URL and grant access to your account:");
System.out.println(authUrl);
// take the PIN and get access token
System.out.print("Enter the PIN:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String pin = ""
pin = br.readLine();
AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, pin);
String message = "from Groovy w/ pin" + pin
Status status = twitter.updateStatus(message);
System.out.println("Successfully sent " + message);
} catch (Exception e) {
e.printStackTrace();
}
Ok, so that works. But every time you run the program, you need to go and fetch the darn token. According to Twitter, the token doesn't expire, so let's persist the thing.
I like using
XStream to serialize Java/Groovy objects to/from XML. A quick update to the code above lets us save an XML file that we can reload as needed.
import twitter4j.Twitter
import twitter4j.TwitterFactory
import twitter4j.Status
import twitter4j.http.AccessToken
import twitter4j.http.RequestToken
import java.io.BufferedReader
import com.thoughtworks.xstream.XStream
try{
Twitter twitter = new TwitterFactory().getInstance();
// set key and secret that you get from Twitter app registeration at:
// http://dev.twitter.com/pages/auth#register
twitter.setOAuthConsumer("Your Consumer key", "Your Consumer secret");
// load access token if it exists
AccessToken accessToken = null
def tokenFile = new File("accessToken.xml")
if (tokenFile.exists()) {
def xstream = new XStream()
tokenFile.withInputStream { ins -> accessToken = xstream.fromXML(ins) }
twitter.setOAuthAccessToken(accessToken)
}
else {
// get the URL to request access to Twitter acct
RequestToken requestToken = twitter.getOAuthRequestToken();
String authUrl = requestToken.getAuthorizationURL()
System.out.println("Open the following URL and grant access to your account:");
System.out.println(authUrl);
// take the PIN and get access token
System.out.print("Enter the PIN:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String pin = ""
pin = br.readLine();
accessToken = twitter.getOAuthAccessToken(requestToken, pin);
// persist token
def xstream = new XStream()
xstream.classLoader = getClass().classLoader
new File("accessToken.xml").withOutputStream { out -> xstream.toXML(accessToken, out) }
}
String message = "from Groovy w/ token" + accessToken.getToken()
Status status = twitter.updateStatus(message);
System.out.println("Successfully sent " + message);
} catch (Exception e) {
e.printStackTrace();
}
The serialized Access Token looks like this:
5555555-AbCdEF
1oKiOlOlOlOl
oauth_token=5555555-AbCdEF
oauth_token_secret=1oKiOlOlOlOl
user_id=007
screen_name=bond
bond
007
Notes:
For the above examples, I used:
- Groovy Version: 1.7.0 JVM: 1.6.0_22
- Twitter4J 2.1.7 For this, I copied only the twitter4j-core-2.1.7.jar into the Groovy lib folder. On Linux, this is /usr/share/Groovy/lib
- XStream 1.3.1 For this, I copied the xstream-1.3.1.jar and xpp3_min-1.1.4c.jar into the Groovy lib
What do you think? Leave a comment.