Chatbot::RiveScript - Rendering Intelligence Very Easily
use Chatbot::RiveScript;
# Create a new RiveScript interpreter. my $rs = new Chatbot::RiveScript;
# Define a macro. $rs->setSubroutine (weather => \&weather);
# Load in some RiveScript documents. $rs->loadDirectory ("./replies");
# Load in another file. $rs->loadFile ("./more_replies.rs");
# Stream in yet more replies. $rs->stream ('! global split_sentences = 1');
# Sort them. $rs->sortReplies;
# Grab a response. my @reply = $rs->reply ('localhost','Hello RiveScript!'); # print $reply[0] . "\n";
RiveScript was formerly known as Chatbot::Alpha. However, Chatbot::Alpha's syntax is not compatible with RiveScript.
RiveScript is a simple input/response language. It is simple, easy to learn, and mimics and perhaps even surpasses the power of AIML (Artificial Intelligence Markup Language).
Creates a new Chatbot::RiveScript instance. Pass in any defaults here.
Define a macro (see Object Macros)
Load a directory of RiveScript (.rs) files.
Load a single file. Don't worry about the STREAM argument, it is handled
in the stream()
method.
Stream RiveScript code directly into the module.
Sorts the replies. This is ideal for matching purposes. If you fail to do so and just go ahead and call reply(), you'll get a nasty Perl warning. It will sort them for you anyway, but it's always recommended to sort them yourself. For example, if you sort them and then load new replies, the new replies will not be matchable because the sort cache hasn't updated.
Get a reply from the bot. This will return an array. The values of this array would be all the replies (i.e. if you use {nextreply} in a response to return multiple).
Search all loaded replies for every trigger that STRING matches. Returns an array of results, containing the trigger, what topic it was under, and the reference to its file and line number.
Set a global variable directly from Perl (alias for ! global)
Set a botvariable (alias for ! var)
Set a substitution setting (alias for ! sub)
Set a user variable (alias for <set var=value>)
Get all variables for a user, returns a hash reference. (alias for <get var> for every variable). If you don't provide a USER_ID, or provide '__rivescript__' (see Reserved Variables), it will return an array reference of hash references, to get variables of all users.
These methods are called on internally and should not be called by you.
# print a debug message.
This should not be called. Call reply instead. This method assumes that the variables are neatly formatted and may cause serious consequences for passing in badly formatted data.
Splits string at the sentence-splitters and returns an array.
Formats the message (runs substitutions, removes punctuation, etc)
Merges the hash from HASH into STRING, where the keys in HASH should be from 1 to 100, for the wildcard captor.
Called on for string format tags (uppercase, lowercase, formal, sentence).
RiveScript documents have a simple format: they're a line-by-line
language. The first symbol(s)
are the commands, and the following text
is typically the command's data.
In its most simple form, a valid RiveScript entry looks like this:
+ hello bot - Hello human.
The following are the commands that RiveScript supports.
! type variable = value
type = the variable type variable = the name of the variable value = the variable's value
The supported types are as follows:
global - Global settings (top-level things) var - BotVariables (i.e. the bot's name, age, etc) array - An array sub - A substitution pattern
// Define a topic > topic some_topic_name
// there'd be some triggers here
< topic // close the topic
+ hello bot - Hello human.
The user would say ``hello bot'' only to get a ``Hello human.'' back.
+ ask me a question - Do you have any pets?
+ yes % do you have any pets - What kind of pet?
// and so-on...
+ tell me a poem - Little Miss Muffit sat on her tuffet ^ in a nonchalant sort of way. ^ With her forcefield around her, ^ the Spider, the bounder, ^ is not in the picture today.
+ my name is * - Nice to meet you, {formal}<star1>{/formal}.
+ people around here call me * @ my name is <star1>
Redirections can also be used inline. See the ``TAGS'' section for more details.
* variable=value => say this
For example, you might want to make a condition to differentiate male from female users.
+ am i a guy or a girl * gender=male => You're a guy. * gender=female => You're a girl. - I don't think you ever told me what you are.
+ what is 2 plus 2 - 500 Internal Error. # $reply = '2 + 2 = 4';
// A one-line comment
/* this comment spans across multiple lines */
The RiveScript engine was designed for your RiveScript brain to hold most of the control. As little programming on the Perl side as possible has made it so that your RiveScript can define its own variables and handle what it wants to. See ``A Good Brain'' for tips on how to approach this.
The + command can be used for more complex things as a simple, 100% dead-on trigger. This part is passed through a regexp. Therefore, any regexp things can be used in the trigger.
Note: an asterisk * is always converted into (.*?) regardless of its context. Keep this in mind.
Alternations: You can use alternations in the triggers like so:
+ what (s|is) your (home|office|cell) phone number
Anything inside of parenthesis, or anything matched by asterisks, can be obtained through the tags <star1> to <star100>. For example (keeping in mind that * equals (.*?):
+ my name is * - Nice to meet you, <star1>.
Optionals: You can use optional words in a trigger. These words don't have to exist in the user's message but they can. Example:
+ what is your [home] phone number - You can call me at 555-5555.
So that would match ``what is your phone number'' as well as ``what is your home phone number''
Optionals can have alternations in them too.
+ what (s|is) your [home|office|cell] phone number
Arrays: This is why it's good to define arrays using the !define tag. The best way to explain how this works is by example.
// Make an array of color names ! array colors = red blue green yellow white black orange
// Now the user can tell us their favorite color from the array + my favorite color is (@colors) - Really! Mine is <star1> too!
It turns your array into regexp form, (red|blue|green|yellow|...) before matching so it saves you a lot of work there. Not to mention arrays can be used in any number of triggers! Just imagine how many triggers you can come up with where a color name would be needed...
As mentioned above, the - command has many many uses.
One-way question/answer: A single + and a single - will lead to a dead-on question and answer reply.
Random Replies: A single + with multiple -'s will yield random results from among the responses. For example:
+ hello - Hey. - Hi. - Hello.
Would randomly return any of those three responses.
Conditional Fallback: When using conditionals, you should always provide at least one response to fall back on, in case every conditional returns false.
Perl Code Fallback: When executing Perl code, you should always have a response to fall back on [even if the Perl is going to redefine $reply for itself]. This is in case of an eval error and the Perl couldn't do its thing.
Weighted Responses: Yes, with random responses you can weight them! Responses with higher weight will have a better chance of being chosen over ones with a low weight. For example:
+ hello - Hello, how are you?{weight=49} - Yo, wazzup dawg?{weight=1}
In this case, ``Hello, how are you?'' will almost always be sent back. A 1 in 50 chance would return ``Yo, wazzup dawg?'' instead.
(as a side note: you don't need to set a weight to 1; 1 is implied for any response without weight. Weights of less than 1 aren't acceptable)
Note: BEGIN statements are not required. That being said, begin statements are executed before any request.
How to define a BEGIN statement
> begin + request - {ok} < begin
Begin statements are sort of like topics. They are called first. If the response given contains {ok} in it, then the module knows it's allowed to get a reply. Also note that {ok} is replaced with the response. In this way, begin might be useful to format all responses in one way. For a good example:
> begin
// Don't give a reply if the bot is down for maintenance. + request * down=yes => The bot is currently deactivated for maintenance. - <font color="red"><b>{ok}</b></font>
< begin
That would give the reply about the bot being under maintenance if the variable ``down'' equals ``yes.'' Else, it would give a response in red bold font.
Note: At the time being, the only trigger that BEGIN ever receives is ``request''
Topics are declared in a way similar to the BEGIN statement. The way to declare and close a topic is generally as follows:
> topic TOPICNAME ... < topic
The topic name should be unique, and only one word.
The Default Topic: The default topic name is ``random''
Setting a Topic: To set a topic, use the {topic} tag (see ``Tags'' below). Example:
+ i hate you - You're not very nice. I'm going to make you apologize.{topic=apology}
> topic apology + * - Not until you admit that you're sorry.
+ sorry - Okay, I'll forgive you.{topic=random} < topic
Always set topic back to ``random'' to break out of a topic.
Special macros (Perl routines) can be defined and then utilized in your RiveScript code.
Define a Macro the RiveScript way: New with version 0.4 is the ability to define objects directly within the RiveScript code. This is currently experimental. More often than not it will work without a problem. Sometimes, very complex objects fail to create via this way for some reason.
The basic way is to do it like this:
> object fortune my ($method,$msg) = @_;
my @fortunes = ( 'You will be rich and famous', 'You will meet a celebrity', 'You will go to the moon', );
return $fortunes [ int(rand(scalar(@fortunes))) ]; < object
Note: the closing tag (last line in the above example) is required for objects. An object isn't included until the closing tag is found.
Define a Macro the Perl way: This must be done from the Perl side (oh, darn, RiveScript doesn't have full control). This is done like so:
# Define a weather lookup macro. $rs->setSubroutine (weather => \&weather_lookup);
Use a Macro: You can use a macro within a reply such as this example:
+ give me the local weather for * - Weather for &weather.cityname(<star1>):\n\n ^ Temperature: &weather.temp(<star1>)\n ^ Feels Like: &weather.feelslike(<star1>)
The subroutine ``weather_lookup'' will receive two variables: the method and the arguments. The method would be the bit following the dot (i.e. ``cityname'', ``temp'', or ``feelslike'' in this example). The arguments would be the value of <star1>.
Whatever weather_lookup would return is inserted into the reply in place of the macro call.
Special tags can be inserted into replies and redirections. They are as follows:
These tags will insert the values of $1 to $100, as matched in the regexp, into the reply. They go in order from left to right. <star> is an alias for <star1>.
Inserts the last 1 to 9 things the user said, and the last 1 to 9 things the bot said, respectively. Good for things like ``You said hello and then I said hi and then you said what's up and then I said not much''
Inserts the user's ID.
Insert a bot variable (defined with ! var).
+ what is your name - I am <bot name>, created by <bot companyname>.
This variable can also be used in triggers.
+ my name is <bot name> - <set name=<bot name>>What a coincidence, that's my name too!
Get and set a user variable. These are local variables for each user.
+ my name is * - <set name={formal}<star1>{/formal}>Nice to meet you, <get name>!
+ who am i - You are <get name> aren't you?
The topic tag. This will set the user's topic to something else (see TOPICS). Only one of these should be in a response, and in the case of duplicates only the first one is evaluated.
Breaks the reply into two (or more) parts there. Will cause the reply method to return multiple responses.
An inline redirection. These work like normal redirections, except are inserted inline into a reply.
+ * or something - Or something. {@<star1>}
An inline definition. These can be used to (re)set variables. This tag is invisible in the final response of the bot; the changes are made silently.
Will insert a bit of random text. This has two syntaxes:
Insert a random word (separate by spaces) {random}red blue green yellow{/random}
Insert a random phrase (separate by pipes) {random}Yes sir.|No sir.{/random}
Will Make Your Text Formal
Will make your text sentence-cased.
WILL MAKE THE TEXT UPPERCASE.
will make the text lowercase.
The following are all the reserved variables and values within RiveScript's processor.
These variables cannot be overwritten with the ! global command:
reserved replies array syntax streamcache botvars uservars botarrays sort users substitutions
The following topic names are reserved and should never be (re)created in your RiveScript files:
__begin__ (used for the BEGIN method) __that__* (used for the %PREVIOUS command)
These are the reserved User ID's that you should not pass in to the reply method when getting a reply.
__rivescript__ (to query the BEGIN method)
Since RiveScript leaves a lot of control up to the brain and not the Perl code, here are some general tips to follow when writing your own brain:
Make a config file. This would probably be named ``config.rs'' and it would handle all your definitions. For example it might look like this:
// Set up globals ! global debug = 0 ! global split_sentences = 1 ! global sentence_splitters = . ! ; ?
// Set a variable to say that we're active. ! var active = yes
// Set up botvariables ! var botname = Rive ! var botage = 5 ! var company = AiChaos Inc. // note that "bot" isn't required in these variables, // it's only there for readibility
// Set up substitutions ! sub won't = will not ! sub i'm = i am // etc
// Set up arrays ! array colors = red green blue yellow cyan fuchsia ...
Here are a list of all the globals you might want to configure.
split_sentences - Whether to do sentence-splitting (1 or 0, default 1) sentence_splitters - Where to split sentences at. Separate items with a single space. The defaults are: ! . ? ; debug - Debug mode (1 or 0, default 0)
Make a begin file. This file would handle your BEGIN code. Again, this isn't required but has its benefits. This file might be called ``begin.rs'' (or you could include it in config.rs if you're a micromanager).
Your begin file could check the ``active'' variable we set in the config file to decide if it should give a reply.
> begin + request * active=no => Sorry but I'm deactivated right now! - {ok} < begin
These are the basic tips, just for organizational purposes.
You might want to take a look at the Chatbot::Alpha manpage, this module's predecessor.
I'm sure there are some, as this is a beta release, but none have come to show themselves yet.
Version 0.04 - Added support for optional parts of the trigger. - Begun support for inline objects to be created.
Version 0.03 - Added search() method. - <bot> variables can be inserted into triggers now (for example having the bot reply to its name no matter what its name is)
Version 0.02 - Fixed a regexp bug; now it stops searching when it finds a match (it would cause errors with $1 to $100) - Fixed an inconsistency that didn't allow uservars to work in conditionals. - Added <id> tag, useful for objects that need a unique user to work with. - Fixed bug that lets comments begin with more than one set of //
Version 0.01 - Initial Release
Here are things I plan to add into the module at a later time.
From Version 0.04 - Allow the ^CONTINUE command to continue ANY command, not just -REPLIES.
Cerone Kirsle, kirsle --at-- rainbowboi.com
Chatbot::RiveScript - Rendering Intelligence Very Easily Copyright (C) 2005 Cerone J. Kirsle
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA