<?php/*
Twitterbox Intermediary Thing v0.41
Ordinal Malaprop
Last updated 2009-04-23 21:45
This script sits on a server somewhere and sends and receives data from Twitter. It is meant to be able to talk to an LSL script in a way that it can easily deal with.
Requires PHP 5.2 for the JSON-parsing functions.
Changes since 0.3:
- added timeouts for web services
- changed fail text to "no response from..."
- timestamp is now absolute rather than relative for logging purposes
- works on LA timezone
- allows for no feeds from client
- uses proper version parameter :)
- tinyurl function does not die completely if it can't get a tinyurl (not that I've ever seen this happen anyway)
- changed the SLURL caption to include the actual location SLURLed
- added SLPOS keyword to insert textual representation of SL location
- tries to detect type of feed before trying to parse it
I considered using the short-lived "since" parameter, but it appears that it encountered problems and is not being used in practice.
Version 0.41 ( 2009-04-23 21:45 )
- Twitter for some reason was demanding that friend item checks be GET rather than POST. Who am I to argue?
LICENCE
This code is licenced under a Creative Commons Attribution licence:
http://creativecommons.org/licenses/by/3.0/
Attribution in this case would probably mean a mention in the credits somewhere; it depends on what the application is really.
In addition, it may not be sold unmodified as is.
*/define("VERSION", 0.4);//-----------------------------------------------------------------------------// Basic function to send a request to Twitter, using curlfunction twitter_send
($file, $user, $pass, $data) {
global $since;
// Construct a curl request and set all the right options
$c = curl_init();
curl_setopt($c, CURLOPT_URL
, "http://twitter.com/statuses/$file.json");
curl_setopt($c, CURLOPT_PATH
, TRUE);
curl_setopt($c, CURLOPT_HTTPAUTH
, CURLAUTH_BASIC
);
curl_setopt($c, CURLOPT_USERPWD
, "$user:$pass");
if ($data) curl_setopt($c, CURLOPT_POSTFIELDS
, $data);
else curl_setopt($c, CURLOPT_HTTPGET
, TRUE);
curl_setopt($c, CURLOPT_RETURNTRANSFER
, TRUE);
curl_setopt($c, CURLOPT_HEADER
, FALSE);
// Don't wait too long for Twitter
curl_setopt($c, CURLOPT_TIMEOUT
, 30);
// Not sure if this is being used, but include it anyway
curl_setopt($c, CURLOPT_USERAGENT
, "TwitterBox ". VERSION
);
// These are special proposed headers to identify the client to Twitter (from the Twitter Development Talk group)
$headers = array(
"X-Twitter-Client" => "Twitterbox",
"X-Twitter-Client-URL" => "http://ordinalmalaprop.com/twitter/",
"X-Twitter-Client-Version" => VERSION
);
curl_setopt($c, CURLOPT_HTTPHEADER
, $headers);
// Now send the response off
$response = @curl_exec($c);
if (chop($response)) {
// We got a non-blank response, so try to parse it as JSON
$json = json_decode($response, TRUE);
curl_close($c);// print_r($json);
// Return the parsed data
return $json;
}
else die("No response from Twitter!");}// Fucntion to get the TinyURL of the first entry in an RSS feedfunction feedlink_get
($url) {
$c = curl_init();
curl_setopt($c, CURLOPT_URL
, $url);
curl_setopt($c, CURLOPT_RETURNTRANSFER
, TRUE);
curl_setopt($c, CURLOPT_HEADER
, FALSE);
curl_setopt($c, CURLOPT_TIMEOUT
, 15);
$feed_data = curl_exec($c);
curl_close($c);
if (!$feed_data) die("Failed to get stream at $url");
else {
// Try RSS parsing
if (strpos($feed_data, 'rss') !== FALSE && preg_match('/<item>.+?<link>\s*?(\S.+?\S)\s*?<\/link>/is', $feed_data, $matches)) {
$feed_data = get_tinyurl
($matches[1]);
return $feed_data;
}
// Try Atom parsing
else if (strpos($feed_data, '<feed xmlns="http://www.w3.org/2005/Atom"') !== FALSE && preg_match('/<entry.+?>.+?<link.+?href="(.+?)".+?>/is', $feed_data, $matches)) {
$feed_data = get_tinyurl($matches[1]);
return $feed_data;
}
// Give up
else {
die("Failed to find new feed link at $url");
}
}
}
// Function to get a tinyurl for something
// We probably don't need to use curl here, but, you know, why not?
function get_tinyurl($bigurl)
{
$c = curl_init();
curl_setopt($c, CURLOPT_URL, "http://tinyurl.com/api-create.php?url=$bigurl");
curl_setopt($c, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($c, CURLOPT_HEADER, FALSE);
// Don't wait more than 10 seconds for a TinyURL
curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 10);
$tinyurl = curl_exec($c);
curl_close($c);
// If we got a tinyurl, return it
if ($tinyurl) return $tinyurl;
// otherwise just return the original one - Twitter will probably be able to tinyurl it
else return $bigurl;
}
//-----------------------------------------------------------------------------
// Main code
// Set timezone to be closest to SL time (no San_Francisco timezone code in PHP?)
date_default_timezone_set("America/Los_Angeles");
header("Content-type: text/plain");
// Read POST input
$post = file('php://input');
// If there isn't anything in the POST, don't do anything
if (!$post) {
die("go away");
}
// Read in the information that was sent
$user = urldecode(chop($post[0]));
$pass = urldecode(chop($post[1]));
$action = urldecode(chop($post[2]));
$status = urldecode(chop($post[3]));
// location to add slurl for, in the form simname/x/y/z/
$location = urldecode(chop($post[4]));
// Text representation of location
$loc_array = explode("/", $location);
$loc_text = $loc_array[0]. " ". $loc_array[1]. ",". $loc_array[2]. ",". $loc_array[3];
// Keywords and RSS feeds to check and replace
$feeds_raw = explode(",", urldecode(chop($post[5])));
$feeds = array();
$f = 0;
do {
$feeds[$feeds_raw[$f]] = $feeds_raw[$f+1];
$f += 2;
} while ($f < sizeof($feeds_raw));
// Unix time of last check - don't return anything before this
$since = urldecode(chop($post[6]));
ob_flush();
flush();
// Allow for slight clock irregularities
$now = time() - 5;
// Now process the action requested
// All of these request feedback from Twitter in JSON format, which is parsed by the above function.
if ($action == "update") {
if (strlen($status) > 0) {
// "Insert current position" keyword
$status = str_replace("SLPOS", $loc_text, $status);
// Go through each feed keyword checking to see if it occurs
foreach ($feeds as $feed_name => $feed_url) {
// Do we have any keywords for that feed here?
if (@strpos($status, $feed_name) !== FALSE) {
// If so, get the tinyurl for the first <link> element there
$tinyurl = feedlink_get($feed_url);
// and replace instances of that keyword with it
$status = str_replace($feed_name, $tinyurl. " ", $status);
ob_flush();
flush();
}
}
if (strpos($status, "SLURL") !== FALSE && $location) {
$loc_array = explode("/", $location);
$loc_text = $loc_array[0]. " ". $loc_array[1]. ",". $loc_array[2]. ",". $loc_array[3];
// SLurl page strips out all HTML in the msg - didn't know that...
$slurl = "http://slurl.com/secondlife/". str_replace(" ", "%20", $location). "?x=200&y=260&title=". urlencode(gmdate("j M y @ H:i", time() - 28800). " SLT"). "&msg=". rawurlencode(htmlentities(str_replace("SLURL", $loc_text, $status))). "&img=http%3A//ordinalmalaprop.com/twitter/twitterbox-credit.jpg";
$tiny_slurl = get_tinyurl($slurl);
$status = str_replace("SLURL", $tiny_slurl, $status);
}
ob_flush();
flush();
}
$json = twitter_send('update', $user, $pass, "status=". rawurlencode($status));
// The update procedure
// Check the time that the last update was posted at
$created = strtotime($json["created_at"]);
// If the time is before now, that means it's failed!
// 2007-07-01 - this doesn't seem to work any more, assume it has succeeded
// if ($created < $now) {
// die("Date was w");
// }
// Otherwise say "ok" and coincidentally update the screen name and ID
if (sizeof($json, COUNT_RECURSIVE) <= 1) die("Twitter gave me bad output - " + print_r($json, TRUE));
else {
echo "OK,". $json["user"]["id"]. ",". $json["user"]["screen_name"]. ",posted";
}
}
else if ($action == "get id" && ($json = twitter_send('update', $user, $pass, ""))) {
// To just update the screen name and ID, send a blank update which will not be displayed
// and read in the information.
if (sizeof($json, COUNT_RECURSIVE) <= 1) die("Twitter gave me bad output - " + print_r($json, TRUE));
else echo "OK,". $json["user"]["id"]. ",". $json["user"]["screen_name"];
}
else if ($action == "check" && ($json = twitter_send('friends_timeline', $user, $pass, ""))) {
// Check recent tweets
// Go through them and compile a list of the last few, up to an appropriate size limit to send back to LSL
// 1500 bytes should work - longer than that and the header gets in the way
// LSL doesn't like receiving any real quantity of data
$length = 0;
$entry = "";
$f = 0;
// Sometimes, Twitter doesn't seem to produce proper output, even if it gets through
if (sizeof($json, COUNT_RECURSIVE) <= 1) die("Twitter gave me bad output");
$max = sizeof($json);
$tweets = array();
do {
$length += strlen($entry);
if ($entry != "") $tweets[] = $entry;
// Format for each tweet:
// <user's screen name>
// <update text> (<SLT timestamp>)
// <UNIX time of creation, to see if it's new>
$unixtime = strtotime($json[$f]["created_at"]);
$entry = $json[$f]["user"]["screen_name"]. "\n". $json[$f]["text"]. " (". date("j M H:i", $unixtime). " SLT)\n$unixtime";
ob_flush();
flush();
} while (++$f < $max && $length + strlen($entry) < 1500);
// Put them in chronological order
$tweets = array_reverse($tweets);
// and output
echo "OK\n". implode("\n", $tweets);
}
else echo "pardon?";
?>