Long meant to share my modest set of functions for posting tweets to Twitter. Maybe useful to someone — none of it came to me right away, especially the signature generation. Plus this is my attempt to lock into my head what I’ve learnt and coded. The best way to do that, I think, is to try to explain it to someone else :) Description and code below the cut.
There are quite a few scripts online for posting messages to Twitter, but when I had to set up automatic tweeting (for work) — none of them quite did it for me, either because of unnecessary complexity or just outright spaghetti code. On top of that, while learning the Twitter API, writing my own bicycle was useful in itself.
Communication with the Twitter API happens via OAuth authorisation (specifically OAuth, not OAuth 2.0 — though maybe 2.0 is fine by now). Requests have to be assembled in a particular way and accompanied by a specially crafted signature.
First, register an app at dev.twitter.com; you’ll get a Consumer key and Consumer secret. They’ll also issue you personally an Access token and Access token secret, so the app can post on behalf of your user right away.
Let’s define the access settings for the script:
# settings
$oauth_token = 'Your access token';
$oauth_token_secret = 'Your access token secret';
$oauth_consumer_key = 'Your consumer key';
$oauth_consumer_secret = 'Your consumer secret';
$url = 'http://api.twitter.com/1/statuses/update.json';
In my case posting a tweet consists of 4 steps:
- Building the tweet text (a 140-character string with text and possibly a link).
- Building the request parameters and signature.
- Building the request headers.
- Sending the request.
The set itself consists of 3 functions. The main function is postTweet(). It takes the tweet text as a parameter — pre-prepared, i.e. already 140 characters long. Usually I prepare it somewhere outside of this function. Inside postTweet, the other 2 functions are called — the signature builder (makeSignature) and the actual function that posts the tweet to Twitter (postTweet).
Let’s go in order. The signature function makeSignature:
/**
* Builds a signature for the data
* @global array $oauth_consumer_secret
* @global string $oauth_token_secret
* @param string $url
* @param Array $data
* @return string
*/
function makeSignature( $url, $data ){
global $oauth_consumer_secret, $oauth_token_secret;
$txt = 'POST&' . rawurlencode( $url ) . '&';
$tmp = array();
foreach( $data as $key => $value )
$tmp[] = rawurlencode( $key ) . "%3D" . rawurlencode( $value );
$txt .= implode( "%26", $tmp );
$key = $oauth_consumer_secret . '&' . $oauth_token_secret ;
return base64_encode( hash_hmac( 'sha1', $txt, $key, true ) ) ;
}
As input, the function takes the data needed to build the signature ($data) and the $url the request will go to. In short — we build a string. First we write POST in it, indicating we’ll use a POST request. The input parameters are assembled into the right form (alphabetical order matters), then this whole thing is encoded with the HMAC-SHA1 algorithm; the encoding key is:
$key = $oauth_consumer_secret . '&' . $oauth_token_secret;
The encoded string is our signature. This deceptively simple function ate up a fair bit of time and nerves =(
Now the main function — postTweet:
/**
* Posts a message to Twitter
* @global string $url
* @param string $oauth_consumer_key
* @param string $oauth_token
* @param string $statusText
*/
function postTweet( $statusText ){
global $url, $oauth_consumer_key, $oauth_token;
$nonce = md5( microtime() . mt_rand() );
$time = time();
$date = date( 'r' );
$data = array(
'oauth_consumer_key' => $oauth_consumer_key,
'oauth_nonce' => $nonce,
'oauth_signature_method' => "HMAC-SHA1",
'oauth_timestamp' => $time,
'oauth_token' => $oauth_token,
'oauth_version' => '1.0',
'status' => rawurlencode( $statusText )
);
$signature = makeSignature( $url, $data );
$data['status'] = $statusText;
$data['oauth_signature'] = $signature;
$header = 'OAuth oauth_nonce="' . rawurlencode( $nonce ) . '", ';
$header .= 'oauth_signature_method="HMAC-SHA1", ';
$header .= 'oauth_timestamp="' . rawurlencode( $time ) . '", ';
$header .= 'oauth_consumer_key="' . rawurlencode( $oauth_consumer_key ) . '", ';
$header .= 'oauth_token="' . rawurlencode( $oauth_token ) . '", ';
$header .= 'oauth_signature="' . rawurlencode( $signature ) . '", ';
$header .= 'oauth_version="1.0"';
return curlPostTweet( $url, $header, $date, $data );
}
This function takes the already-prepared tweet text (140 characters), and the $data array is filled with request data. Then the request signature is built from the same data (calling makeSignature, described above). The signature is added to the data. The HTTP request headers are built — basically the literal string "OAuth" followed by the same $data values. All of that is then passed to curlPostTweet(), which actually posts to Twitter. It does so via CURL.
function curlPostTweet( $url, $header, $date, $postData ){
$ci = curl_init();
curl_setopt( $ci, CURLOPT_URL, $url );
curl_setopt( $ci, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ci, CURLOPT_HTTPHEADER, array( 'Authorization', $header, 'Date: ' . $date ) );
curl_setopt( $ci, CURLOPT_POST, 1 );
curl_setopt( $ci, CURLOPT_POSTFIELDS, http_build_query( $postData ) );
return curl_exec( $ci );
}
I don’t think this needs further explanation. You can also inspect the result the request returns. If something’s wrong, it’ll contain an error and its description. Otherwise the request returns the freshly posted tweet and info about it. And in your timeline, the freshly posted tweet will proudly show as its client the name of the app you registered.
As a bonus, I’m attaching a function for getting a shortened link. Here it uses the clck.ru link shortener by @bobuk
function curlGetShortLink( $url ){
$link = "http://clck.ru/--?url=" . $url;
$ci = curl_init();
curl_setopt( $ci, CURLOPT_URL, $link );
curl_setopt( $ci, CURLOPT_RETURNTRANSFER, true );
while ( true ) {
$returned = curl_exec( $ci );
$status = curl_getinfo( $ci, CURLINFO_HTTP_CODE );
# if the service is unavailable or rate-limited:
if ( $status == "200" ) {
break;
}
sleep( 2 );
}
return $returned;
}
These functions currently power 4 bots, which have been doing their job for several months now. If you’re curious — @funkysouls and @rutracker_ios, which parse RSS and post to Twitter, plus a couple of our work bots.
Thanks to @stay_positive for the timely help — and some of the code here is his. Oh and
