2017-12-13
TypeScript in 4 lines of code

In four lines of code, here is why you should stop using JavaScript, and write every project in TypeScript:

1
2
3
4
5
6
7
interface Message {
text: string;
}

let message: Message = {
text: 'Hello, world!!'
};

Really, that’s it. There’s so much power in just those four lines of code.

TypeScript defined

Before we breakdown the above code, it’s probably worthwhile to talk a little bit about TypeScript and what it is. More importantly, let’s talk about what TypeScript isn’t.

TypeScript is not a replacement for JavaScript or ECMAScript (from here forward I’ll simply call this JavaScript), or another attempt to redefine either of those. Instead, TypeScript is a syntactical superset of JavaScript, and is transcompiled down to the version of JavaScript you desire. You use TypeScript to write JavaScript. Because TypeScript is a superset of JavaScript, you already know a large percentage of the syntax for TypeScript, as TypeScript builds upon JavaScript’s syntax.

Why use TypeScript

There are several reasons to use TypeScript. One of the biggest is to handle all the various versions of JavaScript, and features that are supported. For example, I love using async/await when writing code. However, that’s only supported in more recent versions of JavaScript, which doesn’t share wide adoption quite yet. The transcompiler for TypeScript handles down-leveling the code as appropriate, so I can write what I want, taking advantage of new features and syntax, knowing that TypeScript will manage the versioning for me behind the scenes.

More importantly to me on a day-to-day basis, though, is type checking and the ability to describe data structures. Keep in mind, of course, that TypeScript transcompiles to JavaScript, so the type checking doesn’t actually wind up in the JavaScript, and does not impact the performance. But it does make creating code much easier.

Breaking down the four lines

Creating interfaces

Let’s start with the first line:

1
interface Message { ... }

Interfaces in any programming language of course allow you to describe the structure of data or some other object. It’s a contract, and when something is declared as a type of that interface you as the developer knows what’s there. In addition, because the IDE, such as Visual Studio Code, you can get autocomplete and IntelliSense when using variables declared as the interface type.

Many JavaScript solutions call various REST services, each with their own structure for JSON documents. The ability to declare an interface can speed development by reminding you what properties are required, and ensuring they’ve all been provided.

Declaring types

Let’s bring back the entire code block:

1
2
3
4
5
6
7
interface Message {
text: string;
}

let message: Message = {
text: 'Hello, world!!'
};

You’ll notice the declarations of text: string and message: Message. By using variableName: type, we’ve told TypeScript what type we want the variable to be. This brings a couple of great advantages. The first is pretty obvious - that your IDE will let you know when you’re using it wrong. The second is by indicating the data type, your IDE can offer autocomplete or, in the case of Visual Studio Code, IntelliSense.

Don’t underestimate how important IntelliSense is. While through use and repetition, you absolutely will start to remember various property and method names. But for those edge cases where you need to call something you don’t often reach for, being able to simply hit “dot” and scroll through the list of options (often with documentation) is much more efficient than opening the docs or searching Stack Overflow.

A bit more about types and introducing Typings

In our little application, we created our own interface, so it seems only logical that VS Code would know what to do with that. But what about other packages, such as Express, Restify, or Mongoose?

For packages that are created in TypeScript, you’re covered. But for other packages, such as those listed above, which are created in some form of JavaScript, VS Code won’t be able to automatically figure out what’s available to you, thus you won’t get IntelliSense. Enter Definitely Typed.

Definitely Typed is a community that creates package.d.ts files, which contain the list of interfaces and structures provided by a particular package. These types are installable via NPM, and are almost always solely needed as dev dependencies. To install a package’s types, simply prepend the name of the package with @types/. Note that while not every package has types created, I think you’ll be pleasantly surprised at how many have been done.

As an example, here’s how you’d install the types for Restify.

1
npm install --save-dev @types/restify

Variable declaration

1
let message: Message = { ... }

