///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3      //
///////////////////////////////////////////////////////////////////////////
//
// An eggdrop-like group bot using Corrade.
//
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    list a = llParseStringKeepNulls(data, ["&", "="], []);
    integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
    if(i != -1) return llList2String(a, 2*i+1);
    return "";
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueDelete(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    integer i = llListFindList(
        llList2ListStrided(
            llParseString2List(data, ["&", "="], []),
            0, -1, 2
        ),
    [ k ]);
    if(i != -1) return llDumpList2String(
        llDeleteSubList(
            llParseString2List(data, ["&"], []),
        i, i),
    "&");
    return data;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueSet(string k, string v, string data) {
    if(llStringLength(k) == 0) return "";
    if(llStringLength(v) == 0) return "";
    if(llStringLength(data) == 0) return k + "=" + v;
    integer i = llListFindList(
        llList2ListStrided(
            llParseString2List(data, ["&", "="], []),
            0, -1, 2
        ),
    [ k ]);
    if(i != -1) return llDumpList2String(
        llListReplaceList(
            llParseString2List(data, ["&"], []),
            [ k + "=" + v ],
        i, i),
    "&");
    return data + "&" + k + "=" + v;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
    list k = llList2ListStrided(data, 0, -1, 2);
    list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
    data = [];
    do {
        data += llList2String(k, 0) + "=" + llList2String(v, 0);
        k = llDeleteSubList(k, 0, 0);
        v = llDeleteSubList(v, 0, 0);
    } while(llGetListLength(k) != 0);
    return llDumpList2String(data, "&");
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// escapes a string in conformance with RFC1738
string wasURLEscape(string i) {
    string o = "";
    do {
        string c = llGetSubString(i, 0, 0);
        i = llDeleteSubString(i, 0, 0);
        if(c == "") jump continue;
        if(c == " ") {
            o += "+";
            jump continue;
        }
        if(c == "\n") {
            o += "%0D" + llEscapeURL(c);
            jump continue;
        }
        o += llEscapeURL(c);
@continue;
    } while(i != "");
    return o;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// unescapes a string in conformance with RFC1738
string wasURLUnescape(string i) {
    return llUnescapeURL(
        llDumpList2String(
            llParseString2List(
                llDumpList2String(
                    llParseString2List(
                        i,
                        ["+"],
                        []
                    ),
                    " "
                ),
                ["%0D%0A"],
                []
            ),
            "\n"
        )
    );
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasListToCSV(list l) {
    list v = [];
    do {
        string a = llDumpList2String(
            llParseStringKeepNulls(
                llList2String(
                    l,
                    0
                ),
                ["\""],
                []
            ),
            "\"\""
        );
        if(llParseStringKeepNulls(
            a,
            [" ", ",", "\n", "\""], []
            ) !=
            (list) a
        ) a = "\"" + a + "\"";
        v += a;
        l = llDeleteSubList(l, 0, 0);
    } while(l != []);
    return llDumpList2String(v, ",");
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
list wasCSVToList(string csv) {
    list l = [];
    list s = [];
    string m = "";
    do {
        string a = llGetSubString(csv, 0, 0);
        csv = llDeleteSubString(csv, 0, 0);
        if(a == ",") {
            if(llList2String(s, -1) != "\"") {
                l += m;
                m = "";
                jump continue;
            }
            m += a;
            jump continue;
        }
        if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
            m += a;
            csv = llDeleteSubString(csv, 0, 0);
            jump continue;
        }
        if(a == "\"") {
            if(llList2String(s, -1) != a) {
                s += a;
                jump continue;
            }
            s = llDeleteSubList(s, -1, -1);
            jump continue;
        }
        m += a;
@continue;
    } while(csv != "");
    // postcondition: length(s) = 0
    return l + m;
}
 
// for notecard reading
integer line = 0;
 
// key-value data will be read into this list
list tuples = [];
string configuration = "";
// Corrade's online status.
integer online = FALSE;
integer compatible = FALSE;
string URL = "";
 
// The notifications to bind to.
list notifications = [ "group", "membership", "login", "MQTT" ];
 
default {
    state_entry() {
        if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
            llOwnerSay("[Control] Sorry, could not find a configuration inventory notecard.");
            return;
        }
        // DEBUG
        llOwnerSay("[Control] Reading configuration file...");
        llGetNotecardLine("configuration", line);
    }
    dataserver(key id, string data) {
        if(data == EOF) {
            // invariant, length(tuples) % 2 == 0
            if(llGetListLength(tuples) % 2 != 0) {
                llOwnerSay("[Control] Error in configuration notecard.");
                return;
            }
            key CORRADE = llList2Key(
                tuples,
                llListFindList(
                    tuples,
                    [
                        "corrade"
                    ]
                )
            +1);
            if(CORRADE == NULL_KEY) {
                llOwnerSay("[Control] Error in configuration notecard: corrade");
                return;
            }
            string GROUP = llList2String(
                tuples,
                llListFindList(
                    tuples,
                    [
                        "group"
                    ]
                )
            +1);
            if(GROUP == "") {
                llOwnerSay("[Control] Error in configuration notecard: group");
                return;
            }
            string PASSWORD = llList2String(
                tuples,
                llListFindList(
                    tuples,
                    [
                        "password"
                    ]
                )
            +1);
            if(PASSWORD == "") {
                llOwnerSay("[Control] Error in configuration notecard: password");
                return;
            }
            string VERSION = llList2String(
                tuples,
                llListFindList(
                    tuples,
                    [
                        "version"
                    ]
                )
            +1);
            if(VERSION == "") {
                llOwnerSay("[Control] Error in configuration notecard: version");
                return;
            }
            // DEBUG
            llOwnerSay("[Control] Read configuration notecard...");
            configuration = wasKeyValueEncode(tuples);
            // GC
            tuples = [];
            state request_url_notifications;
        }
        if(data == "") jump continue;
        // No support for inline comments for this one! Needs parsing!
        integer i = llSubStringIndex(data, "#");
        if(i == 0) data = llDeleteSubString(data, i, -1);
        list o = llParseString2List(data, ["="], []);
        // get rid of starting and ending quotes
        string k = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o,
                        0
                    ),
                STRING_TRIM),
            ["\""], []
        ), "\"");
        string v = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o,
                        1
                    ),
                STRING_TRIM),
            ["\""], []
        ), "\"");
        if(k == "" || v == "") jump continue;
        tuples += k;
        tuples += v;
