Start building your own chatbot now >
Note: This article is deprecated. It uses Recast.AI API version 1. You can find an updated tutorial here: NodeJS chatbot tutorial: A Github bot with Recast.AI.

STEP 7: Messenger Buttons

The bot is nearly done. But you still can add some cool features like buttons to make navigation easier inside the conversation.

recast-ai-info-pikachu

1. Create the toButton functions:

  • We already have toText and toImage. Add the toButton and toButtons functions.
// Buttons
 const toButtons = (title, buttons) => {
 return { type: 'buttons', content: buttons, title }
 }

// Button
 const toButton = (title, value) => { return { title, value } }
    • The Button object is 1 button.
      – Title: What is going to be printed on the button.
      – Value: What will be send when you click on it.

 

  • The Buttons object is a container of buttons.
    – Title: What will appear on top of the buttons.
    – Buttons: array of Button.

 

2. Create pokebot Buttons:

  • At the end of the ‘info-pokemon’ intent, create three buttons: resists, stats, moves and push them to answer
infopokemon.js:

const infoPokemonLayout = json => {
   ...
   const prompt = [
     toButton('stats', `show me ${json.name} stats`),
     toButton('moves', `show me ${json.name} moves`),
   ]
   answer.push(toButtons('Click on them!', prompt))
   return answer
 }

 

3. Send the Buttons:

  • Add a method in the sendMessageByType object to make it all work.
pokebot.js:

const sendMessageByType = {
  image: (session, elem) => session.send(new builder.Message().addAttachment({
    contentType: 'image/png',
    contentUrl: elem.content,
  })),
  text: (session, elem) => session.send(elem.content),
  buttons: (session, elem) => {
    const buttons = elem.content.map(button => {
      return (new builder.CardAction().title(button.title).type('imBack').value(button.value))
    })
    const card = new builder.ThumbnailCard().buttons(buttons).subtitle(elem.title)
    session.send(new builder.Message().addAttachment(card))
  },
}

 

STEP 8: Entity detection and Fuzzy-matching

And what if you misspell the pokemon name? That gets you an error. By using fuzzy-matching, you can avoid this.

 

1. Use Fuzzy-Matching:

  • Install the module fuzzy-matching with npm and require it in pokebot.js
npm install --save fuzzy-matching
  • Download the datas.js file here. It contains all pokemon names correctly spelled.
    Require it, and create a new Fuzzy with datas.
pokebot.js:

const datas = require('./datas.js')
 const Fuzzy = require('fuzzy-matching')
 const fmpokemons = new Fuzzy(datas.pokemons)
  • The object returned by the get method contains a distance and a value.
    – Value : The word in the original tab that match the best with the tested word.
    – Distance: A float between 0 and 1 -> percentage of matching with the value found.
    Example of usage:
const match = fmpokemons.get(word)
 if (match.distance > 0.8) { console.log(match.value) }
 else { console.log('wrong') }
  • Create the function CheckEntity in pokebot.js.
    – If the distance is too low, set a boolean ‘wrong’ to true in the entity.
    – If the distance is high enough, change the raw of the entity with the value of the match.
pokebot.js:

const checkEntity = (recast) => {
  const pokemon = recast.get('pokemon')
  if (pokemon) {
    const match = fmpokemons.get(pokemon.raw)
    if (match.distance < 0.8) {
      pokemon.wrong = true
    } else { pokemon.raw = match.value }
    return pokemon
  }
  return null
}

bot.dialog('/', (session) => {
  recastClient.textRequest(session.message.text)
  .then(res => {
    const intent = res.intent()
    const entity = checkEntity(res)
    if (intent) {
      INTENTS[intent.slug](entity)
      .then(res => { res.forEach[1]message) => sendMessageByType[message.type](session, message })
      .catch(err => { err.forEach[2]message) => sendMessageByType[message.type](session, message) })
    }
  })
  .catch[3]) =>session.send('I need some sleep right now... Talk to me later!'
})

  • Now you can add a protection to your dynamic intents: if the name of the entity is ‘wrong’, return an error. Try to ask your bot an unknown or misspelled pokemon to see what changed.
infoPokemon.js

const getInfoPokemon = (entity) => {
  if (!entity) { return Promise.reject([u.toText('Which pokemon?')]) }
  if (entity.wrong) { return Promise.reject([u.toText(`The pokemon ${entity.raw} does not exist... You might have mispelled it.`)]) }
  return new Promise[4]resolve, reject) => {
  ...
  })
}

 

2. Handle simple inputs:

  • To handle simple input like “and bulbasaur?” that don’t match any intent, check every word of the input using fuzzy matching.
pokebot.js

const checkWords = (words) => {
  const split = words.split(' ')
  let entity = null
  split.forEach[5]word) => {
    const match = fmpokemons.get(word)
    if (match.distance > 0.8) {
      entity = {raw: match.value}
    }
  })
  return (entity)
}

