Posted in ColdFusion | Posted on 09-07-2010 | 5,355 views
So, a few weeks ago Twitter replaced Basic Auth for OAuth in their APIs. I was aware of this of course, but it never occurred to me to worry about it as I don't actually work on a Twitter client myself. However, I forgot that one of my sites, CFBloggers.org, makes use of the Twitter API to tweet new blog entries it aggregates. When I tweeted about this today I got a lot of recommendations, but for the most part, the advice, and the docs, focused on applications for humans. By that I mean, the assumption was that your Twitter automation was a tool that random users were using. The docs would explain how you could easily direct them to Twitter to authorize your application and how they would be sent back. That's great, but what about the case for Twitter bots? Nothing out there seemed to address that need. Luckily though I got some great help. In this blog post I'll explain exactly how I updated CFBloggers to post to Twitter. While this is a ColdFusion specific post it really could apply to anyone doing Twitter bots. Credit for this goes to Todd Rafferty, Vic Carter, and Rob O'Brien (and specifically his blog post here: Integrating Twitter and OAuth with ColdFusion)
Ok, so before I get into the exact steps, let me summarize what we are going to do here. We are going to switch to a Java based Twitter library that makes use of OAuth-wrapped calls to Twitter. That handles the sending of tweets. To handle "allow my web site to do this" we are going to use a temporary script. The temporary script is a one time process that we will use to get our tokens that are then fed to the Twitter client. Once we have that we should be gold.
Edited September 16: Reader Angela Haralson pointed out (see comments below) a great time saver. The creation of the temporary script to get AccessToken and Secret is not necessary. You can get those values from the Twitter web site. This makes things even easier! Please keep that in mind when reading below. Basically you can focus more on the Java library and your setup at Twitter.com.
1) The first step is to get the Java library. At this time there are no ColdFusion Twitter clients that make use of OAuth, but if anyone knows of one, or creates one after this entry is released, please post it below. The Java library is called Twitter4J and may be found here: http://twitter4j.org/en/index.html
2) You can put the Twitter4J jar in your ColdFusion class path, or do it the sexy way and make use of JavaLoader. I made use of JavaLoader. This is what I added to my Application.cfc:
2<cfset application.javaloader = createObject("component", "components.javaloader.JavaLoader").init(paths)>
3
4<cfset application.Twitter = application.javaloader.create("twitter4j.Twitter")>
3) This is the beginning of the one time process! We need to create an application on the Twitter web site. This is the application that represents our web site robot. Go to http://dev.twitter.com and login. You can login as your primary Twitter account or the robot's account. In the top nav click "Your Apps" and select Register a New app.
4) The application name, description, and web site are not important. However, the application name is what folks will see when you robot tweets. I picked "CFBloggersRobot" for mine. For the application web site I just used CFBloggers.org. For the organization I said Me. It's a great organization but the benefits suck. Now for two critical parts. Application Type must be Browser. The call back URL is going to be a temporary script we will make in the next step. Notice that the call back URL can be a local url, by that I mean I used dev.coldfusionbloggers.org, which is only recognized by my local machine. I used this url: http://dev.coldfusionbloggers.org/ray.cfm?mode=1. The mode=1 is also critical and will make sense one you see the script. Finally, ensure you set access type to Read and Write. Otherwise you will not be able to send tweets.
5) After you save the application you will go to a settings page. Notice that there are two values here you will need, the consumer key and the consumer secret. This will be used in step 6.
6) Ok, now we are going to create the temporary script:
2<cfset Twitter.setOAuthConsumer('cosumer key','consumer secret')>
3
4
5<cfif structKeyExists(url,'mode') IS FALSE>
6
7 <!--- // 2. Authorize --->
8 <cfset RequestToken = Twitter.getOAuthRequestToken()>
9 <cfset Session.oAuthRequestToken = RequestToken.getToken()>
10 <cfset Session.oAuthRequestTokenSecret = RequestToken.getTokenSecret()>
11 <cflocation url="#RequestToken.getAuthorizationURL()#" addtoken="No">
12
13<cfelse>
14
15 <!--- // 3. Authenticate // --->
16 <cfset AccessToken = Twitter.getOAuthAccessToken(Session.oAuthRequestToken,Session.oAuthRequestTokenSecret)>
17 <cfset session.StoredAccessToken = AccessToken.getToken()>
18 <cfset session.StoredAccessSecret = AccessToken.getTokenSecret()>
19 <cfdump var="#session#"><cfabort>
20
21</cfif>
So the script begins by getting an instance of the Twitter Java library. I have this in Application scope already but as this is a one time script I wanted to keep it simple. Notice the two strings. Replace those with the real value. Now open this baby up in your browser - and to be clear, you can do this all locally just fine.
When you run this you will get sent to the Twitter authorization page. Obviously you want to allow your application. Twitter will then send you right back to the script with mode=1 in the URL. This will trigger the dump you see. Within that dump you want to grab the values for storedaccesstoken and storedaccesssecret.
7) Return back to your application.cfc. You need to provide all 4 values to your Twitter object:
2<cfset application.javaloader = createObject("component", "components.javaloader.JavaLoader").init(paths)>
3
4<cfset application.Twitter = application.javaloader.create("twitter4j.Twitter")>
5<cfset application.Twitter.setOAuthConsumer('consumerkey',consumersecret')>
6<cfset application.Twitter.setOAuthAccessToken("storedaccesstoken" ,"storedaccesssecret")>
8) The final step is to update your code from the old way of sending Tweets to the new way. Luckily this is very trivial. I changed:
to
And that's it. Once things were explained to me the actual coding took approximately 5 minutes.


