eliza chatbot

Creating scripts
Post Reply
User avatar
Gregg Legendary
Posts: 123
Joined: Sun Jun 22, 2014 10:22 am
Has thanked: 214 times
Been thanked: 213 times

eliza chatbot

Post by Gregg Legendary »

this script was open source and used to work in sl and iwz. but now that i am trying to use it again, here... it errors out. Many years ago i coulda fixed it, but unfortunately i am no longer one with brain power. Can anyone fix it?

// ELIZA is a famous 1966 computer program by Joseph Weizenbaum
// ELIZA is a parody of a Rogerian therapist
// ELIZA rephrases many of the patient's statements as questions and posing them to the patient.

// This LSL version was originally ported by Dedric Mauriac
// This LSL version is a port from a BASIC implementation written by John Schugg in January 1985.

// Data Setup:
// Keywords and patterns are contained in a seperate notecard
// a keyword set appears on a line delimited with semi-colon and prefixed with number of responses
// each responses is written on its own line
// if more then one response follows a set of keywords, a random response will be chosen
// the last keyword in the notecard will always be used for responses that are not understood
// blank lines should only appear before keywords or after the last response.
// blank lines are optional and only used for easier management of data

// how long to wait before responding and listening for next user input
float delay = 7.5;

// name of notecard containing patterns and responses
string replyNote = "Chatbot.Eliza.txt";

// stated when end-user repeats themselves
string repeatReply = "please do not repeat yourself";

// greeting
string introduction = "Have any problems? Let me help you.";

// don't listen to objects?
integer ignoreObjects = TRUE;

list keywords; // recognized keywords
list matchStart; // start position in notecard for responses to each keyword
list matchCount; // number of responses for each keyword
integer lastLine;

// used to rephrase input into responses
list conjugations = [
"are", "am",
"were", "was",
"you", "i",
"your", "my",
"ive", "youve",
"im", "youre",
"you", "me"
];

// number of lines in reply notecard
integer replyLines;

// id of request for notecard line count
key replyCountId;

// current line number being requested from notecard
integer replyLine;

// id of request for text in notecard for initialization
key replyLineId;

// characters recognized from user input
string recognized = "abcdefghijklmnopqrstuvwxyz ";

// id of listener for user input
integer listener;

// id of request for text in notecard as a response to input
key responseId;

string said; // what the user said
string matched; // keyword/phrase the program understood

integer processing = FALSE; // state if program still processing last input

string say; // response to user input