@continue;
        llGetNotecardLine("configuration", ++line);
    }
    attach(key id) {
        llResetScript();
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
            llResetScript();
        }
    }
}
 
state request_url_notifications {
    state_entry() {
        // DEBUG
        llOwnerSay("[Control] Requesting URL...");
        llRequestURL();
    }
    http_request(key id, string method, string body) {
        if(method != URL_REQUEST_GRANTED) return;
        URL = body;
        // DEBUG
        llOwnerSay("[Control] Got URL...");
        state unbind_notifications;
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        if((change & CHANGED_INVENTORY) ||
            (change & CHANGED_REGION_START) ||
            (change & CHANGED_OWNER)) {
            llResetScript();
        }
    }
}
 
state unbind_notifications {
    state_entry() {
        // DEBUG
        llOwnerSay("[Control] Releasing notifications...");
        llInstantMessage(
            (key)wasKeyValueGet(
                "corrade",
                configuration
            ),
            wasKeyValueEncode(
                [
                    "command", "notify",
                    "group", wasURLEscape(
                        wasKeyValueGet(
                            "group",
                            configuration
                        )
                    ),
                    "password", wasURLEscape(
                        wasKeyValueGet(
                            "password",
                            configuration
                        )
                    ),
                    "action", "remove",
                    "tag", wasURLEscape(
                        wasKeyValueGet(
                            "notification tag",
                            configuration
                        )
                    ),
                    "callback", wasURLEscape(URL)
                ]
            )
        );
        llSetTimerEvent(60);
    }
    http_request(key id, string method, string body) {
        llHTTPResponse(id, 200, "OK");
        if(wasKeyValueGet("command", body) != "notify" ||
            wasKeyValueGet("success", body) != "True") {
            // DEBUG
            llOwnerSay("[Control] Unable to release tag: " +
                wasURLUnescape(
                    wasKeyValueGet("error", body)
                )
            );
            llResetScript();
        }
        state bind_notifications;
    }
    timer() {
        llOwnerSay("[Control] Timeout releasing notifications");
        llResetScript();
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        if((change & CHANGED_INVENTORY) ||
            (change & CHANGED_REGION_START) ||
            (change & CHANGED_OWNER)) {
            llResetScript();
        }
    }
    state_exit() {
        llSetTimerEvent(0);
    }
}
 
state bind_notifications {
    state_entry() {
        // DEBUG
        llOwnerSay("[Control] Binding to notifications...");
        llInstantMessage(
            wasKeyValueGet(
                "corrade",
                configuration
            ),
            wasKeyValueEncode(
                [
                    "command", "notify",
                    "group", wasURLEscape(
                        wasKeyValueGet(
                            "group",
                            configuration
                        )
                    ),
                    "password", wasURLEscape(
                        wasKeyValueGet(
                            "password",
                            configuration
                        )
                    ),
                    "action", "add",
                    "type", wasURLEscape(
                        wasListToCSV(
                            notifications
                        )
                    ),
                    "URL", wasURLEscape(URL),
                    "tag", wasURLEscape(
                        wasKeyValueGet(
                            "notification tag",
                            configuration
                        )
                    ),
                    "callback", wasURLEscape(URL)
                ]
            )
        );
        llSetTimerEvent(60);
    }
    http_request(key id, string method, string body) {
        llHTTPResponse(id, 200, "OK");
        if(wasKeyValueGet("command", body) != "notify" ||
            wasKeyValueGet("success", body) != "True") {
            // DEBUG
            llOwnerSay("[Control] Unable to bind notifications: " +
                wasURLUnescape(
                    wasKeyValueGet("error", body)
                )
            );
            llResetScript();
        }
        state version_check;
    }
    timer() {
        llOwnerSay("[Control] Timeout binding notifications");
        llResetScript();
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        if((change & CHANGED_INVENTORY) ||
            (change & CHANGED_REGION_START) ||
            (change & CHANGED_OWNER)) {
            llResetScript();
        }
    }
    state_exit() {
        llSetTimerEvent(0);
    }
}
 
