RiveScript::WD(3pm)
πΈ NAME πΈ
RiveScript::WD β RiveScript 2.00 Working Draft (2014/11/30) π
And yes bestie, the manual name RiveScript::WD(3pm) is literally right there in the title. No cap.
β¨ DESCRIPTION β¨
okay so this document? It details the standards for the RiveScript scripting language. The whole purpose is that interpreters for RiveScript could be written in various programming languages that would meet the standards for the RiveScript language itself. Iconic behavior.
The most current version of this document is at http://www.rivescript.com/wd/RiveScript.html β go look, it's giving official. π
π¦ INTRODUCTION π¦
RiveScript is an interpreted scripting language for giving responses to chatterbots and other intelligent chatting entities in a simple trigger/reply format. The scripting language is intended to be simplistic and easy to learn and manage. Lowkey the whole point is that it's easy, and it ate.
π VOCABULARY π
- RiveScript β RiveScript is the name of the scripting language that this document explains.
- Interpreter β The RiveScript interpreter is a program or library in another programming language that loads and parses a RiveScript document.
- RiveScript Document β A RiveScript Document is a text file containing RiveScript code.
- Bot β A Bot (short for robot) is the artificial entity that is represented by an instance of a RiveScript Interpreter object. That is, when you create a new Interpreter object and load a set of RiveScript Documents, that becomes the "brain" of the bot.
- Bot Variable β A variable that describes the bot, such as its name, age, or other details you want to define for the bot.
- Client Variable β A variable that the bot keeps about a specific client, or user of the bot. Usually as the client tells the bot information about itself, the bot could save this information into Client Variables and recite it later.
π FORMAT π
A RiveScript document should be parsed line by line, and preferably arranged in the interpreter's memory in an efficient way. Efficient memory arrangement? Obsessed.
The first character on each line should be the "command", and the rest of the line is the command's "arguments". The "command" should be a single character that is not a number or a letter. That's the vibe check: one char, not a number, not a letter. Periodt.
In its most simple form, a valid RiveScript trigger/response pair looks like this:
+ hello bot
- Hello, human.
β¨ WHITESPACE β¨
A RiveScript interpreter should ignore leading and trailing whitespace characters on any line. It should also ignore whitespace characters surrounding individual arguments of a RiveScript command, where applicable. That is to say, the following two lines should be interpreted as being exactly the same:
! global debug = 1
! global debug= 1
So yeah, spaces are not the main character here β the interpreter lowkey just ignores them. π
π COMMANDS π
These are the command blocks, bestie. Let's get into the definitions. π«Ά
π ! DEFINITION π
The "!" command is for defining variables within RiveScript. It's used to define information about the bot, define global arrays that can be used in multiple triggers, or override interpreter globals such as debug mode. It's giving setup energy.
The format of the "!" command is as follows:
! type name = value
Where "type" is one of version, global, var, array, sub, or person. The "name" is the name of the variable being defined, and "value" is the value of said variable.
Whitespace surrounding the "=" sign should be stripped out. So don't stress about spacing around =.
Setting a value to <undef> will undefine the variable (deleting it or uninitializing it, depending on the implementation). Poof, gone.
The variable types supported are detailed as follows:
version
It's highly recommended practice that new RiveScript documents explicitly define the version of RiveScript that they are following. RiveScript 2.00 has some compatibility issues with the old 1.x line (see "REVERSE COMPATIBILITY"). Newer RiveScript versions should encourage that RiveScript documents define their own version numbers.
! version = 2.00
global
This should override a global variable at the interpreter level. The obvious variable name might be "debug" (to enable/disable debugging within the RiveScript interpreter).
The interpreter should take extra care not to allow reserved globals to be overridden by this command in ways that might break the interpreter. So like, don't break the interpreter, bestie. That's not slay.
Examples:
! global debug = 1
var
This should define a "bot variable" for the bot. This should only be used in an initialization sense; that is, as the interpreter loads the document, it should define the bot variable as it reads in this line. If you'd want to redefine or alter the value of a bot variable, you should do so using a tag inside of a RiveScript document (see "TAGS"). So init only, then use tags later.
Examples:
! var name = RiveScript Bot
! var age = 0
! var gender = androgynous
! var location = Cyberspace
! var generator = RiveScript
array
This will create an array of strings, which can then be used later in triggers (see "+ TRIGGER"). If the array contains single words, separating the words with a space character is fine. If the array contains items with multiple words in them, separate the entries with a pipe symbol ("|"). So spaces for single words, pipes for multi-word entries. Got it.
Examples:
! array colors = red green blue cyan magenta yellow black white orange brown
! array be = is are was were
! array whatis = what is|what are|what was|what were
Arrays have special treatment when spanned over multiple lines. Each extension of the array data is treated individually. For example, to break an array of many single-words into multiple lines of RiveScript code:
! array colors = red green blue cyan
^ magenta yellow black white
^ orange brown
The data structure pulled from that code would be identical to the previous example above for this array. Same energy.
Since each extension line is processed individually, you can combine the space-delimited and pipe-delimited formats. In this case, we can add some color names to our list that have multiple words in them.
! array colors = red green blue cyan magenta yellow
^ light red|light green|light blue|light cyan|light magenta|light yellow
^ dark red|dark green|dark blue|dark cyan|dark magenta|dark yellow
^ white orange teal brown pink
^ dark white|dark orange|dark teal|dark brown|dark pink
Finally, if your array consists of almost entirely single-word items, and you want to add in just one multi-word item, but don't want to require an extra line of RiveScript code to accomplish this, just use the \s tag where you need spaces to go. Sneaky, but iconic.
! array blues = azure blue aqua cyan baby\sblue sky\sblue
sub
The "sub" variables are for defining substitutions that should be run against the client's message before any attempts are made to match it to a reply. So pre-processing the user's text, basically.
The interpreter should do the minimum amount of formatting possible on the client's message until after it has been passed through all the substitution patterns. Don't format early, bestie.
NOTE: Spaces are allowed in both the variable name and the value fields. Yep, spaces are valid there.
Examples:
! sub what's = what is
! sub what're = what are
! sub what'd = what did
! sub a/s/l = age sex location
! sub brb = be right back
! sub afk = away from keyboard
! sub l o l = lol
person
The "person" variables work a lot like "sub"s do, but these are run against the bot's response, specifically within <person> tags (See "TAGS"). So it's the bot's reply that gets the glow-up.
Person substitutions should swap first- and second-person pronouns. This is so that ex. if the client asks the bot a direct question using "you" when addressing the bot, if the bot uses the client's message in the response it should swap "you" for "I". Pronoun swap? We love to see it.
Examples:
! person you are = I am
! person i am = you are
! person you = I
! person i = you
π¦ > LABEL π¦
The ">" and "<" commands are for defining a subset of your code under a certain label. The label command takes between one and three arguments. The first argument defines the type of the label, which is one of begin, topic, or object. The various types are as follows. So it's like a little code neighborhood.
begin
This is a special label used with the "BEGIN block". Every message the client sends to the bot gets passed through the Begin Statement first, and the response in there determines whether or not to get an actual reply. It's the main character of pre-processing.
Here's a full example of the Begin Statement.
> begin
+ request
- {ok}
< begin
In the "BEGIN block", the trigger named ""request"" is called by the interpreter, and it should return the tag ""{ok}"" to tell the interpreter that it's OK to get a real reply. This way the bot could have a "maintenance mode," or could filter the results of your trigger based on a variable. So {ok} is the green light, basically.
Here's a maintenance mode example:
> begin
+ request
* <id> eq <bot master> => {ok} // Always let the bot master get a reply
* <env maint> eq true => Sorry, I'm not available for chat right now!
- {ok}
< begin
// Allow the owner to change the maintenance mode
+ activate maintenance mode
* <id> eq <bot master> => <env maint=true>Maintenance mode activated.
- You're not my master! You can't tell me what to do!
+ deactivate maintenance mode
* <id> eq <bot master> => <env maint=false>Maintenance mode deactivated.
- Only my master can deactivate maintenance mode!
With this example, if the global variable "maint" is set to "true", the bot will always reply "Sorry, I'm not available for chat right now!" when a user sends it a message -- unless the user is the bot's owner. So the owner is exempt, periodt.
Here is another example that will modify the response formatting based on a bot variable called "mood," to simulate humanoid moods for the bot:
> begin
+ request
* <get mood> == happy => {ok} :-)
* <get mood> == sad => {lowercase}{ok}{/lowercase}
* <get mood> == angry => {uppercase}{ok}{/uppercase}
- {ok}
< begin
In this example the bot will use smiley faces when it's happy, reply in all lowercase when it's sad, or all uppercase when it's angry. If its mood doesn't fall into any of those categories, it replies normally. Mood ring but make it chat.
Here is one last example: say you want your bot to interview its users when they first talk to it, by asking them for their name:
> begin
+ request
* <get name> == undefined => {topic=newuser}{ok}
- {ok}
< begin
> topic newuser
+ *
- Hello! My name is <bot name>! I'm a robot. What's your name?
+ _
% * what is your name
- <set name=<formal>>Nice to meet you, <get name>!{topic=random}
< topic
Begin blocks are optional! They are not required. You only need to manually define them if you need to do any "pre-processing" or "post-processing" on the user's message or the bot's response. Having no begin block is the same as having a super basic begin block, which always returns "{ok}". So if you don't need the extra drama, you can skip it. π
topic
Okay so a topic is basically a smaller lil set of responses that your client is bound to until the topic gets changed to something else. And the default topic? "random". Periodt. π
The topic label only needs one extra argument, bestie, and that's the name of the topic. That name should be one word and lowercase. That's the whole vibe. π
Example:
+ i hate you
- Well then, I won't talk to you until you take that back.{topic=apology}
> topic apology
+ *
- I won't listen to you until you apologize for being mean to me.
- I have nothing to say until you say you're sorry.
+ (sorry|i apologize)
- Okay. I guess I'll forgive you then.{topic=random}
< topic
Topics are able to "include" and "inherit" triggers that belong to a different topic. Not me building cross-topic lore π
When a topic "includes" another topic, it means the triggers in that other topic get made available in the topic that did the inclusion (and we call that one the "source topic", which includes triggers from the "included topic").
When a topic inherits another topic, it means the entire collection of triggers of the source topic β plus any included topics β will have a higher matching priority than the inherited topics. It's giving main character energy, honestly. π
See "Sorting +Triggers" to see how triggers are sorted internally. The following example shows how includes and inheritance works:
// This is in the default "random" topic and catches all non-matching
// triggers.
+ *
- I'm afraid I don't know how to reply to that!
> topic alpha
+ alpha trigger
- Alpha's response.
< topic
> topic beta
+ beta trigger
- Beta's response.
<
> topic gamma
+ gamma trigger
- Gamma's response.
< topic
> topic delta
+ delta trigger
- Delta's response.
+ *
- You can't access any other triggers! Haha!
< topic
So these are all normal topics, right? Alpha, beta, and gamma each have a single trigger corresponding to their topic names. If the user were put into one of these topics, that's the only trigger available to them. Anything else would give them a "NO REPLY" error message. π They are unable to match the * trigger at the top, because that trigger belongs to the "random" topic, and they're simply not in that topic. Vibe check: failed.
Now let's see how we can pair these topics up with includes and inheritance. π¦
> topic ab includes alpha
+ hello bot
- Hello human!
< topic
// Matching order:
alpha trigger
hello bot
If the user were put into topic "ab", they could match the trigger hello bot as well as the trigger alpha trigger, as if they were both in the same topic. Slay.
Note that in the matching order, alpha trigger is at the top: this is because it's the longest trigger. So if the user types alpha trigger, the interpreter knows that alpha trigger does not belong to the topic ab β but since ab includes triggers from alpha, the interpreter searches there and finds the trigger. Then it gives the user the correct reply of Alpha's response. β¨
> topic abc includes alpha beta
+ how are you
- Good, how are you?
< topic
// Matching order:
how are you
alpha trigger
beta trigger
In this case, how are you is at the top of the matching list because it has three words, then alpha trigger and beta trigger β and alpha trigger comes first because it's longer than beta trigger, even though they both have 2 words. Length matters, iykyk. π
Now consider this example:
> topic abc includes alpha beta
+ how are you
- Good, how are you?
+ *
- You matched my star trigger!
< topic
// Matching order:
how are you
alpha trigger
beta trigger
*
Notice what happened here: we had a trigger of simply * in the abc topic β and * is the fallback trigger which matches anything that wasn't matched by a better trigger. But bestieβ¦ this trigger is at the end of our matching list! π± This is because the triggers available in the alpha and beta topics are included in the abc topic, meaning they all share the same "space" when the triggers are sorted. Since * has the lowest sort priority, it ends up at the very end of the collective list. Tragic, but correct.
So what if we want *, or any other short trigger, to match in our current topic before anything in an included topic? Well then we need to "inherit" another topic instead. Consider this:
> topic abc inherits alpha beta
+ how are you
- Good, how are you?
+ *
- You matched my star trigger!
< topic
// Matching order:
how are you
*
alpha trigger
beta trigger
Now the * trigger is the second on the matching list. π Because abc inherits alpha and beta, it means the collection of triggers inside abc gets sorted independently, and then the triggers of alpha and beta are sorted. So this way every trigger in abc inherits, or overrides, all triggers in the inherited topics. Iconic behaviour, no cap.
Of course, using a * trigger in a topic that inherits other topics is kinda useless, because you could just leave the topic as it is. However β it might be helpful in the case that a trigger in your topic is very short or has very few words, and you want to make sure that this trigger will have a good chance of matching before anything that appears in a different topic. π«Ά
You can combine inherited and included topics together, too. π
> topic abc includes alpha beta delta inherits gamma
+ how are you
- Good, how are you?
< topic
// Matching order:
how are you
alpha trigger
delta trigger
beta trigger
*
gamma trigger
In this example, the combined triggers from abc, alpha, beta, and delta are all merged together in one pool and sorted amongst themselves, and then triggers from gamma are placed after them in the sort list.
This effectively means you can combine the triggers from multiple topics together, and have ALL of those triggers override triggers from an inherited topic. β¨
You can use as many includes and inherits keywords as you want, but the order you specify them has no effect. So the following two formats are identical:
> topic alpha includes beta inherits gamma
> topic alpha inherits gamma includes beta
In both cases, alpha and beta's triggers are pooled and have higher priority than gamma's. If gamma wants to include beta and have alpha's triggers be higher priority than gamma's and beta's, gamma will need to include beta first.
> topic gamma includes beta
> topic alpha inherits gamma
In this case the triggers in alpha are higher priority than the combined triggers in gamma and beta. π
πΈ object πΈ
Objects are bits of program code that the interpreter should try to process. The programming language that the interpreter was written in will determine whether or not it will even attempt to process the object. Fingers crossed bestie π€
See "OBJECT MACROS" for more information on objects.
The object label should have two arguments: a lowercase single-word name for the object, and the programming language that the object should be interpreted by, which should also be lowercase.
Example:
> object encode perl
my ($obj,$method,@args) = @_;
my $msg = join(" ",@args);
use Digest::MD5 qw(md5_hex);
use MIME::Base64 qw(encode_base64);
if ($method eq 'md5') {
return md5_hex($msg);
}
else {
return encode_base64($msg);
}
< object
β¨ + TRIGGER β¨
The + command is the basis for all things that actually do stuff within a RiveScript document. The trigger command is what matches the user's message to a response. It's literally the moment of truth. π
The trigger's text should be entirely lowercase and should not contain any symbols (except those used for matching complicated messages). That is, a trigger that wants to match "what's your name" shouldn't be used; you should use a substitution to convert what's into what is ahead of time. Do it the right way, bestie, the interpreter will thank you. π
Example:
+ are you a bot
- How did you know I'm a robot?
π Atomic Trigger π
An atomic trigger is a trigger that matches nothing but plain text. It doesn't contain any wildcards (*) or optionals, but it may contain alternations. Atomic triggers should take higher priority for matching a client's message than should triggers containing wildcards and optionals.
Examples:
+ hello bot
+ what is your name
+ what is your (home|office) phone number
+ who is george w bush
β Trigger Wildcards β
Using an asterisk (*) in the trigger will make it act as a wildcard. Anything the user says in place of the wildcard may still match the trigger. So generous of it. π For example:
+ my name is *
- Pleased to meet you, <star>.
An asterisk (*) will match any character (numbers and letters). If you want to only match numbers, use #, and to match only letters use _. Example:
// This will ONLY take a number as the wildcard.
+ i am # years old
- I will remember that you are <star> years old.
// This will ONLY take letters but not numbers.
+ my name is _
- Nice to meet you, <star>.
The values matched by the wildcards can be retrieved in the responses by using the tags <star1>, <star2>, <star3>, etc. in the order that the wildcard appeared. And <star> is an alias for <star1>. π«Ά
π Trigger Alternations π
An alternation in a trigger is a sub-set of strings, in which any one of the strings will still match the trigger. For example, the following trigger should match both are you okay and are you alright:
+ are you (okay|alright)
Alternations can contain spaces in them, too. π
+ (are you|you) (okay|alright)
That would match all of the following questions from the client:
are you okay
are you alright
you okay
you alright
Alternations match the same as wildcards do; they can be retrieved via the <star> tags. β¨
π¦ Trigger Optionals π¦
Triggers can contain optional words as well. Optionals are written similarly to alternations, but they use square braces. The following example would match both what is your phone number as well as what is your home phone number
+ what is your [home] phone number
Optionals do NOT match like wildcards do. They do NOT go into the <star> tags. The reason for this is that optionals are optional, and won't always match anything if the client didn't actually say the optional word(s). So don't be surprised when they ghost the star tags. π
π Arrays in Triggers π
Arrays defined via the ! DEFINITION array commands can be used within a trigger. This is the only place where arrays are used, and they're added as a convenience feature. π
For example, you can make an array of color names, and then use that array in multiple triggers, without having to copy a whole bunch of alternation code between triggers. Reusability queen behavior, honestly. π
! array colors = red green blue cyan magenta yellow black white orange brown
+ i am wearing a (@colors) shirt
- I don't know if I have a shirt that's colored <star>.
+ my favorite color is (@colors)
- I like <star> too.
+ i have a @colors colored *
- Have you thought about getting a <star> in a different color?
Okay so the rule here, bestie, listen up: when an array is called within parenthesis, it should be matched into a "<star>" tag. When the parenthesis are absent, however, it should not be matched into a "<star>" tag. That's it, that's the whole vibe check. π
π Priority Triggers π
A new feature proposed for RiveScript 2.00 is to add a priority tag to triggers. When the interpreter sorts all the loaded triggers into a search sequence, any triggers that have a priority defined will be sorted with higher priority triggers first. Main character triggers go first, as they should. π
The idea is to have "important" triggers that should always be matched before a different trigger, which may have been a better match, can be tried. The best example would be for commands. For example:
+ google *
- Searching Google... <call>google <star></call>
+ * or not
- Or yes. <@>
In that example, if the bot had a Google search function and the user wanted to search for whether or not Perl is a superior programming language to PHP, the user might ask ""google is perl better than php or not"". However, without priorities in effect, that question would actually match the ""* or not"" trigger, because that trigger has more words than ""google *"" does. Not the glow-up we wanted. π
Adding a priority to the ""google *"" trigger would ensure that conflicts like this don't happen, by always sorting the Google search trigger with higher priority than the other.
+ {weight=100}google *
- Searching Google... <call>google <star></call>
NOTE: It would NOT be recommended to put a priority tag on every one of your triggers. To the interpreter this might mean extra processing work to sort prioritized triggers by each number group. Only add priorities to triggers that need them. Don't be extra about it, fr fr.
π - RESPONSE π
The "-" tag is used to indicate a response to a matched trigger. A single response to a single trigger is called an "atomic response." When more than one response is given to a single trigger, the collection of responses become a "random response," where a response is chosen randomly from the list. Random responses can also use a "{weight}" tag to improve the likelihood of one response being randomly chosen over another. RNG but make it β¨intentionalβ¨
πΈ Atomic Response πΈ
A single response to a single trigger makes an Atomic Response. The bot will respond pretty much the same way each time the trigger is matched. Consistent. Reliable. A little bit of a one-trick pony but we love her. π¦
Examples:
+ hello bot
- Hello human.
+ my name is *
- Nice to meet you, <star>.
+ i have a (@colors) shirt
- You're not the only one that has a <star> shirt.
π Random Response π
Multiple responses to a single trigger will be chosen randomly.
+ hello
- Hey there!
- Hello!
- Hi, how are you?
+ my name is *
- Nice to meet you, <star>.
- Hi, <star>, my name is <bot name>.
- <star>, nice to meet you.
β Weighted Random Response β
When using random responses, it's possible to give weight to them to change the likelihood that a response will be chosen. In this example, the response of "Hello there" will be much more likely to be chosen than would the response of "Hi".
+ hello
- Hello there!{weight=50}
- Hi.
When the "{weight}" tag isn't used, a default weight of 1 is implied for that response. The "{weight}" should always be a number greater than zero and must be an integer (no decimal point). No decimals allowed, she's strict about that. π«
π % PREVIOUS π
The "%" command is for drawing the user back to finish a short discussion. Its behavior is similar to using topics, but is implied automatically and used for short-term things. It's also less strict than topics are; if the client replies in a way that doesn't match, a normal reply is given anyway. Chillest command in the whole file, honestly. For example:
+ knock knock
- Who's there?
+ *
% who is there
- <star> who?
+ *
% * who
- lol! <star>! That's hilarious!
The text of the "%" command looks similar to the text next to the trigger. In essence, they work the same; the only difference is that the "%" command matches the last thing that the bot sent to you. It's giving callback energy. π
Here's another example:
+ i have a dog
- What color is it?
+ (@colors)
% what color is it
- That's an odd color for a dog.
In that case, if the client says "I have a dog," the bot will reply asking what color it is. Now, if I tell it the color in my next message, it will reply back and tell me what an odd color that is. However, if I change the topic instead and say something else to the bot, it will answer my new question anyway. This is in contrast to using topics, where I'd be stuck inside of the topic until the bot resets the topic to "random". Freedom! No topic jail! ποΈ
Similarly to the wildcards in "+ Trigger", the wildcards matched in the "% Previous" command are put into "<botstar>". See "TAGS" for more information.
π ^ CONTINUE π
The "^" command is used to continue the text of a lengthy previous command down to the new line. It can be used to extend any other command. Example:
+ tell me a poem
- Little Miss Muffit sat on her tuffet\n
^ in a nonchalant sort of way.\n
^ With her forcefield around her,\n
^ the Spider, the bounder,\n
^ Is not in the picture today.
NOTE: when the"^"command continues the previous command, no spaces or line breaks are implied at the joining of the two lines. The"\s"and"\n"tags must be explicitly defined where needed. Do not assume she'll add a space for you, because she will not. π
πͺ @ REDIRECT πͺ
The "@" command is used to redirect an entire response to appear as though the client asked an entirely different question. Identity theft but consensual. For example:
+ my name is *
- Nice to meet you, <star>.
+ call me *
@ my name is <star>
If the client says "call me John", the bot will redirect it as though the client actually said "my name is John" and give the response of "Nice to meet you, John." SLAY. Seamless. β¨
π§ * CONDITION π§
The "*" command is used with conditionals when replying to a trigger. Put simply, they compare two values, and when the comparison is true the associated response is given. The syntax is as follows:
* value symbol value => response
The following inequality symbols may be used:
== equal to
eq equal to (alias)
!= not equal to
ne not equal to (alias)
<> not equal to (alias)
< less than
<= less than or equal to
> greater than
>= greater than or equal to
In each of the value places, tags can be used to i.e. insert client or bot variables. Mix and match, it's a whole thing. π
Examples:
+ am i a boy or a girl
* <get gender> eq male => You told me you were a boy.
* <get gender> eq female => You told me you were a girl.
- You never told me what you were.
+ am i your master
* <id> eq <bot master> => Yes, you are.
- No, you're not my master.
+ my name is *
* <get name> eq <star> => I know, you told me that already.
* <get name> ne undefined => Did you get a name change?<set name=<star>>
- <set name=<star>>Nice to meet you, <star>.
It's recommended practice to always include at least one response in case all of the conditionals return false. Have a fallback, bestie. Always have a fallback. π«Ά
NOTE: Conditionals are tried in the order they appear in the RiveScript document, and the next condition is tried when the previous ones are false.
π // COMMENT π
The "//" command is for putting comments into your RiveScript document. The C-style multiline comment syntax "/* */" is also supported.
Comments on their own line should be ignored by all interpreters. For inline comments, only the "//" format is acceptable. If you want a literal "//" in your RiveScript data, escape at least one of the symbols, i.e. "\//" or "\/\/" or "/\/". Escape or perish, iykyk. π
Examples:
// A single regular comment
/*
This comment can span
multiple lines
*/
> begin // The "BEGIN" block
+ request // This is required
- {ok} // An {ok} means to get a real reply
< begin//End the begin block
π§ OBJECT MACROS π§
An "object macro" is a piece of program code that is processed by the interpreter to give a little more "kick" to the RiveScript. All objects are required to define the programming language they use. Ones that don't should result in vociferous warnings by the interpreter. Vociferous! She said what she said! π’
Objects should be able to be declared inline within RiveScript code, however they may also be defined by the program utilizing the interpreter as well. All objects should receive, at a minimum, some kind of reference to the RiveScript interpreter object that called them. Gotta know who's calling, that's just manners.
Here is an example of a simple Perl object that encodes a bit of text into MD5 or Base64. Multi-talented icon. π
> object encode perl
my ($obj,$method,@args) = @_;
my $msg = join(" ",@args);
use Digest::MD5 qw(md5_hex);
use MIME::Base64 qw(encode_base64);
if ($method eq 'md5') {
return md5_hex($msg);
}
else {
return encode_base64($msg);
}
< object
To call an object within a response, call it in the format of:
<call>object_name arguments</call>
For example:
+ encode * in md5
- The MD5 hash of "<star>" is: <call>encode md5 <star></call>
+ encode * in base64
- The Base64 hash of "<star>" is: <call>encode base64 <star></call>
In the above examples, "encode" calls on the object named "encode", which we defined above; "md5" and "base64" calls on the method name, which is received by the object as $method. Finally, @args as received by the object would be the value of <star> in this example. Follow the crumbs, they all connect. π
$obj in this example would be a reference to the RiveScript interpreter. That's your link back home. π‘
β¨ TAGS β¨
Okay so bestie, tags are literally little bits of text you tuck inside the argument space of a RiveScript command. π The general rule of thumb: tags with <angle brackets> are for setting and getting a variable or for inserting text. Tags with {curly brackets} modify the text around them β like changing the formatting of the enclosed text. π
π¨ No tags can be used within! Definitionand> Labelunder any circumstances. Periodt. π¨
Unless otherwise specified, all of the tags can be used within every RiveScript command. Slay. β¨
π« <star>, <star1> - <starN> π«
The <star> tags are used for matching responses. See + TRIGGER for usage examples. π
β οΈ The<star>tags can NOT be used within+ Trigger. Not even a little. π
π <botstar>, <botstar1> - <botstarN> π
If the trigger included a % Previous command, <botstar> will match any wildcards that matched the bot's previous response. iykyk. π
+ ask me a question
- What color's your {random}shirt shoes socks{/random}
+ *
% what colors your *
- I wouldn't like <star> as a color for my <botstar>.
π¦ <input1> - <input9>; <reply1> - <reply9>. π¦
The input and reply tags insert the previous 1 to 9 things the client said, and the last 1 to 9 things the bot said, respectively. When these tags are used with + Trigger, they should be formatted against substitutions first. This way, the bot might be able to detect when the client is repeating themself or when they're repeating the bot's replies. fr fr. π
+ <reply1>
- Don't repeat what I say.
+ <input1>
* <input1> eq <input2> => That's the second time you've repeated yourself.
* <input1> eq <input3> => If you repeat yourself again I'll stop talking to you.
* <input1> eq <input4> => That's it. I'm done talking to you.{topic=blocked}
- Please don't repeat yourself.
<input> and <reply> are aliases for <input1> and <reply1>, respectively. π
π <id> π
The <id> tag inserts the client's ID, as told to the RiveScript interpreter when the client's ID and message were passed in. That's the whole vibe. β¨
π€ <bot> π€
Insert a bot variable, which was previously defined via the ! Definition var commands. Main character energy, no cap. π
+ what is your name
- I am <bot name>, a chatterbot created by <bot company>.
+ my name is <bot name>
- <set name=<bot name>>What a coincidence, that's my name too!
The <bot> tag allows assignment as well (which deprecates the old {!...} tag. Not me being deprecated. π
+ set mood to (happy|angry|sad)
* <get master> == true => <bot mood=<star>>Updated my mood.
- Only my botmaster can do that.
π <env> π
Insert a global variable, which was previously defined via ! Definition global commands. Global queen. π
+ is debug mode enabled
* <env debug> == 1 => Yes, debug mode is active.
- No, debug mode is set to "<env debug>"
The <env> tag allows assignment as well (which deprecates the old {!...} tag. Bestie, it's giving upgrade. β¨
+ turn debug mode on
* <get master> == true => <env debug=1>Debug mode enabled.
- You can't turn debug mode on.
π <get>, <set> π
Get and set a client variable. These variables are local to the user ID that is chatting with the bot. So no crossing streams, okay? π
+ my name is *
- <set name=<star>>Nice to meet you, <star>.
<get> can be used within + Trigger, but <set> can not. The audacity. π
β <add>, <sub>, <mult>, <div> β
Add, subtract, multiply, and divide a numeric client variable, respectively. Math ate. π₯
+ give me 5 points
- <add points=5>I've added 5 points to your account.
These tags can not be used within + Trigger. Nope. π
π― {topic=...} π―
Change the client's topic. This tag can only be used with * Condition and - Response. That's the rule, bestie. π
βοΈ {weight=...} βοΈ
When used with - Response, this will weigh the response more heavily to be chosen when random responses are available. When used with + Trigger, this sets that trigger to have a higher matching priority. It's giving main character matching energy. π
π {@...}, <@> π
Perform an inline redirection. This should work like a regular redirection but is embedded within another response. This tag can only be used with - Response, and in the response part of a * Condition. Okay so nested slay. π
<@> is an alias for {@<star>}. π¦
+ your *
- I think you meant to say "you are" or "you're", not "your". {@you are <star>}
π {!...} π
Perform an inline definition. This can be used just like the normal ! Definition command from within a reply. This tag can only be used with - Response. Lowkey powerful but...
β οΈ This tag is deprecated. This tag's purpose was to redefine a global or bot variable on the fly. Instead, the env and bot tags allow assignment. So maybe don't? But it's still in the lore. π
+ set bot mood to *
- <bot mood=<star>>Bot mood set to <star>.
π² {random}...{/random} π²
Insert a sub-set of random text. This tag can NOT be used with + Trigger. Use the same array syntax as when defining arrays (separate single-word groups with spaces and multi-word groups with pipes). Randomize the vibe. β¨
+ say something random
- This {random}sentence statement{/random} has a random {random}set of words|gang of vocabulary{/random}.
π§ {person}...{/person}, <person> π§
Process person substitutions on a group of text. The way that this swaps pronouns? Obsessed. π
+ say *
- Umm... "<person>"
In that example, if the client says say you are a robot, the bot should reply, Umm... "I am a robot." No cap. π«Ά
<person> is an alias for {person}<star>{/person}. πΈ
π {formal}...{/formal}, <formal> π
Formalize A String Of Text (Capitalize Every First Letter Of Every Word). It's giving fancy. π
+ my name is *
- Nice to meet you, <formal>.
<formal> is an alias for {formal}<star>{/formal}. β¨
βοΈ {sentence}...{/sentence}, <sentence> βοΈ
Format a string of text in sentence-case (capitilizing only the first letter of the first word of each sentence). Grammar queen but only the first letter, periodt. π
<sentence> is an alias for {sentence}<star>{/sentence}. π
π {uppercase}...{/uppercase}, <uppercase> π
FORMAT A STRING OF TEXT INTO UPPERCASE. SCREAMING IS THE POINT. π
<uppercase> is an alias for {uppercase}<star>{/uppercase}. π
π€« {lowercase}...{/lowercase}, <lowercase> π€«
format a string of text into lowercase. Soft launch the text. π¦
<lowercase> is an alias for {lowercase}<star>{/lowercase}. π
β
{ok} β
This is used only with the request trigger within the BEGIN block. It tells the interpreter that it's okay to go and get a real response to the client's message. Like a little permission slip. π«Ά
β£ \s β£
Inserts a white space character. This is useful with the ^ Continue command. Space queen. π
β΅ \n β΅
Inserts a line break character. Break it up, bestie. π
β \/ β
Inserts a forward slash. Slash slay. π₯
#οΈβ£ \# #οΈβ£
Inserts a pound symbol. Hashtag iconic. π
β¨ INTERPRETER IMPLEMENTATION β¨
okay bestie so if you're the one writing an interpreter for RiveScript (iconic of you, truly), these are the guidelines you gotta follow. this is the whole "how do I actually process this stuff" part β priorities for handling tags and for sorting your internal data structures. and it's programming-language-independent, so no excuses, every language eats the same rules. π
πΈ STANDARD GLOBAL VARIABLES πΈ
your interpreter must support these standard global variables, no cap:
depth= a recursion limit before an attempt to fetch a reply will be abandoned.
it's recommended you also have a debug variable for consistency, but lowkey it might not apply to you. do with that what you will. π
the depth variable though? strongly encouraged. it's there so the user can set their own recursion limit when fetching a response. because bestie, look at this pair of triggers β this is infinite recursion waiting to happen:
+ one
@ two
+ two
@ one
your interpreter should protect itself against that kind of chaos and give the user a depth variable so they can tune the recursion limit themselves:
! global depth = 25
π¦ PARSING GUIDELINES π¦
interpreters should parse all the RiveScript documents ahead of time and stash them in an efficient way, so replies can be looked up quickly. speed is the vibe. β‘
Sorting +Triggers
triggers get sorted in a "most specific first" order. like, the pickiest match goes on top. here's the tea:
- Atomic triggers first. sort them so the triggers with the most amount of words are on top. if multiple triggers have the same amount of words, sort them by length, and then alphabetically if there are still matches in length.
- Triggers that contain optionals in their triggers go next. sort them in the same manner as the atomic triggers.
- Triggers containing wildcards go next. sort them by the number of words that aren't wildcards. the order of wildcard sorting should be as follows:
A. Alphabetic wildcards (_) B. Numeric wildcards (#) C. Global wildcards (*)
- The very bottom of the list will be a trigger that simply matches
*by itself, if it exists. if triggers of only_or only#exist, sort them in the same order as in step 3.
Sorting %Previous
% Previous triggers should be sorted in the same manner as + Triggers, and associated with the reply group that they belong to. (creating pseudotopics for each % Previous is a good way to go β that's the move. π)
Syntax Checking
it will be helpful if the interpreter also offers syntax checking and gives verbose warnings when it tries to parse something that doesn't follow standards. when possible, it should try to correct the error, but should still emit a warning so the author might fix it. we love a helpful little nudge. π«Ά
it would also be good practice to keep track of file names and line numbers of each parsed command, so that syntax warnings can direct the author to the exact location where the problem occurred. pinpoint accuracy, periodt.
π REPLY FETCHING π
when you're trying to get a response to a client's message, the interpreter should support sending a "sender ID" along with the message. preferably this is a screen name or handle of the client sending the message, and the interpreter should be able to keep different groups of user variables for each user ID. the <id> tag substitutes for the user's ID. β¨
if the BEGIN block was defined in any of the loaded RiveScript documents, it should be tried for the request trigger. that is, this trigger should be matched:
> begin
+ request
- {ok}
< begin
the interpreter should make the request for that trigger in the context of the calling user, and allow it to change the user's topic or set a user variable immediately. do not process any other tags present in the response (see "TAG PRIORITY" below, bestie).
if the response contains the {ok} tag, then make a second request to try to match the client's actual message. when a response was found, substitute the {ok} tag from the BEGIN response with the text of the actual response the client wanted, and then process any remaining tags in the BEGIN response. finally, return the reply to the client. that's the whole flow. π
when fetching responses, this is the order of events that should happen:
- build in a system of recursion prevention. since replies can redirect to other replies, there's the possibility of deep recursion. the first thing the reply fetching routine should do is prevent this from getting out of control.
- dig through the triggers under the client's current topic. check to see if there are any
%Previouscommands on any of these topics and see if they match the bot's last message to the client. if so, make sure the client's current message matches the trigger in question. if so, we have a response set; skip to step 4. - find a trigger that matches the client's message. if one is found, we have a response set; continue to step 4.
- if we found a reply set, process the reply. first check if this reply set has a "solid redirection" (an
@command). if so, recurse the response routine with the redirection trigger and resume from step 1. break when an eventual response was returned. - process conditionals if they exist, in order. as soon as one of them returns true, we have a response and break. if none are true, continue to step 6.
- see if there is more than one response to this trigger. if any of the random responses has a
{weight}, take that into account as a random response is chosen. if we have a reply now, break. - if there is still no reply, insert a generic "no reply" error message.
when a reply was obtained, then tags should be executed on them in the order defined under "TAG PRIORITY". π
π TAG PRIORITY π
Within BEGIN/Request
within the request response of the BEGIN block, the following tags can be executed prior to getting a real response for the client's message:
{topic}
<set>
all other tags, especially modifier tags, must be held off until the final response has been given. substitute {ok} for the final response, and then process the other tags.
things like this should be able to work:
> begin
+ request
* <get name> eq undefined => {topic=new_user}{ok}
* <bot mood> eq happy => {ok}
* <bot mood> eq angry => {uppercase}{ok}{/uppercase}
* <bot mood> eq sad => {lowercase}{ok}{/lowercase}
- {ok}
< begin
Within +Trigger
all tags that appear within the context of + Trigger must be processed prior to any attempts to match on the trigger. no skipping, bestie. π
Within Replies
the order that the tags should be processed within a response β or anywhere else a tag is allowed β is as follows:
<star> # Static text macros
<input> #
<reply> #
<id> #
\s #
\n #
\\ #
\# #
{random} # Random text insertion (which may contain other tags)
<person> # String modifiers
<formal> #
<sentence> #
<uppercase> #
<lowercase> #
<bot>* # Insert bot variables
<env>* # Insert environment variables
<set>* # User variable modifiers
<add>* #
<sub>* #
<mult>* #
<div>* #
<get>* # Get user variables
{topic} # Set user topic
<@> # Inline redirection
<call> # Object macros.
* the variable manipulation tags should all be processed "at the same time", not in any particular order. this will allow, for example, the following sort of trigger to work:
+ my name is *
* <get name> != undefined =>
^ <set oldname=<get name>>I thought your name was <get oldname>?
^ <set name=<formal>>
- <set name=<formal>>Nice to meet you.
in older implementations of RiveScript, set tags were processed earlier than get, making it impossible to copy variables. implementations should process this group of tags from the most-embedded outward. π₯
an easy way to do this is with a regular expression that matches a tag that contains no other tag, and make multiple passes until no tags remain that match the regexp:
/<([^<]+?)>/
π REVERSE COMPATIBILITY π
RiveScript 2.00 will have limited backwards compatibility with RiveScript 1.x documents. here is a full breakdown of the differences, and yes, we're going through all of it:
RiveScript Changes from 1.02 to 2.00
------------------------------------
REMOVED:
- Variants of !DEFINITION
- ! addpath
- ! include
- ! syslib
- RiveScript Libraries (RSL files)
- RiveScript Packages (RSP files)
- These made code management messy. Keep your own
brain's files together!
COMPATIBLE CHANGES:
- Object macros now require the programming language to be defined.
- Old way: > object encode
- New way: > object encode perl
- The ^CONTINUE command can extend every command.
- Most tags can be used with almost every command.
- Topics can inherit triggers from other topics now.
INCOMPATIBLE CHANGES:
- Conditionals work differently now. Instead of comparing variables to values,
they compare values to values, and each value can <get> variables to compare.
- Old way: * name = Bob => Hello Bob!
- New way: * <get name> eq Bob => Hello Bob!
- Conditionals no longer use a single = for "equal to" comparison. Replace it
with either == or "eq".
- Object macros will receive a reference to the RiveScript object as their first
argument.
- Objects are called in a new <call> syntax instead of the old &object one.
NEW THINGS:
- {weight} is a valid tag in triggers now to increase matching priority.
- <env> has been added for calling global variables.
- <botstar> has been added for wildcard matching on %previous.
- Conditionals have more inequality comparisons now:
"==" and "eq" : equal to
"!=", "ne", and "<>" : not equal to
nice interpreters might be able to fix some old RiveScript code to make them work. for example, if a condition is found that has one equals sign instead of two, it could print a warning that it's detected RiveScript 1.x code in action and automatically adjust it to 2.x standards, and perhaps reparse the entire file or group of files, assuming that they are RiveScript 1.x code and fix these inconsistencies altogether.
or perhaps there will just be a converter tool created that would go through code that it already assumes will be RiveScript 1.x and update it to 2.x standards. either way, we love an upgrade arc. β¨π¦
Rev 12 β Nov 30, 2014 β¨
- Added implementation guidelines for dealing with variable-setting tags. π
Rev 11 β Jun 13, 2013 π
- Clarified that the
<bot>and<env>tags can also be used for assignment, bestie.
Rev 10 β May 15, 2012 π«Ά
- Deprecated the
{!...}tag, no cap. It was meant for reassigning global or bot variables. Instead, use<env name=value>,<bot name=value>. That's the move now.
Rev 9 β Jul 31, 2009 π
- Added way more explicit details on how the BEGIN block actually works, tucked under the section on
>Labels/"begin". Go look, it's giving clarity. - Revised the WD, fixing some typos. Little glow-up. π
Rev 8 β Jul 30, 2009 πΈ
- The proper format for the
! versionline is! version = 2.00, and not! version 2.00. The=is not optional, she's essential. π - Included the
"includes"option for triggers and changed how"inherits"works. Things shifted, stay woke.
Rev 7 β Dec 4, 2008 π¦
- Topics can inherit additional triggers that belong to different topics, using the
> topic alpha inherits betasyntax. So iconic. - Added more documentation to the
! arraysection of the document. Also check that section, because there are some changes to the way arrays should be processed by the interpreter. Important!! - Deprecated the
#command for inline comments. Use only//and/* */from now on. The#era is over, periodt. π
Rev 6 β Sep 15, 2008 π
- Updated the section about
#for inline comments: when used next to a+Trigger, there should be at least 2 spaces before the#symbol and 1 space after, to avoid confusion with#as a wildcard character. Detail matters, iykyk.
Rev 5 β Jul 22, 2008 π
- Added two new variants of the wildcard:
#will match only numbers and_will match only letters.*will still match anything at all. Choose your fighter. β
Rev 4 β Jun 19, 2008 π§
- Rearranged tag priorities:
<bot>and<env>moved higher up. β¬οΈ
Rev 3 β Apr 2, 2008 π
- Typo fix: under the
!personsection, the examples were using!sub. Oopsie, fixed. - Inconsistency fix: under
%Previousit was saying the wildcards were unmatchable, but this isn't the case β they go into<botstar>. Corrected, bestie. - Typo fix: under OBJECT MACROS, fixed the explanation of the code to match the new object syntax.
- Inconsistency fix:
<@>can be used in the response portion of conditionals. - Rearranged the tag priorities:
- String modifiers (person - lowercase) come in higher priority than
{random} <env>comes in after<bot>
- String modifiers (person - lowercase) come in higher priority than
- Typo fix: updated the object syntax (
<call>) in the priority list.
Rev 2 β Feb 18, 2008 π₯
- Moved
{random}to a higher tag priority. - Changed the
&objectsyntax to<call>. - Added the
<env>variable. - Added the
<botstar>variable.
Rev 1 β Jan 15, 2008 β
- Added the
{priority}tag to triggers, so you can increase a trigger's matching priority over others, even when another trigger might be a better match to the client's message. Main character trigger energy, honestly.
β οΈ DISCLAIMER β οΈ
Heads up, bestie: this document is only a working draft of the RiveScript 2.00 specification, and it may undergo numerous changes before a final standard is agreed on. So don't go printing it on a poster just yet, fr fr. π
Changes to this document after the creation date on January 14, 2008 will be noted in a change log.
And that's the whole page, bestie!! You made it all the way to the end and honestly? Obsessed with you. π«Ά This beauty is RiveScript::WD(3pm) from perl v5.36.0, dated 2022-12-23. Now go write some chaotic RiveScript triggers and slay the vibe check. Byeee π
β¨
π¦ where this page comes from
- debian bookworm: librivescript-perl 2.0.4-1 Β· MIT
- debian trixie: librivescript-perl 2.0.4-1 Β· MIT
- ubuntu noble: librivescript-perl 2.0.4-1 Β· MIT
- ubuntu resolute: librivescript-perl 2.0.4-1 Β· MIT
- ubuntu stonking: librivescript-perl 2.0.4-1 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms