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

alana

v1.0.6

Published

alana cli

Downloads

3

Readme

Opinionated & Batteries-included chatbot framework for javascript

There are many chabots out there but why are they stuck in the desert of regexs?

Online IDE at https://alana.cloud

Framework documentation online at https://alana.tech or in markdown

What's included

  • Built-in intents, premade intents to understand user input Open Source
  • Scripting system, simple but powerful
  • Testing sophsitcated testing is a first class ssytem allowing you to easily refactor and verify
  • Multiple platforms connectors availabel for many platforms (console, self-hosted web chat, facebook, etc...)

Installation

$ npm install -g alana

Usage

The best way to experience alana development is through the online IDE, however if you want to develop locally...

$ alana
# list all commands
$ alana help <command>
# print help about a specific command

Most useful commands

$ alana init    
# creates a basic template of an alana bot
$ alana run
# Run the bot locally
$ alana test
# Run all test scripts

Trivia bot example

All demos available here

The purpose of the bot will be allowing players to play a game of trivia, present them with questions about a specific topic and have them try to answer them correctly. We'll keep track of the players scores too.

TOC

Setup

First lets create a new bot and play with the echo template

$ alana init  
$ ls
# -- scripts will hold you chat scripts
# -- tests will hold your test scripts
$ alana test
$ alana run
# ^-c (ctrl-c) to qit

Now it's time to make the bot play some trivia! Open scripts/index.js and delete all the template code Let's add some trivia knowledge to the top of the

const trivia = {
  'history': [                                            // this is a trivia topic
    { q: 'Question 1',                                    // this is the question we will ask the user
      w: ['a2', 'a3', 'a4'],                              // these are wrong answers
      c: 'a1',                                            // this is the correct answer
     },
     { q: 'Question 2', w: ['a2', 'a3', 'a4'], c: 'a1' },
     { q: 'Question 3', w: ['a2', 'a3', 'a4'], c: 'a1' },
   ],
   'sports': [
    { q: 'Question 1', w: ['a2', 'a3', 'a4'], c: 'a1' },
    { q: 'Question 2', w: ['a2', 'a3', 'a4'], c: 'a1' },
    { q: 'Question 3', w: ['a2', 'a3', 'a4'], c: 'a1' },
   ],
};

Add a greeting

Let's add a greeting (text that is sent once (and only once) to a new user when they first connect to the bot. Add the following code to the bottom of the file.

addGreeting((user, response) => {
  response.sendText('Welcome to trivia time!');
  user.score = 0;                                 //set the user's score to 0
});
// after the greeting is sent the default script will be called

As a good software engineer, lets add a test to make sure the greeting is sent to users. Open test/index.js. Erase all the tests and add the following code:

test('greeting', function(){
  return newTest()                               // we need to return the test promise
    .expectText('Welcome to trivia time!')       // this is the greeting text we expect
    .run();                                      // we need to call run() at the end of the test
})

Now run the test:

$ alana test
  ✓  greeting
  1 passing

Add a basic menu

We need to let the user pick a trivie topic! So let's make a default script that will handle that. Add the following code to scripts/index.js.

newScript()                                          // create a default script
  .dialog('start', (session, response, stop) => {
    response.sendText('Pick a trivia topic');
    const topics = Object.keys(trivia).join(' or ');
    response.sendText(topics);
  })
  .expect.text((session, response, stop) => {        // this will only be called if the user responds with a text message
    const userInput = session.message.text;          // text of the user's message
    response.startScript(userInput);                 // switch to a new script that has the title of the user's text
  });

Now another test in tests/index.jsfor the menu at the bottom of the file.

test('menu', function() {
  newTest()
    .expectText('Welcome to trivia time!')
    .expectText('Pick a trivia topic')
    .expectText('history or sports')
    .run();
})

Now run the test:

$ alana test
  ✓  greeting
  ✓  menu
  2 passing

Gameplay