When declaring our message variable, we used the keyword let. Let helps fix some of JavaScript’s odd scoping rules. In JavaScript, variables aren’t scoped to blocks, meaning the code below works, and would print the number 42. There is absolutely no reason this should work, but yet here we are…

1
2
3
4
5
if(1 === 1) {
var x = 42;
}
// WHY DOES THIS WORK?!?!
console.log(x);

By using let in TypeScript, the variable x in the above scenario would not be available outside of the if block.

The transcompiled JavaScript

In order to run our code, we will need to transcompile it into JavaScript. This is done by using tsc, which is available after installing TypeScript (which can be done via NPM).

1
tsc app.ts

The final JavaScript will look as follows:

1
2
3
var message = {
text: 'Hello, world!!'
};

All of the syntactical candy provided by TypeScript, such as the type declarations and interfaces, has been omitted. At the end of the day, when you’re using TypeScript, you’re writing JavaScript. But you’re doing so at a higher level, with a better language.

Bring it back around again

There are numerous reasons to use TypeScript. Not having to worry about versioning issues, being able to take advantage of the latest technologies, and many other features, make creating projects in TypeScript rather compelling. For me, the chief is the ability to have typed objects. It makes me a more efficient developer, and helps me spot bugs that much faster.

If you’re looking to get started, TypeScriptLang.org offers quite a bit in the way of documentation, and is well structured. It may take a little bit of getting used to the structure of TypeScript, like learning any new language, but in the long run you will find it’s a much better way of creating JavaScript.

Read More

2017-10-29
Managing versions and timeouts in Bot Framework

Creating bots in the real world brings additional challenges with versioning and user behavior. Obviously, when you update your bot, you want to make sure you seemlessly migrate users over to the new bot, often allowing them to complete the conversation they’re currently having before showing them the new bot. Also, because Bot Framework does not time out users, you can also run into a situation where a user has left for a sizeable amount of time, and either state has changed or you simply want to offer the user the chance to start over.

Let’s break this down by first talking about how things work behind the scenes inside of Bot Framework, introduce middleware, and then put forth a couple of patterns you can implement to handle these situations.

Well, one quick note before we get started - this is a 300 level concept, so I’m making the assumption you’re already familiar with Bot Framework, and have created a few bots. If not, you can check out the Bot Framework MVA.

Bot Framework behind the scenes

A bot is, at the end of the day, simply a web service. Web services, of course, are stateless, meaning that after the message is sent from the user, and a reply returned, the connection is broken down, and all data is lost. When working with a bot, however, this poses a problem, because an operation will typically take place across multiple round trips from the user to the bot.

To handle this, Bot Framework “dehydrates” or serializes the current state of the bot for each user. When the user returns by sending a new message, the user state is “rehydrated” or reloaded, the bot determines where the user left off, and the next function is then run. You can see this happening behind the scenes when tracking your bot in the console, seeing the waterfall in action, and which step the bot is about to call. In a simple “Hello, world” bot, the user would send hello, the bot sends the prompt for the name, the user replies, and then the bot runs the next function - in this case, sending the hello message.

1
2
3
4
5
6
7
8
9
10
11
const bot = new builder.UniversalBot(
connector,
[
(session) => {
builder.Prompts.text(session, 'What is your name?');
},
(session, results) => {
session.endConversation(`Hello, ${results.response}.`);
}
]
);

No timeout

Because we’re dealing with humans, who may take some time to respond back to the bot, there is no timeout for a session[1]. If a user leaves for a couple of hours, and then replies to the What is your name? question, the bot simply picks up where it left off, as if the user replied instantly.

The first challenge here is state may have modified - the price for an item may have changed, a reservation time may no longer be available, or the operation may simply no longer be valid. In addition, this might confuse the user in general - if you didn’t talk to a bot for a couple of days, and potentially had the chat history on your client cleared, you might not expect the bot to pick up, or even know, where you left off.

