/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2012 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// // Cross-region intercom: URL RELAY SCRIPT ////////////////////////////////////////////////////////// //------------------- CONFIGURATION --------------------// ////////////////////////////////////////////////////////// // This is your whole intercom network password. Nobody would // be able to hook up to your network unless they know this // password. Make sure it is something unique. If you want a // good password, google for an md5 hash generator and use that. // ALL the prims in your intercom network must have the same // password that you enter here. It can be anything you choose // but WITHOUT SPACES and without PUNCTUATION MARKS/SYMBOLS. string INTERCOM_PASSWORD = "49c11b5dfa51597b3021578810a1ebd2"; ////////////////////////////////////////////////////////// //--------------------- INTERNALS ----------------------// ////////////////////////////////////////////////////////// integer intercomON = TRUE; integer menuHandle = 0; integer registerHandle = 0; list urlBeacons = []; string selfURL = ""; list requestQueue = []; key nQuery = NULL_KEY; integer nLine = 0; integer isValidURL(string URL) { if(llSubStringIndex(URL, "http://") >= 0) return TRUE; return FALSE; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// // http://grimore.org/fuss:lsl list wasDeleteSubListMatch(list in, string match) { if(llGetListLength(in) == 0) return []; string first = llList2String(in, 0); in = llDeleteSubList(in, 0, 0); if(llSubStringIndex(first, match) == -1) jump next; return wasDeleteSubListMatch(in, match); @next; return first + wasDeleteSubListMatch(in, match); } default { state_entry() { integer itra = llGetInventoryNumber(INVENTORY_NOTECARD)-1; do { if(llGetInventoryName(INVENTORY_NOTECARD, itra) == "Intercoms") jump found_notecard; } while(--itra>0); llOwnerSay("No Intercoms notecard found, proceeding without one..."); state intercom; @found_notecard; nQuery = llGetNotecardLine("Intercoms", nLine); } dataserver(key id, string data) { if(id != nQuery) return; if(data == EOF) state intercom; if(data == "") jump next_line; if(!isValidURL(data)) jump next_line; urlBeacons += data; @next_line; nQuery = llGetNotecardLine("Intercoms", ++nLine); } changed(integer change) { if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_INVENTORY)) llResetScript(); } on_rez(integer pin) { llResetScript(); } } state intercom { state_entry() { llRequestURL(); llSetTimerEvent(60); llSensorRepeat("", NULL_KEY, AGENT, .1, .1, 1); llListen(0, "", "", ""); } touch_start(integer total_number) { if(llDetectedKey(0) != llGetOwner()) return; integer comChannel = ((integer)("0x"+llGetSubString((string)llGetKey(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF; menuHandle = llListen(comChannel, "", llGetOwner(), ""); llDialog(llGetOwner(), "Options description:\n\nAdd URL: Use this to add new Intercoms.\n\nMy URL: Use this to get the address of this Intercom.\n\nList URLs: Use this to list all the linked intercoms.\n\nReset: Use this to permanently reset the current intercom since resettting the script will not unlink it from the network.\n\nOn/Off: This will turn the intercom on and off. It will remain connected to the network but will not receive or broadcast messages.", ["[ Add URL ]", "[ My URL ]", "[ List URLs ]", "[ Reset ]", "[ On ]", "[ Off ]"], comChannel); } listen(integer chan,string name,key id,string mes) { if(chan != 0) jump menu_commands; if(!intercomON) return; if(!llGetListLength(urlBeacons)) return; integer idx = llListFindList(urlBeacons, (list)selfURL); list intercoms = llDeleteSubList(urlBeacons, idx, idx); if(!llGetListLength(intercoms)) return; do { string intercom = llList2String(intercoms, 0); if(!isValidURL(intercom)) jump next_intercom; requestQueue += llList2CSV([intercom, "PUT", name + ": " + mes]); @next_intercom; intercoms = llDeleteSubList(intercoms, 0, 0); } while(llGetListLength(intercoms) != 0); return; @menu_commands; if(mes != "[ On ]") jump menu_off; intercomON = TRUE; llInstantMessage(id, "Intercom is now: ON"); jump close_chans; @menu_off; if(mes != "[ Off ]") jump menu_reset; intercomON = FALSE; llInstantMessage(id, "Intercom is now: OFF"); jump close_chans; @menu_reset; if(mes != "[ Reset ]") jump menu_add_url; llMessageLinked(LINK_THIS, 0, "", "@wipe_storage"); llResetScript(); return; @menu_add_url; if(mes != "[ Add URL ]") jump menu_my_url; llInstantMessage(id, "Please paste an URL on channel " + (string)89 + " in order to register it with the system by typing:\n/" + (string)89 + " URL\nWhere URL is the URL of another intercom."); registerHandle = llListen(89, "", id, ""); jump close_menu; @menu_my_url; if(mes != "[ My URL ]") jump menu_list_urls; if(selfURL == "") { llInstantMessage(id, "I don't have an URL registered yet."); jump close_chans; } llInstantMessage(id, "My URL is: " + selfURL); jump close_chans; @menu_list_urls; if(mes != "[ List URLs ]") jump register_urls; if(!llGetListLength(urlBeacons)) { llInstantMessage(id, "No beacons registered."); jump close_chans; } llInstantMessage(id, "----- INTERCOMS -----"); integer itra=llGetListLength(urlBeacons)-1; do { llInstantMessage(id, llList2String(urlBeacons, itra)); } while(--itra>-1); llInstantMessage(id, "----- INTERCOMS -----"); jump close_chans; @register_urls; if(chan != 89) return; mes = llList2String(llParseString2List(mes, [" "], [""]), 0); if(!isValidURL(mes)) { llInstantMessage(id, "Badly formatted URL"); return; } if(llListFindList(urlBeacons, (list)mes) != -1) { llInstantMessage(id, "URL already exists."); jump close_chans; } urlBeacons += mes; llInstantMessage(id, "URL: " + mes + " has been registered."); @close_chans; llListenRemove(registerHandle); @close_menu; llListenRemove(menuHandle); } no_sensor() { list request = llCSV2List(llList2String(requestQueue, 0)); string url = llList2String(request, 0); if(!isValidURL(url)) jump skip_request; llHTTPRequest(url, [HTTP_METHOD, llList2String(request, 1)], llList2String(request, 2)); @skip_request; requestQueue = llDeleteSubList(requestQueue, 0, 0); } timer() { // if we have no beacons or we are the only ones in the beacon list don't do anything if(llGetListLength(urlBeacons) == 0 || (llGetListLength(urlBeacons) == 1 && llList2String(urlBeacons, 0) == selfURL)) return; // if we still have pending synchronization requests, don't do anything integer itra = llGetListLength(requestQueue)-1; do { list requests = llCSV2List(llList2String(requestQueue, itra)); if(llList2String(requests, 1) == "POST") return; } while(--itra>-1); // enqueue synchronization requests. itra = llGetListLength(urlBeacons)-1; do { string firstBeacon = llList2String(urlBeacons, itra); integer itrb = llGetListLength(urlBeacons)-1; do { if(itrb == itra) jump skip_beacon; string secondBeacon = llList2String(urlBeacons, itrb); requestQueue += llList2CSV([firstBeacon, "POST", secondBeacon + " " + INTERCOM_PASSWORD]); @skip_beacon; } while(--itrb>-1); } while(--itra>-1); } http_request(key id, string method, string body) { if(method != URL_REQUEST_GRANTED) jump method_url_request_denied; selfURL = body; urlBeacons += body; return; @method_url_request_denied; if(method != URL_REQUEST_DENIED) jump method_post; llRequestURL(); return; @method_post; if(method != "POST") jump method_put; list postPayload = llParseString2List(body, [" "], [""]); string firstPayload = llList2String(postPayload, 0); string secondPayload = llList2String(postPayload, 1); if(!isValidURL(firstPayload)) jump sync_error; if(secondPayload != INTERCOM_PASSWORD) jump sync_error; if(llListFindList(urlBeacons, (list)firstPayload) != -1) jump sync_error; urlBeacons += firstPayload; llHTTPResponse(id, 200, "SYNCED"); return; @sync_error; llHTTPResponse(id, 200, "ERROR"); return; @method_put; if(method != "PUT") jump say_error; if(!intercomON) jump say_error; llHTTPResponse(id, 200, "SAID"); list sayPayload = llParseString2List(body, [": "], [""]); llSetObjectName(llList2String(sayPayload, 0)); llSay(0, llList2String(sayPayload, 1)); return; @say_error; llHTTPResponse(id, 200, "ERROR"); return; } http_response(key id, integer status, list metadata, string body) { if(status != 404) return; // if we got a bad beacon string badBeacon = llList2String(llParseString2List(body, [": ", "'"], [""]), 1); // remove the bad beacon from the beacon list and queues. urlBeacons = wasDeleteSubListMatch(urlBeacons, badBeacon); } changed(integer change) { if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_INVENTORY)) llResetScript(); } on_rez(integer pin) { llResetScript(); } }