Welcome, Guest. Please Login or Register
RiveScript HQ
 
  HomeHelpSearchLoginRegister  
 
Pages: 1
Send Topic Print
PHP RiveScript-clone interpreter (Read 757 times)
joe
Newbie
*
Offline

I Love RiveScript!

Posts: 5
Hungary
Gender: male
PHP RiveScript-clone interpreter
12/20/09 at 02:38:59
 
Hi everyone!

A few years ago I made a simple and stupid chatbot. The robot is a macro language to overcomplicated was programmed. I would like to draw up a simplified RiveScript PHP interpreter, and rewrite my chatbot.
Back to top
 
 

Sorry for my English is bad.
View Profile WWW   IP Logged
joe
Newbie
*
Offline

I Love RiveScript!

Posts: 5
Hungary
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #1 - 12/24/09 at 02:59:55
 
Hi!

Which is correct?

Code:
+ question 1
- random answer 1
- random answer 2
@ other question

or:

+ question 2
@ other question
 

Back to top
 
 

Sorry for my English is bad.
View Profile WWW   IP Logged
Kirsle
Administrator
*****
Offline

I Love YaBB 2!

Posts: 60
Los Angeles
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #2 - 12/24/09 at 08:44:05
 
The second one is correct.
Back to top
 
 
View Profile WWW 147180999 Kirsle   IP Logged
joe
Newbie
*
Offline

I Love RiveScript!

Posts: 5
Hungary
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #3 - 12/25/09 at 00:54:36
 
Thanks!

And "*" command followed by a number of unconditional answer? Randomly selected them?

Code:
+ question 1
* condition 1
* condition 2
- unconditional aswer

or:
+ question 2
* condition 1
* condition 2
- unconditional aswer 1
- unconditional answer 2

 


And the "^" command used only unconditional answer, or all command?

Code:
- answer
^answer second part
^answer third part

and:

* condition
^ condition part 2
^ condition part 3

and:
@ redirect
^ redirect part 2, etc...
 

Back to top
 
 

Sorry for my English is bad.
View Profile WWW   IP Logged
Kirsle
Administrator
*****
Offline

I Love YaBB 2!

Posts: 60
Los Angeles
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #4 - 12/25/09 at 12:51:15
 
When looking for a reply to a matched trigger...

1) If any redirection exists (@), it will redirect to that trigger immediately. Also, in case you ask this later, if there is more than one @ for some reason, only the last one is used (the spec only allows for a single @, and I coded the perl rivescript interpreter to only keep room for one, so each @ it finds is copied to the same variable overwriting the previous one).

2) If any conditions exist, it will test all the conditions in the order they appeared in the source code.

3) If no reply was found yet (no redirection, or all the conditions were false), then it looks at the replies. If there's more than one it picks them randomly.

So..

Code:
+ question 1
* condition 1
* condition 2
- unconditional aswer

If you say "question 1", it will try condiiton 1 first, then condition 2, and if neither one is true then it gives the unconditional answer.

+ question 2
* condition 1
* condition 2
- unconditional aswer 1
- unconditional answer 2

here it'll try condition 1, then condition 2, and if neither is true it will randomly pick between unconditional answer 1 and 2.

If you want random redirections, you can use the inline redirection format.

+ question
- {@answer one}
- {@answer two} 



As for the extension commands... your questions are uncovering some interesting errors in my perl module. Smiley

The ^ command is supposed to be able to extend all other commands. When it does so, it should not add any spacing of its own; if the writer intends for there to be a space between the joined parts they should insert the \s tag. This is how it *should* behave:

Code:
- answer
^answer second part
^answer third part

is equivalent to:

- answeranswer second partanswer third part

and:

* condition
^ condition part 2
^ condition part 3

is equivalent to:

* conditioncondition part 2condition part 3

and:
@ redirect
^ redirect part 2, etc...

is equivalent to:

@ redirectredirect part 2, etc... 



Although, in testing this with the Perl RiveScript interpreter, I found some bugs that I need to fix. Here's my test code:

Code:
+ test one
- answer
^ answer second part
^ answer third part

/*
   + test two
   * <id> ==
   ^ localuser =>
   ^ conditions can span multiple lines too
   - normal reply
*/

+ test three
* <id> == localuser => conditions
^ can span multiple lines too
- normal reply

+ test four
@ test
^ one

+ test five
@ test
^ \sone 