The second challenge is when updating the bot. If you modified a dialog, the bot might not be able to pick up where it left off seamlessly. You may have removed a step from a waterfall, meaning the bot will error, now knowing what to run. Or, if a step changed, it may ask the user a question that simply doesn’t make sense for the current flow.

Addressing these issues doesn’isn’t necessarily difficult, but it does take a little bit of forethought and planning. With a little bit of middleware, you can transition as needed.

Middleware

Middleware allows you to intercept messages before they’re passed into the dialog flow, where the bot processes them as normal. This is useful for logging, rerouting messages (say for bot to human handoff), or, in our case, managing how a user is directed through the dialogs offered by the bot.

To implement middleware, you call use, passing in an object that implements at least one of three methods:

  • receive, which fires when a event is received by the bot
  • send, which fires when an event is sent by the bot
  • botbuilder, which executes after receive and the message is bound to a session

The last part of botbuilder is key in our case, because it’s going to allow us to open up where the user currently sits in the flow of the dialog(s), and determine the next step.

Strategies

Let’s walk through a few strategies for managing timeouts and updating your bot. I’m not going to walk through every possibility, but rather show off a few representative solutions that will hopefully illustrate how to approach the problem, and provide enough information for you to implement what’s needed.

Adding a timeout

As mentioned above, a bot doesn’t timeout. As a result, if it’s been even a few days since the user last replied, the bot is going to attempt to pick up where it left off. This can either confuse the user, or operate on bad information, neither of which is a good thing.

Reset the conversation

The first potential solution, and the easiest to implement, is to simply reset the conversation. We’ll do this by adding a lastAccess property to userData. We’ll then query this to see how long it’s been since the user last talked to the bot, and, if necessary, reset the conversation.

We determine if the user is currently in a conversation by taking a look at the dialogStack and determining if there is at least one dialog there. Our threshold is 10 seconds, which is obviously artificially small, but I want you to be able to easily test the implementation.

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
bot.use(
{
botbuilder: (session, next) => {
// grab the current time
const currentTime = Date.now();
// see how long it's been (in milliseconds) since the user last
// accessed the bot
const lastAccessGap = currentTime - session.userData.lastAccess;
// update the last access
session.userData.lastAccess = currentTime;
// see if there is a dialog and it's been greater than 10 seconds
// since the user last talked to the bot
if(session.dialogStack().length > 0 && lastAccessGap > 10000) {
// a couple of friendly messages
session.send(`Hey there! It looks like it's been a while since you were last here.`);
session.send(`Let's start over to make sure we're both on the same page.`);
// reset the conversation, sending the user to the root dialog
session.reset('/');
} else {
// allow the flow to continue as normal
next();
}
},
}
)

Ask the user what they want to do

The next possibility, and slightly more advanced, is to ask the user what they want to do. We’ll use the same timestamp technique as above, but this time we’ll route the user to a confirmation dialog. If they say yes, we’ll end the dialog, which will reprompt the user. If they say no, we’ll reset the dialog like before. Again, the threshold is artificially small for testing purposes.

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
33
34
35
36
37
38
39
40
bot.use(
{
botbuilder: (session, next) => {
// grab the current time
const currentTime = Date.now();
// see how long it's been (in milliseconds) since the user last
// accessed the bot
const lastAccessGap = currentTime - session.userData.lastAccess;
// update the last access
session.userData.lastAccess = currentTime;
// see if there is a dialog and it's been greater than 10 seconds
// since the user last talked to the bot
if(session.dialogStack().length > 0 && lastAccessGap > 10000) {
// begin the confirmation dialog
session.beginDialog('confirm-continue');
} else {
// allow the user to continue as normal
next();
}
},
}
)

bot.dialog('confirm-continue', [
(session, args) => {
// prompt the user
builder.Prompts.confirm(session, `It looks like you've been gone for a little while. Do you want to continue where you left off?`);
},
(session, results) => {
if(results.response) {
// redirect back to the original dialog
// the last active prompt will be re-run
session.endDialog(`Sounds good. Here's the last question I asked you:`);
} else {
// reset the conversation
session.send(`Sounds good. Let's start over.`);
session.reset('/');
}
}
]);