To support actually playing the game we'll have to ask the user a question and give them some answers to choose from. With alana we can have named scripts that can be called from other scripts. So we'll make a new script for each topic. Each script is divided into dialogs. There are some special dialogs like begin, expect.

Object.keys(trivia).forEach((topic) => {                             // iterate through each topic
  newScript(topic)                                                   // a new script with the topic name
    /* 
     * begin dialogs are like greetings for scripts, they are only sent once
     */
    .begin((session, response, stop) => { 
      response.sendText(`Time to test you on ${topic}!`);
      session.user.question_number = 0;                              // Reset what question the user is on
    })
    /* 
     * If the dialog doesn't call stop() then the script will automatically flow 
     * to the next dialog. We can also use named dialogs to move around a script
     * we'll use this later to loop around the questions
     */
    .dialog('start', (session, response, stop) => {
      const question = trivia[topic][session.user.question_number];
      response
        .sendText(question.q)
        .sendText('Is it:');                                         //notice how you can chain responses (but don't have to!)
      response.sendText(question.w.concat(question.c).join(' or ')); //exercise for the reader to shuffle these!
    })
    /* 
     * The script will stop here because the next dialog is an expect dialog, which tells
     * alana to wait for input from the user. We can even specialize the expect to only 
     * response to a text message.
     */
    .expect.text((session, response, stop) => {
      const question = trivia[topic][session.user.question_number];
      if (session.message.text === question.c) {
        response.sendText('Correct!');
        session.user.score++;
      } else {
        response.sendText(`Wrong :( it was ${question.c}`);
        session.user.score = Math.max(0, session.user.score - 1);
      }
      response.sendText(`Your score is ${session.user.score}`);
      session.user.question_number++;
      if (session.user.question_number < trivia[topic].length) {
        // we still have questions, ask the next one by looping back to the 
        // 'start' dialog
        response.goto('start')
       }
    });
    /* 
     * At the end of script, alana will automatically move the user to the default
     * script, usually the main menu
     */
});

Now let's add a test for gameplay in tests/index.js

test('gameplay', function() {
  return newTest()
    .checkForTrailingDialogs()                  // this checks if any extra text was send after the test ended
    .expectText('Welcome to trivia time!')
    .expectText('Pick a trivia topic')
    .expectText('history or sports')
    .sendText('history')
    .expectText('Time to test you on history!')
    .expectText('Question 1')
    .run();
})

Now run the test:

$ alana test
  ✓  greeting
  ✓  menu
  1)  gameplay
  2 passing
  1 failing
  1)  gameplay:
  Error: Got an extra message: { type: 'text', text: 'Is it:' }

Oh no, our test wasn't complete, let's add the missing text to the test and try again

test('gameplay', function() {
  return newTest()
    .checkForTrailingDialogs()
    .expectText('Welcome to trivia time!')
    .expectText('Pick a trivia topic')
    .expectText('history or sports')
    .sendText('history')
    .expectText('Time to test you on history!')
    .expectText('Question 1')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .run();
})
$ alana test
  ✓  greeting
  ✓  menu
  ✓  gameplay
  3 passing

Recognizing intent

What if the user is confused and needs instructions? Let's recognize the help intent. Add the following code between .begin(...) and .dialog(...) in scripts\index.js

// scripts/index.js
//newScript(topic)
//.begin(...)
.intent.always('general', 'help', (session, response) => {  // recognize the help action in the general domain
  response.sendText(`You are in the ${topic} section`);     // send something helpful
  response.goto('start');                                   // ask the current question again
})
//.dialog('start', ...)
// tests/index.js
test('intent', function() {
  return newTest()
    .checkForTrailingDialogs()
    .expectText('Welcome to trivia time!')
    .expectText('Pick a trivia topic')
    .expectText('history or sports')
    .sendText('history')
    .expectText('Time to test you on history!')
    .expectText('Question 1')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .sendText('a1')
    .expectText('Correct!')
    .expectText('Your score is 1')
    .expectText('Question 2')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .sendText('help')
    .expectText('You are in the history section')
    .expectText('Question 2')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .run();
})

