From 02bce124c9bca765d10c38d0ba3dffef567f2ab5 Mon Sep 17 00:00:00 2001 From: JSpru Date: Mon, 28 Oct 2019 15:42:43 -0700 Subject: [PATCH] custom recognizer sample for nodejs (#1755) * Adding sample files * Update readme * Remove constructor, no longer needed * Adding null check for intents array before running switch statement * Remove unnecessary if * Only mention email address if user had input a valid email address --- .../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 | 53 ++++++++++++++++ .../customRecognizer.js | 42 +++++++++++++ .../Node/custom-recognizer-example/index.js | 51 ++++++++++++++++ .../custom-recognizer-example/package.json | 32 ++++++++++ 7 files changed, 256 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..a491153fe7 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/README.md @@ -0,0 +1,61 @@ +# custom-recognizer-example + +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. + +## 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..fd31a96624 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/bot.js @@ -0,0 +1,53 @@ +// 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 = 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.'); + } + } + 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..2616ef3838 --- /dev/null +++ b/MigrationV3V4/Node/custom-recognizer-example/customRecognizer.js @@ -0,0 +1,42 @@ +class CustomRecognizer { + + 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; 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" + } +}