Yesterday I created a custom bot for Microsoft Teams. Today I’m going for a deeper dive. Where in yesterday’s post I looked at how to create a bot that simply copies what you tell the bot, today I’m going to have a look at the code behind this bot.

So first I need to find my bot back in Azure. So if I was clever I would have put it on the Dashboard so that I could find it back.

But what if I wasn’t clever?

Simply click on All resources and find the pvteamsapp back.

Have you noticed that there is also an Application Insight for my app? This can help you find out how well your app is performing. But that is worth another post.

Today I’m just going to click on my pvteamsapp. Quite quickly I’m getting the following to appear:

So now all I have to do is open the online code editor.

This is the editor that feels a bit like Visual Studio Code. I noticed in the previous screen that it is possible to download the solution as a zip and then republish my changes later. For now however I’m quite happy with this editor. Also, I quite like that whenever I save the files in my editor that the bot is straightaway working with the latest code. This helps with trial and error kind of development. So for production development it might be useful to go for the offline editing mode.

There are a few more files, but for now I’m going to focus on the app.js file

/*-----------------------------------------------------------------------------
A simple echo bot for the Microsoft Bot Framework.
-----------------------------------------------------------------------------*/

var restify = require('restify');
var builder = require('botbuilder');

// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});

// Create chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword,
stateEndpoint: process.env.BotStateEndpoint,
openIdMetadata: process.env.BotOpenIdMetadata
});

// Listen for messages from users
server.post('/api/messages', connector.listen());

/*----------------------------------------------------------------------------------------
* Bot Storage: This is a great spot to register the private state storage for your bot.
* We provide adapters for Azure Table, CosmosDb, SQL Azure, or you can implement your own!
* For samples and documentation, see: https://github.com/Microsoft/BotBuilder-Azure
* ---------------------------------------------------------------------------------------- */

// Create your bot with a function to receive messages from the user
var bot = new builder.UniversalBot(connector, function (session) {
session.send("You said ....: %s", session.message.text);
});

Ok, so what is included in this file.

It all starts by some of the modules that are required.

var restify = require('restify');
var builder = require('botbuilder');

Restify is a Node.js web service framework for building RESTfil web services.

Botbuilder is the bot framework for Node.js that is used.

Then the next bit of code will create a listener using Restify

var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});

The above code shows that there is a process listening on port 3978 by default. Alternatively, the environment variable PORT or port is used.

The next step is building a connector.

var connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword,
stateEndpoint: process.env.BotStateEndpoint,
openIdMetadata: process.env.BotOpenIdMetadata
});

With the ChatConnector class a connection is set up. To set up the connection the MicrosoftAppId, MicrosoftAppPassword, BotStateEndpoint, BotOpenIdMetadata are used.

So what are these values set to?

I’m going to do a small test.

I’m adding the following 4 lines of code to my bot responses ( so that is just below line 36 session.send(“You said:….)

session.send("AppId: %s", process.env.MicrosoftAppId);
session.send("AppPassword: %s", process.env.MicrosoftAppPassword);
session.send("stateEndpoint: %s", process.env.BotStateEndpoint);
session.send("openIdMetadata: %s", process.env.BotOpenIdMetadata);

For the testing purposes I’m using session.send to get the information to appear in my app, but of course I could use console.log() instead and all I would need to do is check the console in Azure. Within the editor in Azure you can get to the console. This is the highlighted button in the banner below:

Back to my testing my app, I’m starting to talk to my bot:

and all the variables are displayed. Do you remember that within Azure I got a warning, that you couldn’t get the AppPassword back if you didn’t take a copy? Well we got it here!

Ok, on to the next line of code:

server.post('/api/messages', connector.listen());

So now we are listening for message using a REST API hook using the Restify server that we created earlier.

Then now the final part of the code. This is where I added a few lines as mentioned above. So you might want to make sure that you don’t include the last 4 console.log lines in your code as you might not want this to appear in your logs every time someone sends a message to your bot:

var bot = new builder.UniversalBot(connector, function (session) {
session.send("You said: %s", session.message.text);
console.log("AppId: %s", process.env.MicrosoftAppId);
console.log("AppPassword: %s", process.env.MicrosoftAppPassword);
console.log("stateEndpoint: %s", process.env.BotStateEndpoint);
console.log("openIdMetadata: %s", process.env.BotOpenIdMetadata);
});

Where earlier we created the builder.

Now we’re specifying what we want to do when a message is sent to the bot.

To understand the above code better I thought I try a different template for my bot. This time I selected the question and answer bot. This time the code is slightly different:

var bot = new builder.UniversalBot(connector);

var recognizer = new builder_cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
subscriptionKey: process.env.QnASubscriptionKey});

var basicQnAMakerDialog = new builder_cognitiveservices.QnAMakerDialog({
recognizers: [recognizer],
defaultMessage: 'No match! Try changing the query terms!',
qnaThreshold: 0.3}
);

bot.dialog('/', basicQnAMakerDialog);

