Perl RiveScript 1.19|Kirsle|casey@cuvou.net|1239576457|Kirsle|xx|0|69.232.33.121|I've released Perl RiveScript 1.19. It has a couple small bug fixes, but the biggest new feature is that custom object handlers can be created. For instance your Perl RiveScript can handle &quot;javascript&quot; objects found within the RS code.<br /><br />Download it from: http://www.rivescript.com/?p=interpreters<br /><br />From the documentation:<br /><br />[b]setHandler ($LANGUAGE =&gt; $CODEREF, ...)[/b]<br /><br />    Define some code to handle objects of a particular programming language. If the coderef is undef, it will delete the handler.<br /><br />    The code receives the variables $rs, $action, $name, and $data. These variables are described here:<br /><br />[code] &nbsp; &nbsp; &nbsp;$rs     = Reference to Perl RiveScript object.<br /> &nbsp; &nbsp; &nbsp;$action = &quot;load&quot; during the parsing phase when an &gt;object is found.<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;    &quot;call&quot; when provoked via a &lt;call&gt; tag for a reply<br /> &nbsp; &nbsp; &nbsp;$name   = The name of the object.<br /> &nbsp; &nbsp; &nbsp;$data   = The source of the object during the parsing phase, or an array<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;    reference of arguments when provoked via a &lt;call&gt; tag.[/code]<br /><br />    There is a default handler set up that handles Perl objects. The source code of this object is as follows, for your reference:<br /><br />[code] &nbsp; &nbsp; &nbsp;$self-&gt;setHandler (perl =&gt; sub &#123;<br /> &nbsp; &nbsp; &nbsp;  my ($rs,$action,$name,$data) = @_;<br /><br /> &nbsp; &nbsp; &nbsp;  # $action will be &quot;load&quot; during the parsing phase, or &quot;call&quot;<br /> &nbsp; &nbsp; &nbsp;  # when called via &lt;call&gt;.<br /><br /> &nbsp; &nbsp; &nbsp;  # Loading<br /> &nbsp; &nbsp; &nbsp;  if ($action eq &quot;load&quot;) &#123;<br /> &nbsp; &nbsp; &nbsp;    # Create a dynamic Perl subroutine.<br /> &nbsp; &nbsp; &nbsp;    my $code = &quot;sub RSOBJ_$name &#123;\n&quot;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;. $data<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;. &quot;&#125;&quot;;<br /><br /> &nbsp; &nbsp; &nbsp;    # Evaluate it.<br /> &nbsp; &nbsp; &nbsp;    eval ($code);<br /> &nbsp; &nbsp; &nbsp;    if ($@) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$rs-&gt;issue(&quot;Perl object $name creation failed: $@&quot;);<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# Load it.<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$rs-&gt;setSubroutine($name =&gt; \&amp;&#123;&quot;RSOBJ_$name&quot;&#125;);<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;  &#125;<br /><br /> &nbsp; &nbsp; &nbsp;  # Calling<br /> &nbsp; &nbsp; &nbsp;  elsif ($action eq &quot;call&quot;) &#123;<br /> &nbsp; &nbsp; &nbsp;    # Make sure the object exists.<br /> &nbsp; &nbsp; &nbsp;    if (exists $rs-&gt;&#123;objects&#125;-&gt;&#123;$name&#125;) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# Call it.<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;my @args = @&#123;$data&#125;;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;my $return = &amp;&#123; $rs-&gt;&#123;objects&#125;-&gt;&#123;$name&#125; &#125; ($rs,@args);<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return $return;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return &quot;[ERR: Object Not Found]&quot;;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;  &#125;<br /> &nbsp; &nbsp; &nbsp;&#125;);[/code]<br /><br />    If you want to block Perl objects from being loaded, you can just set it to be undef, and its handler will be deleted and Perl objects will be skipped over:<br /><br />[code] &nbsp; &nbsp; &nbsp;$rs-&gt;setHandler (perl =&gt; undef);[/code]<br /><br />    The rationale behind this &quot;pluggable&quot; object interface is that it makes RiveScript more flexible given certain environments. For instance, if you use RiveScript on the web where the user chats with your bot using CGI, you might define a handler so that JavaScript objects can be loaded and called. Perl itself can't execute JavaScript, but the user's web browser can.<br /><br />    Here's an example of defining a handler for JavaScript objects:<br /><br />[code] &nbsp; &nbsp; &nbsp;my $scripts = &#123;&#125;; # Place to store JS code.<br /><br /> &nbsp; &nbsp; &nbsp;$rs-&gt;setHandler (javascript =&gt; sub &#123;<br /> &nbsp; &nbsp; &nbsp;  my ($self,$action,$name,$data) = @_;<br /><br /> &nbsp; &nbsp; &nbsp;  # Loading the object.<br /> &nbsp; &nbsp; &nbsp;  if ($action eq &quot;load&quot;) &#123;<br /> &nbsp; &nbsp; &nbsp;    # Just store the code.<br /> &nbsp; &nbsp; &nbsp;    $scripts-&gt;&#123;$name&#125; = $data;<br /> &nbsp; &nbsp; &nbsp;  &#125;<br /> &nbsp; &nbsp; &nbsp;  else &#123;<br /> &nbsp; &nbsp; &nbsp;    # Turn the args into a JavaScript array.<br /> &nbsp; &nbsp; &nbsp;    my $code = &quot;var fields = new Array();\n&quot;;<br /> &nbsp; &nbsp; &nbsp;    for (my $i = 0; $i &lt; scalar @&#123;$data&#125;; $i++) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$code .= &quot;fields[$i] = \&quot;$data-&gt;[$i]\&quot;;\n&quot;;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /><br /> &nbsp; &nbsp; &nbsp;    # Come up with code for the web browser.<br /> &nbsp; &nbsp; &nbsp;    $code .= &quot;function rsobject (args) &#123;\n&quot;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;     . &quot;$scripts-&gt;&#123;$name&#125;\n&quot;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;     . &quot;&#125;&quot;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;     . &quot;document.writeln( rsobject(fields) );\n&quot;;<br /> &nbsp; &nbsp; &nbsp;    return &quot;[script type=\&quot;text/javascript\&quot;&gt;\n&quot;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;. $code<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;. &quot;[/script&gt;&quot;; &nbsp; &nbsp; &nbsp;  &#125; &nbsp; &nbsp; &nbsp;&#125;);[/code]<br /><br />    So, the above example just loads the JavaScript source code into a hash reference named $scripts, and then when called it creates some JavaScript code to put the call's arguments into an array, creates a function that accepts the args, then calls this function in a document.writeln. Here's an example of how this would be used in the RiveScript code:<br /><br />[code] &nbsp; &nbsp; &nbsp;// Define an object to encode text into rot13 to be executed by the web browser<br /> &nbsp; &nbsp; &nbsp;&gt; object rot13 javascript<br /> &nbsp; &nbsp; &nbsp;  var txt = args.join(&quot; &quot;); // Turn the args array into a string<br /> &nbsp; &nbsp; &nbsp;  var result = &quot;&quot;;<br /><br /> &nbsp; &nbsp; &nbsp;  for (var i = 0; i &lt; txt.length; i++) &#123;<br /> &nbsp; &nbsp; &nbsp;    var b = txt.charCodeAt(i);<br /><br /> &nbsp; &nbsp; &nbsp;    // 65 = A    97 = a<br /> &nbsp; &nbsp; &nbsp;    // 77 = M   109 = m<br /> &nbsp; &nbsp; &nbsp;    // 78 = N   110 = n<br /> &nbsp; &nbsp; &nbsp;    // 90 = Z   122 = z<br /><br /> &nbsp; &nbsp; &nbsp;    var isLetter = 0;<br /><br /> &nbsp; &nbsp; &nbsp;    if (b &gt;= 65 &amp;&amp; b &lt;= 77) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b += 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else if (b &gt;= 97 &amp;&amp; b &lt;= 109) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b += 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else if (b &gt;= 78 &amp;&amp; b &lt;= 90) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b -= 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else if (b &gt;= 110 &amp;&amp; b &lt;= 122) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b -= 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /><br /> &nbsp; &nbsp; &nbsp;    if (isLetter) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result += String.fromCharCode(b);<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result += String.fromCharCode(b);<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;  &#125;<br /><br /> &nbsp; &nbsp; &nbsp;  return result;<br /> &nbsp; &nbsp; &nbsp;&lt; object<br /><br /> &nbsp; &nbsp; &nbsp;// Use the object<br /> &nbsp; &nbsp; &nbsp;+ say * in rot13<br /> &nbsp; &nbsp; &nbsp;- &quot;&lt;star&gt;&quot; in rot13 is: &lt;call&gt;rot13 &lt;star&gt;&lt;/call&gt;.[/code]<br /><br />    Now, when the user at the web browser provokes this reply, it will get back a bunch of JavaScript code as part of the response. It might be like this:<br /><br />[code] &nbsp; &nbsp; &nbsp;&lt;b&gt;User:&lt;/b&gt; say hello world in rot13&lt;br&gt;<br /> &nbsp; &nbsp; &nbsp;&lt;b&gt;Bot:&lt;/b&gt; &quot;hello world&quot; in rot13 is: [script type=&quot;text/javascript&quot;&gt;<br /> &nbsp; &nbsp; &nbsp;var fields = new Array();<br /> &nbsp; &nbsp; &nbsp;fields[0] = &quot;hello&quot;;<br /> &nbsp; &nbsp; &nbsp;fields[1] = &quot;world&quot;;<br /> &nbsp; &nbsp; &nbsp;function rsobject (args) &#123;<br /> &nbsp; &nbsp; &nbsp;  var txt = args.join(&quot; &quot;); // Turn the args array into a string<br /> &nbsp; &nbsp; &nbsp;  var result = &quot;&quot;;<br /><br /> &nbsp; &nbsp; &nbsp;  for (var i = 0; i &lt; txt.length; i++) &#123;<br /> &nbsp; &nbsp; &nbsp;    var b = txt.charCodeAt(i);<br /><br /> &nbsp; &nbsp; &nbsp;    // 65 = A    97 = a<br /> &nbsp; &nbsp; &nbsp;    // 77 = M   109 = m<br /> &nbsp; &nbsp; &nbsp;    // 78 = N   110 = n<br /> &nbsp; &nbsp; &nbsp;    // 90 = Z   122 = z<br /><br /> &nbsp; &nbsp; &nbsp;    var isLetter = 0;<br /><br /> &nbsp; &nbsp; &nbsp;    if (b &gt;= 65 &amp;&amp; b &lt;= 77) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b += 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else if (b &gt;= 97 &amp;&amp; b &lt;= 109) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b += 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else if (b &gt;= 78 &amp;&amp; b &lt;= 90) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b -= 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else if (b &gt;= 110 &amp;&amp; b &lt;= 122) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;isLetter = 1;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b -= 13;<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /><br /> &nbsp; &nbsp; &nbsp;    if (isLetter) &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result += String.fromCharCode(b);<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;    else &#123;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result += String.fromCharCode(b);<br /> &nbsp; &nbsp; &nbsp;    &#125;<br /> &nbsp; &nbsp; &nbsp;  &#125;<br /><br /> &nbsp; &nbsp; &nbsp;  return result;<br /> &nbsp; &nbsp; &nbsp;&#125;<br /> &nbsp; &nbsp; &nbsp;document.writeln(rsobject(fields));<br /> &nbsp; &nbsp; &nbsp;[/script&gt;.[/code]<br /><br />    And so, the JavaScript gets executed inside the bot's response by the web browser.<br /><br />    In this case, Perl itself can't handle JavaScript code, but considering the environment the bot is running in (CGI served to a web browser), the web browser is capable of executing JavaScript. So, we set up a custom object handler so that JavaScript objects are given directly to the browser to be executed there.||1239576480|Kirsle|
