/source/eggdrop/control.lsl |
@@ -1,5 +1,5 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2016 - License: CC BY 2.0 // |
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
// |
// An eggdrop-like group bot using Corrade. |
@@ -17,10 +17,52 @@ |
if(i != -1) return llList2String(a, 2*i+1); |
return ""; |
} |
|
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 // |
// 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); |
@@ -34,7 +76,7 @@ |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 // |
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
// escapes a string in conformance with RFC1738 |
string wasURLEscape(string i) { |
@@ -58,7 +100,7 @@ |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 // |
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
// unescapes a string in conformance with RFC1738 |
string wasURLUnescape(string i) { |
@@ -67,15 +109,15 @@ |
llParseString2List( |
llDumpList2String( |
llParseString2List( |
i, |
["+"], |
i, |
["+"], |
[] |
), |
), |
" " |
), |
["%0D%0A"], |
), |
["%0D%0A"], |
[] |
), |
), |
"\n" |
) |
); |
@@ -82,7 +124,7 @@ |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 // |
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
string wasListToCSV(list l) { |
list v = []; |
@@ -90,18 +132,18 @@ |
string a = llDumpList2String( |
llParseStringKeepNulls( |
llList2String( |
l, |
l, |
0 |
), |
["\""], |
), |
["\""], |
[] |
), |
"\"\"" |
); |
if(llParseStringKeepNulls( |
a, |
a, |
[" ", ",", "\n", "\""], [] |
) != |
) != |
(list) a |
) a = "\"" + a + "\""; |
v += a; |
@@ -110,9 +152,48 @@ |
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 = ""; |
@@ -144,7 +225,7 @@ |
key CORRADE = llList2Key( |
tuples, |
llListFindList( |
tuples, |
tuples, |
[ |
"corrade" |
] |
@@ -157,7 +238,7 @@ |
string GROUP = llList2String( |
tuples, |
llListFindList( |
tuples, |
tuples, |
[ |
"group" |
] |
@@ -170,7 +251,7 @@ |
string PASSWORD = llList2String( |
tuples, |
llListFindList( |
tuples, |
tuples, |
[ |
"password" |
] |
@@ -183,7 +264,7 @@ |
string VERSION = llList2String( |
tuples, |
llListFindList( |
tuples, |
tuples, |
[ |
"version" |
] |
@@ -201,8 +282,9 @@ |
state request_url_notifications; |
} |
if(data == "") jump continue; |
// No support for inline comments for this one! Needs parsing! |
integer i = llSubStringIndex(data, "#"); |
if(i != -1) data = llDeleteSubString(data, i, -1); |
if(i == 0) data = llDeleteSubString(data, i, -1); |
list o = llParseString2List(data, ["="], []); |
// get rid of starting and ending quotes |
string k = llDumpList2String( |
@@ -209,10 +291,10 @@ |
llParseString2List( |
llStringTrim( |
llList2String( |
o, |
o, |
0 |
), |
STRING_TRIM), |
), |
STRING_TRIM), |
["\""], [] |
), "\""); |
string v = llDumpList2String( |
@@ -219,10 +301,10 @@ |
llParseString2List( |
llStringTrim( |
llList2String( |
o, |
o, |
1 |
), |
STRING_TRIM), |
), |
STRING_TRIM), |
["\""], [] |
), "\""); |
if(k == "" || v == "") jump continue; |
@@ -261,8 +343,8 @@ |
llResetScript(); |
} |
changed(integer change) { |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
(change & CHANGED_OWNER)) { |
llResetScript(); |
} |
@@ -275,21 +357,21 @@ |
llOwnerSay("[Control] Releasing notifications..."); |
llInstantMessage( |
(key)wasKeyValueGet( |
"corrade", |
"corrade", |
configuration |
), |
), |
wasKeyValueEncode( |
[ |
"command", "notify", |
"group", wasURLEscape( |
wasKeyValueGet( |
"group", |
"group", |
configuration |
) |
), |
"password", wasURLEscape( |
wasKeyValueGet( |
"password", |
"password", |
configuration |
) |
), |
@@ -296,7 +378,7 @@ |
"action", "remove", |
"tag", wasURLEscape( |
wasKeyValueGet( |
"notification tag", |
"notification tag", |
configuration |
) |
), |
@@ -311,7 +393,7 @@ |
if(wasKeyValueGet("command", body) != "notify" || |
wasKeyValueGet("success", body) != "True") { |
// DEBUG |
llOwnerSay("[Control] Unable to release tag: " + |
llOwnerSay("[Control] Unable to release tag: " + |
wasURLUnescape( |
wasKeyValueGet("error", body) |
) |
@@ -328,8 +410,8 @@ |
llResetScript(); |
} |
changed(integer change) { |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
(change & CHANGED_OWNER)) { |
llResetScript(); |
} |
@@ -345,21 +427,21 @@ |
llOwnerSay("[Control] Binding to notifications..."); |
llInstantMessage( |
wasKeyValueGet( |
"corrade", |
"corrade", |
configuration |
), |
), |
wasKeyValueEncode( |
[ |
"command", "notify", |
"group", wasURLEscape( |
wasKeyValueGet( |
"group", |
"group", |
configuration |
) |
), |
"password", wasURLEscape( |
wasKeyValueGet( |
"password", |
"password", |
configuration |
) |
), |
@@ -372,7 +454,7 @@ |
"URL", wasURLEscape(URL), |
"tag", wasURLEscape( |
wasKeyValueGet( |
"notification tag", |
"notification tag", |
configuration |
) |
), |
@@ -387,7 +469,7 @@ |
if(wasKeyValueGet("command", body) != "notify" || |
wasKeyValueGet("success", body) != "True") { |
// DEBUG |
llOwnerSay("[Control] Unable to bind notifications: " + |
llOwnerSay("[Control] Unable to bind notifications: " + |
wasURLUnescape( |
wasKeyValueGet("error", body) |
) |
@@ -394,7 +476,7 @@ |
); |
llResetScript(); |
} |
state serve_configuration; |
state version_check; |
} |
timer() { |
llOwnerSay("[Control] Timeout binding notifications"); |
@@ -404,8 +486,8 @@ |
llResetScript(); |
} |
changed(integer change) { |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
(change & CHANGED_OWNER)) { |
llResetScript(); |
} |
@@ -415,7 +497,7 @@ |
} |
} |
|
state serve_configuration { |
state version_check { |
state_entry() { |
// DEBUG |
llOwnerSay("[Control] Checking version..."); |
@@ -429,13 +511,13 @@ |
"command", "version", |
"group", wasURLEscape( |
wasKeyValueGet( |
"group", |
"group", |
configuration |
) |
), |
"password", wasURLEscape( |
wasKeyValueGet( |
"password", |
"password", |
configuration |
) |
), |
@@ -448,16 +530,12 @@ |
http_request(key id, string method, string body) { |
llHTTPResponse(id, 200, "OK"); |
llSetTimerEvent(0); |
if(wasKeyValueGet("command", body) != "version") { |
llMessageLinked(LINK_THIS, 0, body, "notification"); |
return; |
} |
|
|
if(wasKeyValueGet("success", body) != "True") { |
llOwnerSay("[Control] Version check failed..."); |
return; |
} |
|
|
list v = llParseString2List( |
wasKeyValueGet( |
"data", |
@@ -485,19 +563,18 @@ |
" for this template." |
); |
compatible = FALSE; |
//llReleaseURL(URL); |
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; |
//llReleaseURL(URL); |
return; |
|
state serve_configuration; |
} |
link_message(integer sender, integer num, string message, key id) { |
if(message != "configuration") return; |
llMessageLinked(LINK_THIS, 0, configuration, "configuration"); |
} |
timer() { |
llOwnerSay("[Control] Timeout checking version..."); |
llResetScript(); |
@@ -506,8 +583,8 @@ |
llResetScript(); |
} |
changed(integer change) { |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
if((change & CHANGED_INVENTORY) || |
(change & CHANGED_REGION_START) || |
(change & CHANGED_OWNER)) { |
llResetScript(); |
} |
@@ -516,3 +593,91 @@ |
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); |
} |
} |