initialize()
{
lastLine = -1;
replyLine = 0;
replyLines = -1;
showProgress();
keywords = [];
matchCount = [];
matchStart = [];
replyCountId = llGetNumberOfNotecardLines(replyNote);
}
string conjugateWord(string word)
{
// find word to replace
integer i = llListFindList(conjugations, [word]);

// not found
if(i == -1) return word;

// word found. opposites in pairs.
if(i % 2 == 0) return llList2String(conjugations, i + 1);
return llList2String(conjugations, i - 1);
}
string processConjugates()
{
// rephrase what user said after the matched keyword.

integer i = llSubStringIndex(said, matched);
string text = llGetSubString(said, i + llStringLength(matched), -1);
list words = llParseString2List(text, [" "], []);
text = "";

integer n = llGetListLength(words);

for(i = 0; i < n; i++)
text += " " + conjugateWord(llList2String(words, i));

return text;
}
processResponse(string message)
{
// format response
message = llToLower(message);

// if wildcard in response, rephrase what user said
integer i = llSubStringIndex(message, "*");
if(i != -1)
{
message = llDeleteSubString(message, i, i);
message = llInsertString(message, i, processConjugates());
}

// prepare to respond
say = message;
llSetTimerEvent(delay);
}
respondTo(integer keywordIndex)
{

// determine what keyword to response to
matched = llList2String(keywords, keywordIndex);


// determine what to respond with
integer start = llList2Integer(matchStart, keywordIndex);
integer count = llList2Integer(matchCount, keywordIndex);
integer line = start + llFloor(llFrand(count));

// prevent repeat response
while(line == lastLine && count != 1)
line = start + llFloor(llFrand(count));
lastLine = line;



// request text for chosen response
responseId = llGetNotecardLine(replyNote, line);
}
processMessage(string message)
{
// don't process if we are still working on the last message
if(processing) return;
processing = TRUE;

message = formatMessage(message);

// do not bother with repeat input
if(said == message)
{
say = repeatReply;
llSetTimerEvent(delay);
return;
}

// remember what user said last
said = message;

// itterate through keywords
integer n = llGetListLength(keywords);
integer i;
for(i = 0; i < n; i++)
{
// padd keyword with spaces
string keyword = " " + llList2String(keywords, i) + " ";

// if keyword in user-input, respond to it.
if(llSubStringIndex(message, keyword) != -1)
{
respondTo(i);
return;
}
}

// since keyword not found, response with last keywords replies (not understood)
respondTo(n - 1);
}
string formatMessage(string message)
{
// remove punctuation and change everything to lowercase

string message = llToLower(message);
string format = " ";
integer i;
integer n = llStringLength(message);
for(i = 0; i < n; i++)
{
string char = llGetSubString(message, i, i);
if(llSubStringIndex(recognized, char) != -1)
format += char;
}
return format + " ";
}
showProgress()
{
// determine how much is done
integer percent = (integer)(((float)replyLine / (float)replyLines) * 100);

// build a text based progress bar - 59% [|||||......]
string progress = (string)percent + "%\n[";
integer i = 0;
for(i = 0; i < 100; i+= 3)
if(i <= percent) progress += "|";
else progress += ".";
progress += "]";

llSetText("Initializing\n" + progress, <1,1,1>, 1);
}
readReplies()
{
// at end of notecard?
if(replyLine >= replyLines)
{
startSession();
return;
}

showProgress();

// read next line
replyLineId = llGetNotecardLine(replyNote, replyLine++);
}
initializeReply(string data)
{
data = llToLower(data);

// is this a pattern?
if(llSubStringIndex(data, ";") != -1)
{
list patterns = llParseString2List(data, [";"], []);
integer count = llList2Integer(patterns, 0);
keywords += llDeleteSubList(patterns, 0, 0);
replyLine++;
setMatchStart();
setMatchLength(count);
replyLine += count;
}

readReplies();
}
setMatchStart()
{
// set starting index for all keywords that do not yet have it set
integer count = llGetListLength(keywords);
integer i = llGetListLength(matchStart);
for(; i < count; i++)
matchStart += [replyLine - 1];
}
setMatchLength(integer length)
{
// determine number of replies for keyword set
integer count = llGetListLength(keywords);
integer i = llGetListLength(matchCount);
for(; i < count; i++)
matchCount += [length];
}
startSession()
{
// start listening
llSetText("", ZERO_VECTOR, 0);
if(listener != 0) llListenRemove(listener);
listener = llListen(PUBLIC_CHANNEL, "", NULL_KEY, "");
llSay(0, introduction);
}
default
{
state_entry()
{
initialize();
}
on_rez(integer start_param)
{
initialize();
}
changed(integer change)
{
if(change && CHANGED_INVENTORY) initialize();
}
dataserver(key queryId, string data)
{
// finding reply count
if(queryId == replyCountId)
{
replyLines = (integer)data;
readReplies();
return;
}

// initializing keywords/replies
if(queryId == replyLineId)
{
initializeReply(data);
return;
}

// retrieving response template
if(queryId == responseId)
{
processResponse(data);
return;
}

}
listen(integer channel, string name, key id, string message)
{
if(ignoreObjects && llGetOwnerKey(id) != id) return;

processMessage(message);
}
touch_start(integer count)
{
llSay(0, introduction);
}
timer()
{
// say what we previously decided to say
llSay(PUBLIC_CHANNEL, say);
llSetTimerEvent(0);

// listen for next input
processing = FALSE;
}
}


thanks in advance
These users thanked the author Gregg Legendary for the post:
Shandon Loring
Graham Mills
Posts: 1314
Joined: Sun Dec 23, 2012 2:26 pm
Has thanked: 1134 times
Been thanked: 1142 times

Re: eliza chatbot

Post by Graham Mills »

I think this should work. Bear in mind it needs a notecard named Chatbot.Eliza.txt.

Code: Select all