The output:

Code:
You> test one
Bot> answeranswer second partanswer third part
You> test two
Bot> ERR: No Reply Matched
You> test three
Bot> conditionscan span multiple lines too
You> test four
Bot> ERR: No Reply Matched
You> test five
Bot> answeranswer second partanswer third part
You> ^C 



So, there's a few bugs here:

* Test one behaved the way I expected it to.

* Test two gave me syntax errors, so I had to comment it out of the file. It said "Syntax error in ./extend.rs line 6: Invalid format for !Condition: should be like `* value symbol value => response` (near: * <id> ==)"

* Test three worked the way I expected it to. After the syntax error from test two I rewrote it so that the majority of the condition logic is on the same line, and the only part extended is the response part for the condition.

* Test four worked the way I expected it to. Since the join caused the line to be effectively "@ testone" with no space, it didn't match any trigger.

* Test five also worked the way I expected it to. I put a \s in there so that the redirection became "@ test one" -- however, there was a small bug. If I put the \s on the same line as the @ symbol, it gave me a syntax error, saying that "\s" isn't a legal character for a trigger or redirection.

So, generally yes, the ^ command can extend any other command. I just need to fix my Perl module so that it doesn't whine about syntax errors before it actually joins the lines together. My commented-out condition test should've worked just fine, for example.

The spec can still be changed though, so certain things could still be added to it, such as allowing multiple @ commands for a single reply and have them be chosen randomly...
Back to top
 
 
View Profile WWW 147180999 Kirsle   IP Logged
joe
Newbie
*
Offline

I Love RiveScript!

Posts: 5
Hungary
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #5 - 12/25/09 at 16:22:17
 
