From 914bbfd8d8f1829025277a53a6b641de0ed7d270 Mon Sep 17 00:00:00 2001 From: "Jonathan Spruance (Insight Global)" Date: Tue, 10 Sep 2019 15:37:35 -0700 Subject: [PATCH 1/6] Adding sample files --- .../custom-recognizer-example/.eslintrc.js | 14 +++++ .../Node/custom-recognizer-example/.gitignore | 3 + .../Node/custom-recognizer-example/README.md | 61 +++++++++++++++++++ .../Node/custom-recognizer-example/bot.js | 49 +++++++++++++++ .../customRecognizer.js | 44 +++++++++++++ .../Node/custom-recognizer-example/index.js | 51 ++++++++++++++++ .../custom-recognizer-example/package.json | 32 ++++++++++ 7 files changed, 254 insertions(+) create mode 100644 MigrationV3V4/Node/custom-recognizer-example/.eslintrc.js create mode 100644 MigrationV3V4/Node/custom-recognizer-example/.gitignore create mode 100644 MigrationV3V4/Node/custom-recognizer-example/README.md create mode 100644 MigrationV3V4/Node/custom-recognizer-example/bot.js create mode 100644 MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js create mode 100644 MigrationV3V4/Node/custom-recognizer-example/index.js create mode 100644 MigrationV3V4/Node/custom-recognizer-example/package.json diff --git a/MigrationV3V4/Node/custom-recognizer-example/.eslintrc.js b/MigrationV3V4/Node/custom-recognizer-example/.eslintrc.js new file mode 100644 index 0000000000..b4a10dfc6c --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + "extends": "standard", + "rules": { + "semi": [2, "always"], + "indent": [2, 4], + "no-return-await": 0, + "space-before-function-paren": [2, { + "named": "never", + "anonymous": "never", + "asyncArrow": "always" + }], + "template-curly-spacing": [2, "always"] + } +}; \ No newline at end of file diff --git a/MigrationV3V4/Node/custom-recognizer-example/.gitignore b/MigrationV3V4/Node/custom-recognizer-example/.gitignore new file mode 100644 index 0000000000..221d37f803 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/.gitignore @@ -0,0 +1,3 @@ +node_modules +lib +.env diff --git a/MigrationV3V4/Node/custom-recognizer-example/README.md b/MigrationV3V4/Node/custom-recognizer-example/README.md new file mode 100644 index 0000000000..4211ec6d40 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/README.md @@ -0,0 +1,61 @@ +# custom-recognizer-example + +Demonstrate the core capabilities of the Microsoft Bot Framework + +This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back. + +## Prerequisites + +- [Node.js](https://nodejs.org) version 10.14.1 or higher + + ```bash + # determine node version + node --version + ``` + +## To run the bot + +- Install modules + + ```bash + npm install + ``` + +- Start the bot + + ```bash + npm start + ``` + +## Testing the bot using Bot Framework Emulator + +[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. + +- Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases) + +### Connect to the bot using Bot Framework Emulator + +- Launch Bot Framework Emulator +- File -> Open Bot +- Enter a Bot URL of `http://localhost:3978/api/messages` + +## Deploy the bot to Azure + +To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions. + + +## Further reading + +- [Bot Framework Documentation](https://docs.botframework.com) +- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) +- [Dialogs](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0) +- [Gathering Input Using Prompts](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0) +- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0) +- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) +- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) +- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest) +- [Azure Portal](https://portal.azure.com) +- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/) +- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0) +- [Restify](https://www.npmjs.com/package/restify) +- [dotenv](https://www.npmjs.com/package/dotenv) diff --git a/MigrationV3V4/Node/custom-recognizer-example/bot.js b/MigrationV3V4/Node/custom-recognizer-example/bot.js new file mode 100644 index 0000000000..84da02494e --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/bot.js @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +const { ActivityHandler } = require('botbuilder'); +const CustomRecognizer = require('./customRecognizer'); + +class MyBot extends ActivityHandler { + constructor() { + super(); + // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. + this.onMessage(async (context, next) => { + + // pass the response through custom recognizer to detect email addresses + const recognizer = new CustomRecognizer(); + const recognizerResult = recognizer.recognize(context.activity.text); + const intent = recognizer.getTopIntent(recognizerResult); + + // respond according to the top intent provided by the recognizer + switch(intent) { + case 'SendEmail': + await context.sendActivity('Your response includes an email address. Would you like to contact us by email?'); + case 'SearchForEmailAddress': + await context.sendActivity('Your response includes an email address. Would you like to search for an email address?'); + case 'OpenEmailAccount': + await context.sendActivity('Your response includes an email address. Would you like to open an email account?'); + default: + await context.sendActivity('Your response includes an email address.'); + } + + await context.sendActivity(`You said '${ context.activity.text }'`); + + // By calling next() you ensure that the next BotHandler is run. + await next(); + }); + + 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) { + await context.sendActivity('Hello and welcome!'); + } + } + // By calling next() you ensure that the next BotHandler is run. + await next(); + }); + } +} + +module.exports.MyBot = MyBot; diff --git a/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js b/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js new file mode 100644 index 0000000000..7b5efc5f35 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js @@ -0,0 +1,44 @@ +class CustomRecognizer { + + construction() {} + + recognize(text) { + const recognizerResult = { + text: text, + intents: [] + }; + const regex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; + const isEmailAddress = regex.test(text); + if (isEmailAddress) { + recognizerResult.intents.push({ + SearchForEmailAddress: { + score: 60 + } + }); + recognizerResult.intents.push({ + SendEmail: { + score: 90 + } + }); + recognizerResult.intents.push({ + OpenEmailAccount: { + score: 30 + } + }); + } + return recognizerResult; + } + + getTopIntent(recognizerResult) { + const sortedResults = recognizerResult.intents.sort((a, b) => { + const keyA = Object.keys(a); + const keyB = Object.keys(b); + return (a[keyA].score > b[keyB].score) ? -1 : 1; + }); + const topIntent = Object.keys(sortedResults[0])[0]; + return topIntent; + } + +} + +module.exports = CustomRecognizer; \ No newline at end of file diff --git a/MigrationV3V4/Node/custom-recognizer-example/index.js b/MigrationV3V4/Node/custom-recognizer-example/index.js new file mode 100644 index 0000000000..235f586055 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/index.js @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +const dotenv = require('dotenv'); +const path = require('path'); +const restify = require('restify'); + +// Import required bot services. +// See https://aka.ms/bot-services to learn more about the different parts of a bot. +const { BotFrameworkAdapter } = require('botbuilder'); + +// This bot's main dialog. +const { MyBot } = require('./bot'); + +// Import required bot configuration. +const ENV_FILE = path.join(__dirname, '.env'); +dotenv.config({ path: ENV_FILE }); + +// Create HTTP server +const server = restify.createServer(); +server.listen(process.env.port || process.env.PORT || 3978, () => { + console.log(`\n${ server.name } listening to ${ server.url }`); + console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`); + console.log(`\nTo test your bot, see: https://aka.ms/debug-with-emulator`); +}); + +// Create adapter. +// See https://aka.ms/about-bot-adapter to learn more about how bots work. +const adapter = new BotFrameworkAdapter({ + appId: process.env.MicrosoftAppId, + appPassword: process.env.MicrosoftAppPassword +}); + +// Catch-all for errors. +adapter.onTurnError = async (context, error) => { + // This check writes out errors to console log .vs. app insights. + console.error(`\n [onTurnError]: ${ error }`); + // Send a message to the user + await context.sendActivity(`Oops. Something went wrong!`); +}; + +// Create the main dialog. +const myBot = new MyBot(); + +// Listen for incoming requests. +server.post('/api/messages', (req, res) => { + adapter.processActivity(req, res, async (context) => { + // Route to main dialog. + await myBot.run(context); + }); +}); diff --git a/MigrationV3V4/Node/custom-recognizer-example/package.json b/MigrationV3V4/Node/custom-recognizer-example/package.json new file mode 100644 index 0000000000..3ea8b1eac7 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/package.json @@ -0,0 +1,32 @@ +{ + "name": "custom-recognizer-example", + "version": "1.0.0", + "description": "Demonstrate the core capabilities of the Microsoft Bot Framework", + "author": "Generated using Microsoft Bot Builder Yeoman generator v4.5.0", + "license": "MIT", + "main": "index.js", + "scripts": { + "start": "node ./index.js", + "watch": "nodemon ./index.js", + "lint": "eslint .", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com" + }, + "dependencies": { + "botbuilder": "~4.5.1", + "dotenv": "~8.0.0", + "restify": "~8.3.3" + }, + "devDependencies": { + "eslint": "^6.0.1", + "eslint-config-standard": "^13.0.1", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-node": "^9.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.0", + "nodemon": "~1.19.1" + } +} From 5619dd0d2b01ac07fe6f8dc454110176e8b65cf8 Mon Sep 17 00:00:00 2001 From: "Jonathan Spruance (Insight Global)" Date: Tue, 10 Sep 2019 15:45:26 -0700 Subject: [PATCH 2/6] Update readme --- MigrationV3V4/Node/custom-recognizer-example/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MigrationV3V4/Node/custom-recognizer-example/README.md b/MigrationV3V4/Node/custom-recognizer-example/README.md index 4211ec6d40..a491153fe7 100644 --- a/MigrationV3V4/Node/custom-recognizer-example/README.md +++ b/MigrationV3V4/Node/custom-recognizer-example/README.md @@ -1,6 +1,6 @@ # custom-recognizer-example -Demonstrate the core capabilities of the Microsoft Bot Framework +This sample bot demonstrates how to implement a custom recognizer. The recognizer code (customRecognizer.js) uses a regex expression to detect an email address in the user input and suggests possible intents if present. Then the bot uses the top intent returned from the recognizer to respond accordingly. This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back. From a0175b8e0d265f50842792c76145f9e26bc01a74 Mon Sep 17 00:00:00 2001 From: "Jonathan Spruance (Insight Global)" Date: Tue, 10 Sep 2019 16:04:07 -0700 Subject: [PATCH 3/6] Remove constructor, no longer needed --- .../Node/custom-recognizer-example/customRecognizer.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js b/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js index 7b5efc5f35..2616ef3838 100644 --- a/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js +++ b/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js @@ -1,7 +1,5 @@ class CustomRecognizer { - construction() {} - recognize(text) { const recognizerResult = { text: text, @@ -41,4 +39,4 @@ class CustomRecognizer { } -module.exports = CustomRecognizer; \ No newline at end of file +module.exports = CustomRecognizer; From edbfea432d09163b34a2b99023d141dfe4b64055 Mon Sep 17 00:00:00 2001 From: "Jonathan Spruance (Insight Global)" Date: Tue, 10 Sep 2019 16:14:22 -0700 Subject: [PATCH 4/6] Adding null check for intents array before running switch statement --- .../Node/custom-recognizer-example/bot.js | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/MigrationV3V4/Node/custom-recognizer-example/bot.js b/MigrationV3V4/Node/custom-recognizer-example/bot.js index 84da02494e..4acf3142cb 100644 --- a/MigrationV3V4/Node/custom-recognizer-example/bot.js +++ b/MigrationV3V4/Node/custom-recognizer-example/bot.js @@ -13,18 +13,23 @@ class MyBot extends ActivityHandler { // pass the response through custom recognizer to detect email addresses const recognizer = new CustomRecognizer(); const recognizerResult = recognizer.recognize(context.activity.text); - const intent = recognizer.getTopIntent(recognizerResult); + const intent = recognizerResult.intents.length > 0 ? recognizer.getTopIntent(recognizerResult) : ''; // respond according to the top intent provided by the recognizer - switch(intent) { - case 'SendEmail': - await context.sendActivity('Your response includes an email address. Would you like to contact us by email?'); - case 'SearchForEmailAddress': - await context.sendActivity('Your response includes an email address. Would you like to search for an email address?'); - case 'OpenEmailAccount': - await context.sendActivity('Your response includes an email address. Would you like to open an email account?'); - default: - await context.sendActivity('Your response includes an email address.'); + if (intent) { + switch(intent) { + case 'SendEmail': + await context.sendActivity('Your response includes an email address. Would you like to contact us by email?'); + break; + case 'SearchForEmailAddress': + await context.sendActivity('Your response includes an email address. Would you like to search for an email address?'); + break; + case 'OpenEmailAccount': + await context.sendActivity('Your response includes an email address. Would you like to open an email account?'); + break; + default: + await context.sendActivity('Your response includes an email address.'); + } } await context.sendActivity(`You said '${ context.activity.text }'`); From 924f847bb9067111f4a5fb6259e68abd2f4f3cd6 Mon Sep 17 00:00:00 2001 From: "Jonathan Spruance (Insight Global)" Date: Tue, 10 Sep 2019 16:15:53 -0700 Subject: [PATCH 5/6] Remove unnecessary if --- .../Node/custom-recognizer-example/bot.js | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/MigrationV3V4/Node/custom-recognizer-example/bot.js b/MigrationV3V4/Node/custom-recognizer-example/bot.js index 4acf3142cb..3e7b15a505 100644 --- a/MigrationV3V4/Node/custom-recognizer-example/bot.js +++ b/MigrationV3V4/Node/custom-recognizer-example/bot.js @@ -16,20 +16,18 @@ class MyBot extends ActivityHandler { const intent = recognizerResult.intents.length > 0 ? recognizer.getTopIntent(recognizerResult) : ''; // respond according to the top intent provided by the recognizer - if (intent) { - switch(intent) { - case 'SendEmail': - await context.sendActivity('Your response includes an email address. Would you like to contact us by email?'); - break; - case 'SearchForEmailAddress': - await context.sendActivity('Your response includes an email address. Would you like to search for an email address?'); - break; - case 'OpenEmailAccount': - await context.sendActivity('Your response includes an email address. Would you like to open an email account?'); - break; - default: - await context.sendActivity('Your response includes an email address.'); - } + switch(intent) { + case 'SendEmail': + await context.sendActivity('Your response includes an email address. Would you like to contact us by email?'); + break; + case 'SearchForEmailAddress': + await context.sendActivity('Your response includes an email address. Would you like to search for an email address?'); + break; + case 'OpenEmailAccount': + await context.sendActivity('Your response includes an email address. Would you like to open an email account?'); + break; + default: + await context.sendActivity('Your response includes an email address.'); } await context.sendActivity(`You said '${ context.activity.text }'`); From 69225da61ddbd583aed0162240b1289206a30b60 Mon Sep 17 00:00:00 2001 From: "Jonathan Spruance (Insight Global)" Date: Mon, 28 Oct 2019 15:28:06 -0700 Subject: [PATCH 6/6] Only mention email address if user had input a valid email address --- .../Node/custom-recognizer-example/bot.js | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/MigrationV3V4/Node/custom-recognizer-example/bot.js b/MigrationV3V4/Node/custom-recognizer-example/bot.js index 3e7b15a505..fd31a96624 100644 --- a/MigrationV3V4/Node/custom-recognizer-example/bot.js +++ b/MigrationV3V4/Node/custom-recognizer-example/bot.js @@ -16,20 +16,21 @@ class MyBot extends ActivityHandler { const intent = recognizerResult.intents.length > 0 ? recognizer.getTopIntent(recognizerResult) : ''; // respond according to the top intent provided by the recognizer - switch(intent) { - case 'SendEmail': - await context.sendActivity('Your response includes an email address. Would you like to contact us by email?'); - break; - case 'SearchForEmailAddress': - await context.sendActivity('Your response includes an email address. Would you like to search for an email address?'); - break; - case 'OpenEmailAccount': - await context.sendActivity('Your response includes an email address. Would you like to open an email account?'); - break; - default: - await context.sendActivity('Your response includes an email address.'); + if (intent) { + switch(intent) { + case 'SendEmail': + await context.sendActivity('Your response includes an email address. Would you like to contact us by email?'); + break; + case 'SearchForEmailAddress': + await context.sendActivity('Your response includes an email address. Would you like to search for an email address?'); + break; + case 'OpenEmailAccount': + await context.sendActivity('Your response includes an email address. Would you like to open an email account?'); + break; + default: + await context.sendActivity('Your response includes an email address.'); + } } - await context.sendActivity(`You said '${ context.activity.text }'`); // By calling next() you ensure that the next BotHandler is run.