bot.dialog('/', (session) => {
  recastClient.textRequest(session.message.text)
  .then(res => {
    const intent = res.intent()
    const entity = checkEntity(res)
    if (!intent) {
      if (!entity) { entity = checkWords(u.cleanText(res.source }
    }
    if (intent) {
      INTENTS[intent.slug](entity)
      .then(res => { res.forEach[6]message) => sendMessageByType[message.type](session, message })
      .catch(err => { err.forEach[7]message) => sendMessageByType[message.type](session, message) })
    }
  })
  .catch[8]) => session.send('I need some sleep right now... Talk to me later!'
})

 

STEP 9: Bot Memory

Our bot is starting to have a lot of features, but memory is one of the most important: it makes the conversation much more ‘human’. To handle memory, Microsoft Bot Framework has something sweet: session.userData. You can store all the datas you want about the current user in this object to retrieve them later.

 

1. Reuse the last Pokemon:

  • Add some expressions on Recast.AI for infomove:

  • You have to do it also for the others.

– “show me its stats”
– “its Stats”
– “Show me informations about it”
– “Show info”
etc… without any pokemon to tag!

  • Pass the session.userData object to each intent function
  • Save the name of the pokemon you’re currently handling in this object
  • Use this saved pokemon at the beginning of the intents when no entity has been found
infopokemon.js:

const getInfoPokemon = (entity, user) {
  const pokemon = entity ? entity.raw : user.pokemon
  if (!pokemon) { return Promise.reject('Which pokemon?')}
  if (entity.wrong) { return Promise.reject(`The pokemon ${entity.raw} doesn't exist. You might have mispelled it.`) }
  user.pokemon = entity.raw
  return new Promise[9]resolve, reject {
    request.get('https://pokeapi.co/api/v2/pokemon/' + entity.raw)
    .end((err, res) => {})
  })
}

 

2. Reuse the last intent.

  • Create a tab with all the intents that doesn’t need to be saved.
pokebot.js

const noMemoryIntents = [
  'help',
  'greetings',
  'goodbyes',
  'feelings',
]
  • Every time the bot catches an intent, save it if it doesn’t appear in this tab.
pokebot.js

...
if (intent && noMemoryIntents.indexOf(intent.slug) === -1) { user.intent = intent.slug }
if (intent) {
  INTENTS[intent.slug](entity, user)
  .then()
...
  • Use this saved intent if no intent has been matched but a ‘pokemon’ entity has been found.
pokebot.js

...
if (!intent) {
  if (!entity) { entity = checkWords(u.cleanText(res.source }
  if (entity) {
    intent = {}
    intent.slug = user.intent
  }
}
...

 

There you go! You should now have a well functioning bot, and it’s only the beginning: you can add as many intents as you like (comparing stats between two pokemons, determining the localisation of pokemons, and much more). The world of bot is in your hands! Try lots of APIs, read lots of docs, and have fun with Recast.AI!


Cheers!

References   [ + ]

1, 2. message) => sendMessageByType[message.type](session, message
3. ) =>session.send('I need some sleep right now... Talk to me later!'
4. resolve, reject) => { ... }) }

 

2. Handle simple inputs:

  • To handle simple input like “and bulbasaur?” that don’t match any intent, check every word of the input using fuzzy matching.
pokebot.js

const checkWords = (words) => {
  const split = words.split(' ')
  let entity = null
  split.forEach((word) => {
    const match = fmpokemons.get(word)
    if (match.distance > 0.8) {
      entity = {raw: match.value}
    }
  })
  return (entity)
}

bot.dialog('/', (session) => {
  recastClient.textRequest(session.message.text)
  .then(res => {
    const intent = res.intent()
    const entity = checkEntity(res)
    if (!intent) {
      if (!entity) { entity = checkWords(u.cleanText(res.source
5. word) => { const match = fmpokemons.get(word) if (match.distance > 0.8) { entity = {raw: match.value} } }) return (entity) } bot.dialog('/', (session) => { recastClient.textRequest(session.message.text) .then(res => { const intent = res.intent() const entity = checkEntity(res) if (!intent) { if (!entity) { entity = checkWords(u.cleanText(res.source } } if (intent) { INTENTS[intent.slug](entity) .then(res => { res.forEach((message) => sendMessageByType[message.type](session, message
6. message) => sendMessageByType[message.type](session, message }) .catch(err => { err.forEach((message) => sendMessageByType[message.type](session, message
7. message) => sendMessageByType[message.type](session, message) }) } }) .catch(() => session.send('I need some sleep right now... Talk to me later!'
8. ) => session.send('I need some sleep right now... Talk to me later!' })

 

STEP 9: Bot Memory

Our bot is starting to have a lot of features, but memory is one of the most important: it makes the conversation much more ‘human’. To handle memory, Microsoft Bot Framework has something sweet: session.userData. You can store all the datas you want about the current user in this object to retrieve them later.

 

1. Reuse the last Pokemon:

  • Add some expressions on Recast.AI for infomove:

  • You have to do it also for the others.

– “show me its stats”
– “its Stats”
– “Show me informations about it”
– “Show info”
etc… without any pokemon to tag!

  • Pass the session.userData object to each intent function
  • Save the name of the pokemon you’re currently handling in this object
  • Use this saved pokemon at the beginning of the intents when no entity has been found
infopokemon.js:

const getInfoPokemon = (entity, user) {
  const pokemon = entity ? entity.raw : user.pokemon
  if (!pokemon) { return Promise.reject('Which pokemon?')}
  if (entity.wrong) { return Promise.reject(`The pokemon ${entity.raw} doesn't exist. You might have mispelled it.`) }
  user.pokemon = entity.raw
  return new Promise((resolve, reject
9. resolve, reject {    request.get('https://pokeapi.co/api/v2/pokemon/' + entity.raw)    .end((err, res) => {})  }) }

 

2. Reuse the last intent.

  • Create a tab with all the intents that doesn’t need to be saved.
pokebot.js

const noMemoryIntents = [
  'help',
  'greetings',
  'goodbyes',
  'feelings',
]
  • Every time the bot catches an intent, save it if it doesn’t appear in this tab.
pokebot.js

...
if (intent && noMemoryIntents.indexOf(intent.slug) === -1) { user.intent = intent.slug }
if (intent) {
  INTENTS[intent.slug](entity, user)
  .then()
...
  • Use this saved intent if no intent has been matched but a ‘pokemon’ entity has been found.
pokebot.js

...
if (!intent) {
  if (!entity) { entity = checkWords(u.cleanText(res.source

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

Subscribe to our newsletter


There are currently no comments.