Some thoughts on timeouts

It’s really up to you to decide how to best manage timeouts and what works best for your bot. You might decide to time the user out after twenty minutes, or maybe a couple of hours. You might automatially reset, especially in simpler two or three turn dialogs, or to prompt the user for more complex bots. You might even go a couple steps further, detecting which dialog the user is in, and make the decision based on that.

Managing versions

Versioning a live bot is a little trickier. You want to get users onto the new bot as quickly as possible, but you probably don’t want to interrupt the current flow of a user already in the middle of a dialog. If nothing else, when you version your bot, you need to handle the fact that a user in the middle of a dialog might be returning to a dialog step that doesn’t exist, or a dialog that’s changed profoundly, meaning the message the user sent might not make sense in the flow you deployed.

Reloading an older version

If you do want to transition the user, you will need to pay a little bit of attention to how you update your bot. In theory, you could do this on a dialog by dialog basis, but the code becomes trickier as you need to figure out where the user is, and swap out the dialog. I find it much easier to simply swap out the entire bot. To help manage this, I’ll put the bot, and its associated dialogs, into a folder. When it comes time to update it, I’ll copy the entire folder structure, update the copy, and then update app.js to call the updated (new) bot.

When you need to send a user to the old bot, you simply update the library property of the session, which sets the bot that will run for the incoming message. In the code snippet below, I have both an updated and original bot in the same file for simplicity’s sake. I hope you can see from here how you would implement this with multiple folders.

In the sample below, I’m going to transition the user once their current conversation completes. You could, if you so desire, update this code to always reset the conversation, with a message of course, which would cause the user to be rerouted to the new bot. Also, the code could be streamlined a little bit, but breaking it apart like I’ve done makes it (I think) a little easier to explain with comments.

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const connector = new builder.ChatConnector();
const bot = new builder.UniversalBot(
// this is the updated bot
connector,
[
(session) => {
builder.Prompts.text(session, 'What is your name?');
},
(session, results) => {
session.endConversation(`Hello, ${results.response}, from the updated bot`);
}
]
);
// set a version number using duck typing
bot.version = 2;

const getOriginalBot = (connector) =>{
// this will return the original bot
const originalBot = new builder.UniversalBot(
connector,
[
(session) => {
builder.Prompts.text(session, 'What is your name?');
},
(session, results) => {
session.endConversation(`Hello, ${results.response}, from the original bot`);
}
]
);
// set a version number by using duck typing
originalBot.version = 1;
return originalBot;
};

bot.use(
{
botbuilder: (session, next) => {
if(!session.userData.version) {
// new user. set the version
session.userData.version = bot.version;
}

// check if user is in conversation and
// if the user is on the prior version
if(session.dialogStack().length > 0 && session.userData.version < bot.version) {
// load the original bot
session.library = getOriginalBot(connector);
// pass control to the original bot
// the correc step and dialog will be executed
next();
} else {
// update the version in userData
session.userData.version = bot.version;
// pass control to the new bot
next();
}
},
}
);

If you wanted to test this, start by making the new bot version 1 and sending a message to get yourself into the dialog. Then update the version to 2, rerun the application while not restarting the conversation in Bot Emulator, and sending the second message. You should see the original bot message.

Some thoughts on versioning

Versioning is tricky, especially if you’re in a CI/CD environment where you expect to make multiple updates to the bot. Bot Framework (unfortunately?) doesn’t automatically handle transitions for you, so you will need to decide what pattern will work best for you. This is also important when developing and testing your bot in a channel such as Skype or Slack, where how to reset the state from the client isn’t overly obvious. In dev you could certainly skip the transition, but in production you’ll likely want to implement a mechanism for doing so.

Final thoughts

