/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2011 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// // CONFIGURATION // ////////////////////////////////////////////////////////// // Permanent short URL map name using external database. string SHORT_URL = "sales"; // API key - can be anything, used as a password. string API_KEY = "A1364788-B702-4413-B937-7A57E32B43CE"; /////////////////////////////////////////////////////////////////////////// // INTERNALS // /////////////////////////////////////////////////////////////////////////// integer nLine = 0; list aList = []; list vList = []; key nQuery = NULL_KEY; string llURL = ""; string visitor = ""; key agentKey = NULL_KEY; string agentName = ""; // The following is an activity indicator that can be found // separately at: http://grimore.org/secondlife:spinner list spinChars = [ "↑", "↗", "→", "↘", "↓", "↙", "←", "↖" ]; string spin() { string text = llList2String(llGetLinkPrimitiveParams(LINK_THIS, [PRIM_TEXT]), 0); do { string tok = llGetSubString(text, llStringLength(text)-1, llStringLength(text)-1); if(!~llListFindList(spinChars, (list)tok) && tok != "\n" && tok != "[" && tok != "]") jump show; text = llDeleteSubString(text, llStringLength(text)-1, llStringLength(text)-1); } while(llStringLength(text)); @show; string next = llList2String(spinChars, 0); spinChars = llDeleteSubList(spinChars, 0, 0); spinChars += next; return "[" + next + "]"; } integer isAgentKey(key k) { if(k) if(k != NULL_KEY) return 1; return 0; } default { state_entry() { llSetText("Reading agent list..." + "\n" + spin(), <1,1,0>, 1); integer itra = llGetInventoryNumber(INVENTORY_NOTECARD)-1; do { if(llGetInventoryName(INVENTORY_NOTECARD, itra) == "Agents") jump notecard; } while(--itra>=0); llSetText("Failed to find Agents notecard." + "\n" + spin(), <1,1,1>, 1); return; @notecard; nQuery = llGetNotecardLine("Agents", nLine); } dataserver(key id, string data) { if(id != nQuery) return; if(data == EOF) { llSetText("Notecard read..." + "\n" + spin(), <0,1,0>, 1); state url; } if(data == "") jump next; aList += data; @next; nQuery = llGetNotecardLine("Agents", ++nLine); } changed(integer change) { if (change & (CHANGED_REGION | CHANGED_REGION_START)) llResetScript(); } on_rez(integer param) { llResetScript(); } } state url { state_entry() { llSetText("Requesting URL..." + "\n" + spin(), <1,1,0>, 1); llReleaseURL(llURL); llRequestURL(); // alarm 60s llSetTimerEvent(60); } http_request(key id, string method, string body) { if (method != URL_REQUEST_GRANTED) return; llSetText("Registering with website..." + "\n" + spin(), <1,1,0>, 1); llURL = body; nQuery = llHTTPRequest("http://server.tld:90/SIM/registerURL.php?action=register&apiKey=" + API_KEY + "&shortURL=" + SHORT_URL + "&llURL=" + body, [HTTP_METHOD, "GET"], ""); } timer() { llResetScript(); } http_response(key reqID, integer status, list metadata, string body) { if(nQuery != reqID) return; if(body != SHORT_URL) return; llHTTPResponse(nQuery, 200, "OK"); state agents; } changed(integer change) { if (change & (CHANGED_REGION | CHANGED_REGION_START)) llResetScript(); } on_rez(integer param) { llResetScript(); } } state agents { state_entry() { llSetTimerEvent(1); } http_request(key id, string method, string body) { list data = llParseString2List(body, ["="], []); string action = llList2String(data, 0); // Sign ping-pong to make sure all links are established. if(action != "ping") jump step_process; llHTTPResponse(id, 200, "pong"); return; @step_process; // This is not for us, return. if(llGetListLength(data) != 2) return; // Alright, this is something for us... string agentLine = llList2String(data, 1); // A sales agent cannot be a visitor so we knock that possiblity off. if(action != "visitor") jump step_signoff; list al = llParseString2List(agentLine, ["|"], []); if(~llListFindList(aList, (list)(llList2String(al, 0) + "#" + llList2String(al, 1)))) { llHTTPResponse(id, 200, ""); return; } llSetText("Visitor on land..." + "\n" + spin() , <1,1,0>, 1); if(llGetListLength(vList) == 0) { llSetText("☹ Visitor detected, but no agents registered. ☹" + "\n" + spin(), <1,0,0>, 1); llHTTPResponse(id, 200, ""); return; } // Store visitor over state change. visitor = llList2String(al, 0) + "|" + llList2String(al, 2); // Confirm receiving request. llHTTPResponse(id, 200, ""); // stop timer llSetTimerEvent(0); // jump to searching for agents. state onlineagents; @step_signoff; // Any signon or signoff requests must be matched to the agents the notecard. if(!~llListFindList(aList, (list)agentLine)) { llHTTPResponse(id, 200, ""); return; } if(action != "signoff") jump step_signon; nLine = llListFindList(vList, (list)agentLine); if(~nLine) vList = llDeleteSubList(vList, nLine, nLine); llHTTPResponse(id, 200, "ACK"); return; @step_signon; if(action != "signon") return; if(~llListFindList(vList, (list)agentLine)) { llHTTPResponse(id, 200, ""); return; } vList += agentLine; llHTTPResponse(id, 200, "ACK"); } timer() { if(llGetListLength(vList) != 0) jump step_agents; llSetText("☹ No agents registered. ☹" + "\n" + spin(), <1,0,0>, 1); jump send_time; @step_agents; list nList = []; integer itra = llGetListLength(vList)-1; do { nList += llList2String(llParseString2List(llList2String(vList, itra), ["#"], []), 1); } while(--itra>=0); llSetText("☺ Registered Agents ☺\n" + llDumpList2String(nList, "\n") + "\n" + spin(), <0,1,0>, 1); @send_time; llMessageLinked(LINK_THIS, 0, llDumpList2String(vList, "|"), (key)API_KEY); } changed(integer change) { if (change & (CHANGED_REGION | CHANGED_REGION_START)) llResetScript(); } on_rez(integer param) { llResetScript(); } } ////////////////////////////////////////////////////////// // We use the list of sales agents as a queue, // // dequeueing and checking whether they are online and // // enqueueing them back if they are online and active. // // This achieves a permutation of all online agents. // ////////////////////////////////////////////////////////// state onlineagents { state_entry() { // stop timer llSetTimerEvent(0); // searching... llSetText("Searching online agents..." + "\n" + spin() , <1,1,0>, 1); // pop stack @next_agent; if(llGetListLength(vList) == 0) state agents; list agentData = llParseString2List(llList2String(vList, 0), ["#"], []); if(llGetListLength(agentData) != 2) { vList = llDeleteSubList(vList, 0, 0); jump next_agent; } agentKey = llList2Key(agentData, 0); if(!isAgentKey(agentKey)) { vList = llDeleteSubList(vList, 0, 0); jump next_agent; } agentName = llList2String(agentData, 1); // remove them vList = llDeleteSubList(vList, 0, 0); // alarm 60 llSetTimerEvent(60); // ask for online status nQuery = llRequestAgentData(agentKey, DATA_ONLINE); } dataserver(key id, string data) { if(nQuery != id) return; llSetTimerEvent(0); if(data != "1") jump step_offline; // show who we're going to page. llSetText("☎ Paging: " + agentName + " ☎\n" + spin(), <0,1,0>, 1); // send visitor message key visitorKey = llList2Key(llParseString2List(visitor, ["|"], []), 0); llInstantMessage(agentKey, llKey2Name(visitorKey) + " ( secondlife:///app/agent/" + (string)visitorKey + "/about ) is visiting the land parcel at: " + llList2String(llParseString2List(visitor, ["|"], []), 1)); // push stack // add them vList += (string)agentKey + "#" + agentName; state agents; @step_offline; // extract next agent since they are offline. // searching... llSetText("Searching online agents..." + "\n" + spin() , <1,1,0>, 1); // pop stack @next_agent; if(llGetListLength(vList) == 0) state agents; list agentData = llParseString2List(llList2String(vList, 0), ["#"], []); if(llGetListLength(agentData) != 2) { vList = llDeleteSubList(vList, 0, 0); jump next_agent; } agentKey = llList2Key(agentData, 0); if(!isAgentKey(agentKey)) { vList = llDeleteSubList(vList, 0, 0); jump next_agent; } agentName = llList2String(agentData, 1); // remove them vList = llDeleteSubList(vList, 0, 0); // alarm 60 llSetTimerEvent(60); // ask for online status nQuery = llRequestAgentData(agentKey, DATA_ONLINE); } timer() { // extract next agent since they are offline. // searching... llSetText("Searching online agents..." + "\n" + spin() , <1,1,0>, 1); // pop stack @next_agent; if(llGetListLength(vList) == 0) state agents; list agentData = llParseString2List(llList2String(vList, 0), ["#"], []); if(llGetListLength(agentData) != 2) { vList = llDeleteSubList(vList, 0, 0); jump next_agent; } agentKey = llList2Key(agentData, 0); if(!isAgentKey(agentKey)) { vList = llDeleteSubList(vList, 0, 0); jump next_agent; } agentName = llList2String(agentData, 1); // remove them vList = llDeleteSubList(vList, 0, 0); // alarm 60 llSetTimerEvent(60); // ask for online status nQuery = llRequestAgentData(agentKey, DATA_ONLINE); } changed(integer change) { if (change & (CHANGED_REGION | CHANGED_REGION_START)) llResetScript(); } on_rez(integer param) { llResetScript(); } }