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.

  • http://www.mattgifford.co.uk/ Matt

    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 :)

  • Philip

    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

  • Philip

    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

  • David Phipps

    @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

  • Nick Walters

    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.

  • Rio

    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.

    • http://www.mattgifford.co.uk/ Matt

      Hi Rio

      I’m not experiencing any issues, but I can look into it for you. Which methods are you calling that are returning the errors?

  • Rio

    @Matt:

    just a regular posting of tweets.

  • http://www.tweetface.co Philip

    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

  • http://www.mattgifford.co.uk/ Matt

    @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.

  • Jim O’Keefe

    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.

    • http://www.mattgifford.co.uk/ Matt

      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?

  • http://www.puavault.net Rio

    Hi Matt,

    I figured out the error “‘request accepted but cannot be understood’” in more detail and its now saying the text of the tweet is now too long.

    You should be able to see the error here: http://www.puavault.net/pua/twitter_process_schedule.cfm

    I print out the tweet length and it’s 136 chars, so I’m not sure how it’s coming out to be too long.

  • http://www.eluncher.com Craig328

    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!

    • http://www.mattgifford.co.uk/ Matt

      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

      • http://www.eluncher.com Craig328

        Matt:

        I was referencing the material on this page: https://dev.twitter.com/docs/api/1/get/statuses/user_timeline

        I’d need to refresh my memory for what it was that I’d done but if it’s working for you as is, it’s probably me. Not the most shocking revelation, to be sure. :)

  • Andrew Myers

    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.

  • http://www.mattgifford.co.uk/ Matt

    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.

    • Andrew Myers

      Hi Matt,

      Sorry for the slow response but I tried your fix, and it worked great. Thank you!

      Best regards,
      Andrew.

      • http://www.mattgifford.co.uk/ Matt

        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

  • http://www.mattgifford.co.uk/ Matt

    Version 1.3.0 of monkehTweet is now out and available! It includes the update_with_media method to upload and attach an image file to your status, as well as other updates.

    http://www.mattgifford.co.uk/monkehtweet-release-and-twitter-update_with_media-enhancements

  • http://www.tweetface.co Philip

    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

    • http://www.mattgifford.co.uk/ Matt

      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

    • http://www.eluncher.com Craig328

      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.

  • http://www.tweetface.co Philip

    @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

  • Daryl

    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

    • Brandon

      Hey Daryl, did you ever figure out what the fix was, getting the same error.

      Thanks!

  • Ian Turton

    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’

  • http://www.stevemckeogh.co.uk Steve McKeogh

    Great work Matt, saved me tons of time here mate, works a treat for my needs!

    • http://www.mattgifford.co.uk/ Matt

      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!

  • http://www.mattgifford.co.uk/ Matt

    Hi all.

    The monkehTweets package has been updated to reflect the changes to the Twitter API and the push to v1.1. More details are available here: http://www.mattgifford.co.uk/monkehtweets-update-now-supports-twitter-api-v1-1

  • Tom Miller

    Any news on the streaming API? I’ve no idea how to handle this, but it’s perfect for my application

  • Ranjan Vadlamani

    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.

    • Ranjan Vadlamani

      If you could release new documentation and new files, I would greatly appreciate it.