Once you introduce your bot to the “real world”, things change. Management of the experience becomes much more important, and dealing with timeouts and versioning allows you to ensure a good experience for your users. This is something you should thing about early on, as it’s easier to introduce this at the beginning than to an existing, complex bot.

There are numerous ways you could handle this. Hopefully I’ve provided enough of a foundation for you to create an implementation that’s right for your bot.

[1] I am referring to session in general, not to the session object in Bot Framework.

Read More

2017-07-12
Create bots with TypeScript

Whenever I’m doing demos or other presentations I work very hard to keep things as real world as possible. While there will certainly be little “cheats”, such as keeping all items in a single file, or having snippets already available, to help make the demo easier to digest, there’s one big lie I wind up telling every bot presentation I do. I do all my demos in JavaScript, even though I create all my bots using TypeScript.

Why the lie?

Whenever I’m talking about how to build a chat bot using Bot Framework, I’m trying to demonstrate how you can do so using skills you already have. If you know JavaScript, and have even a cursory understanding of Node.js, you can get up and running relatively quickly with your first bot. The last thing in the world I want to do is throw another hurdle in front of attendees, like learning another programming language.

Even though I don’t want to put another hurdle in front of someone, the thing about TypeScript is it turns out it’s not much of a hurdle after all.

What is TypeScript

If you’re not already familiar with TypeScript, it’s a language headed by Anders Hejlsberg, whose claims to fame include Delphi and C#. TypeScript is designed to help overcome the various shortcomings of JavaScript, and do so in such a fashion that allows you to begin using cutting edge features, such as async/await, which aren’t globally available in ECMAScript.

TypeScript is designed to build on the existing syntax of JavaScript; in fact, 70% of the syntax in TypeScript is simply JavaScript. This helps make the transition to the language smoother for experienced JavaScript developers. In a lot of ways, TypeScript is a combination of Java and JavaScript.

TypeScript offers many features you expect from programming languages, such as static (or strong) typing, OOP, and better module management.

One key about TypeScript is it transcompiles into JavaScript, and to the version of JavaScript/ECMAScript you need. So you can write in TypeScript, take advantage of the features the language offers, knowing the resulting code will run in the environment you’re targeting.

Why use TypeScript when creating bots?

One of the biggest weaknesses of JavaScript is the inability to declare the type of a variable. This limits the amount of support an IDE, such as VS Code, can offer you. Below is the code one might have in a separate file for a dialog in a bot:

1
2
3
4
5
module.exports = [
(session) => {
session.endConversation('Thank you for your input!');
}
]

If you put that into a JavaScript file, you would notice when hitting dot after session, VS Code wouldn’t show you endConversation as an available option, or if it did, it’s because it’s seeing it from another file in your project. The IDE has no way of knowing that session is actually of type builder.session.

Contrast that with the following bit of TypeScript, where we are able to identify the type:

1
2
3
4
5
export default [
(session: builder.Session) => {
session.endConversation('Thank you for your input!');
}
]

When you create the TypeScript file, you’ll notice VS Code knows exactly what the session parameter is, and is able to offer you IntelliSense.

Getting started with TypeScript

Installing TypeScript

In order to start programming in TypeScript, you will need to install TypeScript. TypeScript is available as an NPM package, and you can simply add it as a developer dependency to your project. Personally, because I use TypeScript extensively, I install it globally.

1
npm install -g typescript

Creating and configuring the project

If you use the Bot Framework Yeoman generator I created you will notice there is a template already available for TypeScript. For purposes of this post, we’ll create everything from scratch, so you can see how it is all brought together.

Add packages and dependencies

Create a new folder, and add a package.json file with the following contents:

1
2
3
4
5
6
7
8
9
10
11
{
"name": "type-script-bot",
"dependencies": {
"botbuilder": "^3.4.4",
"restify": "^4.3.0"
},
"devDependencies": {
"@types/node": "^6.0.52",
"@types/restify": "^2.0.35"
}
}