Once again the UniversalBot is initialized, but this time Microsoft’s QnAMaker is used. The QnAMaker is part of the Cognitive Services. So this is handling all the clever language interpreting stuff.

 

Then finally, I also had a quick look at my Microsoft Teams logs. These logs come out of the Microsoft Teams app as described in one of my earlier posts about troubleshooting Microsoft Teams. There seems to be an error: “Failed to get extension definition for app” that is always included. I’ve got the feeling that this can be safely ignored.

2017-11-09T10:49:46.521Z War Analytics service: Panel data not present to populate panel action, falling back to main
2017-11-09T10:49:44.718Z Inf Telemetry: [TelemetryManagerImpl]: Processing telemetry events took 22 ms for 43 events with total size of 112449 bytes, remaining events - 0
2017-11-09T10:49:43.687Z Inf CustomScrollBar: Stopped SCROLL
2017-11-09T10:49:43.687Z Inf CustomScrollBar: Started SCROLL
2017-11-09T10:49:43.672Z Inf scroll-item-tracker: Init for item id 1510224583489
2017-11-09T10:49:43.505Z Inf message-list: loggedFrom: firstTimeMessageInViewport - end, unreadIncomingMessages length: 0, conversationId: 19:e41a2ef4-3c91-4d41-a2ee-5baa222baafe_38a255f2-793e-4b92-8688-dbef1bfcca7a@unq.gbl.spaces, replyChainId: undefined
2017-11-09T10:49:43.504Z Inf PeopleService: Chat list Mris length - 12
2017-11-09T10:49:43.497Z Inf message-list: Consumption horizon set to message with date: 2017-11-09T10:49:43.489Z, id 1510224583489
2017-11-09T10:49:43.497Z Inf message-list: unreadIncomingMessages ids and dateTimeReceived:
id 1510224583489, parentMessageId 1510224583489, originalArrivalTimeUtc 1510224583489, is root or found in expandedReplies true
2017-11-09T10:49:43.497Z Inf message-list: loggedFrom: firstTimeMessageInViewport - beginning, unreadIncomingMessages length: 1, conversationId: 19:e41a2ef4-3c91-4d41-a2ee-5baa222baafe_38a255f2-793e-4b92-8688-dbef1bfcca7a@unq.gbl.spaces, replyChainId: undefined
2017-11-09T10:49:43.497Z Inf message-list: firstTimeMessageInViewport id: 1510224583489, conversationId: 19:e41a2ef4-3c91-4d41-a2ee-5baa222baafe_38a255f2-793e-4b92-8688-dbef1bfcca7a@unq.gbl.spaces
2017-11-09T10:49:43.497Z Inf message-list: shouldScrollWithUpdates(): isScrolledToBottom = true, isActive = true, isComposingEditOrReply = false, appStateService = Interactive
2017-11-09T10:49:43.497Z Inf message-list: onNewMessage: id 1510224583489, parentMessageId 1510224583489, conversationId 19:e41a2ef4-3c91-4d41-a2ee-5baa222baafe_38a255f2-793e-4b92-8688-dbef1bfcca7a@unq.gbl.spaces, replyChainId undefined, isMessageFromMe = false
2017-11-09T10:49:43.493Z Inf PeopleService: Chat list Mris length - 12
>>>>2017-11-09T10:49:37.276Z Err Failed to get extension definition for app 28:38a255f2-793e-4b92-8688-dbef1bfcca7a
2017-11-09T10:49:33.288Z Inf PeopleService: Chat list Mris length - 12
2017-11-09T10:49:33.271Z War MessageService: Item was resolved but still has an offline action. id: 5998220913171619000
2017-11-09T10:49:33.252Z Inf CustomScrollBar: Stopped SCROLL
2017-11-09T10:49:33.252Z Inf CustomScrollBar: Started SCROLL
2017-11-09T10:49:33.236Z Inf scroll-item-tracker: Init for item id -1
2017-11-09T10:49:33.206Z Inf MessageService: ----createMessage: conversationId: 19:e41a2ef4-3c91-4d41-a2ee-5baa222baafe_38a255f2-793e-4b92-8688-dbef1bfcca7a@unq.gbl.spaces, clientId: 5998220913171619000, replyChainClientId: clientId_8456532771219784000
2017-11-09T10:49:33.144Z Inf message-list: onNewMessage: id -1, parentMessageId clientId_8456532771219784000, conversationId 19:e41a2ef4-3c91-4d41-a2ee-5baa222baafe_38a255f2-793e-4b92-8688-dbef1bfcca7a@unq.gbl.spaces, replyChainId undefined, isMessageFromMe = true
2017-11-09T10:49:29.694Z Inf Telemetry: [TelemetryManagerImpl]: Processing telemetry events took 1 ms for 1 events with total size of 3257 bytes, remaining events - 0
2017-11-09T10:49:13.386Z Inf Telemetry: [TelemetryManagerImpl]: Processing telemetry events took 1 ms for 2 events with total size of 4701 bytes, remaining events - 0
Advertisements