// ELIZA is a famous 1966 computer program by Joseph Weizenbaum 
// ELIZA is a parody of a Rogerian therapist
// ELIZA rephrases many of the patient's statements as questions and posing them to the patient.

// This LSL version was originally ported by Dedric Mauriac
// This LSL version is a port from a BASIC implementation written by John Schugg in January 1985.

// Data Setup:
// Keywords and patterns are contained in a seperate notecard
// a keyword set appears on a line delimited with semi-colon and prefixed with number of responses
// each responses is written on its own line
// if more then one response follows a set of keywords, a random response will be chosen
// the last keyword in the notecard will always be used for responses that are not understood
// blank lines should only appear before keywords or after the last response.
// blank lines are optional and only used for easier management of data

// how long to wait before responding and listening for next user input
float delay = 7.5;

// name of notecard containing patterns and responses
string replyNote = "Chatbot.Eliza.txt";

// stated when end-user repeats themselves
string repeatReply = "please do not repeat yourself";

// greeting
string introduction = "Have any problems? Let me help you.";

// don't listen to objects?
integer ignoreObjects = TRUE;

list keywords; // recognized keywords
list matchStart; // start position in notecard for responses to each keyword
list matchCount; // number of responses for each keyword
integer lastLine;

// used to rephrase input into responses
list conjugations = [
"are", "am",
"were", "was",
"you", "i",
"your", "my",
"ive", "youve",
"im", "youre",
"you", "me"
];

// number of lines in reply notecard
integer replyLines;

// id of request for notecard line count
key replyCountId;

// current line number being requested from notecard
integer replyLine;

// id of request for text in notecard for initialization
key replyLineId;

// characters recognized from user input
string recognized = "abcdefghijklmnopqrstuvwxyz ";

// id of listener for user input
integer listener;

// id of request for text in notecard as a response to input
key responseId;

string said; // what the user said
string matched; // keyword/phrase the program understood

integer processing = FALSE; // state if program still processing last input

string say; // response to user input