Play it yourself

$ alana run --console

Final file

/** scripts/index.js **/
const trivia = {
  'history': [ // this is a trivia topic
    { q: 'Question 1', // this is the question we will ask the user
      w: ['a2', 'a3', 'a4'], // these are wrong answers
      c: 'a1', // this is the correct answer
     },
     { q: 'Question 2', w: ['a2', 'a3', 'a4'], c: 'a1' },
     { q: 'Question 3', w: ['a2', 'a3', 'a4'], c: 'a1' },
   ],
   'sports': [
    { q: 'Question 1', w: ['a2', 'a3', 'a4'], c: 'a1' },
    { q: 'Question 2', w: ['a2', 'a3', 'a4'], c: 'a1' },
    { q: 'Question 3', w: ['a2', 'a3', 'a4'], c: 'a1' },
   ],
};

addGreeting((user, response) => {
  response.sendText('Welcome to trivia time!');
  user.score = 0; //set the user's score to 0
});
// after the greeting is sent the default script will be called

newScript() // 
  .dialog('start', (session, response, stop) => {
    response.sendText('Pick a trivia topic');
    const topics = Object.keys(trivia).join(' or ');
    response.sendText(topics);
  })
  .expect.text((session, response, stop) => {
    response.startScript(session.message.text);
  });

Object.keys(trivia).forEach((topic) => { // iterate through each topic
  newScript(topic) // a new script with the topic name
    .begin((session, response, stop) => { 
      response.sendText(`Time to test you on ${topic}!`);
      session.user.question_number = 0; // Reset what question the user is on
    })
    .intent.always('general', 'help', (session, response) => {
      response.sendText(`You are in the ${topic} section`);
      response.goto('start');
    })
    .dialog('start', (session, response, stop) => {
      const question = trivia[topic][session.user.question_number];
      response
        .sendText(question.q)  
        .sendText('Is it:');
      response.sendText(question.w.concat(question.c).join(' or ')); //exercise for the reader to shuffle these!
    })
    .expect.text((session, response, stop) => {
      const question = trivia[topic][session.user.question_number];
      if (session.message.text === question.c) {
        response.sendText('Correct!');
        session.user.score++;
      } else {
        response.sendText(`Wrong :( it was ${question.c}`);
        session.user.score = Math.max(0, session.user.score - 1);
      }
      response.sendText(`Your score is ${session.user.score}`);
      session.user.question_number++;
      if (session.user.question_number < trivia[topic].length) {
        // we still have questions, ask the next one
        response.goto('start');
       }
    });
});
/** tests/index.js **/
test('greeting', function(){
  return newTest()
    .expectText('Welcome to trivia time!')
    .run();
})

test('menu', function(){
  return newTest()
    .expectText('Welcome to trivia time!')
    .expectText('Pick a trivia topic')
    .expectText('history or sports')
    .run();
})

test('gameplay', function() {
  return newTest()
    .checkForTrailingDialogs()
    .expectText('Welcome to trivia time!')
    .expectText('Pick a trivia topic')
    .expectText('history or sports')
    .sendText('history')
    .expectText('Time to test you on history!')
    .expectText('Question 1')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .run();
})

test('intent', function() {
  return newTest()
    .checkForTrailingDialogs()
    .expectText('Welcome to trivia time!')
    .expectText('Pick a trivia topic')
    .expectText('history or sports')
    .sendText('history')
    .expectText('Time to test you on history!')
    .expectText('Question 1')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .sendText('a1')
    .expectText('Correct!')
    .expectText('Your score is 1')
    .expectText('Question 2')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .sendText('help')
    .expectText('You are in the history section')
    .expectText('Question 2')
    .expectText('Is it:')
    .expectText('a2 or a3 or a4 or a1')
    .run();
})