// Twitterbox v0.4 // Post to Twitter and receive updates from within SL // Ordinal Malaprop // 2007-02-26 // Last updated 2007-04-18 // 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) //------------------------------------------------------------------ // Edit these to your own specifications // The email address you signed up to Twitter with string EMAIL = ""; // Your Twitter password string PASS = ""; // 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 = 60.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; //------------------------------------------------------------------ key twitter_send(string action, string subject) { if (EMAIL == "" || PASS == "") { llOwnerSay("No email/password set - edit script and try again"); 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 (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) > 10) { // 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 != "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-bbcdb3c307 89", 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"); } } } } }