Sample Notecard

All the agents listed in the Agents notecard should contain all your sales agents line-by-line. The format is given by the avatar key and the name of the avatar separated by a hash (#) sign. You can see an example below:

febf982d-c541-4e73-a807-a2faf7c878c0#Morgan LeFay
8ed5bedb-3e15-4a50-86a1-41c4c8ec1b74#Some Agent

Please remember to leave a blank line in the notecard after the agent lines.

Code: Agent Registry

agentregistry.lsl
///////////////////////////////////////////////////////////////////////////
//  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();
    }
}