npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@rotorsoft/chatbot

v1.0.7

Published

A simple js chatbot

Downloads

10

Readme

chatbot

A functional js chatbot

This is just a semantic layer on top of the flow module showing how to implement a basic chatbot engine in JavaScript.

Chatbot actions represent statements and questions asked to the end user. Answers are used as transition payloads to drive the conversation.

The bot API provides the following methods:

  • ids(): Returns the ids (tenant, id, legid)
  • params(): Returns the parameters
  • body(): Returns any remaining arguments
  • state(): Returns a copy of the current state
  • activity(): Returns a copy of the current activity
  • version(): Returns the current version (events length)
  • events(): Returns array of events
  • last(): Last event or null if empty
  • play(events = []): Plays talked or answered from events array in order. Can be used to rehydrate the bot aggregate
  • talk(mode): Pushes and returns current talked event. Can be used to repeat questions and handle timeouts
  • answer({ version, mode, ...payload}): Pushes answered event with provided transition payload (when active and version matches last event), and then pushes next talked event in flow. Returns object with pushed events (and current state when flow ends).

Event Schemas

talked: {
  name: "talked",
  time: new Date().toISOString(),
  activity: 'current activity name, "end" when flow is done'
  type: 'current activity type',
  mode: 'how talk event is delivered (voice, chat, sms, etc)',
  say: 'optional array of statements to say',
  ask: 'optional string with something to ask',
  sms: 'optional sms string to deliver',
  transfer: 'optional string to transfer the conversation',
  asked: 'how many times this question has been asked',
  timeout: 'timeout to control retries',
}

answered: {
  name: "answered",
  time: new Date().toISOString(),
  activity: 'current activity name',
  mode: 'how answer was received (voice, chat, sms, etc)',
  ...payload: 'object with trasition payload'
}

How to use

This module exports two function closures: bot and reducer

The reducer handles transition payloads (answers to drive the chat) and action payloads to produce talked events with commands like say, ask, sms, transfer.

The bot is initialized with a map of actions, the reducer, and the following required arguments:

  • tenant: Tenant id (to be used in multitenant environments)
  • id: End user id
  • legid: Leg id
  • root: Name of root action
  • params: Map with flow parameters including languageCode

The optional array of events can also be passed as argument to rehydrate the flow from persisted storage.

const { bot, reducer } = require("@rotorsoft/chatbot");
const common = require("./actions/common");
const { main } = require("./actions/main");

const b = bot({ ...common, main }, reducer("en"), {
  tenant: "tenant123",
  id: "user1",
  legid: "chat456",
  root: "main",
  params: { name: "John", age: 20, languageCode: "en" },
});
const state = b.state();
console.log(state);

Test

npm test

The provided tests are self explanatory and should log a trace like this:

[ 0] main() { // [{"say":{"en":"Hi.","...}, authenticate(state,scope,{params}), begin({authenticate={}}), end()]
[ 0]    {"say":{"en":"Hi.","es":"Hola."}}
[ 'Hi.' ]
[ 3]    authenticate() { // [{"ask":{"en":"Am I s...}, (state,{recur})]
[ 3]       {"ask":{"en":"Am I speaking with John?","es":"Estoy hablando con John?"}}
[ 'Hi.' ] Am I speaking with John?
[ 3]       (state,{recur}) ... [ 4]       canComeToThePhone() { // [{"ask":{"en":"Ok, ca...}, (state,{recur})]
[ 4]          {"ask":{"en":"Ok, can John come to the phone?","es":"John puede venir al telefono?"}}
[] Ok, can John come to the phone?
[ 4]          (state,{recur}) ... [ 5]          canComeToThePhone:1() { // [{"ask":{"en":"Ok, ca...}, (state,{recur})]
[ 5]             {"ask":{"en":"Ok, can John come to the phone?","es":"John puede venir al telefono?"}}
[ "I didn't get that. Can you repeat?" ] Ok, can John come to the phone?
[ 5]             (state,{recur}) ... [ 6]             verifyPhone() { // [{"ask":{"en":"Is thi...}, (state,{recur})]
[ 6]                {"ask":{"en":"Is this the correct number for John?","es":"Es este el numero correcto para hablar con John?"}}
[] Is this the correct number for John?
[ 6]                (state,{recur}) ... [ 6]                {"say":{"en":"Ok, thank you. Could you please tell John that this is a test? We’ll try calling back at a later time.","es":"Ok, gracias. Le prodria decir a John que this is a test? Llamaremos en otra ...}
[
  'Ok, thank you. Could you please tell John that this is a test? We’ll try calling back at a later time.'
]
[ 6]             } // verifyPhone
[ 5]          } // canComeToThePhone:1
[ 4]       } // canComeToThePhone
[ 3]    } // authenticate
[ 2]    begin() {
[ 2]    } // begin
[ 1]    end() {
[ 1]       {"say":{"en":"Good bye.","es":"Adios."}}
[
  'Ok, thank you. Could you please tell John that this is a test? We’ll try calling back at a later time.',
  'Good bye.'
]
[ 1]    } // end
[ 0] } // main
{
  main: {},
  authenticate: { mode: undefined, text: undefined, value: undefined, intent: 'no' },
  canComeToThePhone: { mode: undefined, text: undefined, value: undefined, intent: 'no' },
  verifyPhone: { mode: undefined, text: undefined, value: undefined, intent: 'yes' },
  say: [
    'Ok, thank you. Could you please tell John that this is a test? We’ll try calling back at a later time.',
    'Good bye.'
  ],
  end: {}
}
    √ should verify phone when user cannot come to the phone (63ms)

Leonardo da Vinci


Contributing

In lieu of a formal style guide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code.

License

MIT