Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update sample 21 to align with core bot #2219

Merged
merged 5 commits into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,24 @@ const { DialogBot } = require('./dialogBot');
const WelcomeCard = require('./resources/welcomeCard.json');

class DialogAndWelcomeBot extends DialogBot {
/**
* @param {ConversationState} conversationState
* @param {UserState} userState
* @param {Dialog} dialog
*/
constructor(conversationState, userState, dialog) {
super(conversationState, userState, dialog);

this.onMembersAdded(async (turnContext, next) => {
const membersAdded = turnContext.activity.membersAdded;

for (let pos = 0; pos < membersAdded.length; pos++) {
if (membersAdded[pos].id !== turnContext.activity.recipient.id) {
this.onMembersAdded(async (context, next) => {
const membersAdded = context.activity.membersAdded;
for (let cnt = 0; cnt < membersAdded.length; cnt++) {
if (membersAdded[cnt].id !== context.activity.recipient.id) {
const welcomeCard = CardFactory.adaptiveCard(WelcomeCard);

await turnContext.sendActivity({ attachments: [welcomeCard] });
await dialog.run(turnContext, conversationState.createProperty('DialogState'));
await context.sendActivity({ attachments: [welcomeCard] });
await dialog.run(context, conversationState.createProperty('DialogState'));
}
}
// Ensure next BotHandler is executed.

// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
}

module.exports.DialogAndWelcomeBot = DialogAndWelcomeBot;

40 changes: 17 additions & 23 deletions samples/javascript_nodejs/21.corebot-app-insights/bots/dialogBot.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,35 @@ class DialogBot extends ActivityHandler {
*/
constructor(conversationState, userState, dialog) {
super();

if (!conversationState) { throw new Error('[DialogBot]: Missing conversationState parameter.'); }
if (!userState) { throw new Error('[DialogBot]: Missing userState parameter.'); }
if (!dialog) { throw new Error('[DialogBot]: Missing dialog parameter.'); }
if (!conversationState) throw new Error('[DialogBot]: Missing parameter. conversationState is required');
if (!userState) throw new Error('[DialogBot]: Missing parameter. userState is required');
if (!dialog) throw new Error('[DialogBot]: Missing parameter. dialog is required');

this.conversationState = conversationState;
this.userState = userState;
this.dialog = dialog;
this.dialogState = this.conversationState.createProperty('DialogState');

this.onTurn(async (turnContext, next) => {
await this.conversationState.saveChanges(turnContext, false);
await this.userState.saveChanges(turnContext, false);

// Ensure next BotHandler is executed.
await next();
});

this.onMessage(async (turnContext, next) => {
this.onMessage(async (context, next) => {
console.log('Running dialog with Message Activity.');
const dialogState = this.conversationState.createProperty('DialogState');

await this.dialog.run(turnContext, dialogState);
// Run the Dialog with the new message Activity.
await this.dialog.run(context, this.dialogState);

// Ensure next BotHandler is executed.
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}

this.onDialog(async (context, next) => {
// Save any state changes. The load happened during the execution of the Dialog.
await this.conversationState.saveChanges(context, false);
await this.userState.saveChanges(context, false);
/**
* Override the ActivityHandler.run() method to save state changes after the bot logic completes.
*/
async run(context) {
await super.run(context);

// By calling next() you ensure that the next BotHandler is run.
await next();
});
// Save any state changes. The load happened during the execution of the Dialog.
await this.conversationState.saveChanges(context, false);
await this.userState.saveChanges(context, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Image",
"url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
"size": "stretch"
},
{
"type": "TextBlock",
"spacing": "medium",
"size": "default",
"weight": "bolder",
"text": "Welcome to Bot Framework!",
"wrap": true,
"maxLines": 0
},
{
"type": "TextBlock",
"size": "default",
"isSubtle": "true",
"text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
"wrap": true,
"maxLines": 0
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Get an overview",
"url": "https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0"
},
{
"type": "Action.OpenUrl",
"title": "Ask a question",
"url": "https://stackoverflow.com/questions/tagged/botframework"
},
{
"type": "Action.OpenUrl",
"title": "Learn how to deploy",
"url": "https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
}
]
}
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Image",
"url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
"size": "stretch"
},
{
"type": "TextBlock",
"spacing": "medium",
"size": "default",
"weight": "bolder",
"text": "Welcome to Bot Framework!",
"wrap": true,
"maxLines": 0
},
{
"type": "TextBlock",
"size": "default",
"isSubtle": true,
"text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
"wrap": true,
"maxLines": 0
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Get an overview",
"url": "https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0"
},
{
"type": "Action.OpenUrl",
"title": "Ask a question",
"url": "https://stackoverflow.com/questions/tagged/botframework"
},
{
"type": "Action.OpenUrl",
"title": "Learn how to deploy",
"url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ class CancelAndHelpDialog extends ComponentDialog {

switch (text) {
case 'help':
case '?':
case '?': {
const helpMessageText = 'Show help here';
await innerDc.context.sendActivity(helpMessageText, helpMessageText, InputHints.ExpectingInput);
return { status: DialogTurnStatus.waiting };
}
case 'cancel':
case 'quit':
case 'quit': {
const cancelMessageText = 'Cancelling...';
await innerDc.context.sendActivity(cancelMessageText, cancelMessageText, InputHints.IgnoringInput);
return await innerDc.cancelAllDialogs();
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@
const { LuisRecognizer } = require('botbuilder-ai');

class FlightBookingRecognizer {
constructor(config) {
constructor(config, telemetryClient) {
const luisIsConfigured = config && config.applicationId && config.endpointKey && config.endpoint;
if (luisIsConfigured) {
this.recognizer = new LuisRecognizer(config, {}, true);
// Set the recognizer options depending on which endpoint version you want to use e.g v2 or v3.
// More details can be found in https://docs.microsoft.com/azure/cognitive-services/luis/luis-migration-api-v3
const recognizerOptions = {
apiVersion: 'v3',
telemetryClient: telemetryClient
};

this.recognizer = new LuisRecognizer(config, recognizerOptions);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class MainDialog extends ComponentDialog {
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt)
const luisResult = await this.luisRecognizer.executeLuisQuery(stepContext.context);
switch (LuisRecognizer.topIntent(luisResult)) {
case 'BookFlight':
case 'BookFlight': {
// Extract the values for the composite entities from the LUIS result.
const fromEntities = this.luisRecognizer.getFromEntities(luisResult);
const toEntities = this.luisRecognizer.getToEntities(luisResult);
Expand All @@ -95,23 +95,28 @@ class MainDialog extends ComponentDialog {

// Run the BookingDialog passing in whatever details we have from the LUIS call, it will fill out the remainder.
return await stepContext.beginDialog('bookingDialog', bookingDetails);
case 'GetWeather':
}

case 'GetWeather': {
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
const getWeatherMessageText = 'TODO: get weather flow here';
await stepContext.context.sendActivity(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
break;
default:
}

default: {
// Catch all for unhandled intents
const didntUnderstandMessageText = `Sorry, I didn't get that. Please try asking in a different way (intent was ${ LuisRecognizer.topIntent(luisResult) })`;
await stepContext.context.sendActivity(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
}
}

return await stepContext.next();
}

/**
* Shows a warning if the requested From or To cities are recognized as entities but they are not in the Airport entity list.
* In some cases LUIS will recognize the From and To composite entities as valid cities but the From and To Airport values
* In some cases LUIS will recognize the From and To composite entities as a valid cities but the From and To Airport values
* will be empty if those entity values can't be mapped to a canonical item in the Airport.
*/
async showWarningForUnsupportedCities(context, fromEntities, toEntities) {
Expand Down
63 changes: 48 additions & 15 deletions samples/javascript_nodejs/21.corebot-app-insights/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const { FlightBookingRecognizer } = require('./dialogs/flightBookingRecognizer')
// This bot's main dialog.
const { DialogAndWelcomeBot } = require('./bots/dialogAndWelcomeBot');
const { MainDialog } = require('./dialogs/mainDialog');

// the bot's booking dialog
const { BookingDialog } = require('./dialogs/bookingDialog');
const BOOKING_DIALOG = 'bookingDialog';

Expand All @@ -33,22 +35,36 @@ const adapter = new BotFrameworkAdapter({
});

// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to console log
const onTurnErrorHandler = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
// NOTE: In production environment, you should consider logging this to Azure
// application insights.
console.error(`\n [onTurnError]: ${ error }`);
console.error(`\n [onTurnError] unhandled error: ${ error }`);

// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);

// Send a message to the user
const onTurnErrorMessage = 'Sorry, it looks like something went wrong!';
let onTurnErrorMessage = 'The bot encountered an error or bug.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
onTurnErrorMessage = 'To continue to run this bot, please fix the bot source code.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
// Clear out state
await conversationState.delete(context);
};

// Set the onTurnError for the singleton BotFrameworkAdapter.
adapter.onTurnError = onTurnErrorHandler;

// Add telemetry middleware to the adapter middleware pipeline
var telemetryClient = getTelemetryClient(process.env.InstrumentationKey);
var telemetryLoggerMiddleware = new TelemetryLoggerMiddleware(telemetryClient, true);
var initializerMiddleware = new TelemetryInitializerMiddleware(telemetryLoggerMiddleware, true);
var telemetryLoggerMiddleware = new TelemetryLoggerMiddleware(telemetryClient);
var initializerMiddleware = new TelemetryInitializerMiddleware(telemetryLoggerMiddleware);
adapter.use(initializerMiddleware);

// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage.
Expand All @@ -65,25 +81,21 @@ const userState = new UserState(memoryStorage);
const { LuisAppId, LuisAPIKey, LuisAPIHostName } = process.env;
const luisConfig = { applicationId: LuisAppId, endpointKey: LuisAPIKey, endpoint: `https://${ LuisAPIHostName }` };

const luisRecognizer = new FlightBookingRecognizer(luisConfig);
const luisRecognizer = new FlightBookingRecognizer(luisConfig, telemetryClient);

// Create the main dialog.
const bookingDialog = new BookingDialog();
const bookingDialog = new BookingDialog(BOOKING_DIALOG);
const dialog = new MainDialog(luisRecognizer, bookingDialog);
dialog.telemetryClient = telemetryClient;

const bot = new DialogAndWelcomeBot(conversationState, userState, dialog);

dialog.telemetryClient = telemetryClient;

// Create HTTP server
const server = restify.createServer();

// Enable the Application Insights middleware, which helps correlate all activity
// based on the incoming request.
server.use(restify.plugins.bodyParser());

server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});

// Listen for incoming activities and route them to your bot main dialog.
Expand All @@ -95,10 +107,31 @@ server.post('/api/messages', (req, res) => {
});
});

// Enable the Application Insights middleware, which helps correlate all activity
// based on the incoming request.
server.use(restify.plugins.bodyParser());
WashingtonKayaker marked this conversation as resolved.
Show resolved Hide resolved

// Creates a new TelemetryClient based on a instrumentation key
function getTelemetryClient(instrumentationKey) {
if (instrumentationKey) {
return new ApplicationInsightsTelemetryClient(instrumentationKey);
}
return new NullTelemetryClient();
}

// Listen for Upgrade requests for Streaming.
server.on('upgrade', (req, socket, head) => {
// Create an adapter scoped to this WebSocket connection to allow storing session data.
const streamingAdapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
// Set onTurnError for the BotFrameworkAdapter created for each connection.
streamingAdapter.onTurnError = onTurnErrorHandler;

streamingAdapter.useWebSocket(req, socket, head, async (context) => {
// After connecting via WebSocket, run this logic for every request sent over
// the WebSocket connection.
await bot.run(context);
});
});