I did a test page, where you can try it, how to load the script of the interpreter: kaszazsolt.hu/bot/rtest.php
( Cry I can't write a link)

(Does not do anything, just print the loaded information.)

This code works well:
Code:
+ test one
% previous message\s
^ with multi
^ ple lines
- answer
^ answer second part
^ answer third part
- second answer

+ test two
* <id> ==
^ localuser =>
^ conditions/*can span*/ multiple /*lines*/ too // many comments
- normal reply

+ test three
* <id> == localuser => conditions
^ can span multiple lines too
- normal reply

+ test four
@ test
^ one

+ test five
@ test
^ \sone  

Back to top
 
 

Sorry for my English is bad.
View Profile WWW   IP Logged
Kirsle
Administrator
*****
Offline

I Love YaBB 2!

Posts: 60
Los Angeles
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #6 - 12/25/09 at 17:18:43
 
You need 5 posts before you can start posting links.  Wink

A while back I was working on a rivescript implementation in pure JavaScript (using ajax to load the rs files)... I can't seem to find it though. It supported almost as much as my current Java one does, like it could chat and some tags worked but it didn't support everything. Anyway, your PHP thing that writes out the data structures reminded me of it, cuz mine had a button to dump the data out too.
Back to top
 
 
View Profile WWW 147180999 Kirsle   IP Logged
joe
Newbie
*
Offline

I Love RiveScript!

Posts: 5
Hungary
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #7 - 12/26/09 at 02:22:10
 
The _ and the # wildcard match only one word? And the * wildchard match several words?

And Another problem.  Roll Eyes The sub variables.

Code:
! sub l o l = lol
 



The example code change "this is l o l message" text to "this is lol message". But! Change "mill o load" to "milloload"?
(sorry this stupid text, I hope to understand in some degree.  Grin)
Back to top
 
« Last Edit: 12/26/09 at 05:05:35 by joe »  

Sorry for my English is bad.
View Profile WWW   IP Logged
Kirsle
Administrator
*****
Offline

I Love YaBB 2!

Posts: 60
Los Angeles
Gender: male
Re: PHP RiveScript-clone interpreter
Reply #8 - 12/26/09 at 12:33:15
 
All the wildcard symbols should match multiple words (if the perl module doesn't do this, it's another bug and will be fixed Tongue). _ should match letters and spaces, # should match numbers and spaces, and * matches anything.

Code:
_ = /[A-Za-z\s]+?/
# = /[0-9\s]+?/
* = /.+?/ 



And when "l o l" is substituted, the "l o l" has to be isolated in the message. So "mill o load" is left unmodified. This can be seen in the `rsdemo` app that comes with the perl RiveScript module, if you run it on some code that has a substitution for "l o l" (the default set doesn't include this substitution) using `rsdemo --debug`

Quote:
You> my name is Kirsle l o l
RiveScript: Get reply to [localuser] my name is Kirsle l o l
RiveScript: Checking topic __begin__ for any %previous's.
RiveScript: Trying to match "request" against request (request)
RiveScript: Found a match!
RiveScript: Processing responses to this trigger.
RiveScript: Reply: {ok}
RiveScript: Checking topic random for any %previous's.
RiveScript: There's a %previous in this topic
RiveScript: lastReply: that is interesting please continue
RiveScript: Try to match lastReply (that is interesting please continue) to whats her name
RiveScript: Try to match lastReply (that is interesting please continue) to whats his name
RiveScript: Trying to match "my name is kirsle lol" against shutdown{weight=10000} (shutdown)
[...]
Bot> Kirsle Lol, nice to meet you.

and

You> my name is mill o load
[...]
RiveScript: Trying to match "my name is mill o load" against my name is * (my name is (.+?))
RiveScript: Found a match!
RiveScript: Processing responses to this trigger.
RiveScript: Reply: <set name=<formal>>Nice to meet you, <get name>.
RiveScript: Set uservar name => Mill O Load
Bot> Nice to meet you, Mill O Load.


PHP is pretty similar to Perl in syntax so you might be able to adapt the substitution code directly over to PHP. Here's the code:

Code:
sub _formatMessage {
	my ($self,$string) = @_;

	# Lowercase it.
	$string = lc($string);

	# Run substitutions on it.
	foreach my $pattern (@{$self->{sortlist}->{subs}}) {
		my $result = $self->{subs}->{$pattern};
		$result =~ tr/A-Za-z/N-ZA-Mn-za-m/;
		my $qm = quotemeta($pattern);
		$string =~ s/^$qm$/<rot13sub>$result<bus31tor>/ig;
		$string =~ s/^$qm(\W+)/<rot13sub>$result<bus31tor>$1/ig;
		$string =~ s/(\W+)$qm(\W+)/$1<rot13sub>$result<bus31tor>$2/ig;
		$string =~ s/(\W+)$qm$/$1<rot13sub>$result<bus31tor>/ig;
	}
	while ($string =~ /<rot13sub>(.+?)<bus31tor>/i) {
		my $rot13 = $1;
		$rot13 =~ tr/A-Za-z/N-ZA-Mn-za-m/;
		$string =~ s/<rot13sub>(.+?)<bus31tor>/$rot13/i;
	}

	# Format punctuation.
	$string =~ s/[^A-Za-z0-9 ]//g;
	$string =~ s/^\s+//g;
	$string =~ s/\s+$//g;

	return $string;
} 



Basically, it takes the result text for each substitution (e.g. "what is" for "whats"), rot13-encodes it, and then substitutes "whats" for "<rot13sub>jung vf</bus31tor>" until it's all done running substitutions, and then it goes through and looks for the rot13-encoded text it put in and decodes it and removes the temporary tags it put around it.

quotemeta() is a Perl function that automatically escapes regular expression special characters.

The regexps themselves:

Code:
/^$qm$/
/^$qm(\W+)/
/(\W+)$qm(\W+)/
/(\W+)$qm$/ 



(\W+) matches any character that is not a word character (word characters are numbers and letters, but not spaces). So for "l o l" these regexps match:

1. if the message consists *only* of the text "l o l"
2. if the message begins with "l o l" & is then followed by a space or other non-word character
3. if the message contains "l o l" as one of its words (surrounded by a space or non-word character on either side)
4. if the message ends with "l o l" and has a space or non-word char before it.

The reason for the rot13 encoding is to stop the substitutions from undoing each other. This is especially important for person substitutions, where you might have:

Code:
! person you are = I am
! person i am = you are 



If it replaces "you are" with "I am", and then replaces "I am" with "you are", you'd end up with the same text you started with. So, it rot13-encodes it when it puts in the substituted text so that the rest of the substitutions don't interfere with the text that it already replaced. It's a weak approach, I know; perhaps a better one would be to keep an array of all the substitutions it made and just insert small numbered tags, like <0>, <1>, <2>, etc... and then replace those tags with the items from the array.
Back to top
 
 
View Profile WWW 147180999 Kirsle   IP Logged
Pages: 1
Send Topic Print