RiveScript-JS

repository·master·Indexed 18 days ago

https://github.com/aichaos/rivescript-js

A JavaScript interpreter library for RiveScript, a scripting language for building chatterbots using trigger/response pairs. It supports Node.js and web browser environments and includes extensions such as rivescript-contrib-coffeescript for CoffeeScript object macros and rivescript-redis for Redis-based session management and user variable persistence.

Tokens
24.5K
Snippets
90
Records
142
Agent score
63%

What's inside rivescript-js

  1. Disable JavaScript object macros for security

    master

    If you are concerned about security risks associated with users injecting JavaScript via object macros, you can disable the parsing of JavaScript object macros directly within RiveScript source files using setHandler("javascript", null).

    Warning: Using the origMessage approach with untrusted users is potentially dangerous, as they might introduce syntax errors or attempt to inject JavaScript object macros.

    var bot = new RiveScript();
    
    // This will prevent `> object * javascript` in source code from being
    // parsed and executed.
    bot.setHandler("javascript", null);
    
    // You can still define macros from the program side via subroutines
    bot.setSubroutine("learn", function(rs, args) {
      // Implementation for learning logic
    });
  2. What is a SessionManager and how to use one

    master

    A SessionManager is an interface used to store user variables in RiveScript. These variables include those set via the <set> tag or setUservar() function, as well as recent reply history and private internal state.

    By default, RiveScript uses MemorySessionManager, which stores data in memory. This means data is lost when the program restarts. To persist data across restarts, you can replace the default manager with a custom implementation or a community contribution (like rivescript-contrib-redis) that connects to a database like MySQL, MongoDB, or Redis.

    To use a custom session manager, pass it as the sessionManager option when instantiating RiveScript.

    const RedisSessions = require("rivescript-contrib-redis");
    
    // Provide the sessionManager option to use this instead of
    // the default MemorySessionManager.
    var bot = new RiveScript({
    	sessionManager: new RedisSessions("localhost:6379")
    });
  3. MemorySessionManager

    master

    The MemorySessionManager is the default in-memory session store. It keeps all user variables in an object in memory and does not persist them to disk. Data is lost when the bot program reboots.

    To manually persist data using the default manager, you can use the RiveScript methods getUservars() and setUservars() to export and import user variables as JSON-serializable objects.

  4. Use the `scope` parameter in `reply()` to control JS macro context

    master

    The scope parameter in the reply() function allows you to change the execution context of JavaScript macros. Specifically, it redefines the meaning of the built-in this variable within the macro to point to the object provided in the scope argument.

    This is useful when your RiveScript bot instance is encapsulated within another JavaScript object (e.g., a class or a controller). By passing the parent object as the scope, your RiveScript JS macros can directly access and manipulate the properties and methods of that parent object using this.

    // Conceptual usage:
    // If your bot is inside a ScopedBot instance:
    
    class ScopedBot {
      constructor() {
        this.hello = "Hello world";
        this.counter = 1;
        this.bot = new RiveScript();
        // ... setup bot ...
      }
    
      async testScope(userInput) {
        // Passing 'this' (the ScopedBot instance) as the scope
        // allows JS macros in RiveScript to access ScopedBot's properties.
        return await this.bot.reply(this, userInput);
      }
    }
  5. Limitations of asynchronous macros in RiveScript conditionals

    master

    RiveScript conditionals (*Conditions) do not support asynchronous object macros on the conditional side (the part before the => separator).

    Because RiveScript requires conditional results to be immediately available to evaluate truthiness, <call> tags within a conditional can only execute synchronous object macros (those returning a string, not a Promise).

    What works:

    • You can use asynchronous macros in the reply portion (the part after the => separator). When using replyAsync(), the text returned by an async macro in the reply section will be handled correctly as a promise.

    What fails:

    // This will NOT work if 'weather' is an async macro
    + is it sunny outside
    * <call>weather <get zipcode></call> == sunny => It appears it is!
    - It doesn't look sunny outside.
  6. Use Async Objects in RiveScript Conditions

    master

    RiveScript v2.0.0+ supports asynchronous object macros that return Promises. Because of the Async/Await architecture, these objects can now be used within *Condition statements in your .rive files. This allows you to perform asynchronous logic (like timers or database lookups) and use the result to branch your bot's responses.

    // <call>wait-limited $timeout $maxTimeout</call>
    // If the $timeout > $maxTimeout, it resolves "too long" immediately.
    // Otherwise it waits $timeout seconds and resolves "done"
    > object wait-limited javascript
    	var timeout = parseInt(args[0]);
    	var max     = parseInt(args[1]);
    
    	return new Promise(function(resolve, reject) {
    		if (timeout > max) {
    			resolve("too long");
    		} else {
    			setTimeout(function() {
    				resolve("done");
    			}, timeout*1000);
    		}
    	});
    < object
    
    + can you wait # seconds
    * <call>wait-limited <star> 6</call> == done => I can!
    - No the longest I'll wait is 6 seconds.
  7. Use JavaScript support for RiveScript Macros

    master

    RiveScript.js includes built-in support for using JavaScript within RiveScript macros. This allows you to execute arbitrary JavaScript code directly from your RiveScript files. This feature is enabled by default.

    If you need to disable this functionality for security or performance reasons, you can override the javascript language handler by setting it to null using bot.setHandler("javascript", null);.

    // To disable JavaScript macro support:
    bot.setHandler("javascript", null);
  8. Understand the RiveScript Brain concept

    master
    A 'Brain' in RiveScript refers to the collection of RiveScript files (containing objects, responses, and logic) that define the chatbot's personality and knowledge. The standard demo brain is an Eliza-like chatbot implementation used to demonstrate RiveScript features such as complex replies and logic handling.
  9. Use RiveScript as a Router for programmatic chatbots

    master

    You can use RiveScript as a router to map specific input patterns (triggers) directly to JavaScript object macro handlers. This approach is useful for heavily programmatic chatbots where you want to avoid manually writing <call> tags in RiveScript files for every single trigger. Instead, you can define a mapping of trigger arrays to JavaScript functions and dynamically generate the RiveScript source code to register these macros.

    // Conceptual pattern for mapping routes to handlers
    const replies = {
      ["add 5 and 7", "what is 12 divided by 3"]: mathFunction,
      ["reverse hello world"]: reverseFunction
    };
  10. Configure Global Concatenation Mode

    master

    The concat option controls how RiveScript joins two lines of code when a ^Continue command is used.

    Options:

    • none (default): Joins lines with no symbols.
    • newline: Joins lines with line breaks.
    • space: Joins lines with a single space.

    Best Practice: Avoid setting a global concat if you plan to share your RiveScript personality. Instead, use the local command ! local concat = <mode> within your source files to ensure consistent behavior across different environments.

  11. Use Force Case to allow uppercase triggers

    master

    Setting forceCase: true in the constructor makes RiveScript lowercase all triggers during the parsing phase. This prevents parse errors when authors use capital letters (e.g., + I am *) that would otherwise be invalid.

    Warning: This may cause issues with certain Unicode symbols due to how case folding works in Unicode.

  12. Understand the `deparse()` data schema

    master

    The object returned by deparse() follows a specific hierarchical structure representing the bot's logic and variables.

    Root Structure

    • begin: Contains global definitions:
      • global: Map of ! global variables.
      • var: Map of ! var bot variables.
      • sub: Map of ! sub substitution definitions.
      • person: Map of ! person substitution definitions.
      • array: Map of ! array names to arrays of values.
      • triggers: Array of trigger data for the > begin block.
    • topics: Map of topic names to an array of trigger data (the default topic is named "random").
    • inherits: Map of topic names to maps of inherited topics.
    • includes: Map of topic names to maps of included topics.
    • objects: Source code of JavaScript/CoffeeScript object macros (Note: macros defined via rs.setSubroutine() may appear here, but stringifying them is not recommended).

    Trigger Data Schema

    Each trigger object in begin.triggers or topics.$NAME contains:

    • trigger: The plain text trigger string.
    • reply: An array of plain text -Reply commands (or []).
    • condition: An array of plain text *Condition commands (or []).
    • redirect: The text of the @Redirect command, or null.
    • previous: The text of the %Previous command, or null.