monkehTweets ColdFusion Twitter CFC Update

Following on from a change in authentication protocol at Twitter HQ, monkehTweets stopped working at the end of August.

The previous release of monkehTweets used only the Basic Authentication method, which sent the username and password through as a header in the HTTP request for all API calls. Twitter decided to completely shut down this authentication method and go purely for an OAuth-based format.

OAuth is fun… honest

OAuth is an incredibly powerful form of authentication, and theoretically one that is quite easy. Theoretically.

This is the third open-source project of mine that uses OAuth authentication (the other two haven’t actually been released yet). Although the essential basics are the same, the implementation differs from site to site, what is needed to make the call, which parameters to send, what is returned etc etc

I have had more headaches with OAuth over the last week than I care to recall, but each and every one has been a learning experience; exposure to something else, which can only be a good thing. (remain positive :) )

Simplicity is key

The addition of the OAuth amendments to the existing monkehTweets library threw up many questions and thoughts about the structure and implementation of the library.

I knew from download stats, comments and messages that people were using the original release package, which is fantastic. As such, I didn’t want to change the actual monkehTweets.cfc service layer / facade object more than i needed to; after all, this was the file holding the methods being called by applications written by others.

I also wanted to keep the instantiation requirements to a minimum if possible; I always prefer to have an object that is quick and easy to get up and running, and I wanted to try and keep that with the revised version of monkehTweets.

Set up your application

To begin with the OAuth authentication and integration with the monkehTweets CFC, you need to create an application on http://dev.twitter.com/apps, which will generate the consumer key and consumer secret values that you need.

Three points are highlighted in the above screen shot:

  • The applicaiton type is set to browser
  • The callback URL is defined here. This can be over-written when making authorisation calls
  • The access type is set to read & write

After completing your application registration form, you will have your consumer key and consumer secret values, which you will need as the basic required attributes for the instantiation of the monkehTweets object.

You can also obtain the access token and secret values, which you can add into the init() method.

This is primarily if you are using the Twitter API for a single user account only (in this case, the account with which you signed in and created the application registration).

Instantiation of the object has changed slightly from the original release, which asked for only the username and password of the twitter account.

In the revised release, you are required to use the consumer key and secret values. You can optionally add in the access token, access secret and account username values within the init() method if you wish to bypass the OAuth authentication and access the Twitter API using the single user account.


	/* 
		If you are using this for a number of different accounts 
		(allowing numerous users to acces Twitter)
		you will need to specify only the consumerKey and consumerSecret

		If you are using this for a single account only, 
		set the oauthToken, oauthTokenSecret and your account name
		in the init() method also.
	*/
	application.objMonkehTweet = createObject('component',
        'com.coldfumonkeh.monkehTweet')
		.init(
			consumerKey			=	'< enter your consumer key >',
			consumerSecret		=	'< enter your consumer secret >',
			/*
			oauthToken			=	'< enter your oauth token  >',
			oauthTokenSecret	=	'< enter your oauth secret >',
			userAccountName		=	'< enter your twitter account name >',
			*/
			parseResults		=	true
		);
	return true;

The project download contains example code on using the OAuth implementation to request access and authorization tokens etc, and how to make a call to the API using an authorized header.

There is also a word and pdf installation document which explains which details you need to get up and running, and how to set up your application.

There is also a link to my Amazon wish list, should you feel warm, tingly and generous :)

I want it. Where can I get it?

The revised API wrapper has been updated and is available to download from the official monkehTweet.riaforge.org page.

142 Comments