Look at this guy's blog: blog.abusalah.info
And this open source project: facebookgraph.riaforge.org
My one question is that it appears that you can send a location with your status updates by simply adding a location parameter.
<cfset Twitter.updateStatus(statusUpdate, location)>
However, I'm not sure how to create a GeoLocation from ColdFusion. Haven't worked at all with Java API's, but it appears that GeoLocation is part of the twitter4j package...just don't know how to get to it.
401:Authentication credentials were missing or incorrect. {"request":"/1/statuses/update.json","error":"Invalid / expired Token"} null
How often do you need to validate the Token?
I have the same info that you have on the app.cfc
When I call the Twitter.test() it does return OK... but any other function like getScreenName(), or updateStatus()... I get that error.
I do have my storedaccesstoken and storedaccesssecret saved on my DB... and if I try to authorize the application again I get the same token that I have on my DB.
<cfset location = createObject("java", "twitter4j.GeoLocation").init(Lat, Lon)>
<cfset Twitter.updateStatus(statusUpdate, location)>
Since my particular app is all server based and gets the lat/long from a GPS, there is no need to use any lookups. I just needed to figure out how to send it to Twitter along with the update. Worked great yesterday, but today it doesn't. I think that Twitter has disabled location tweets today, likely for a technical issue, since I don't see anybody else with locations on them either.
http://dev.twitter.com/apps/
Click on your application. The page that comes up has your Consumer Key and Consumer Secret, but if you look at the nav options to the right, there should be an option "My Access Token", that page will give you your Access Token and Access Token Secret.
Here is a twitter link that describes how to use OAuth for single users:
http://dev.twitter.com/pages/oauth_single_token
Is that right?
Thanks also for the tip on using JavaLoader!
Is it clear?
Thank you again, Angela.
For anyone else that stumbles across this - once you sign up at dev.twitter.com you'll need to grab the Consumer key and the consumer secret from the application details page then the oauth_token and oauth_token_secret from the my access tokens page.
just skip to step 7 once you get those.
<cfset application.Twitter.setOAuthConsumer('REPLACE_consumerkey','REPLACE_consumersecret')>
<cfset application.Twitter.setOAuthAccessToken("REPLACE_oauth_token" ,"REPLACE_oauth_token_secret")>
http://projects.flowingdata.com/inauguration/
It would let you to the textual analysis of the twitter text.
It tells me
java.lang.InstantiationException: twitter4j.Twitter
Any ideas? Would greatly appreciate any help you can give me with this. It seems to work for everyone else which is why it's driving me so crazy.
Here is my BUTT AS* simple App (old school style), registering the class under the old style (just cause it's gonna stay there, so why not?) instead of using the dynamic Java loader.
3 files.
Applicaction.cfm CONTENTS:::
<!--- 1. your app token and secret --->
<cfparam name="session.oAuthRequestToken" default="">
<cfparam name="session.oAuthRequestTokenSecret" default="">
<!--- 2. the user tokens --->
<cfparam name="session.userAccessToken" default="">
<cfparam name="session.userAccessSecret" default="">
index.cfm file CONTENTS::
<cfsilent>
<!--- always create the java object --->
<cfset Twitter = createObject("java", "twitter4j.Twitter")>
<cfset Twitter.setOAuthConsumer(application.twitterConsumerKey, application.twitterConsumerSecret)>
</cfsilent>
<cfif structKeyExists(url,'mode') IS FALSE>
<cfset requestToken = Twitter.getOAuthRequestToken()>
<cfset session.oAuthRequestToken = RequestToken.getToken()>
<cfset session.oAuthRequestTokenSecret = RequestToken.getTokenSecret()>
<cflocation url="#RequestToken.getAuthorizationURL()#" addtoken="No">
<cfelse>
<cfset accessToken = Twitter.getOAuthAccessToken(session.oAuthRequestToken,session.oAuthRequestTokenSecret)>
<cfset session.userAccessToken = accessToken.getToken()>
<cfset session.userAccessSecret = accessToken.getTokenSecret()>
</cfif>
demo.cfm CONTENTS::
<cfset Twitter = createObject("java", "twitter4j.Twitter")>
<cfset Twitter.setOAuthConsumer(#application.twitterConsumerKey#, #application.twitterConsumerSecret#)>
<cfset Twitter.setOAuthAccessToken(#session.StoredAccessToken# ,#session.StoredAccessSecret#)>
<cfset Twitter.updateStatus("Test: thank you to Rob OBrien and Ray Camden and Twitter4J!")>
<cfset application.twitterApiKey="abcdefg">
<cfset application.twitterConsumerKey="hijklmnop">
<cfset application.twitterConsumerSecret="qrstuvwxyz">
<cfset begintwitter = application.javaloader.create("twitter4j.TwitterFactory")>
<cfset Twitter = begintwitter.getinstance()>
<cfset Twitter.setOAuthConsumer('key','secret')>
I am able to properly redirect to twitter, login and send the oauth back, but for some reason I can't seem to figure out why I'm getting this:
The screen name / password combination seems to be invalid. null <br>The error occurred on line 36.
Line 36 is:
<cfset session.StoredAccessSecret = AccessToken.getTokenSecret()>
I don't know if getTokenSecret() is deprecated or not. I can't find it in the class wiki.
<cfset AccessToken = Twitter.getOAuthAccessToken(session.oAuthRequestToken,session.oAuthRequestTokenSecret)>
I went through this myself for jquerybloggers this past week and it worked fine - but I used the Java code from my zip.
I've been trying to figure this out all day and I am stumped. Firstly it seems the setOAuthConsumer method is not used anymore in the latest release of twitter4j. - it says it's VOID.
I have opened the jar file as a zip and I don't even see the method in there to call. So I was able to find version twitter4j-core-2.1.4-SNAPSHOT.jar online and I opened that and the setOAuthConsumer method isn't in there either. Am I missing something?
I get the following error:
//////////////////////////////////////////
The setOAuthConsumer method was not found.
Either there are no methods with the specified method name and argument types or the setOAuthConsumer method is overloaded with argument types that ColdFusion cannot decipher reliably. ColdFusion found 0 methods that match the provided arguments. If this is a Java object and you verified that the method exists, use the javacast function to reduce ambiguity.
///////////////////////////////////////
I have tried on CF7, CF8, and CF9. My main requirement is to get it up on CF7 as a client of mine is using that version. I initially got Matt's monkeyTweets working and it's awesome but unfortunately it breaks on CF7.
Any ideas? Thx.
https://github.com/cfjedimaster/ColdFusion-Blog-Ag...
Grab the JAR there.
Initially I was getting the same issue as Ennio above:
401:Authentication credentials were missing or incorrect.
I then dug around a little more and found out that my dev server's clock was not accurate which was causing that error. It said it was the year 2001, and the time was wrong too. Once I fixed that, I was successfully forwarded on.
http://blainegarrett.com/2009/07/14/failed-to-vali...
This is CF7 btw, antiquated, I know but the client didn't want to change.
I also had the server time issues - 26 minutes off and it would not work.
Great post Ray, and nice meeting you at NCDEVCON this weekend.
I post a very short blog entry here: http://techblog.troywebconsulting.com/2011/09/auth...
If we cont use javaloader and put the jar in the class file how does the java code change
cheers
[Add Comment] [Subscribe to Comments]