Code summary:
This is the LSL code for the latest version of the Twitterbox itself.
// Twitterbox v0.5
// Post to Twitter and receive updates from within SL
// Ordinal Malaprop
// 2007-02-26
// Last updated 2007-05-12
// Free for distribution and use, but if you use it in something else
// I would like at least a mention.
// Full instructions and the latest version are always at:
// http://ordinalmalaprop.com/twitter/
//------------------------------------------------------------------
// Modifications from 0.3:
// - only works when attached to prevent boxes on the floor spamming you
// - keeps list of 10 most recent tweets, menu item added for this
// - option to show or hide your own tweets, showing by default
// - immediately checks for new tweets on startup
// - option to repeat the last tweet you made, useful for failures
// - checks for updates (separate script)
// Changes from 0.4:
// - option to block posts over 140 characters
//------------------------------------------------------------------
// Edit these to your own specifications
// The email address you signed up to Twitter with
string EMAIL = "ordinal.malaprop@fastmail.fm";
// Your Twitter password
string PASS = "h8gtied";
// Your public RSS feeds (leave blank if you don't have any or you don't know what this means)
list FEEDS = [
"FLICKR", "http://api.flickr.com/services/feeds/photos_public.gne?id=25972087@N00&format=rss_200",
"VIMEO", "http://www.vimeo.com/user:ordinal/clips/rss",
"BLOG", "http://feeds.feedburner.com/engine-proceeding"
];
// Seconds between checks, change if desired
float CHECK_INTERVAL = 120.0;
// This is the URL of the intermediary script. Don't change it unless
// you are using an intermediary of your own.
string TWITTERPING = "http://ordinalmalaprop.com/twitter/control-0.4.php";
// Leave these alone.
integer gTime = 0;
integer gCode = 0;
string gScreenName = "";
integer gManual = FALSE;
list gRecent = [];
integer gShowMine = TRUE;
string gLastTweet = "";
// gNotify defines the type of notification
// 0 = none
// 1 = private sound
// 2 = full animation and public sound
integer gNotify = 2;
integer gBlock140 = TRUE;
//------------------------------------------------------------------
key twitter_send(string action, string subject)
{
if (EMAIL == "" || PASS == "") {
llOwnerSay("No email/password set - edit script and try again");
return NULL_KEY;
}
else if (gBlock140 && llStringLength(subject) > 140) {
llOwnerSay("Lawks! Your post was over 140 characters and has been blocked - please try again or touch me to turn this option off. Your post was:");
llOwnerSay(subject);
return NULL_KEY;
}
else {
vector pos = llGetPos();
return llHTTPRequest(TWITTERPING,
[HTTP_METHOD, "POST"],
EMAIL + "\n" + PASS + "\n" + action + "\n" + subject + "\n" + llGetRegionName() + "/" + (string)llRound(pos.x) + "/" + (string)llRound(pos.y) + "/" + (string)llRound(pos.z) + "/\n" + llDumpList2String(FEEDS, ","));
}
}
menu()
{
list buttons = ["Check Now", "Web", "List Feeds",
"Recent", "Repeat", "Notify", "Help"];
string text = "Current email: " + EMAIL + "\n";
if (gShowMine) {
text += "Showing your own tweets.\n";
buttons = ["Hide Mine"] + buttons;
}
else {
text += "Hiding your own tweets.\n";
buttons = ["Show Mine"] + buttons;
}
if (gBlock140) {
text += "Blocking posts > 140 chars\n";
buttons = ["Allow >140"] + buttons;
}
else {
text += "Allowing posts > 140 chars\n";
buttons = ["Block >140"] + buttons;
}
if (gLastTweet != "") text += "Your last tweet: '" + gLastTweet + "'\n";
llDialog(llGetOwner(), text + "\nPlease select an option:", buttons, 282);
}
list_feeds()
{
integer feeds = llGetListLength(FEEDS);
if (feeds == 0) {
llOwnerSay("You have no feed keywords defined at the moment.");
} else {
llOwnerSay("Your current feed keywords are...");
integer f = 0;
do {
llOwnerSay(llList2String(FEEDS, f) + " - " + llList2String(FEEDS, f + 1));
f += 2;
} while (f < feeds);
}
llOwnerSay("Edit the script to add or remove feed keywords - for more information see http://ordinalmalaprop.com/twitter/");
}
string notify_level()
{
if (gNotify == 0) return "Quiet";
else if (gNotify == 1) return "Private";
else if (gNotify == 2) return "Public";
return "** something illegal **";
}
help()
{
if (EMAIL == "" || PASS == "") {
llOwnerSay(
"BEWARE! You have not configured an `email and password! " + "Open the object, open the TwitterBox script, and fill in " + "the variables at the top.");
}
llOwnerSay(
"To send a tweet, say '/282
', or touch the TwitterBox " + "HUD for more options.");
}
about()
{
llOwnerSay("A simple SL client for Twitter - http://twitter.com/ " + "- by Ordinal Malaprop");
llOwnerSay(
"TwitterBox will automatically check for new tweets from your " +
"friends every minute, or when you tell it to manually from " +
"the menu, obtainable by touching the HUD.");
llOwnerSay(
"To use, you need to have registered with Twitter, and edited " + "the script to include your email address and password.");
llOwnerSay(
"For more information or the latest version, visit " +
"http://ordinalmalaprop.com/twitter/");
}
twitterball()
{
llStartAnimation("Twitter");
llSleep(1.0);
llRezObject("Twitterball", llGetPos() + <0.0, 0.0, 1.5>, ZERO_VECTOR, ZERO_ROTATION, 1);
}
startup()
{
// Get screen name again whenever it is attached,
// as this may change
twitter_send("get id", "");
// Check for new updates immediately
twitter_send("check", "");
// Also display the help message
help();
list_feeds();
llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
llSetTimerEvent(CHECK_INTERVAL);
}
startup_check()
{
if (llGetAttached()) startup();
else llOwnerSay("I must be attached to operate - please wear me");
}
list push(string tweet, list tweetlist)
{
tweetlist = tweetlist + [tweet];
if (llGetListLength(tweetlist) > 8) {
// remove first element
tweetlist = llDeleteSubList(tweetlist, 0, 0);
}
return tweetlist;
}
list_recent()
{
llOwnerSay("Recent tweets received:");
if (gRecent == []) {
llOwnerSay("None received yet");
}
integer f = 0;
integer n = llGetListLength(gRecent);
do {
llOwnerSay(llList2String(gRecent, f));
} while (++f < n);
}
//------------------------------------------------------------------
default
{
state_entry()
{
llOwnerSay("Initialising...");
// At the start...
// reset the clock to now
// gTime = llGetUnixTime();
// and also start listening for commands
llListen(282, "", llGetOwner(), "");
// and start everything up if attached
startup_check();
}
on_rez(integer p)
{
if (llGetAttached() == 0) {
llOwnerSay("I must be attached to operate - please wear me");
}
}
changed(integer change)
{
if (change & CHANGED_OWNER) {
llResetScript();
}
}
attach(key id)
{
// Turn off check when not attached
if (id == NULL_KEY) {
llSetTimerEvent(0.0);
llOwnerSay("Detached - regular check has been cancelled");
} else {
startup();
}
}
timer()
{
twitter_send("check", "");
}
touch_start(integer n)
{
// On owner touch, launch a control menu
if (llDetectedKey(0) != llGetOwner()) return;
menu();
}
listen(integer c, string name, key id, string msg)
{
if (msg == "Check Now") {
llOwnerSay("Checking latest entries...");
gManual = TRUE;
twitter_send("check", "");
llSetTimerEvent(CHECK_INTERVAL);
}
else if (msg == "List Feeds") {
list_feeds();
}
else if (msg == "TestTwit") {
llSleep(1.0);
twitterball();
}
else if (msg == "Web") {
llLoadURL(id, "Visit your own Twitter page", "http://twitter.com/" + gScreenName);
}
else if (msg == "Show Mine") {
llOwnerSay("Showing your tweets");
gShowMine = TRUE;
}
else if (msg == "Hide Mine") {
llOwnerSay("Hiding your tweets");
gShowMine = FALSE;
}
else if (msg == "Help") {
about();
}
else if (msg == "Notify") {
llDialog(llGetOwner(), "Please select an option for notifications - currently " + notify_level(), ["Quiet", "Private", "Public", "Cancel"], 282);
}
else if (msg == "Quiet") {
gNotify = 0;
llOwnerSay("No sound or animation notifications");
}
else if (msg == "Private") {
gNotify = 1;
llOwnerSay("Private sound only for notifications");
}
else if (msg == "Public") {
gNotify = 2;
llOwnerSay("Animations and public sound when twittering, private sound for new tweets");
}
else if (msg == "Recent") {
list_recent();
}
else if (msg == "Repeat") {
llOwnerSay("Repeating your last update...");
twitter_send("update", gLastTweet);
}
else if (msg == "Allow >140") {
llOwnerSay("Allowing posts over 140 characters");
gBlock140 = FALSE;
}
else if (msg == "Block >140") {
llOwnerSay("Blocking posts over 140 characters");
gBlock140 = TRUE;
}
else if (msg != "Cancel") {
llOwnerSay("Twittering...");
gLastTweet = msg;
twitter_send("update", msg);
}
}
http_response(key id, integer status, list metadata, string body)
{
if (llGetSubString(body, 0, 1) == "OK") {
// A success
list lines = llParseString2List(body, ["\n"], []);
string firstLine = llList2String(lines, 0);
if (llGetListLength(lines) == 1) {
// A successful update, or an ID check
list bits = llParseString2List(firstLine, [","], []);
gCode = llList2Integer(bits, 1);
gScreenName = llList2String(bits, 2);
if (llList2String(bits, 3) == "posted") {
llOwnerSay("Successfully Twittered!");
if (gNotify == 1) {
llPlaySound("c923f3d9-83a6-99dc-9b7d-bbcdb3c30789", 1.0);
} else if (gNotify == 2) {
twitterball();
}
}
}
else {
integer f = 1;
integer maxTime = gTime;
string tweet = "";
do {
string user = llList2String(lines, f);
integer time = llList2Integer(lines, f + 2);
// llOwnerSay("gTime = " + (string)gTime);
// llOwnerSay(user + " @ " + (string)time);
if ((gShowMine || user != gScreenName) && time > gTime) {
if (maxTime == gTime) {
// First new tweet, play sound
if (gNotify != 0) {
llPlaySound("c923f3d9-83a6-99dc-9b7d-bbcdb3c30789", 1.0);
}
}
tweet = llList2String(lines, f) + ": " + llList2String(lines, f + 1);
llOwnerSay(tweet);
gRecent = push(tweet, gRecent);
maxTime = time;
}
f += 3;
} while (f < llGetListLength(lines));
if (maxTime == gTime) {
if (gManual) {
llOwnerSay("No new entries");
gManual = FALSE;
}
} else {
gTime = maxTime;
}
}
} else {
if (body == "") {
// SL sends back a blank entry if it can't get in touch at all
body = "Could not contact intermediary server " + TWITTERPING;
}
else {
if (status == 200) {
// Intermediary server worked okay
llOwnerSay("I received an error - '" + body + "'");
} else {
// Something else went wrong, don't bother the user with the exact details
// If they are techie enough to know what the details mean, they can edit
// this script so that it tells them :)
llOwnerSay("I received an error with code " + (string)status + " - please try again in a bit");
}
}
}
}
}