Start building your own chatbot now >

This tutorial is now deprecated. Please check our NodeJS moviebot tutorial for current information.

By the end of this tutorial, you will be able to build a fully functional Github bot! We’re using the Recast.AI platform to build the bot and the Github API to let the bot dive into code repositories.

Here’s a conversation example your bot will be able to have when we’re done:

User: Hello!
Bot: Hi there! I’m the Github bot! Give me two projects, I will make a battle 😉
User: okay, so Angular
Bot: ok, and the second one ?
User: React
BOT: haha: React wins… , Do you want more informations about React ?
User: Yes

A little more about Recast.AI

There are 4 phases in the bot creation process, represented on our platform by 4 tabs:

recast bot

  • Train – teach your bot what it needs to understand.
  • Build – create your conversational flow with our Bot Builder tool.
  • Run – connect your bot to a messaging channel of your choice.
  • Monitor – regularly train your bot to keep it up to date, and get insights on its usage!
  • Your first Hello world

Create your bot

To create a new bot, login and click on + NEW BOT in the header section.

  • Choose your name: ‘github-bot’
  • Add a description: ‘Order food easily with RestoBot‘
  • Set English as the default language.
  • Choose a template (the default bot is a good start).
  • You can keep your bot public as there is no private info

create bot with recast

After that, you are redirected to the main page. This is the heart of your bot, where all its comprehensive abilities are gathered, decomposed into intents.
Intents are “boxes” of sentences (that we often call “expressions”) that all mean the same thing but are phrased differently. Each intent matches one action your user potentially wants to perform.

For instance, an intent ‘greetings’ makes your bot understand when a user says ‘Hello’ or ‘Hi’.

Explore each intent by clicking on their names.

Discover the Bot Builder tool

Click on the BUILD tab to find Bot Builder! Bot Builder is a tool to help you create your conversation flow.
Now, you have two blocks, “greetings, and “goodbyes”. We call them actions. An action is a step in your conversation. It is triggered by a user input and completed by a bot response. An action is always paired with an intent.

recast github

Hover over the greetings action and click on the pen to edit it. You will see the process of what’s happening when a user says “hello”. If you click on the button “ADD BOT REPLIES”, you can see what the bot will reply when this action is done.

Test it on the platform

Click on the TEST bubble icon on the top right to make it appear. Type a sentence to test your bot training: “Hello”.

recast hello console

Connect your bot

How does that work? Well, with our Bot Connector, you can connect your bot to multiple channels. Bot Connector takes care of receiving all messages and transfers it to your bot in a uniform format.

recast demo

Go to the Run tab and toggle the channel you want to connect your bot to. You need to have an account on the messaging platforms and setup tokens. Follow the instruction to configure your channel.recast connect

After completion of all steps, go to the relevant messaging app and surprise surprise, your bot is live!
Now, let’s focus on adding the main part of the bot: the github repository battle.

Create your bot knowledge

Fork intents

There are certain intents all bots should have in order to understand basic things such as ‘greetings’, ‘agree’, ‘disagree’, and when a user asks for help.
Our platform being collaborative, you do not have to recreate each individual intent every single time! You can ‘fork’ an intent someone already created to clone it right into your bot.

To add an existing intent called “Help”
Write ‘help’ in the input Search and Fork from the community

recast train

Click on search. Select one of the first results and just click on the ‘FORK’ button on the right.

fork intent recast

Repeat this process for ‘agree’ and ‘disagree’

Create a new intent

If you want a custom intent, you can build it from scratch.
Here, we want the bot to understand when someone asks for a battle of repos. Let’s create these intents.

Write ‘battle’ in the input Search and Fork from the community

Click on create.

recast github

Add expressions

Now that intents are created, we have to enter various expressions.
The optimal amount of paraphrased expressions for an intent lies at around 20.
Click on one intent, and add expressions you want your bot to understand. Put yourself in the shoes of the people talking to your bot. What could they possibly ask? Enter a new expression by typing in the field Add an expression.

