Chatbot
Compile and run the LSL you chat on a channel, faster than you can compile and run it thru the 2007-08 SL GUI.
Created by Ppaatt Lynagh. "Allow anyone to copy", "next owner can copy & modify", and "mark item for sale price L$0", are the rights on my copies of this script in SL, as of 2007-09-04.
The LSL commands you can chat include:
/7 llSetColor(<0.3, 0.3, 0.3>, ALL_SIDES); /7 llSetColor(<1, 1, 1>, ALL_SIDES);
/7 llSetText("look at me white", <1.0, 1.0, 1.0>, 1.0);
/7 llSetText("look at me green", <0.0, 1.0, 0.0>, 1.0);
/7 llSetText("look at me black", <0.0, 0.0, 0.0>, 1.0);
/7 llSetText("", <0.0, 0.0, 0.0>, 1.0);
/7 ZERO_VECTOR /7 llGetAgentSize(llGetLinkKey(llGetNumberOfPrims())) // often not ZERO_VECTOR while avatar sits
The sample script follows.
To keep this sample script conveniently short, this script understands only a few library function names, a few code constant names, a few punctuation marks, and a few data types. I hope you find it easy to add the library function names and code constant names that interest you, even prefix (not infix) function names like the "*" multiply operator. Adding the list data type as a possible parameter type might require real work: LSL doesn't understand lists of lists yet.
See the discussion tab for talk of more example command lines that we could/ should soon add.
Enjoy,
// http://wiki.secondlife.com/wiki/Chatbot
//
// Compile and run the Lsl you type on a channel,
// faster than you can thru the 2007-08 SL GUI.
//
integer theChannel = 7;
// Evaluate any one parameter
list theCodes = [
TRUE, FALSE,
PI,
TWO_PI,
PI_BY_TWO,
DEG_TO_RAD,
RAD_TO_DEG,
SQRT2,
NULL_KEY,
ALL_SIDES,
EOF,
ZERO_VECTOR,
ZERO_ROTATION,
0
];
list theCodenames = [
"TRUE", "FALSE",
"PI",
"TWO_PI",
"PI_BY_TWO",
"DEG_TO_RAD",
"RAD_TO_DEG",
"SQRT2",
"NULL_KEY",
"ALL_SIDES",
"EOF",
"ZERO_VECTOR",
"ZERO_ROTATION",
"0"
];
list valueOf(string word)
{
if (0 <= llSubStringIndex(word, "."))
{
return [(float) word];
}
else if (0 <= llSubStringIndex("0123456789", llGetSubString(word, 0, 0)))
{
return [(integer) word];
}
else
{
integer index = llListFindList(theCodenames, [word]);
if (0 <= index)
{
return llList2List(theCodes, index, index);
} else {
return [word]; // unevaluated
}
}
}
// Strip the leading and trailing quotes from a quoted evaluated string
string cbList2String(list parameters, integer index)
{
string chars = llList2String(parameters, index);
if (llStringLength(chars) <= 2)
{
return "";
}
return llGetSubString(chars, 1, -2);
}
// Pass the parameters to the named routine.
// Return no result, one result, else the callable and parameters.
list resultOf(string callable, list parameters)
{
list ps = parameters; // abbreviate "parameters"
llOwnerSay(callable + "(" + llList2CSV(ps) + ")");
// Return one result to say callable found and one result collected
// Or fall thru to say callable found and no result collected
if ("llGetAgentSize" == callable) // id
{
return [llGetAgentSize(llList2Key(ps, 0))];
}
else if ("llGetLinkKey" == callable) // linknum
{
return [llGetLinkKey(llList2Integer(ps, 0))];
}
else if ("llGetNumberOfPrims" == callable) // linknum
{
return [llGetNumberOfPrims()];
}
else if ("llSetColor" == callable) // color, face
{
llSetColor(llList2Vector(ps, 0), llList2Integer(ps, 1));
}
else if ("llSetText" == callable) // text, color, alpha
{
llSetText(cbList2String(ps, 0), llList2Vector(ps, 1), llList2Float(ps, 2));
}
// Return more than one result to say callable not found
else
{
return [callable, "not found"];
}
// Return no result to say callable found but no result
return [];
}
// Obey one command.
list callEach(list words)
{
// Begin with almost nothing
list values = [""];
list depths = [0];
// Take each action in order
integer lenWords = llGetListLength(words);
integer index;
for (index = 0; index < lenWords; ++index)
{
string word = llList2String(words, index);
integer beyond = llGetListLength(values);
// Count values of "( ... )" or of "< ... >"
if ((word == "(") || (word == "<"))
{
depths += [beyond]; // push the depth
}
else if ((word == ")") || (word == ">"))
{
// Pop the depth
integer first = llList2Integer(depths, -1);
depths = llList2List(depths, 0, -2); // pop the tail
// Pop the parameters
integer last = beyond - 1;
list parameters = [];
if (first <= last)
{
parameters = llList2List(values, first, last);
values = llList2List(values, 0, first - 1);
}
// Join together the components of a vector
if (word == ">")
{
integer depth = llGetListLength(parameters);
if (depth == 3)
{
vector vec;
vec.x = llList2Float(parameters, 0);
vec.y = llList2Float(parameters, 1);
vec.z = llList2Float(parameters, 2);
values += [vec];
}
// Join together the components of a rotation
if (depth == 4)
{
rotation rot;
rot.x = llList2Float(parameters, 0);
rot.y = llList2Float(parameters, 1);
rot.z = llList2Float(parameters, 2);
rot.s = llList2Float(parameters, 2);
values += [rot];
}
}
// Call with a list of parameters (of non-list types)
else if (word == ")")
{
string callable = llList2String(values, -1);
values = llList2List(values, 0, -2); // pop the tail
list results = resultOf(callable, parameters);
if (llGetListLength(results) == 1)
{
values += results;
}
}
}
// Push any other word as a parameter
else
{
values += word;
}
}
// Succeed
return llList2List(values, 1, -1); // pop the head (only if head is not tail)
}
// Divide a string into words,
// by returning each of the spacers as a word
// and then all the chars between as words.
list split(string source, list spacers)
{
list outputs = [source];
// Pass never more than 8 spacers to llParseString2List
integer lenSpacers = llGetListLength(spacers);
integer ss;
for (ss = 0; ss < lenSpacers; ss += 8)
{
list someSpacers = llList2List(spacers, ss, ss + 7);
// Call llParseString2List again for each result so far
list inputs = outputs;
outputs = [];
integer lenInputs = llGetListLength(inputs);
integer ii;
for (ii = 0; ii < lenInputs; ++ii)
{
string input = llList2String(inputs, ii);
outputs += llParseString2List(input, [], someSpacers);
}
}
// Succeed
return outputs;
}
// Divide the command into words.
// Take care to join together the words of each quotation.
// Return a list of the words of the command.
list valueEach(string chars)
{
// Begin with nothing
list values = [];
// Describe the language of an LSL expression without operators
string escape = "\\";
string quote = "\"";
list separators = ["\t", " ", ",", ";"];
list spacers = [quote, escape, "(", ")", "/", "<", ">", "[", "]"];
// Divide the command into words, but also shatter quotations
list words = split(chars, separators + spacers);
integer lenWords = llGetListLength(words);
// Consider each word or word of a quotation
integer index = 0;
while (index < lenWords)
{
string word = llList2String(words, index++);
// Join together the words of a quotation
if (word == quote)
{
string value = "";
do
{
word = llList2String(words, index++);
if (word != quote)
{
value += word;
}
} while ((word != quote) && (index < lenWords));
// Quote the quotation simply, so the quotation is never found in spacers
values += quote + value + quote;
}
// Discard slash slash commentary
else if ((word == "/") && (llList2String(words, index) == "/"))
{
return values;
}
// Discard separators
else if ((0 <= llListFindList(separators, [word])) || (word == ""))
{
;
}
// Keep spacers
else if (0 <= llListFindList(spacers, [word]))
{
values += word;
}
// Evaluate other words
else
{
values += valueOf(word);
}
}
// Succeed
return values;
}
// Hear and obey the chat of the owner at the channel.
default
{
state_entry()
{
llListen(theChannel, "", llGetOwner(), "");
}
listen(integer channel, string name, key id, string message)
{
llOwnerSay(message);
list values = valueEach(message);
// llOwnerSay(llList2CSV(values));
list results = callEach(values);
if (results != [])
{
llOwnerSay(llList2CSV(results));
}
}
}