initialize()
{
lastLine = -1;
replyLine = 0;
replyLines = -1;
showProgress();
keywords = [];
matchCount = [];
matchStart = [];
replyCountId = llGetNumberOfNotecardLines(replyNote);
}
string conjugateWord(string word)
{
// find word to replace
integer i = llListFindList(conjugations, [word]);

// not found
if(i == -1) return word;

// word found. opposites in pairs.
if(i % 2 == 0) return llList2String(conjugations, i + 1);
return llList2String(conjugations, i - 1);
}
string processConjugates()
{
// rephrase what user said after the matched keyword.

integer i = llSubStringIndex(said, matched);
string text = llGetSubString(said, i + llStringLength(matched), -1);
list words = llParseString2List(text, [" "], []);
text = "";

integer n = llGetListLength(words);

for(i = 0; i < n; i++)
text += " " + conjugateWord(llList2String(words, i));

return text;
}
processResponse(string message)
{
// format response
message = llToLower(message);

// if wildcard in response, rephrase what user said
integer i = llSubStringIndex(message, "*");
if(i != -1)
{
message = llDeleteSubString(message, i, i);
message = llInsertString(message, i, processConjugates());
}

// prepare to respond
say = message;
llSetTimerEvent(delay);
}
respondTo(integer keywordIndex)
{

// determine what keyword to response to
matched = llList2String(keywords, keywordIndex);


// determine what to respond with
integer start = llList2Integer(matchStart, keywordIndex);
integer count = llList2Integer(matchCount, keywordIndex);
integer line = start + llFloor(llFrand(count));

// prevent repeat response
while(line == lastLine && count != 1)
line = start + llFloor(llFrand(count));
lastLine = line;



// request text for chosen response
responseId = llGetNotecardLine(replyNote, line);
}
processMessage(string message)
{
// don't process if we are still working on the last message
if(processing) return;
processing = TRUE;

message = formatMessage(message);

// do not bother with repeat input
if(said == message)
{
say = repeatReply;
llSetTimerEvent(delay);
return;
}

// remember what user said last
said = message;

// itterate through keywords
integer n = llGetListLength(keywords);
integer i;
for(i = 0; i < n; i++)
{
// padd keyword with spaces
string keyword = " " + llList2String(keywords, i) + " ";

// if keyword in user-input, respond to it.
if(llSubStringIndex(message, keyword) != -1)
{
respondTo(i);
return;
}
}

// since keyword not found, response with last keywords replies (not understood)
respondTo(n - 1);
}
string formatMessage(string msg)
{
// remove punctuation and change everything to lowercase

string message = llToLower(msg);
string format = " ";
integer i;
integer n = llStringLength(message);
for(i = 0; i < n; i++)
{
string char = llGetSubString(message, i, i);
if(llSubStringIndex(recognized, char) != -1)
format += char;
}
return format + " ";
}
showProgress()
{
// determine how much is done
integer percent = (integer)(((float)replyLine / (float)replyLines) * 100);

// build a text based progress bar - 59% [|||||......]
string progress = (string)percent + "%\n[";
integer i = 0;
for(i = 0; i < 100; i+= 3)
if(i <= percent) progress += "|";
else progress += ".";
progress += "]";

llSetText("Initializing\n" + progress, <1,1,1>, 1);
}
readReplies()
{
// at end of notecard?
if(replyLine >= replyLines)
{
startSession();
return;
}

showProgress();

// read next line
replyLineId = llGetNotecardLine(replyNote, replyLine++);
}
initializeReply(string data)
{
data = llToLower(data);

// is this a pattern?
if(llSubStringIndex(data, ";") != -1)
{
list patterns = llParseString2List(data, [";"], []);
integer count = llList2Integer(patterns, 0);
keywords += llDeleteSubList(patterns, 0, 0);
replyLine++;
setMatchStart();
setMatchLength(count);
replyLine += count;
}

readReplies();
}
setMatchStart()
{
// set starting index for all keywords that do not yet have it set
integer count = llGetListLength(keywords);
integer i = llGetListLength(matchStart);
for(; i < count; i++)
matchStart += [replyLine - 1];
}
setMatchLength(integer length)
{
// determine number of replies for keyword set
integer count = llGetListLength(keywords);
integer i = llGetListLength(matchCount);
for(; i < count; i++)
matchCount += [length];
}
startSession()
{
// start listening
llSetText("", ZERO_VECTOR, 0);
if(listener != 0) llListenRemove(listener);
listener = llListen(PUBLIC_CHANNEL, "", NULL_KEY, "");
llSay(0, introduction);
}
default
{
state_entry()
{
initialize();
}
on_rez(integer start_param)
{
initialize();
}
changed(integer change)
{
if(change & CHANGED_INVENTORY) initialize();
}
dataserver(key queryId, string data)
{
// finding reply count
if(queryId == replyCountId)
{
replyLines = (integer)data;
readReplies();
return;
}

// initializing keywords/replies
if(queryId == replyLineId)
{
initializeReply(data);
return;
}

// retrieving response template
if(queryId == responseId)
{
processResponse(data);
return;
}

}
listen(integer channel, string name, key id, string message)
{
if(ignoreObjects && llGetOwnerKey(id) != id) return;

processMessage(message);
}
touch_start(integer count)
{
llSay(0, introduction);
}
timer()
{
// say what we previously decided to say
llSay(PUBLIC_CHANNEL, say);
llSetTimerEvent(0);

// listen for next input
processing = FALSE;
}
}
These users thanked the author Graham Mills for the post (total 3):
Ilan TochnerGregg LegendaryGusher Castaignede
User avatar
Gregg Legendary
Posts: 123
Joined: Sun Jun 22, 2014 10:22 am
Has thanked: 214 times
Been thanked: 213 times

Re: eliza chatbot

Post by Gregg Legendary »

THANK YOU SOOO MUCH
These users thanked the author Gregg Legendary for the post (total 2):
Graham MillsShandon Loring
User avatar
Shandon Loring
Posts: 1348
Joined: Sat Oct 26, 2013 3:25 am
Has thanked: 979 times
Been thanked: 1588 times
Contact:

Re: eliza chatbot

Post by Shandon Loring »

Very cool! I set this up in an NPC at our Spaceworld but never really got it tuned in and working right...
Will give this a shot.. thanks all!
Post Reply