The dependencies section is pretty standard for a bot, but you might not be familiar with the devDependencies. The devDependencies contain the types of the various packages we’ll be using. Types are the various interfaces for the objects and classes in a particular package. So @types/restify contains the interfaces provided by restify. This will add IntelliSense to the project. In the case of botbuilder, we don’t need to add a types file, as the framework is written in TypeScript, and contains all of the necessary types. After saving the file, run the installation process like normal.

1
npm install

Configuring TypeScript compilation

As mentioned before, TypeScript will be transcompiled to JavaScript. You configure how this occurs by using a tsconfig.json file.

The module and target options tell the transcompiler to emit JavaScript that is compliant with ES6. outDir specifies where the JavaScript files will be output. The files section identifies which files will be transcompiled. Add a file named tsconfig.json with the following content.

1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "./built"
},
"files": [
"dialog.ts",
"app.ts"
]
}

Creating the bot and dialog

Creating the dialog

Let’s start by creating a basic dialog. Add a file to your project named dialog.ts, and add the code you see below. You will notice this is standard Node.js bot code, with a couple of differences.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// dialog.ts
import * as builder from 'botbuilder';

interface IResults {
response: string;
}

export default [
(session: builder.Session) => {
builder.Prompts.text(session, 'What is your name?');
},
(session: builder.Session, results: IResults) => {
session.endConversation(`Hello, ${results.response}`);
}
]

From the top, you’ll notice the import statement is different than in JavaScript. Rather than using require, you use the import command. The * means you’ll be importing everything from the package, and as allows you to identify an alias. The end result is effectively the same as using const builder = require('botbuilder');, like you would have done traditionally.

Second, you’ll notice the creation of the interface IResults. You added a single property named response, and marked it as type string. Interfaces, just as in C# or Java, give you the ability to identify the structure. In the case of TypeScript, interfaces are completely weightless; they are not compiled to the JavaScript. Interfaces help give you a better development experience.

Rather than using module.exports to export the array that contains your waterfall, you use export default, followed by the array. The syntax is slightly different, but the results are the same.

Finally, you’re declaring the data type of session and results on each of the waterfall steps to aid your development experience. results is using the interface you created earlier in the file. You’ll notice when you do this, and you start typing response.results, VS Code will provide IntelliSense, and show you response as an available property of type string.

Creating the bot and host

The code to create the bot will be similar to what you would typically do with JavaScript. The main difference you’ll notice is the way packages and other items are imported. Add a file named app.ts with the following code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app.ts
import * as restify from 'restify';
import * as builder from 'botbuilder';
import dialog from './dialog'

const connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});

const bot = new builder.UniversalBot(connector, dialog);

const server = restify.createServer();
server.post('/api/messages', (bot.connector('*') as builder.ChatConnector).listen());
server.listen(process.env.PORT, () => console.log(`${server.name} listening to ${server.url}`));

As you enter the code, you’ll notice that VS Code is able to offer you support throughout the entire process. Because every variable has a type that VS Code can identify, it’s able to provide IntelliSense, unlike when using JavaScript, where the level of support will vary.

Running the code

In order to run the code, it will need to be transcompiled. You can do this by simply running tsc. Because we created a tsconfig.json file, the transcompiler will know what to transcompile, and how to do it. The watch switch will automatically detect changes to the TypeScript files, and transcompile on the fly.

1
tsc --watch

If you take a look at the JavaScript files, you’ll notice they’re relatively similar to what was created in TypeScript. A couple of key differences you’ll notice is the interface we created wasn’t emitted, and the type declarations for parameters are also not part of the JavaScript file that was created. Running the bot is done just as you normally would, using either node or nodemon.

1
node app.js

Next steps

There’s quite a bit that I left on the table when it comes to TypeScript. We could make better use of interfaces with our dialogs, we could create a class for our app, we could …, we could …. My goal with this post was to help get you up and running with TypeScript, and show some of the power that’s made available.

If you want to know more about TypeScript, you can check out the official site, or an edX course.

Read More