This discussion is closed: you can't post new comments.

Twitter

A Modification to the Twitterbox, such that it Works Once More

I have been informed more than once over the past few days that my Twitterbox appears not to be operating as Desired - more specifically, that Twitterboxers were able to send updates quite satisfactorily, but not actually receive any, which was understandably frustrating for them.

On further investigation it appears that the Twitterfolk decided at some point to restrict requests to receive an Updated List of Friends' Tweets to those using the GET method. I have no idea why, but who am I to argue? In any case I have modified my own server script so that things should now Operate As Expected. For those using the script on their own Aethernet Servers, they may find the latest version here:

There should be no need for Twitterbox users inworld to do anything at all regarding this - it is mostly for Informational Purposes. No updating or such is required, as long as you are using version 0.4 or above.

Twitterbox control.php

Code summary: 

This is the latest version of the server PHP script for use with the Twitterbox, which does all of the hard work in contacting Twitter.

<?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 curl
function 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 feed
function 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?";

?>

An Absolute Outrage

I flatter myself to think that I am at least known as someone of mostly Phlegmatic Temperament, even when peculiarities of Second Life are concerned. I am, I believe, reasonable tolerant of Flaws, even if I will occasionally make squawks of protest; these are mostly due to surprise.

There are some things, however, up with which I will not put.

NO NO NO

"Twitterbox say"?

Scatterbrained Once Again

A perpetual problem of mine is that I am unable to concentrate on one thing for more than about, oh, an afternoon, leading to the unfortunate situation where possible projects and Matters Upon Which To Write flare brightly and then burn out within moments, like phosphorus sparrows. No sooner do I plan to write a Definitive Article upon some subject, which must be Gotten Right, than some new shiny object appears and drives the original one completely away from my attention.

The result of this is that I am rather poor at actually writing anything, as perfectionism restrains me from making an entry on something incomplete, yet I do not have the urge to complete it, and it is only when I reach a level of self-disgust at this that I throw out whatever peculiar half-finished objects that remain in my mind into this Journal, often in the form of Bullet Points. I believe that it was easier in my earlier days, when I was rarely thinking about more than three things at one time.

Enough of this preambling though; I will do my best to clear the decks. The subject of this entry is something that I call the Twitterbomb, though really it isn't much of a bomb, unless one considers very slow-moving fragments that are in any case phantom to be dangerous. This was actually mentioned previously by Nick Wilson writing in Metaversed as he is a proper journalist who investigates things and writes about them, whereas I am no such thing and do not even have a picture of my own creation at the time of writing.

The Twitterbomb, as Mr Wilson says and as I mention in the comment section there, is a device for the Visualisation of timed and differently-authored data; the Twitter friends timeline is the easiest to work with, as it collects data into a single feed, but I suppose RSS feeds and such could be used. There is a central "bomb", which reads in Twitter data via a proxy (a stripped-down version of the Twitterbox one) throws out differently-coloured "fragments" - the angle of movement and colour of these fragments is individual to each different author, thus each person's output is represented by a line of tweets stretching out from the centre. Each fragment's size is proportional to the number of letters in the tweet.

The fragments move outwards at a constant speed, with their distance from the bomb being a function of their age. There is a configurable maximum age, with fragments disappearing once they reach this. (Once rezzed, the fragments are independent, and with a busy friends timeline this could result in an awful lot of prims being around, thus best to use this in a fairly empty lot.)

I confess to not having a specific target to achieve here, but it is a toy that enables one to play with the possibilities of visualising data in Three Dimensional Form. One can look at the fragments produced and see, say, how active an individual is and their "rhythm" by observing the "clumping" and size of clumps produced; a wordy but regular poster will have fat fragments evenly spaced, someone who posts in bursts of short pieces will have thin lines separated by empty space and so on. A particularly significant event for one's friends will be marked by a "shell" of tweets all appearing at the same time and expanding outwards. Changes could certainly be made to the way the fragments are emitted to test different concepts of visualisation.

I say all this, but actually, in practice I have Given Up on the Twitterbomb for the moment - which is likely to mean forever - since Twitter has taken to caching my requests and not giving me recent updates reliably at all. This is odd, since the Twitterbox seems to be working perfectly well, or at least is when both Twitter and SL are working at the same time (a combination of reliabilities that one is not advised to bet one's life or significant bodily organs upon). With this in mind, once I am able to return to the world I shall be sure to post the relevant Code here.

Well, that is over with in any case. Next, something else, I believe.

The Twitterbadge

Before I dive back into acts of proper engineering within SL (usually located at <0,0,0>) I would quickly like to note one more Twitter-related product - the Twitterbadge.

Twitterbox 0.4

For what it is worth, I have finally gotten around to releasing the latest version of the Twitterbox, which includes a number of new features and is generally a bit more reliable. Of course, at this time the Grid is... well, it is a Wednesday.

If you wish to view a list of changes from the previous version, by all means read on, and do excuse the scrappiness of my list-making.

Now, hopefully, I might be able to work on, you know, a thing, rather than a succession of ephemeral seance devices. Something which exists in its own right, and allows one to do something upon the Grid itself which one previously could not. Alas, the fractious nature of reality is making this just a tad tricky, with some of my best works now appearing foolish and error-ridden, but the time will come I'm sure when events do not just occur in random orders.

An Unsatisfying Morsel

~ Herein, one may find a few notes dashed off in a hasty hand whilst standing in a damp Caledon drizzle, repeatedly poking a riveted cube.

A Brief Moment of TwitterHeading

Of little interest to those not inclined to develop Twittery Things, but proposals have emerged for those creating Clients (such as the Twitterbox) to be able to identify their Device to the Twitterverse.

Twitterbox 0.3

Oh my giddy aunt, I forgot entirely to announce the release of version 0.3 of the Twitterbox before retiring the previous night.

Well, version 0.3 of the Twitterbox has been released. It is fairly similar to the previous version, as one might expect, with the addition of the ability to automatically post the URL of the latest entry in an "RSS" or "Atom" "feed".

Porcupines, Applescript and Automatic Flickring

I mentioned in my last Twitter-related post of inordinate length that it might be possible to set a Folder Action on the Apple Platform which would automatically upload full-sized snapshots to Flickr, although dealing with Applescript was like having porcupines in one's socks. The porcupines have turned out not to be quite as troublesome as I first thought, and I have managed to squash together two existing Applescripts in order to make this possible.

Syndicate content