state version_check {
    state_entry() {
        // DEBUG
        llOwnerSay("[Control] Checking version...");
        llInstantMessage(
            wasKeyValueGet(
                "corrade",
                configuration
            ),
            wasKeyValueEncode(
                [
                    "command", "version",
                    "group", wasURLEscape(
                        wasKeyValueGet(
                            "group",
                            configuration
                        )
                    ),
                    "password", wasURLEscape(
                        wasKeyValueGet(
                            "password",
                            configuration
                        )
                    ),
                    "callback", wasURLEscape(URL)
                 ]
            )
        );
        llSetTimerEvent(60);
    }
    http_request(key id, string method, string body) {
        llHTTPResponse(id, 200, "OK");
        llSetTimerEvent(0);
 
        if(wasKeyValueGet("success", body) != "True") {
            llOwnerSay("[Control] Version check failed...");
            return;
        }
 
        list v = llParseString2List(
            wasKeyValueGet(
                "data",
                body
            ),
            ["."],
            []
        );
        integer receivedVersion = (integer)(llList2String(v, 0) + llList2String(v, 1));
        v = llParseString2List(
            wasKeyValueGet(
                "version",
                configuration
            ),
            ["."],
            []
        );
        integer notecardVersion = (integer)(llList2String(v, 0) + llList2String(v, 1));
        if(receivedVersion < notecardVersion) {
            llOwnerSay("[Control] Version is incompatible! You need a Corrade of at least version: " +
                wasKeyValueGet(
                    "version",
                    configuration
                ) +
                " for this template."
            );
            compatible = FALSE;
            return;
        }
 
        // Add the URL to the configuration so it can be used for all components.
        configuration = wasKeyValueSet("URL", URL, configuration);
 
        // DEBUG
        llOwnerSay("[Control] Version is compatible!");
        compatible = TRUE;
 
        state serve_configuration;
    }
    timer() {
        llOwnerSay("[Control] Timeout checking version...");
        llResetScript();
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        if((change & CHANGED_INVENTORY) ||
            (change & CHANGED_REGION_START) ||
            (change & CHANGED_OWNER)) {
            llResetScript();
        }
    }
    state_exit() {
        llSetTimerEvent(0);
    }
}
 
state serve_configuration {
    state_entry() {
        // DEBUG
        llOwnerSay("[Control] Serving configuration and passing callbacks...");
    }
    http_request(key id, string method, string body) {
        llHTTPResponse(id, 200, "OK");
 
        if(wasKeyValueGet("command", body) != "") {
            llMessageLinked(LINK_THIS, 0, body, "callback");
            return;
        }
 
        // Check if this group message is from Corrade and passed through Discord.
        if(wasKeyValueGet("type", body) == "group" &&
            llToUpper(wasKeyValueGet("agent", body)) == llToUpper(wasKeyValueGet("corrade", configuration)) &&
            llSubStringIndex(wasURLUnescape(wasKeyValueGet("message", body)), "[Discord]:") != -1) {
 
            // Split message in Discord discriminator and message body.
            list messageSplit = llParseString2List(
                wasURLUnescape(
                    wasKeyValueGet("message", body)
                ),
                ["[Discord]:"],
                []
            );
 
            // Retrive the Discord discriminator.
            string did = llStringTrim(
                llList2String(
                    messageSplit,
                    0
                ),
                STRING_TRIM
            );
 
 
            // Retrieve a list of Discord discriminator that are allowed to send administrative commands.
            list admins = wasCSVToList(wasKeyValueGet("discord admin", configuration));
 
            // If the sender is not amongst the administrative Discord discriminators, then strip the agent details.
            if(llListFindList(admins, [ did ]) == -1) {
                // DEBUG
                llOwnerSay("[Control] Non admin, issuing command, strip the agent details from the command.");
 
                body = wasKeyValueDelete("firstname", body);
                body = wasKeyValueDelete("lastname", body);
                body = wasKeyValueDelete("agent", body);
            }
 
            // Pack the message back without the Discord discriminator identifier.
            body = wasKeyValueSet(
                "message",
                wasURLEscape(
                    llStringTrim(
                        llList2String(
                            messageSplit,
                            1
                        ),
                        STRING_TRIM
                    )
                ),
                body
            );
        }
 
        llMessageLinked(LINK_THIS, 0, body, "notification");
    }
    link_message(integer sender, integer num, string message, key id) {
        if(message != "configuration") return;
 
        llMessageLinked(LINK_THIS, 0, configuration, "configuration");
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        if((change & CHANGED_INVENTORY) ||
            (change & CHANGED_REGION_START) ||
            (change & CHANGED_OWNER)) {
            llResetScript();
        }
    }
    state_exit() {
        llSetTimerEvent(0);
    }
}

secondlife/scripted_agents/corrade/projects/in_world/eggdrop/control.txt ยท Last modified: 2022/11/24 07:45 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.