recast add an expression

Create an entity

For some expressions, you’re going to need to extract some key data to use later. That’s what entities are for. Entities are keywords detected in expressions. You can see them by clicking on an expression.

recast illustration of keywords

31 entities are automatically detected (like dates, temperatures, emojis or locations) and are called gold entities.
Here we need to detect repositories, so let’s create a custom entity called “repository”. Tag all the repositories in your expressions to train your bot to understand what a repo is.

recast tags

tags

Test your bot

Add additional expressions and check if the entity “repository” is automatically tagged. If not, keep tagging, you need more examples of different expressions and repository (with caps, without, long words, small..).

Now that we’ve filled the mind of your bot with tons of phrases and expressions, let’s test it with the console:
open it (TEST tab on the right) and go into the “ANALYSE” tab (not converse). Type a sentence like “Ionic vs Boostrap”:
If your training has been effective, you can see the intent matched with your sentence and the extracted entities. If the repositories are not extracted, you need to add more sentences in your intent!

recast console repo

Click on the “Smart view” toggle to switch the view to the JSON mode.
The JSON contains a lot of useful information about the message you’ve sent, because of the enrichments we provide for gold entities.

recast console json

Build your flow

Now that the brain of your bot is all filled up, click on the ‘build’ tab, launch the builder, and discover the magic!

The section on the left is your command panel. It allows you to create actions and link them to each other to create a flow.
To create an action, click on the “+” button and select an intent (“battle” for example). Place it and create all the other actions.

recast flow

Link the actions

There are two types of links:

Green links create an action flow where the user is guided in one direction, but remains free to start at any other step of the conversation.
Red links create an action flow where the user has to follow a defined path.

Connect actions:

Click on the right end of the action you want to link.
Click now on the left end of the other action you want to connect it to.
Change the color of your link by clicking on it then on the right color in the toolbar.

After the ‘battle intent’, we only have red links. Why? Because when the bot asks the user if he wants more information, the following step can only occur if we’ve established a battle winner.

github links

Add a notion

Now that we’ve set the flow, some of our actions certain pieces of information (entities) to be complete: we call them notions.

Click on the small pen that appears on the “battle” action.

recast notions

Click on the ‘Add Notion’ Button
Search the ‘repository’ custom entity we’ve created earlier and set the alias as ‘repo-1’

Recast.AI

Then click on the back button on the bottom, and you will have your repo-1 notion added in your overview.

recast repository

You now have your repository notion added in your overview.

Create a second notion for the second repository and name it ‘repo-2’. Now you have this overview:

Set the bot replies

There are 3 different replies you can set:

  • A reply when the action is done
  • A reply when a notion is missing in the sentence
  • A reply when you need a previous action to be done before (red-linked)

To set them:

  • Just go on the overview of any action card
  • Click on ‘Add bot replies’.
  • Add replies for each state of your action

You can also add variables by using the alias of your notion like this:

Let’s compare {{repo-1}} and {{repo-2}} !

Help:

Battle:

Agree:

Disagree:

Don’t understand:

Setup the end of conversation

Open the goodbye action and check the “This is the end of my conversation”.
This will delete the state and the memory of your conversation to let you restart 😉

Test your flow

Now that the flow is complete and the replies set, we can finally test the bot!
Use the console as you did earlier in the project.
Say Hello and the bot will start the flow!

Test your bot in the channel you’ve configured before (Messenger, Kik, Slack, etc):

Now, let’s add the Github API to retrieve information about the two repositories. Let’s start coding!

Code

Go to the RUN tab and click on the “Code” menu.
Follow the instructions to install the starter kit. Once your ngrok and server are running, and that your webhook url is the ngrok url, test yet again your bot on the channels you have connected to it. It still works 😉

So, what’s happening here? Each message your user types is sent to your bot webhook. Here, the url is the ngrok url and it’s your local server that receives the message.
The server.js file contains the server configuration and you can see that for each request received on ‘/’ we call