Leave a Comment
  1. Hi Philip

    Thanks for your comment. I believe Twitter changed some default settings when creating / registering your application with them.

    If you don’t provide a callback URL when setting up your application the API will assume that any calls to using your API key are for a desktop application (which handles the OAuth authorisation through the out of bounds specification).

    To get around this, you’ll need to log back into the dev,twitter.com site, edit your application details and specify a callback URL. Even if you eventually use a different callback within the code, as long as you set one here the API will then expect the browser-based OAuth protocol.

    I hope that helps :)

  2. Thanks matt, I realised that about call back as your document says, pick the application and now twitter don’t give that option and linked it with call back, thanks

    now I am getting error when cflocation as authorize.cfm page expects a url.oauth_verifier var,

    what should I pass it, and also please ignore my request to you on twitter :)

    Thanks

    Philip

  3. I found the reason, actually when I was getting that 401 error I removed the use of authstruct.authurl and because of that I was getting errors,
    now put it back and it is taking me to authorisation page.

    Thanks matt, great work and utility.

    Cheers

    Philip

  4. @Matt just updated to the latest version (1.2.9) and I am getting an invalid construct error from line 241 of base.cfc:

    stuErrInfo.api_Info.request = xmlSearch(arguments.data.FileContent,’hash/request’)[1].XmlText;

    I am on CF801 and I think you have to break this over 2 lines like so:

    reqXML = xmlSearch(arguments.data.FileContent,’hash/request’);
    stuErrInfo.api_Info.request = reqXML[1].XmlText;

    It could be that the original code works on cf9+. Anyway I have applied the above patch to my copy and it seems to be working ok.

    Cheers,

    Dave

  5. Mr. Phipps is correct, that syntax is CF9-only. Just a heads up for anyone using CF8 to apply his change to fix the code. Otherwise, this works perfectly. Thank you not only for the CFC but the excellent documentation and sample code.

  6. Can someone tell me if anything’s changed since a week or so ? I’m getting ‘request accepted but cannot be understood’ errors. I’d appreciate if someone could point me to what might be happening/has happened. Code was working fine until about a week ago.

  7. Hi Matt,
    I wanted to upload image with text on twitter,

    I added one more argument to postUpdate

    media type string

    changed twitter method to https://upload.twitter.com/1/statuses/update_with_media.json
    and it was trying to upload the image with text but throwing following error:
    Cannot cast an object of type java.io.ByteArrayOutputStream to XML.

    and error is coming at coldfumonkeh/base.cfc:239
    coldfumonkeh/base.cfc:497

    could you please point me to right direction to fix the issue or by any chance you have already worked on this?
    Thanks

  8. @Philip

    The new upload_with_media method requires a lot of amendment to work, and sadly isnt just a case of adding in a new argument to accept media. The transaction to send the media files requires a heavy change to the OAuth flow as the data needs to be sent in the header of the request. I’m working on adding this in this week, and will let you know once it’s done.

  9. I get a lot of errors on lines 239-241 of base.cfc. It seems like the error handling is not working quite right. Maybe twitter has changed some of their responses? It’s hard to debug what I’m doing when I can’t get the error info back from twitter. thanks.

    1. Hi Jim

      What format are you using for the request? (json or xml)

      There are some issues with error handling for json responses, which are being addressed in the revised released to come out soon. Is it an existing method you’ve had in place that now suddenly throws errors?

  10. Matt:

    A quick note: this morning I had to make changes to the existing getStatusByID method in monkehTweet.cfc as the version there wasn’t playing well with Twitter.

    Originally, the method was building a string using the “statuses/show/” bits and when that wasn’t working I discovered that Twitter’s API docs recommended using a format version of “statuses/user_timeline”. Change that and the id attribute to user_id and got it working again.

    If you’re compiling notes for an update, that’d be one to look into. Thanks again for such a great tool!

    1. Hi Craig

      Thanks for the comment and for the update.
      I’ve just searched the docs and cant find any mention of the change required to the getStatusByID method (or the ‘statuses/show’ function, as Twitter knows it). Can you send me a link through to it at all?
      I’ve just run the getStatusByID method and am still getting accurate results back.

      Many thanks,

      Matt

  11. Hi Matt,

    Great library – thanks for sharing it.

    I’m trying to use the search function to search for a hashtag, but I can’t seem to get it to return any data.

    Here’s what I’ve tried:

    returnData = application.objMonkehTweet.search(q=URLEncodedFormat(‘coldfusion’)); // returns data

    returnData = application.objMonkehTweet.search(q=URLEncodedFormat(‘##coldfusion’)); // doesn’t return data

    Do you have an example of how searching for a hashtag is done?

    Regards,
    Andrew.

  12. Hi Andrew

    Thanks for your comment, and I’m glad that you’re getting use out of monkehTweet.

    By default, the search method URLEncodes the q parameter.. this is the only method in the package that does so, and I added it in for safety reasons, but it seems to be throwing the searches off, as you’ve discovered.

    I’ve amended the issue in the latest deployment, which will be released as soon as I have ironed out the upload_with_media authentication issues.

    However, in the meantime you can do the following to rectify the small problem and get back on track:

    1 – remove the following line from the search method in the monkehTweet.cfc: cfset arguments.q = urlEncodedFormat(arguments.q)

    2 – change your method call to the following: search(q=urlDecode(‘%23ColdFusion’))

    You can see here I’m sending through the encoded hash, but asking CF to decode it for me. This will return the hashtags for you in the search.

    I hope that helps, and apologies for any inconvenience caused.

      1. Hey Andrew! My pleasure. Thank you for finding the issue. it was resolved in the latest release and credit was passed to you in the comments. Many thanks

  13. Hi Matt,

    I was trying gif and it had animation and Twitter doesn’t like it, but the error I am getting back is not being handled properly, and even my error handler is not working on that error, if you try to upload an animated gif, you would se it, please have a look and let me know, how should I handle it. another thing, is thee a way to find out the image which is a gif is a normal one or animated one, as norrmal gif can be uploaded on Twitter.

    Thanks

    1. Hi Philip

      I’ve run a test using an animated gif to update with media and I’m receiving the dumped / output error structure as I would expect. Can you send me more details of what error you are receiving and in what format?

      In terms of checking the image, there is nothing built in to the API itself to check if the gif is animated, and I havent seen any code at the moment that could handle this, although I’ll keep my eyes peeled.

      Many thanks

    2. Just a suggestion but .gif files tend to be smaller size-wise than other formats. If you have an animated .gif, it should be showing out as rather large in terms of file size. Consider checking it with CFIMAGE per the height/width vs size. You might also consider resaving it with CFIMAGE as a static image in another format.

      I’ve never tried either with an animated .gif but I’d suggest some CF side processing/examination of the image before trying to load it to Twitter.

  14. @Craig328 – thanks for the suggestion, but CFImage tag can’t handle animated GIF conversion to other formats.

    @Matt – I have try catch around this request :
    postUpdateWithMedia(status=local.msgStr, media=twitterImage, format=’json’)

    still i get error on the page like this:
    struct
    API_INFO
    struct
    ERROR Error creating status.
    ERROR_MESSAGE 403 Forbidden-The request is understood, but it has been refused.
    FULL_REQUEST
    struct
    Charset utf-8
    ErrorDetail [empty string]
    Filecontent
    object of java.io.ByteArrayOutputStream
    Class Name java.io.ByteArrayOutputStream
    Methods
    Method Return Type
    close() void
    reset() void
    size() int
    toByteArray() byte[]
    toString() java.lang.String
    toString(int) java.lang.String
    toString(java.lang.String) java.lang.String
    write(byte[], int, int) void
    write(int) void
    writeTo(java.io.OutputStream) void
    Parent Class
    Header HTTP/1.1 403 Forbidden X-Transaction-Mask: a6183ffa5f8ca943ff1b53b5644ef11486e746e2 X-Transaction: a705b8575b8588a8 Last-Modified: Wed, 07 Dec 2011 22:00:36 GMT X-Frame-Options: SAMEORIGIN X-MediaRateLimit-Remaining: 30 X-MediaRateLimit-Class: photos X-Runtime: 0.18640 Connection: close Vary: Accept-Encoding Set-Cookie: k=86.166.142.219.1323295234264134; path=/; expires=Wed, 14-Dec-11 22:00:34 GMT; domain=.twitter.com Set-Cookie: guest_id=v1%3A132329523620538670; domain=.twitter.com; path=/; expires=Sat, 07-Dec-2013 10:00:36 GMT Set-Cookie: dnt=; domain=.twitter.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT Set-Cookie: lang=en; path=/ Set-Cookie: lang=en; path=/ Set-Cookie: twid=u%3D23437223%7CxF9mfWhBUEQA051WH8flLFllhj0%3D; domain=.twitter.com; path=/ Set-Cookie: _twitter_sess=BAh7CjoMY3NyZl9pZCIlYzBjMzZiYzdhMWZmYmYxMzdkZDU3MDU3MDgxODU0%250ANTM6D2NyZWF0ZWRfYXRsKwh54IoaNAE6B3VhMDoHaWQiJTk0NDc5OWY4Nzc0%250AYTE4ODY0MWFjNWU4MTI4NjE0MzI2IgpmbGFzaElDOidBY3Rpb25Db250cm9s%250AbGVyOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA–bc01341c087dba9a35ace460e574977437aa530d; domain=.twitter.com; path=/; HttpOnly Pragma: no-cache X-Revision: DEV X-MID: 00fe746266763ff6f080e124c7603b8bd91a0cb1 Content-Type: application/json; charset=utf-8 Server: hi X-MediaRateLimit-Limit: 30 Status: 403 Forbidden X-MediaRateLimit-Reset: 1323381636 X-Access-Level: read-write Date: Wed, 07 Dec 2011 22:00:36 GMT Cache-Control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0 Expires: Tue, 31 Mar 1981 05:00:00 GMT

  15. Hi Matt,

    I’m getting error Element OAUTH_TOKEN is undefined in OAUTHKEYS. on the file base.cfc : line 410 after hitting Authorize App and redirects to our server when instantiating the application.

    I have the same set of codes running for three other twitter accounts and they’re running fine. They are working fine. Any thoughts on what could be wrong?

    Thanks

  16. I’ve hit the special version of the 80/20 rule. The 99/1 rule. Everything worked perfectly within 10 minutes of downloading except I cannot for the life of me work out how to include the UK pound sign (£) in my status updated. Make it much more difficult searching for an answer because of course in the US ‘pound’ can mean ‘#’. And that symbol already has a degree of significance for twitter…

    Tried straight symbol and HTML entities (£ and £) Tried setting everything that moves to UTF-8 encoding (including the cfhttp call in Matt’s base.cfc ‘httpOAuthCall’ method. I either see a literal ‘&pound’ or the replacement symbol (white question mark on a black diamond’

    1. Hi Steve. Thanks for the comment, and I’m glad the project has helped you out! If you can, share a link to the completed project with the library in use.. it’s always great to see what others build using the library!

  17. Hi Matt, I’d been following your blog to implement oauth. It was working fine until a couple of days ago when I received the 410 gone error. I made some changes suggested by you and now I receive {“errors”:[{"message":"Invalid or expired token","code":89.0}]}.

    Please advise.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>