Skip to content

Plugins and Functions

Marius Ursache edited this page Aug 8, 2015 · 10 revisions

IntroductionTriggersRepliesConversationsTopicsPlugins and FunctionsKnowledgeUnder the Hood


Plugins and Functions

A plugin is a collection of functions (see below for details or look at individual functions from the Functions Reference).

Functions

This is where things get interesting. Replies can execute a custom function where they have access to the entire message object, the user object and other resources like the fact databases. You can also pass parameters to the functions from captured input parts:

+ * weather in <name>?
- ^getWeather(<cap1>)

Functions are stored in plugins (sets of related functions), which are imported from the plugins folder at load time and are async Node.js functions:

exports.getWeather = function(city, cb) {
  cb(null, "It is probably sunny in " + city);
}

Multiple functions

You can have more than one function in a reply, so this is also valid:

+ It is ^callFunction1() and ^callFunction2()

Nesting functions

The results of the callback will replace the function name. You can even nest functions within callback replies, so this is valid too.

+ ^nestedAFunction()
exports.nestedAFunction = function(cb) {
  cb(null, "We ^nestedBFunction()");
}
exports.nestedBFunction = function(cb) {
  cb(null, "are done");
}

The final reply will be We are done. The above example is contrived, remember you can also require the module and call the function directly from within the plugin code. This is also true for other NPM modules.

There will be an entire new section on custom functions, but for now, explore the inside the plugins folder and review the test suite to get a better picture of some of the things possible.

Filters

Filters are functions used to filter out replies or triggers from being used. They are like using conditional logic in your app, and are actually functions (plugins) which return a TRUE or FALSE value. In the SuperScript Editor, filters have their own text box with an auto complete.

+ i am *1
- {^functionX(true)}  Yes, you are <cap>.
- {^functionX(false)} You’re lying, you’re not <cap>!

In this example the ^functionX() is evaluated when we are making the decision to pick a reply. Lets see a real example based on one of the Loebner questions.

+ my name is <name>
- {^hasName(false)} ^save(name,<cap1>) Nice to meet you, <cap1>!
- {^hasName(true)}  I know, you already told me your name!

Out of bound data

We can pass extra metadata back to the client, using a specific function, ^addMessageProp(key,value). The reply object can have extra properties added on from any reply.

+ can you (text/sms) me the name of the place
- {^hasNumber(false)} Can I have your phone number, please?
- {^hasNumber(true)}  ^addMessageProp(medium,txt) The Canteen

In the above example, if the user asks Can you text me the name of the place?, the system will check if the system has the user’s phone number (using the ^hasNumber filter). If the user does not have a phone number, the reply will be Can I have your phone number, please? If the user has a phone number, the generated reply object looks like this:

{ 
  string: "34345 Old Yale Rd.", 
  medium: "txt", 
  createdAt: "..." 
}

and is sent to user’s mobile phone number as text (if the system has this defined). The function can be used from any reply, or you can augment the message props variable directly from within any custom function: this.message.props['key'] = value;

Another example can be found here, and returns the color as a HEX RGB value, updating the page background color. You can think of this channel as anything non-text or non-verbal (perhaps an avatar emoji or something else to represent mood etc.).

Another frequent case can be injecting HTML content in the reply:

+ show * car
- Here’s my car: ^addMessageProp(img,http://www.exoticspotter.com/images/19/18281.jpg)

Rendering these props should be addressed by the client layer (web interface, mobile app, SMS etc.)


For more functions, check Functions Reference


Writing plugins

What is this

Custom functions are called in their own scope. This is done to give you control over most of the important bits of the system.

this.message

We expose the entire message object which is broken down 12 different ways and includes all the words in raw and lemma form, all the parts-of-speech and even compound 'things' like entities and dates.

this.user

The user who sent the message as we know them. This will allow us to see the past messages in their history, how many conversation replies we have had, and when the conversation started.

We also have direct access to the user’s memory.

this.user.memory
this.user.memory.db

Also, the entire message and reply history from the user (an array of past utterances and replies can accessed using

this.user.__history__

From the user memory you can query a sub-level graph DB for known facts the user may have shared or save new ones.

this.botfacts
this.botfacts.db 

This is identical to the sub-level graph for the user except it contains only things about the bot and can be loaded on init.

this.facts
this.facts.db

This is similar to the users sub-level graph except it contains all the global facts.

this.topicSystem

We expose the topic system to the plugins in case you want to create a new topic reply or trigger dynamically.

Because you are in the fun wild world of Node.js you can also install any library from NPM and use it, and since the plugs are async, you can even make calls to Databases, API's or other remote web services.

Making a custom function (or object) available from within a plugin

For this you just need to pass it into the bot options, when creating the new superscript bot object. Here is an example:

var superscript = require("superscript");

var options = {};
options['factSystem'] = factSystem;
options['mongoose'] = mongoose;
options['scope'] = {
  'customFunction': function(a, b) { return a + b }
}

new superscript(options, function(err, bot) {
   // bot stuff, request, reply
});

The keyname to use is 'scope' key, this will mixin to the plugins. Then from your custom plugin you have the function available. i.e..: this.customFunction()

This is useful, if you want some resource to be available in all the plugins and want to get it initialized at the start of the bot server. Another way to make some resource available in all the plugins is to declare and initialize it in one of the plugins and export it as a module, which can be require()'ed by other plugins by referencing to that plugin file. But that will introduce dependencies among plugins, which will be hard to document and they loose their modularity. Here is the line that calls the plugin: https://github.com/silentrob/superscript/blob/master/lib/processtags.js#L51-L52


Continue to Knowledge

Clone this wiki locally