bot(request.body, response)

In the bot.js file, we use the Recast.AI SDK to handle the message and get the data we need from the request:

connect.handleMessage({ body }, response, replyMessage)

Then the main function is in message.js:

Each text inside the message received is sent to Recast.AI API where it’s analysed to determine where the user is in the conversation. This API call is done here:

request.converseText(text, { conversationToken: senderId })

The result of this request contains the replies you set in the Bot Builder tool, along with other necessary flow information.

To understand this data, go back to the platform and type “Hello” in the test console.
Switch to the JSON view. You can see that you have the action triggered by the sentence, “greetings”, and the next action. You also see a memory object with our two notions: ‘repo-1’ and ‘repo-2’. Memory is empty for now, but if you say “React”, the memory will fill up 😉

The repos versus

Create a file battle.js.

touch src/github.js

and paste these lines:

const battle = (repo1, repo2) => {
  return Promise.resolve(
    { type: 'text', content: `${repo1} VS ${repo2}` }
  )
}

module.exports = battle

and in the message.js, import the file

const battle = require('./battle’)

Add these lines inside the then callback of the message.reply()

if (result.action && result.action.slug === 'battle' && result.action.done) {
  battle(result.getMemory('repo-1').raw, result.getMemory('repo-2').raw)
    .then(res => {
      message.addReply(res)
        message.reply()
     })
}

Your file will look like this:

/*
 * message.js
 * This file contains your bot code
 */

const recastai = require('recastai')
const battle = require('./battle')

// This function is the core of the bot behaviour
const replyMessage = (message) => {

  // Instantiate Recast.AI SDK, just for request service
  const request = new recastai.request(process.env.REQUEST_TOKEN, process.env.LANGUAGE)

  // Get text from message received
  const text = message.content
  console.log('I receive: ', text)

  // Get senderId to catch unique conversation_token
  const senderId = message.senderId

  // Call Recast.AI SDK, through /converse route
  request.converseText(text, { conversationToken: senderId })
  .then(result => {
    /*
    * Here, you can add your own process.
    * Ex: You can call any external API
    * Or: Update your mongo DB
    * etc...
    */
    if (result.action) {
      console.log('The conversation action is: ', result.action.slug)
    }

    // Add each replies received from API to replies stack
    result.replies.forEach(replyContent => message.addReply({ type: 'text', content: replyContent }))

    // Send all replies
    message.reply()
    .then(() => {
      if (result.action && result.action.slug === 'battle' && result.action.done) {
        battle(result.getMemory('repo-1').raw, result.getMemory('repo-2').raw)
          .then(res => {
            message.addReply(res)
            message.reply()
          })
      }
    })
    .catch(err => {
      console.error('Error while sending message to channel', err)
    })
  })
  .catch(err => {
    console.error('Error while sending message to Recast.AI', err)
  })
}

module.exports = replyMessage

Test it to see the message appear at the right time in the conversation. Then get your Github credentials to continue.

Log in on Github and go to your settings:
Click on “generate new token” and copy this token in your config.js:

process.env.PORT = '5000'
process.env.REQUEST_TOKEN = ''
process.env.LANGUAGE = ''
process.env.GITHUB_TOKEN = ''
process.env.GITHUB_USERNAME = ''

Then stop your server, and install a module to make a promise request:

npm install --save request request-promise

and restart your server

npm start

Then go to your battle.js file and import the request module and the config file:

const rp = require('request-promise')

Create a Github call function to get information about a repo. To do that, we’ll be using the search method of the Github API since we’re not sure if the user can correctly spell the repo name 😉

const githubCall = (repo) => {
  var options = {
    url: `https://api.github.com/search/repositories?q=${repo}`,
    headers: {
      'User-Agent': process.env.GITHUB_USERNAME,
      'Authorization': `token ${process.env.GITHUB_TOKEN}`,
    }
  }

  return rp(options).then(result => {
    const body = JSON.parse(result)
    return {
      name: body.items[0].full_name,
      stars: body.items[0].stargazers_count,
      url: body.items[0].html_url,
    }
  })
}

and a function to print the winner:

const getWinner = (repo1, repo2) => {
  const winner = repo1.stars > repo2.stars ? repo1 : repo2
  const looser = repo1.stars > repo2.stars ? repo2 : repo1

  const reply = `${winner.name} got more stars (${winner.stars}) than ${looser.name} (${looser.stars})`

  return reply
}

Now change your battle function to call the githubCall twice, with each repo, and then call the winner function to print a beautiful message 😉

const battle = (repo1, repo2) => {
  return Promise.all([
    githubCall(repo1),
    githubCall(repo2),
  ]).then(repos => {
    return { type: 'text', content: getWinner(repos[0], repos[1]) }
  })
}

At the end, your battle.js will be like this:

const rp = require('request-promise')

const battle = (repo1, repo2) => {
  return Promise.all([
    githubCall(repo1),
    githubCall(repo2),
  ]).then(repos => {
    return { type: 'text', content: getWinner(repos[0], repos[1]) }
  })
}

const githubCall = (repo) => {
  var options = {
    url: `https://api.github.com/search/repositories?q=${repo}`,
    headers: {
      'User-Agent': process.env.GITHUB_USERNAME,
      'Authorization': `token ${process.env.GITHUB_TOKEN}`,
    }
  }

  return rp(options).then(result => {
    const body = JSON.parse(result)
    return {
      name: body.items[0].full_name,
      stars: body.items[0].stargazers_count,
      url: body.items[0].html_url,
    }
  })
}

const getWinner = (repo1, repo2) => {
  const winner = repo1.stars > repo2.stars ? repo1 : repo2
  const looser = repo1.stars > repo2.stars ? repo2 : repo1

  const reply = `${winner.name} got more stars (${winner.stars}) than ${looser.name} (${looser.stars})`

  return reply
}

module.exports = battle

Now, test it in your channel: you get the complete response!

One step further

Now, at the end of the battle, the bot will ask if the user wants additional information.
Let’s add a line in the message.js:

message.addReply({ type: 'text': content: 'Do you want more informations?'})

here:

if (result.action && result.action.slug === 'battle' && result.action.done) {
  battle(result.getMemory('repo-1').raw, result.getMemory('repo-2').raw)
    .then(res => {
      message.addReply(res)
      message.addReply({ type: 'text', content: 'Do you want more informations?'})
      message.reply()
    })
}

Now we need to respond with the data we have about the winner, but save it somewhere. What we can do is save in the Bot Builder memory information about the winner! Therefore, when the action “agree” is triggered, you can get this information and print it 😉

Go back to the interface, and add a memory notion “winner” in the ”agree action”:

Then in your code, set the value of this notion when we detect who the winner is, in the getWinner function in the battle.js:

Add a result in first parameter (The Recast.AI conversation result), and call the setMemory function of the SDK (see the doc here).

export const getWinner = (result, repo1, repo2) => {
  const winner = repo1.stars > repo2.stars ? repo1 : repo2
  const looser = repo1.stars > repo2.stars ? repo2 : repo1

  const reply = `${winner.name} got more stars (${winner.stars}) than ${looser.name} (${looser.stars})`

  result.setMemory({ winner })

  return reply
}

Then, pass the result to this function, add this parameter in the battle function:

const battle = (result, repo1, repo2) => {
  return Promise.all([
    githubCall(repo1),
    githubCall(repo2),
  ]).then(repos => {
    return { type: 'text', content: getWinner(result, repos[0], repos[1]) }
  })
}

And change the code in message.js to add the result parameter to the battle function. Also, add the detection of “agree” action to reply with information about the winner.

// Send all replies
    message.reply()
    .then(() => {
      if (result.action && result.action.done) {
        if (result.action.slug === 'battle') {
          battle(result, result.getMemory('repo-1').raw, result.getMemory('repo-2').raw)
            .then(res => {
              message.addReply(res)
              message.addReply({ type: 'text', content: 'Do you want more informations?'})
              message.reply()
            })
        } else if (result.action.slug === 'agree') {
          const winner = result.getMemory('winner')
          message.addReply({ type: 'text', content: `Informations: ${winner.url}` })
          message.reply()
        }
      }
    })

One last thing to make a cooler message when you give additional info is making a card with a button 😉
You can send a lot of different messages, see the documentation here:

Change the scope of the “if action is agree”:

const winner = result.getMemory('winner')
message.addReply({
  type: 'card',
  content: {
    title: winner.name,
    subtitle: winner.stars,
    imageUrl: 'https://assets-cdn.github.com/images/modules/logos_page/Octocat.png',
    buttons: [
      {
        title: 'More informations',
        type: 'web_url',
        value: winner.url,
      }
    ],
  },
})
message.reply()

Now your file should look like this:

/*
 * message.js
 * This file contains your bot code
 */

const recastai = require('recastai')
const battle = require('./battle)

// This function is the core of the bot behaviour
const replyMessage = (message) => {

  // Instantiate Recast.AI SDK, just for request service
  const request = new recastai.request(process.env.REQUEST_TOKEN, process.env.LANGUAGE)

  // Get text from message received
  const text = message.content
  console.log('I receive: ', text)

  // Get senderId to catch unique conversation_token
  const senderId = message.senderId

  // Call Recast.AI SDK, through /converse route
  request.converseText(text, { conversationToken: senderId })
  .then(result => {
    /*
    * Here, you can add your own process.
    * Ex: You can call any external API
    * Or: Update your mongo DB
    * etc...
    */
    if (result.action) {
      console.log('The conversation action is: ', result.action.slug)
    }

    // Add each replies received from API to replies stack
    result.replies.forEach(replyContent => message.addReply({ type: 'text', content: replyContent }))

    // Send all replies
    message.reply()
    .then(() => {
      if (result.action && result.action.done) {
        if (result.action.slug === 'battle') {
          battle(result, result.getMemory('repo-1').raw, result.getMemory('repo-2').raw)
            .then(res => {
              message.addReply(res)
              message.addReply({ type: 'text', content: 'Do you want more informations?'})
              message.reply()
            })
        } else if (result.action.slug === 'agree') {
          const winner = result.getMemory('winner')
          message.addReply({
            type: 'card',
            content: {
              title: winner.name,
              subtitle: winner.stars,
              imageUrl: 'https://assets-cdn.github.com/images/modules/logos_page/Octocat.png',
              buttons: [
                {
                  title: 'More informations',
                  type: 'web_url',
                  value: winner.url,
                }
              ],
            },
          })
          message.reply()
        }
      }
    })
    .catch(err => {
      console.error('Error while sending message to channel', err)
    })
  })
  .catch(err => {
    console.error('Error while sending message to Recast.AI', err)
  })
}

module.exports = replyMessage

Deploy your code

Create a git repository in your Github account and fill the form.

Copy your git url:

If you cloned the starter kit, change the remote url with your github url.

git remote set-url origin your-git-repo-url

and then:

git push -u origin master

If you downloaded the repo:

git init
git remote add origin your-git-repo-url
git push -u origin master

Now, go to your bot page, in the RUN tab and click on the “Bot Hosting” menu.

Connect your Github account, and accept the permission to create a hook on your repository to deploy your code.
Then select your repository in the list, and click on “CONNECT MY REPO”.

Wait until the loader is “RUNNING”, and change the current webhook url of your bot:
Copy the Recast.AI bot instance url in the Current webhook, so your bot is no longer running on your computer but on a server. You can chat with it when you want 😉

recast webhook

If you want to develop your bot in local, simply re-change the bot webhook with your ngrok url to use your laptop version.
Every time you push on master, your code is deployed and your bot updated!

Want to build your own conversational bot? Get started with Recast.AI !

Subscribe to our newsletter


There are currently no comments.