Posts How I write a chatbot?
Post
Cancel

How I write a chatbot?

It’s just an internal chatbot for my company, it’s available on skype/messenger/slack and will answer some questions like

  • How many hours did I work last year?
  • Get Vu’s working hours yesterday?
  • Get Vu’s working hours on 1st November 2016?
  • Get my hours from Jan - Mar 2016
  • Get my hours in week 23, 2016

Bot Example

Some technologies I learned and used for this project are

  • Bot Framework to build and deploy chatbot
  • LUIS to understand human language
  • Chrono a natural language date parser in Javascript

Bot Framework and LUIS

After some researching, I decide to use Bot Framework to build my chatbot since it supports nodejs which’s my favorite programming language.

I use LUIS to add natural language understanding to my bot. I compared it with api.ai and wit.ai and see it has a good design, friendly UI to define an intent, add new utterance, train and publish the application. They also have 2 videos for me can easily get the overview of LUIS 😍.

LUIS - builtin.datetime

Seem everything relates to coding with bot framework, training LUIS to understand intent is easy… until I have many types of Date Ranges need to be supported in my chatbot. For example, in 5 questions above, I need to parse date range texts such as today, yesterday, last year, Jan - Mar 2016, Week 23, 2016

First of all, thanks to LUIS, it has a set of pre-built entities to recognize date and time in builtin.datetime. It will understand when I say yesterday, 1st November or last week, in 3 first questions above, and will convert that to a date.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// tomorrow
"resolution": { "date": "2016-11-20" }

// last quarter
"resolution": { "date": "XXXX-Q4" }

// last year
"resolution": { "date": "2015" }

// last two years
"resolution": { "duration": "P2Y" }

// last week
"resolution": { "date": "2016-W45" }

// past three weeks
"resolution": { "duration": "P3W" }

// this month
"resolution": { "date": "2016-11" }

// last ten months
"resolution": { "duration": "P10M" }

Chrono

Since the LUIS with builtin datetime did good jobs on parsing Datetime, the next requirement is how I can parse a date ranges from a specific date (or month) to another specific date (or month)? For example in my fourth question, it’s Jan - Mar 2016

Chrono supports most date and time formats, such as:

  • Today, Tomorrow, Yesterday, Last Friday, etc
  • 17 August 2016 - 19 August 2016
  • January - March 2016
  • 5 days ago

So I used it to try to recognize if LUIS couldn’t give me any information about date ranges. Two things I would like to note is I had to use every values in the knownValues to parse a date and

always parse date in UTC

since the date object on server can be in difference timezone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var chrono = require('chrono-node');

var chronoParsedResults = chrono.parse(message);
if (chronoParsedResults && chronoParsedResults.length) {
    // we just want to get the first parsed value
    var firstResult = chronoParsedResults[0];
    console.log('firstResult start', firstResult.start);

    // we always can get start date from chrono
    var startValues = firstResult.start.knownValues;
    var startDate = createUTCDateFromKnownValues(startValues.year, startValues.month, startValues.day, true);

    //...
}

function createUTCDateFromKnownValues(year, month, day, isFrom) {
    // (year, null, null, ?)
    if (year && !month && !day)
        return isFrom ? new Date(Date.UTC(year, 0, 1)) : new Date(Date.UTC(year, 11, 31, 23, 59, 59));
    // (year, month, null, ?)
    if (year && month && !day) {
        if (isFrom) return new Date(Date.UTC(year, month - 1, 1));

        var firstNextMonth = new Date(Date.UTC(year, month, 1, 23, 59, 59));
        firstNextMonth.setDate(firstNextMonth.getDate() - 1);
        return firstNextMonth;
    }
    // (year, month, day, ?)
    if (year && month && day) {
        return isFrom ? new Date(Date.UTC(year, month - 1, day)) : new Date(Date.UTC(year, month - 1, day, 23, 59, 59));
    }
}

Parsing Week Of Year

The last challenge is I would like to parse date ranges for a specific week in year, for example is week 23, 2016, week 1 (default year is current year). But LUIS or chrono doesn’t support it yet (hopefully it will support in future 😂). So I had to define an entity WeekOfYear to recognize

Parsing Week Of Year

and try to parse with moment

1
2
3
4
5
6
7
8
9
var weekOfYearEntity = builder.EntityRecognizer.findEntity(entities, 'WeekOfYear');
var weekOfYear = weekOfYearEntity ? weekOfYearEntity.entity : '';

// we check if week of year found on message
if (weekOfYear) {
    var definedWeekOfYearFormat = ['[week] W YYYY', '[week] W, YYYY'];
    var m = moment(weekOfYear, definedWeekOfYearFormat);
    //...
}
This post is licensed under CC BY 4.0 by the author.